Просмотр исходного кода

money: switch to new nullifier scheme N = hash(secret, coin)

zero 2 лет назад
Родитель
Сommit
30eb6bba19

+ 15 - 15
src/contract/money/proof/burn_v1.zk

@@ -39,8 +39,21 @@ witness "Burn_V1" {
 
 
 # The definition of our circuit
 # The definition of our circuit
 circuit "Burn_V1" {
 circuit "Burn_V1" {
+    # Derive the public key used in the coin from its secret counterpart
+    pub = ec_mul_base(secret, NULLIFIER_K);
+    # Coin hash
+    coin = poseidon_hash(
+        ec_get_x(pub),
+        ec_get_y(pub),
+        value,
+        token,
+        serial,
+        spend_hook,
+        user_data,
+    );
+
     # Poseidon hash of the nullifier
     # Poseidon hash of the nullifier
-    nullifier = poseidon_hash(secret, serial);
+    nullifier = poseidon_hash(secret, coin);
     constrain_instance(nullifier);
     constrain_instance(nullifier);
 
 
     # Pedersen commitment for coin's value
     # Pedersen commitment for coin's value
@@ -57,25 +70,12 @@ circuit "Burn_V1" {
     token_commit = poseidon_hash(token, token_blind);
     token_commit = poseidon_hash(token, token_blind);
     constrain_instance(token_commit);
     constrain_instance(token_commit);
 
 
-    # Derive the public key used in the coin from its secret counterpart
-    pub = ec_mul_base(secret, NULLIFIER_K);
-    # Coin hash
-    C = poseidon_hash(
-        ec_get_x(pub),
-        ec_get_y(pub),
-        value,
-        token,
-        serial,
-        spend_hook,
-        user_data,
-    );
-
     # With this, we can actually produce a fake coin of value 0
     # With this, we can actually produce a fake coin of value 0
     # above and use it as a dummy input. The inclusion merkle tree
     # above and use it as a dummy input. The inclusion merkle tree
     # has a 0x00 leaf at position 0, so zero_cond will output value
     # has a 0x00 leaf at position 0, so zero_cond will output value
     # iff value is 0 - which is equivalent to 0x00 so that's the
     # iff value is 0 - which is equivalent to 0x00 so that's the
     # trick we use to make the inclusion proof.
     # trick we use to make the inclusion proof.
-    coin_incl = zero_cond(value, C);
+    coin_incl = zero_cond(value, coin);
 
 
     # Merkle root
     # Merkle root
     root = merkle_root(leaf_pos, path, coin_incl);
     root = merkle_root(leaf_pos, path, coin_incl);

+ 14 - 14
src/contract/money/proof/fee_v1.zk

@@ -46,20 +46,6 @@ witness "Fee_V1" {
 }
 }
 
 
 circuit "Fee_V1" {
 circuit "Fee_V1" {
-    nullifier = poseidon_hash(input_secret, input_serial);
-    constrain_instance(nullifier);
-
-    # Pedersen commitment for the input coin value
-    input_vcv = ec_mul_short(input_value, VALUE_COMMIT_VALUE);
-    input_vcr = ec_mul(input_value_blind, VALUE_COMMIT_RANDOM);
-    input_value_commit = ec_add(input_vcv, input_vcr);
-    constrain_instance(ec_get_x(input_value_commit));
-    constrain_instance(ec_get_y(input_value_commit));
-
-    # Commitment for the token ID
-    token_commit = poseidon_hash(token, token_blind);
-    constrain_instance(token_commit);
-
     # Derive the input coin
     # Derive the input coin
     pub = ec_mul_base(input_secret, NULLIFIER_K);
     pub = ec_mul_base(input_secret, NULLIFIER_K);
     pub_x = ec_get_x(pub);
     pub_x = ec_get_x(pub);
@@ -74,6 +60,20 @@ circuit "Fee_V1" {
         input_user_data,
         input_user_data,
     );
     );
 
 
+    nullifier = poseidon_hash(input_secret, input_coin);
+    constrain_instance(nullifier);
+
+    # Pedersen commitment for the input coin value
+    input_vcv = ec_mul_short(input_value, VALUE_COMMIT_VALUE);
+    input_vcr = ec_mul(input_value_blind, VALUE_COMMIT_RANDOM);
+    input_value_commit = ec_add(input_vcv, input_vcr);
+    constrain_instance(ec_get_x(input_value_commit));
+    constrain_instance(ec_get_y(input_value_commit));
+
+    # Commitment for the token ID
+    token_commit = poseidon_hash(token, token_blind);
+    constrain_instance(token_commit);
+
     # Merkle root
     # Merkle root
     root = merkle_root(input_leaf_pos, input_path, input_coin);
     root = merkle_root(input_leaf_pos, input_path, input_coin);
     constrain_instance(root);
     constrain_instance(root);

+ 4 - 2
src/contract/money/src/client/fee_v1.rs

@@ -42,7 +42,7 @@ use rand::rngs::OsRng;
 
 
 use crate::{
 use crate::{
     client::{compute_remainder_blind, Coin, MoneyNote, OwnCoin},
     client::{compute_remainder_blind, Coin, MoneyNote, OwnCoin},
-    model::{CoinAttributes, Input, MoneyFeeParamsV1, Output},
+    model::{CoinAttributes, Input, MoneyFeeParamsV1, NullifierAttributes, Output},
 };
 };
 
 
 /// Append a fee-paying call to the given `TransactionBuilder`.
 /// Append a fee-paying call to the given `TransactionBuilder`.
@@ -264,7 +264,6 @@ fn create_fee_proof(
     token_blind: pallas::Base,
     token_blind: pallas::Base,
     signature_secret: SecretKey,
     signature_secret: SecretKey,
 ) -> Result<(Proof, FeeRevealed)> {
 ) -> Result<(Proof, FeeRevealed)> {
-    let nullifier = Nullifier::from(poseidon_hash([input.secret.inner(), input.note.serial]));
     let public_key = PublicKey::from_secret(input.secret);
     let public_key = PublicKey::from_secret(input.secret);
     let signature_public = PublicKey::from_secret(signature_secret);
     let signature_public = PublicKey::from_secret(signature_secret);
 
 
@@ -279,6 +278,9 @@ fn create_fee_proof(
     }
     }
     .to_coin();
     .to_coin();
 
 
+    let nullifier =
+        NullifierAttributes { secret_key: input.secret, coin: input_coin.clone() }.to_nullifier();
+
     let merkle_root = {
     let merkle_root = {
         let position: u64 = input.leaf_position.into();
         let position: u64 = input.leaf_position.into();
         let mut current = MerkleNode::from(input_coin.inner());
         let mut current = MerkleNode::from(input_coin.inner());

+ 14 - 14
src/contract/money/src/client/stake_v1.rs

@@ -37,7 +37,7 @@ use rand::rngs::OsRng;
 
 
 use crate::{
 use crate::{
     client::{MoneyNote, OwnCoin},
     client::{MoneyNote, OwnCoin},
-    model::{Input, MoneyStakeParamsV1},
+    model::{CoinAttributes, Input, MoneyStakeParamsV1, NullifierAttributes},
 };
 };
 
 
 pub struct MoneyStakeCallDebris {
 pub struct MoneyStakeCallDebris {
@@ -160,25 +160,25 @@ pub fn create_stake_burn_proof(
     user_data_blind: pallas::Base,
     user_data_blind: pallas::Base,
     signature_secret: SecretKey,
     signature_secret: SecretKey,
 ) -> Result<(Proof, MoneyStakeBurnRevealed)> {
 ) -> Result<(Proof, MoneyStakeBurnRevealed)> {
-    let nullifier = Nullifier::from(poseidon_hash([input.secret.inner(), input.note.serial]));
     let public_key = PublicKey::from_secret(input.secret);
     let public_key = PublicKey::from_secret(input.secret);
-    let (pub_x, pub_y) = public_key.xy();
-
     let signature_public = PublicKey::from_secret(signature_secret);
     let signature_public = PublicKey::from_secret(signature_secret);
 
 
-    let coin = poseidon_hash([
-        pub_x,
-        pub_y,
-        pallas::Base::from(input.note.value),
-        input.note.token_id.inner(),
-        input.note.serial,
-        input.note.spend_hook,
-        input.note.user_data,
-    ]);
+    let coin = CoinAttributes {
+        public_key,
+        value: input.note.value,
+        token_id: input.note.token_id,
+        serial: input.note.serial,
+        spend_hook: input.note.spend_hook,
+        user_data: input.note.user_data,
+    }
+    .to_coin();
+
+    let nullifier =
+        NullifierAttributes { secret_key: input.secret, coin: coin.clone() }.to_nullifier();
 
 
     let merkle_root = {
     let merkle_root = {
         let position: u64 = input.leaf_position.into();
         let position: u64 = input.leaf_position.into();
-        let mut current = MerkleNode::from(coin);
+        let mut current = MerkleNode::from(coin.inner());
         for (level, sibling) in input.merkle_path.iter().enumerate() {
         for (level, sibling) in input.merkle_path.iter().enumerate() {
             let level = level as u8;
             let level = level as u8;
             current = if position & (1 << level) == 0 {
             current = if position & (1 << level) == 0 {

+ 5 - 4
src/contract/money/src/client/transfer_v1/proof.rs

@@ -17,7 +17,7 @@
  */
  */
 
 
 use darkfi::{
 use darkfi::{
-    zk::{halo2::Value, Proof, ProvingKey, Witness, ZkCircuit},
+    zk::{export_witness_json, halo2::Value, Proof, ProvingKey, Witness, ZkCircuit},
     zkas::ZkBinary,
     zkas::ZkBinary,
     Result,
     Result,
 };
 };
@@ -33,7 +33,7 @@ use log::debug;
 use rand::rngs::OsRng;
 use rand::rngs::OsRng;
 
 
 use super::{TransferCallInput, TransferCallOutput};
 use super::{TransferCallInput, TransferCallOutput};
-use crate::model::{Coin, CoinAttributes};
+use crate::model::{Coin, CoinAttributes, NullifierAttributes};
 
 
 pub struct TransferMintRevealed {
 pub struct TransferMintRevealed {
     pub coin: Coin,
     pub coin: Coin,
@@ -90,9 +90,7 @@ pub fn create_transfer_burn_proof(
     token_blind: pallas::Base,
     token_blind: pallas::Base,
     signature_secret: SecretKey,
     signature_secret: SecretKey,
 ) -> Result<(Proof, TransferBurnRevealed)> {
 ) -> Result<(Proof, TransferBurnRevealed)> {
-    let nullifier = Nullifier::from(poseidon_hash([input.secret.inner(), input.note.serial]));
     let public_key = PublicKey::from_secret(input.secret);
     let public_key = PublicKey::from_secret(input.secret);
-
     let signature_public = PublicKey::from_secret(signature_secret);
     let signature_public = PublicKey::from_secret(signature_secret);
 
 
     let coin = CoinAttributes {
     let coin = CoinAttributes {
@@ -105,6 +103,9 @@ pub fn create_transfer_burn_proof(
     }
     }
     .to_coin();
     .to_coin();
 
 
+    let nullifier =
+        NullifierAttributes { secret_key: input.secret, coin: coin.clone() }.to_nullifier();
+
     let merkle_root = {
     let merkle_root = {
         let position: u64 = input.leaf_position.into();
         let position: u64 = input.leaf_position.into();
         let mut current = MerkleNode::from(coin.inner());
         let mut current = MerkleNode::from(coin.inner());

+ 22 - 5
src/contract/money/src/model.rs

@@ -19,7 +19,7 @@
 use darkfi_sdk::{
 use darkfi_sdk::{
     crypto::{
     crypto::{
         ecvrf::VrfProof, note::AeadEncryptedNote, pasta_prelude::PrimeField, poseidon_hash,
         ecvrf::VrfProof, note::AeadEncryptedNote, pasta_prelude::PrimeField, poseidon_hash,
-        MerkleNode, Nullifier, PublicKey, TokenId,
+        MerkleNode, Nullifier, PublicKey, SecretKey, TokenId,
     },
     },
     error::ContractError,
     error::ContractError,
     pasta::pallas,
     pasta::pallas,
@@ -56,11 +56,17 @@ impl Coin {
     }
     }
 }
 }
 
 
+use core::str::FromStr;
+darkfi_sdk::fp_from_bs58!(Coin);
+darkfi_sdk::fp_to_bs58!(Coin);
+darkfi_sdk::ty_from_fp!(Coin);
+
 #[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
 #[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
 pub struct CoinAttributes {
 pub struct CoinAttributes {
     pub public_key: PublicKey,
     pub public_key: PublicKey,
     pub value: u64,
     pub value: u64,
     pub token_id: TokenId,
     pub token_id: TokenId,
+    /// Simultaneously blinds the coin and ensures uniqueness
     pub serial: pallas::Base,
     pub serial: pallas::Base,
     pub spend_hook: pallas::Base,
     pub spend_hook: pallas::Base,
     pub user_data: pallas::Base,
     pub user_data: pallas::Base,
@@ -82,10 +88,21 @@ impl CoinAttributes {
     }
     }
 }
 }
 
 
-use core::str::FromStr;
-darkfi_sdk::fp_from_bs58!(Coin);
-darkfi_sdk::fp_to_bs58!(Coin);
-darkfi_sdk::ty_from_fp!(Coin);
+#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
+pub struct NullifierAttributes {
+    /// Secret key for the public key in the coin.
+    /// We need some secret info to avoid revealing which coin this
+    /// nullifier is connected to. We use the secret key for that.
+    pub secret_key: SecretKey,
+    /// The corresponding coin being spent.
+    pub coin: Coin,
+}
+
+impl NullifierAttributes {
+    pub fn to_nullifier(&self) -> Nullifier {
+        Nullifier::from(poseidon_hash([self.secret_key.inner(), self.coin.inner()]))
+    }
+}
 
 
 /// A contract call's clear input
 /// A contract call's clear input
 #[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
 #[derive(Clone, Debug, SerialEncodable, SerialDecodable)]

+ 2 - 2
src/contract/test-harness/src/vks.rs

@@ -50,8 +50,8 @@ use darkfi_serial::{deserialize, serialize};
 use log::debug;
 use log::debug;
 
 
 /// Update this if any circuits are changed
 /// Update this if any circuits are changed
-const VKS_HASH: &str = "8936e8806f8b05af04a5cbdd5dba446a7cb07ea46a9bcd0f2d7db6ff1c28da66";
-const PKS_HASH: &str = "4ca45a982ecc5e1689a95a6f2d341c61efd38a4b1ef6178eb6aecf9b8bf122a1";
+const VKS_HASH: &str = "0df16c8bdfec0f71e5ffca305385ce7ab6f4b9fd7388c04118d9de160712ed18";
+const PKS_HASH: &str = "57a3b7537ce19ea61a27fb10d78768198c95f4453001aae334fceb1626db9046";
 
 
 fn pks_path(typ: &str) -> Result<PathBuf> {
 fn pks_path(typ: &str) -> Result<PathBuf> {
     let output = Command::new("git").arg("rev-parse").arg("--show-toplevel").output()?.stdout;
     let output = Command::new("git").arg("rev-parse").arg("--show-toplevel").output()?.stdout;