فهرست منبع

money_transfer contract: introduce an internal API for fine grained control over the transfer call structure.
The API will change so don't use it just yet.

x 2 سال پیش
والد
کامیت
d9083ffe37

+ 17 - 1
src/contract/dao/src/client/exec.rs

@@ -26,7 +26,7 @@ use log::debug;
 use rand::rngs::OsRng;
 
 use darkfi::{
-    zk::{Proof, ProvingKey, Witness, ZkCircuit},
+    zk::{export_witness_json, Proof, ProvingKey, Witness, ZkCircuit},
     zkas::ZkBinary,
     Result,
 };
@@ -104,6 +104,14 @@ impl DaoExecCall {
             user_spend_hook,
             user_data,
         ]);
+        debug!("created coin {:?}", coin_0);
+        debug!("  proposal_dest_x: {:?}", proposal_dest_x);
+        debug!("  proposal_dest_y: {:?}", proposal_dest_y);
+        debug!("  proposal_amount: {:?}", proposal_amount);
+        debug!("  proposal.token_id: {:?}", self.proposal.token_id.inner());
+        debug!("  user_serial: {:?}", self.user_serial);
+        debug!("  user_spend_hook: {:?}", user_spend_hook);
+        debug!("  user_data: {:?}", user_data);
 
         let coin_1 = poseidon_hash::<7>([
             dao_pub_x,
@@ -114,6 +122,14 @@ impl DaoExecCall {
             self.hook_dao_exec,
             dao_bulla,
         ]);
+        debug!("created coin {:?}", coin_1);
+        debug!("  dao_pub_x: {:?}", dao_pub_x);
+        debug!("  dao_pub_y: {:?}", dao_pub_y);
+        debug!("  change: {:?}", change);
+        debug!("  proposal.token_id: {:?}", self.proposal.token_id.inner());
+        debug!("  dao_serial: {:?}", self.dao_serial);
+        debug!("  hook_dao_exec: {:?}", self.hook_dao_exec);
+        debug!("  dao_bulla: {:?}", dao_bulla);
 
         let yes_vote_commit = pedersen_commitment_u64(self.yes_vote_value, self.yes_vote_blind);
         let yes_vote_commit_coords = yes_vote_commit.to_affine().coordinates().unwrap();

+ 5 - 6
src/contract/money/src/client/genesis_mint_v1.rs

@@ -30,10 +30,7 @@ use rand::rngs::OsRng;
 
 use crate::{
     client::{
-        transfer_v1::{
-            create_transfer_mint_proof, TransactionBuilderClearInputInfo,
-            TransactionBuilderOutputInfo,
-        },
+        transfer_v1::{create_transfer_mint_proof, TransferCallClearInput, TransferCallOutput},
         MoneyNote,
     },
     model::{ClearInput, Coin, MoneyTokenMintParamsV1, Output},
@@ -85,16 +82,18 @@ impl GenesisMintCallBuilder {
         // Only DARK_TOKEN_ID can be minted on genesis slot.
         let token_id = *DARK_TOKEN_ID;
 
-        let input = TransactionBuilderClearInputInfo {
+        let input = TransferCallClearInput {
             value: self.amount,
             token_id,
             signature_secret: self.keypair.secret,
         };
 
-        let output = TransactionBuilderOutputInfo {
+        let output = TransferCallOutput {
             value: self.amount,
             token_id,
             public_key: self.keypair.public,
+            spend_hook: pallas::Base::ZERO,
+            user_data: pallas::Base::ZERO,
         };
 
         // We just create the commitment blinds here. We simply encofce

+ 9 - 7
src/contract/money/src/client/pow_reward_v1.rs

@@ -34,10 +34,7 @@ use rand::rngs::OsRng;
 
 use crate::{
     client::{
-        transfer_v1::{
-            create_transfer_mint_proof, TransactionBuilderClearInputInfo,
-            TransactionBuilderOutputInfo,
-        },
+        transfer_v1::{create_transfer_mint_proof, TransferCallClearInput, TransferCallOutput},
         MoneyNote,
     },
     model::{ClearInput, Coin, MoneyPoWRewardParamsV1, Output},
@@ -97,10 +94,15 @@ impl PoWRewardCallBuilder {
         // Only DARK_TOKEN_ID can be minted as PoW reward.
         let token_id = *DARK_TOKEN_ID;
 
-        let input =
-            TransactionBuilderClearInputInfo { value, token_id, signature_secret: self.secret };
+        let input = TransferCallClearInput { value, token_id, signature_secret: self.secret };
 
-        let output = TransactionBuilderOutputInfo { value, token_id, public_key: self.recipient };
+        let output = TransferCallOutput {
+            value,
+            token_id,
+            public_key: self.recipient,
+            spend_hook: pallas::Base::ZERO,
+            user_data: pallas::Base::ZERO,
+        };
 
         // We just create the commitment blinds here. We simply encofce
         // that the clear input and the anon output have the same commitments.

+ 7 - 5
src/contract/money/src/client/swap_v1.rs

@@ -36,8 +36,8 @@ use rand::rngs::OsRng;
 use crate::{
     client::{
         transfer_v1::{
-            create_transfer_burn_proof, create_transfer_mint_proof, TransactionBuilderInputInfo,
-            TransactionBuilderOutputInfo,
+            create_transfer_burn_proof, create_transfer_mint_proof, TransferCallInput,
+            TransferCallOutput,
         },
         MoneyNote, OwnCoin,
     },
@@ -110,17 +110,20 @@ impl SwapCallBuilder {
         let leaf_position = self.coin.leaf_position;
         let merkle_path = self.tree.witness(leaf_position, 0).unwrap();
 
-        let input = TransactionBuilderInputInfo {
+        let input = TransferCallInput {
             leaf_position,
             merkle_path,
             secret: self.coin.secret,
             note: self.coin.note.clone(),
+            user_data_blind: self.user_data_blind_send,
         };
 
-        let output = TransactionBuilderOutputInfo {
+        let output = TransferCallOutput {
             value: self.value_recv,
             token_id: self.token_id_recv,
             public_key: self.pubkey,
+            spend_hook: pallas::Base::ZERO,
+            user_data: pallas::Base::ZERO,
         };
 
         // Now we fill this with necessary stuff
@@ -138,7 +141,6 @@ impl SwapCallBuilder {
             &input,
             self.value_blinds[0],
             self.token_blinds[0],
-            self.user_data_blind_send,
             signature_secret,
         )?;
 

+ 6 - 4
src/contract/money/src/client/token_mint_v1.rs

@@ -33,7 +33,7 @@ use rand::rngs::OsRng;
 
 use crate::{
     client::{
-        transfer_v1::{TransactionBuilderClearInputInfo, TransactionBuilderOutputInfo},
+        transfer_v1::{TransferCallClearInput, TransferCallOutput},
         MoneyNote,
     },
     model::{ClearInput, Coin, MoneyTokenMintParamsV1, Output},
@@ -98,16 +98,18 @@ impl TokenMintCallBuilder {
         // The mint authority pubkey is used to derive the token ID.
         let token_id = TokenId::derive(self.mint_authority.secret);
 
-        let input = TransactionBuilderClearInputInfo {
+        let input = TransferCallClearInput {
             value: self.amount,
             token_id,
             signature_secret: self.mint_authority.secret,
         };
 
-        let output = TransactionBuilderOutputInfo {
+        let output = TransferCallOutput {
             value: self.amount,
             token_id,
             public_key: self.recipient,
+            spend_hook: pallas::Base::ZERO,
+            user_data: pallas::Base::ZERO,
         };
 
         // We just create the pedersen commitment blinds here. We simply
@@ -170,7 +172,7 @@ impl TokenMintCallBuilder {
 pub fn create_token_mint_proof(
     zkbin: &ZkBinary,
     pk: &ProvingKey,
-    output: &TransactionBuilderOutputInfo,
+    output: &TransferCallOutput,
     mint_authority: &Keypair,
     value_blind: pallas::Scalar,
     token_blind: pallas::Base,

+ 248 - 137
src/contract/money/src/client/transfer_v1.rs

@@ -40,6 +40,7 @@ use crate::{
     model::{ClearInput, Coin, Input, MoneyTransferParamsV1, Output},
 };
 
+// TODO: split this into secret and non-secret squads
 /// Output metadata claimed from building a `Money::Transfer` call
 pub struct TransferCallDebris {
     /// The parameters for `Money::Transfer` respective to this call
@@ -49,9 +50,34 @@ pub struct TransferCallDebris {
     /// The ephemeral secret keys created for signing
     pub signature_secrets: Vec<SecretKey>,
     /// The coins that have been spent in this builder
+    // TODO: this is duplicate field, use params.inputs instead
     pub spent_coins: Vec<OwnCoin>,
     /// The coins that have been minted in this builder
+    // TODO: this is duplicate field, use params.outputs instead
     pub minted_coins: Vec<OwnCoin>,
+
+    // TODO: should we maybe pass these into the builder explicitly?
+    /// The value blinds created for the inputs
+    pub input_value_blinds: Vec<pallas::Scalar>,
+    /// The value blinds created for the outputs
+    pub output_value_blinds: Vec<pallas::Scalar>,
+}
+
+impl TransferCallDebris {
+    // TODO: implement these methods
+    // fn spent_coins()
+    // fn minted_coins()
+}
+
+struct TransferCallSecrets {
+    /// The ZK proofs created in this builder
+    pub proofs: Vec<Proof>,
+    /// The ephemeral secret keys created for signing
+    pub signature_secrets: Vec<SecretKey>,
+
+    pub output_notes: Vec<MoneyNote>,
+
+    // TODO: should we maybe pass these into the builder explicitly?
     /// The value blinds created for the inputs
     pub input_value_blinds: Vec<pallas::Scalar>,
     /// The value blinds created for the outputs
@@ -105,23 +131,194 @@ impl TransferBurnRevealed {
     }
 }
 
-pub struct TransactionBuilderClearInputInfo {
+// TODO: these names are wrong, should be Transfer..., also drop Info suffix
+pub struct TransferCallClearInput {
     pub value: u64,
     pub token_id: TokenId,
     pub signature_secret: SecretKey,
 }
 
-pub struct TransactionBuilderInputInfo {
+pub struct TransferCallInput {
     pub leaf_position: bridgetree::Position,
     pub merkle_path: Vec<MerkleNode>,
     pub secret: SecretKey,
     pub note: MoneyNote,
+    // In the DAO all inputs must have the same user_data_enc and use the same blind
+    // So support allowing the user to set their own blind.
+    pub user_data_blind: pallas::Base,
 }
 
-pub struct TransactionBuilderOutputInfo {
+pub struct TransferCallOutput {
     pub value: u64,
     pub token_id: TokenId,
     pub public_key: PublicKey,
+    pub spend_hook: pallas::Base,
+    pub user_data: pallas::Base,
+}
+
+/// Struct holding necessary information to build a `Money::TransferV1` contract call.
+pub struct TransferCallBuilder2 {
+    /// Clear inputs
+    pub clear_inputs: Vec<TransferCallClearInput>,
+    /// Anonymous inputs
+    pub inputs: Vec<TransferCallInput>,
+    /// Anonymous outputs
+    pub outputs: Vec<TransferCallOutput>,
+    /// `Mint_V1` zkas circuit ZkBinary
+    pub mint_zkbin: ZkBinary,
+    /// Proving key for the `Mint_V1` zk circuit
+    pub mint_pk: ProvingKey,
+    /// `Burn_V1` zkas circuit ZkBinary
+    pub burn_zkbin: ZkBinary,
+    /// Proving key for the `Burn_V1` zk circuit
+    pub burn_pk: ProvingKey,
+}
+
+impl TransferCallBuilder2 {
+    fn compute_remainder_blind(
+        clear_inputs: &[ClearInput],
+        input_blinds: &[pallas::Scalar],
+        output_blinds: &[pallas::Scalar],
+    ) -> pallas::Scalar {
+        let mut total = pallas::Scalar::zero();
+
+        for input in clear_inputs {
+            total += input.value_blind;
+        }
+
+        for input_blind in input_blinds {
+            total += input_blind;
+        }
+
+        for output_blind in output_blinds {
+            total -= output_blind;
+        }
+
+        total
+    }
+
+    fn build(self) -> Result<(MoneyTransferParamsV1, TransferCallSecrets)> {
+        debug!("Building Money::TransferV1 contract call");
+        assert!(self.clear_inputs.len() + self.inputs.len() > 0);
+
+        let mut params =
+            MoneyTransferParamsV1 { clear_inputs: vec![], inputs: vec![], outputs: vec![] };
+        let mut signature_secrets = vec![];
+        let mut proofs = vec![];
+
+        let token_blind = pallas::Base::random(&mut OsRng);
+        debug!("Building clear inputs");
+        for input in self.clear_inputs {
+            let signature_public = PublicKey::from_secret(input.signature_secret);
+            let value_blind = pallas::Scalar::random(&mut OsRng);
+
+            params.clear_inputs.push(ClearInput {
+                value: input.value,
+                token_id: input.token_id,
+                value_blind,
+                token_blind,
+                signature_public,
+            });
+        }
+
+        let mut input_blinds = vec![];
+        let mut output_blinds = vec![];
+
+        debug!("Building anonymous inputs");
+        for (i, input) in self.inputs.iter().enumerate() {
+            let value_blind = pallas::Scalar::random(&mut OsRng);
+            input_blinds.push(value_blind);
+
+            let signature_secret = SecretKey::random(&mut OsRng);
+            signature_secrets.push(signature_secret);
+
+            info!("Creating transfer burn proof for input {}", i);
+            let (proof, public_inputs) = create_transfer_burn_proof(
+                &self.burn_zkbin,
+                &self.burn_pk,
+                input,
+                value_blind,
+                token_blind,
+                signature_secret,
+            )?;
+
+            params.inputs.push(Input {
+                value_commit: public_inputs.value_commit,
+                token_commit: public_inputs.token_commit,
+                nullifier: public_inputs.nullifier,
+                merkle_root: public_inputs.merkle_root,
+                spend_hook: public_inputs.spend_hook,
+                user_data_enc: public_inputs.user_data_enc,
+                signature_public: public_inputs.signature_public,
+            });
+
+            proofs.push(proof);
+        }
+
+        // This value_blind calc assumes there will always be at least a single output
+        assert!(!self.outputs.is_empty());
+
+        let mut output_notes = vec![];
+
+        for (i, output) in self.outputs.iter().enumerate() {
+            let value_blind = if i == self.outputs.len() - 1 {
+                Self::compute_remainder_blind(&params.clear_inputs, &input_blinds, &output_blinds)
+            } else {
+                pallas::Scalar::random(&mut OsRng)
+            };
+
+            output_blinds.push(value_blind);
+
+            let serial = pallas::Base::random(&mut OsRng);
+
+            info!("Creating transfer mint proof for output {}", i);
+            let (proof, public_inputs) = create_transfer_mint_proof(
+                &self.mint_zkbin,
+                &self.mint_pk,
+                output,
+                value_blind,
+                token_blind,
+                serial,
+                output.spend_hook,
+                output.user_data,
+            )?;
+
+            proofs.push(proof);
+
+            // Encrypted note
+            let note = MoneyNote {
+                serial,
+                value: output.value,
+                token_id: output.token_id,
+                spend_hook: output.spend_hook,
+                user_data: output.user_data,
+                value_blind,
+                token_blind,
+                memo: vec![],
+            };
+
+            let encrypted_note = AeadEncryptedNote::encrypt(&note, &output.public_key, &mut OsRng)?;
+            output_notes.push(note);
+
+            params.outputs.push(Output {
+                value_commit: public_inputs.value_commit,
+                token_commit: public_inputs.token_commit,
+                coin: public_inputs.coin,
+                note: encrypted_note,
+            });
+        }
+
+        // Now we should have all the params, zk proofs, and signature secrets.
+        // We return it all and let the caller deal with it.
+        let secrets = TransferCallSecrets {
+            proofs,
+            signature_secrets,
+            output_notes,
+            input_value_blinds: input_blinds,
+            output_value_blinds: output_blinds,
+        };
+        Ok((params, secrets))
+    }
 }
 
 /// Struct holding necessary information to build a `Money::TransferV1` contract call.
@@ -144,9 +341,8 @@ pub struct TransferCallBuilder {
     pub change_spend_hook: pallas::Base,
     /// User data for the change output
     pub change_user_data: pallas::Base,
-    /// User data blind for the change output
-    // TODO (CRITICAL): this is wrongly labelled and used over several inputs
-    pub change_user_data_blind: pallas::Base,
+    /// User data blind for inputs
+    pub input_user_data_blind: pallas::Base,
     /// Set of `OwnCoin` we're given to use in this builder
     pub coins: Vec<OwnCoin>,
     /// Merkle tree of coins used to create inclusion proofs
@@ -163,8 +359,12 @@ pub struct TransferCallBuilder {
     pub clear_input: bool,
 }
 
+// cannot use different select_coins() algos
+// low level api mixing concerns - owncoin, select, and build
+// unable to specify exact structure of tx (multiple outputs, clear and anon inputs)
+
 impl TransferCallBuilder {
-    pub fn build(&self) -> Result<TransferCallDebris> {
+    pub fn build(self) -> Result<TransferCallDebris> {
         debug!("Building Money::TransferV1 contract call");
         assert!(self.value != 0);
         assert!(self.token_id.inner() != pallas::Base::zero());
@@ -181,15 +381,13 @@ impl TransferCallBuilder {
         let mut clear_inputs = vec![];
         let mut inputs = vec![];
         let mut outputs = vec![];
-        let mut change_outputs = vec![];
+
+        //let mut change_outputs = vec![];
         let mut spent_coins = vec![];
         let mut minted_coins = vec![];
-        let mut signature_secrets = vec![];
-        let mut proofs = vec![];
 
         if self.clear_input {
-            debug!("Building clear input");
-            let input = TransactionBuilderClearInputInfo {
+            let input = TransferCallClearInput {
                 value: self.value,
                 token_id: self.token_id,
                 signature_secret: self.keypair.secret,
@@ -197,7 +395,6 @@ impl TransferCallBuilder {
 
             clear_inputs.push(input);
         } else {
-            debug!("Building anonymous inputs");
             let mut inputs_value = 0;
 
             for coin in self.coins.iter() {
@@ -210,11 +407,12 @@ impl TransferCallBuilder {
                 let merkle_path = self.tree.witness(leaf_position, 0).unwrap();
                 inputs_value += coin.note.value;
 
-                let input = TransactionBuilderInputInfo {
+                let input = TransferCallInput {
                     leaf_position,
                     merkle_path,
                     secret: coin.secret,
                     note: coin.note.clone(),
+                    user_data_blind: self.input_user_data_blind,
                 };
 
                 inputs.push(input);
@@ -228,152 +426,58 @@ impl TransferCallBuilder {
 
             if inputs_value > self.value {
                 let return_value = inputs_value - self.value;
-                change_outputs.push(TransactionBuilderOutputInfo {
+                outputs.push(TransferCallOutput {
                     value: return_value,
                     token_id: self.token_id,
                     public_key: self.keypair.public,
+                    spend_hook: self.change_spend_hook,
+                    user_data: self.change_user_data,
                 });
             }
-
-            debug!("Finished building inputs");
         }
+        debug!("Selected inputs");
 
-        outputs.push(TransactionBuilderOutputInfo {
+        outputs.push(TransferCallOutput {
             value: self.value,
             token_id: self.token_id,
             public_key: self.recipient,
+            spend_hook: self.rcpt_spend_hook,
+            user_data: self.rcpt_user_data,
         });
 
         assert!(clear_inputs.len() + inputs.len() > 0);
 
-        // We now fill this with necessary stuff
-        let mut params =
-            MoneyTransferParamsV1 { clear_inputs: vec![], inputs: vec![], outputs: vec![] };
-
-        let token_blind = pallas::Base::random(&mut OsRng);
-        for input in clear_inputs {
-            let signature_public = PublicKey::from_secret(input.signature_secret);
-            let value_blind = pallas::Scalar::random(&mut OsRng);
-
-            params.clear_inputs.push(ClearInput {
-                value: input.value,
-                token_id: input.token_id,
-                value_blind,
-                token_blind,
-                signature_public,
-            });
-        }
-
-        let mut input_blinds = vec![];
-        let mut output_blinds = vec![];
-
-        for (i, input) in inputs.iter().enumerate() {
-            let value_blind = pallas::Scalar::random(&mut OsRng);
-            input_blinds.push(value_blind);
-
-            let signature_secret = SecretKey::random(&mut OsRng);
-            signature_secrets.push(signature_secret);
-
-            info!("Creating transfer burn proof for input {}", i);
-            let (proof, public_inputs) = create_transfer_burn_proof(
-                &self.burn_zkbin,
-                &self.burn_pk,
-                input,
-                value_blind,
-                token_blind,
-                self.change_user_data_blind, // FIXME: We assume this, but it's just 1 usecase
-                signature_secret,
-            )?;
-
-            params.inputs.push(Input {
-                value_commit: public_inputs.value_commit,
-                token_commit: public_inputs.token_commit,
-                nullifier: public_inputs.nullifier,
-                merkle_root: public_inputs.merkle_root,
-                spend_hook: public_inputs.spend_hook,
-                user_data_enc: public_inputs.user_data_enc,
-                signature_public: public_inputs.signature_public,
-            });
-
-            proofs.push(proof);
-        }
-
-        // This value_blind calc assumes there will always be at least a single output
-        assert!(!outputs.is_empty());
-
-        for (i, output) in change_outputs.iter().chain(outputs.iter()).enumerate() {
-            let value_blind = if i == outputs.len() + change_outputs.len() - 1 {
-                compute_remainder_blind(&params.clear_inputs, &input_blinds, &output_blinds)
-            } else {
-                pallas::Scalar::random(&mut OsRng)
-            };
-
-            output_blinds.push(value_blind);
-
-            let serial = pallas::Base::random(&mut OsRng);
-
-            let (scoped_sh, scoped_ud) = {
-                if i >= change_outputs.len() {
-                    (self.rcpt_spend_hook, self.rcpt_user_data)
-                } else {
-                    (self.change_spend_hook, self.change_user_data)
-                }
-            };
-
-            info!("Creating transfer mint proof for output {}", i);
-            let (proof, public_inputs) = create_transfer_mint_proof(
-                &self.mint_zkbin,
-                &self.mint_pk,
-                output,
-                value_blind,
-                token_blind,
-                serial,
-                scoped_sh,
-                scoped_ud,
-            )?;
-
-            proofs.push(proof);
-
-            // Encrypted note
-            let note = MoneyNote {
-                serial,
-                value: output.value,
-                token_id: output.token_id,
-                spend_hook: scoped_sh,
-                user_data: scoped_ud,
-                value_blind,
-                token_blind,
-                memo: vec![],
-            };
-
-            let encrypted_note = AeadEncryptedNote::encrypt(&note, &output.public_key, &mut OsRng)?;
+        let xfer_builder = TransferCallBuilder2 {
+            clear_inputs,
+            inputs,
+            outputs,
+            mint_zkbin: self.mint_zkbin,
+            mint_pk: self.mint_pk,
+            burn_zkbin: self.burn_zkbin,
+            burn_pk: self.burn_pk,
+        };
+        let (params, secrets) = xfer_builder.build()?;
 
+        for (output, output_note) in params.outputs.iter().zip(secrets.output_notes.iter()) {
             minted_coins.push(OwnCoin {
-                coin: public_inputs.coin,
-                note,
+                coin: output.coin,
+                note: output_note.clone(),
                 secret: SecretKey::from(pallas::Base::ZERO),
                 nullifier: Nullifier::from(pallas::Base::ZERO),
                 leaf_position: 0.into(),
             });
-
-            params.outputs.push(Output {
-                value_commit: public_inputs.value_commit,
-                token_commit: public_inputs.token_commit,
-                coin: public_inputs.coin,
-                note: encrypted_note,
-            });
         }
 
         // Now we should have all the params, zk proofs, and signature secrets.
         // We return it all and let the caller deal with it.
         let debris = TransferCallDebris {
             params,
-            proofs,
-            signature_secrets,
+            proofs: secrets.proofs,
+            signature_secrets: secrets.signature_secrets,
             spent_coins,
             minted_coins,
-            input_value_blinds: input_blinds,
-            output_value_blinds: output_blinds,
+            input_value_blinds: secrets.input_value_blinds,
+            output_value_blinds: secrets.output_value_blinds,
         };
         Ok(debris)
     }
@@ -382,10 +486,9 @@ impl TransferCallBuilder {
 pub fn create_transfer_burn_proof(
     zkbin: &ZkBinary,
     pk: &ProvingKey,
-    input: &TransactionBuilderInputInfo,
+    input: &TransferCallInput,
     value_blind: pallas::Scalar,
     token_blind: pallas::Base,
-    user_data_blind: pallas::Base,
     signature_secret: SecretKey,
 ) -> Result<(Proof, TransferBurnRevealed)> {
     let nullifier = Nullifier::from(poseidon_hash([input.secret.inner(), input.note.serial]));
@@ -418,7 +521,7 @@ pub fn create_transfer_burn_proof(
         current
     };
 
-    let user_data_enc = poseidon_hash([input.note.user_data, user_data_blind]);
+    let user_data_enc = poseidon_hash([input.note.user_data, input.user_data_blind]);
     let value_commit = pedersen_commitment_u64(input.note.value, value_blind);
     let token_commit = poseidon_hash([input.note.token_id.inner(), token_blind]);
 
@@ -440,7 +543,7 @@ pub fn create_transfer_burn_proof(
         Witness::Base(Value::known(input.note.serial)),
         Witness::Base(Value::known(input.note.spend_hook)),
         Witness::Base(Value::known(input.note.user_data)),
-        Witness::Base(Value::known(user_data_blind)),
+        Witness::Base(Value::known(input.user_data_blind)),
         Witness::Base(Value::known(input.secret.inner())),
         Witness::Uint32(Value::known(u64::from(input.leaf_position).try_into().unwrap())),
         Witness::MerklePath(Value::known(input.merkle_path.clone().try_into().unwrap())),
@@ -457,7 +560,7 @@ pub fn create_transfer_burn_proof(
 pub fn create_transfer_mint_proof(
     zkbin: &ZkBinary,
     pk: &ProvingKey,
-    output: &TransactionBuilderOutputInfo,
+    output: &TransferCallOutput,
     value_blind: pallas::Scalar,
     token_blind: pallas::Base,
     serial: pallas::Base,
@@ -477,6 +580,14 @@ pub fn create_transfer_mint_proof(
         spend_hook,
         user_data,
     ]));
+    debug!("Created coin {:?}", coin);
+    debug!("  pub_x: {:?}", pub_x);
+    debug!("  pub_y: {:?}", pub_y);
+    debug!("  value: {:?}", pallas::Base::from(output.value));
+    debug!("  token_id: {:?}", output.token_id.inner());
+    debug!("  serial: {:?}", serial);
+    debug!("  spend_hook: {:?}", spend_hook);
+    debug!("  user_data: {:?}", user_data);
 
     let public_inputs = TransferMintRevealed { coin, value_commit, token_commit };
 

+ 9 - 3
src/contract/test-harness/src/dao_exec.rs

@@ -30,7 +30,10 @@ use darkfi_money_contract::{
     MoneyFunction, MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
 };
 use darkfi_sdk::{
-    crypto::{pasta_prelude::Field, MerkleNode, SecretKey, DAO_CONTRACT_ID, MONEY_CONTRACT_ID},
+    crypto::{
+        pasta_prelude::Field, pedersen_commitment_u64, MerkleNode, SecretKey, DAO_CONTRACT_ID,
+        MONEY_CONTRACT_ID,
+    },
     pasta::pallas,
     ContractCall,
 };
@@ -93,8 +96,7 @@ impl TestHarness {
             rcpt_user_data_blind,
             change_spend_hook,
             change_user_data,
-            // TODO (ERROR): incorrectly named
-            change_user_data_blind: input_user_data_blind,
+            input_user_data_blind,
             coins,
             tree,
             mint_zkbin: mint_zkbin.clone(),
@@ -117,6 +119,10 @@ impl TestHarness {
             input_value += input.note.value;
             input_value_blind += blind;
         }
+        assert_eq!(
+            pedersen_commitment_u64(input_value, input_value_blind),
+            xfer_debris.params.inputs.iter().map(|input| input.value_commit).sum()
+        );
 
         // First output is change, second output is recipient.
         let dao_serial = xfer_debris.minted_coins[0].note.serial;

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

@@ -76,8 +76,8 @@ pub fn init_logger() {
     // We check this error so we can execute same file tests in parallel,
     // otherwise second one fails to init logger here.
     if simplelog::TermLogger::init(
-        simplelog::LevelFilter::Info,
-        //simplelog::LevelFilter::Debug,
+        //simplelog::LevelFilter::Info,
+        simplelog::LevelFilter::Debug,
         //simplelog::LevelFilter::Trace,
         cfg.build(),
         simplelog::TerminalMode::Mixed,

+ 1 - 1
src/contract/test-harness/src/money_airdrop.rs

@@ -69,7 +69,7 @@ impl TestHarness {
             rcpt_user_data_blind: pallas::Base::random(&mut OsRng),
             change_spend_hook: change_spend_hook.unwrap_or(pallas::Base::ZERO),
             change_user_data: change_user_data.unwrap_or(pallas::Base::ZERO),
-            change_user_data_blind: pallas::Base::random(&mut OsRng),
+            input_user_data_blind: pallas::Base::random(&mut OsRng),
             coins: vec![],
             tree: faucet.money_merkle_tree.clone(),
             mint_zkbin: mint_zkbin.clone(),

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

@@ -65,7 +65,7 @@ impl TestHarness {
         // TODO: verify this is correct
         let change_spend_hook = pallas::Base::zero();
         let change_user_data = pallas::Base::zero();
-        let change_user_data_blind = pallas::Base::random(&mut OsRng);
+        let input_user_data_blind = pallas::Base::random(&mut OsRng);
 
         let builder = TransferCallBuilder {
             keypair: wallet.keypair,
@@ -77,7 +77,7 @@ impl TestHarness {
             rcpt_user_data_blind,
             change_spend_hook,
             change_user_data,
-            change_user_data_blind,
+            input_user_data_blind,
             coins: owncoins.to_owned(),
             tree: wallet.money_merkle_tree.clone(),
             mint_zkbin: mint_zkbin.clone(),