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

contract/money/tests/verification_bench: migrated to new contract/test-harness plus some minor cleanups

aggstam 3 лет назад
Родитель
Сommit
7fcc5953ff

+ 12 - 10
src/contract/money/tests/genesis_mint.rs

@@ -27,6 +27,7 @@
 
 use darkfi::Result;
 use darkfi_contract_test_harness::{init_logger, Holder, TestHarness};
+use darkfi_sdk::crypto::DARK_TOKEN_ID;
 use log::info;
 
 #[async_std::test]
@@ -68,7 +69,7 @@ async fn genesis_mint() -> Result<()> {
     info!(target: "money", "[Malicious] ==================================");
     th.execute_erroneous_genesis_mint_tx(
         Holder::Alice,
-        vec![genesis_mint_tx.clone(), genesis_mint_tx.clone()],
+        &vec![genesis_mint_tx.clone(), genesis_mint_tx.clone()],
         current_slot,
         1,
     )
@@ -79,7 +80,7 @@ async fn genesis_mint() -> Result<()> {
     info!(target: "money", "[Malicious] ============================================");
     th.execute_erroneous_genesis_mint_tx(
         Holder::Alice,
-        vec![genesis_mint_tx.clone()],
+        &vec![genesis_mint_tx.clone()],
         current_slot + 1,
         1,
     )
@@ -115,7 +116,7 @@ async fn genesis_mint() -> Result<()> {
 
     // Alice gathers her new owncoin
     let alice_oc = th.gather_owncoin(Holder::Alice, genesis_mint_params.output, None)?;
-    alice_owncoins.push(alice_oc.clone());
+    alice_owncoins.push(alice_oc);
 
     info!(target: "money", "[Bob] ========================");
     info!(target: "money", "[Bob] Building genesis mint tx");
@@ -155,13 +156,14 @@ async fn genesis_mint() -> Result<()> {
     info!(target: "money", "[Alice] ====================================================");
     info!(target: "money", "[Alice] Building Money::Transfer params for a payment to Bob");
     info!(target: "money", "[Alice] ====================================================");
-    let (transfer_tx, transfer_params) =
-        th.transfer(ALICE_SEND, Holder::Alice, Holder::Bob, &alice_oc)?;
+    let (transfer_tx, transfer_params, spent_coins) =
+        th.transfer(ALICE_SEND, Holder::Alice, Holder::Bob, &alice_owncoins, *DARK_TOKEN_ID)?;
 
     // Validating transfer params
     assert!(transfer_params.inputs.len() == 1);
     assert!(transfer_params.outputs.len() == 2);
-    alice_owncoins.retain(|x| x != &alice_oc);
+    assert!(spent_coins.len() == 1);
+    alice_owncoins.retain(|x| x != &spent_coins[0]);
     assert!(alice_owncoins.is_empty());
 
     info!(target: "money", "[Faucet] ==============================");
@@ -196,14 +198,14 @@ async fn genesis_mint() -> Result<()> {
     info!(target: "money", "[Bob] ======================================================");
     info!(target: "money", "[Bob] Building Money::Transfer params for a payment to Alice");
     info!(target: "money", "[Bob] ======================================================");
-    let bob_oc = bob_owncoins[0].clone();
-    let (transfer_tx, transfer_params) =
-        th.transfer(BOB_SEND, Holder::Bob, Holder::Alice, &bob_oc)?;
+    let (transfer_tx, transfer_params, spent_coins) =
+        th.transfer(BOB_SEND, Holder::Bob, Holder::Alice, &bob_owncoins, *DARK_TOKEN_ID)?;
 
     // Validating transfer params
     assert!(transfer_params.inputs.len() == 1);
     assert!(transfer_params.outputs.len() == 2);
-    bob_owncoins.retain(|x| x != &bob_oc);
+    assert!(spent_coins.len() == 1);
+    bob_owncoins.retain(|x| x != &spent_coins[0]);
     assert!(bob_owncoins.len() == 1);
 
     info!(target: "money", "[Faucet] ==============================");

+ 3 - 1
src/contract/money/tests/integration.rs

@@ -27,7 +27,9 @@
 //! With this test we want to confirm the money contract state transitions
 //! work between multiple parties and are able to be verified.
 //!
-//! TODO: Malicious cases
+//! TODO:
+//!      1. Add missing functionalities(transfers, atomic swaps)
+//!      2. Malicious cases
 
 use darkfi::Result;
 use darkfi_contract_test_harness::{init_logger, Holder, TestHarness};

+ 5 - 3
src/contract/money/tests/txs_verification.rs

@@ -78,7 +78,8 @@ async fn txs_verification() -> Result<()> {
 
     // Alice gathers her new owncoin
     let alice_oc = th.gather_owncoin(Holder::Alice, token_mint_params.output, None)?;
-    alice_owncoins.push(alice_oc.clone());
+    let alice_token_id = alice_oc.note.token_id;
+    alice_owncoins.push(alice_oc);
 
     // Now Alice can send a little bit of funds to Bob.
     // We can duplicate this transaction to simulate double spending.
@@ -89,12 +90,13 @@ async fn txs_verification() -> Result<()> {
         info!(target: "money", "[Alice] ======================================================");
         info!(target: "money", "[Alice] Building Money::Transfer params for payment {i} to Bob");
         info!(target: "money", "[Alice] ======================================================");
-        let (transfer_tx, transfer_params) =
-            th.transfer(ALICE_SEND, Holder::Alice, Holder::Bob, &alice_oc.clone())?;
+        let (transfer_tx, transfer_params, spent_coins) =
+            th.transfer(ALICE_SEND, Holder::Alice, Holder::Bob, &alice_owncoins, alice_token_id)?;
 
         // Validating transfer params
         assert!(transfer_params.inputs.len() == 1);
         assert!(transfer_params.outputs.len() == 2);
+        assert!(spent_coins.len() == 1);
 
         // Now we simulate nodes verification, as transactions come one by one.
         // Validation should pass, even when we are trying to double spent.

+ 60 - 242
src/contract/money/tests/verification_bench.rs

@@ -18,30 +18,18 @@
 
 use std::{env, str::FromStr};
 
-use darkfi::{tx::Transaction, Result};
-use darkfi_sdk::{
-    crypto::{pasta_prelude::*, poseidon_hash, MerkleNode, Nullifier, MONEY_CONTRACT_ID},
-    pasta::pallas,
-    ContractCall,
-};
-use darkfi_serial::Encodable;
+use darkfi::Result;
+use darkfi_contract_test_harness::{init_logger, Holder, TestHarness};
 use log::info;
-use rand::{prelude::IteratorRandom, rngs::OsRng, Rng};
-
-use darkfi_money_contract::{
-    client::{transfer_v1::TransferCallBuilder, MoneyNote, OwnCoin},
-    model::Coin,
-    MoneyFunction::TransferV1 as MoneyTransfer,
-    MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
-};
-
-mod harness;
-use harness::{init_logger, MoneyTestHarness};
+use rand::{prelude::IteratorRandom, Rng};
 
 #[async_std::test]
 async fn alice2alice_random_amounts() -> Result<()> {
     init_logger();
 
+    // Holders this test will use
+    const HOLDERS: [Holder; 2] = [Holder::Faucet, Holder::Alice];
+
     const ALICE_AIRDROP: u64 = 1000;
 
     // Slot to verify against
@@ -60,55 +48,31 @@ async fn alice2alice_random_amounts() -> Result<()> {
     }
 
     // Initialize harness
-    let mut th = MoneyTestHarness::new().await?;
-    let (mint_pk, mint_zkbin) = th.proving_keys.get(&MONEY_CONTRACT_ZKAS_MINT_NS_V1).unwrap();
-    let (burn_pk, burn_zkbin) = th.proving_keys.get(&MONEY_CONTRACT_ZKAS_BURN_NS_V1).unwrap();
+    let mut th = TestHarness::new(&["money".to_string()]).await?;
 
-    info!(target: "money", "[Faucet] ===================================================");
-    info!(target: "money", "[Faucet] Building Money::Transfer params for Alice's airdrop");
-    info!(target: "money", "[Faucet] ===================================================");
-    let contract_id = *MONEY_CONTRACT_ID;
-    let (airdrop_tx, airdrop_params) = th.airdrop_native(ALICE_AIRDROP, th.alice.keypair.public)?;
+    info!(target: "money", "[Faucet] ========================");
+    info!(target: "money", "[Faucet] Building Alice's airdrop");
+    info!(target: "money", "[Faucet] ========================");
+    let (airdrop_tx, airdrop_params) = th.airdrop_native(ALICE_AIRDROP, Holder::Alice)?;
 
     info!(target: "money", "[Faucet] ==========================");
     info!(target: "money", "[Faucet] Executing Alice airdrop tx");
     info!(target: "money", "[Faucet] ==========================");
-    let erroneous_txs = th
-        .faucet
-        .state
-        .read()
-        .await
-        .verify_transactions(&[airdrop_tx.clone()], current_slot, true)
+    th.execute_airdrop_native_tx(Holder::Faucet, &airdrop_tx, &airdrop_params, current_slot)
         .await?;
-    assert!(erroneous_txs.is_empty());
-    th.faucet.merkle_tree.append(MerkleNode::from(airdrop_params.outputs[0].coin.inner()));
+
     info!(target: "money", "[Alice] ==========================");
     info!(target: "money", "[Alice] Executing Alice airdrop tx");
     info!(target: "money", "[Alice] ==========================");
-    let erroneous_txs = th
-        .alice
-        .state
-        .read()
-        .await
-        .verify_transactions(&[airdrop_tx.clone()], current_slot, true)
-        .await?;
-    assert!(erroneous_txs.is_empty());
-    th.alice.merkle_tree.append(MerkleNode::from(airdrop_params.outputs[0].coin.inner()));
+    th.execute_airdrop_native_tx(Holder::Alice, &airdrop_tx, &airdrop_params, current_slot).await?;
 
-    assert!(th.faucet.merkle_tree.root(0).unwrap() == th.alice.merkle_tree.root(0).unwrap());
+    th.assert_trees(&HOLDERS);
 
     // Gather new owncoins
     let mut owncoins = vec![];
-    let leaf_position = th.alice.merkle_tree.mark().unwrap();
-    let note: MoneyNote = airdrop_params.outputs[0].note.decrypt(&th.alice.keypair.secret)?;
-    let token_id = note.token_id;
-    owncoins.push(OwnCoin {
-        coin: Coin::from(airdrop_params.outputs[0].coin),
-        note: note.clone(),
-        secret: th.alice.keypair.secret,
-        nullifier: Nullifier::from(poseidon_hash([th.alice.keypair.secret.inner(), note.serial])),
-        leaf_position,
-    });
+    let owncoin = th.gather_owncoin(Holder::Alice, airdrop_params.outputs[0].clone(), None)?;
+    let token_id = owncoin.note.token_id;
+    owncoins.push(owncoin);
 
     // Execute transactions loop
     for i in 0..n {
@@ -121,43 +85,8 @@ async fn alice2alice_random_amounts() -> Result<()> {
         let amount = rand::thread_rng().gen_range(1..ALICE_AIRDROP);
         info!(target: "money", "[Alice] Sending: {}", amount);
         info!(target: "money", "[Alice] ===============================================");
-        let call_debris = TransferCallBuilder {
-            keypair: th.alice.keypair,
-            recipient: th.alice.keypair.public,
-            value: amount,
-            token_id,
-            rcpt_spend_hook: pallas::Base::zero(),
-            rcpt_user_data: pallas::Base::zero(),
-            rcpt_user_data_blind: pallas::Base::random(&mut OsRng),
-            change_spend_hook: pallas::Base::zero(),
-            change_user_data: pallas::Base::zero(),
-            change_user_data_blind: pallas::Base::random(&mut OsRng),
-            coins: owncoins.clone(),
-            tree: th.alice.merkle_tree.clone(),
-            mint_zkbin: mint_zkbin.clone(),
-            mint_pk: mint_pk.clone(),
-            burn_zkbin: burn_zkbin.clone(),
-            burn_pk: burn_pk.clone(),
-            clear_input: false,
-        }
-        .build()?;
-        let (params, proofs, secret_keys, spent_coins) = (
-            call_debris.params,
-            call_debris.proofs,
-            call_debris.signature_secrets,
-            call_debris.spent_coins,
-        );
-
-        info!(target: "money", "[Alice] ============================");
-        info!(target: "money", "[Alice] Building payment tx to Alice");
-        info!(target: "money", "[Alice] ============================");
-        let mut data = vec![MoneyTransfer as u8];
-        params.encode(&mut data)?;
-        let calls = vec![ContractCall { contract_id, data }];
-        let proofs = vec![proofs];
-        let mut tx = Transaction { calls, proofs, signatures: vec![] };
-        let sigs = tx.create_sigs(&mut OsRng, &secret_keys)?;
-        tx.signatures = vec![sigs];
+        let (tx, params, spent_coins) =
+            th.transfer(amount, Holder::Alice, Holder::Alice, &owncoins, token_id)?;
 
         // Remove the owncoins we've spent
         for spent in spent_coins {
@@ -168,58 +97,29 @@ async fn alice2alice_random_amounts() -> Result<()> {
         info!(target: "money", "[Faucet] ================================");
         info!(target: "money", "[Faucet] Executing Alice2Alice payment tx");
         info!(target: "money", "[Faucet] ================================");
-        let erroneous_txs = th
-            .faucet
-            .state
-            .read()
-            .await
-            .verify_transactions(&[tx.clone()], current_slot, true)
-            .await?;
-        assert!(erroneous_txs.is_empty());
-        for output in &params.outputs {
-            th.faucet.merkle_tree.append(MerkleNode::from(output.coin.inner()));
-        }
+        th.execute_transfer_tx(Holder::Faucet, &tx, &params, current_slot, true).await?;
+
         info!(target: "money", "[Alice] ================================");
         info!(target: "money", "[Alice] Executing Alice2Alice payment tx");
         info!(target: "money", "[Alice] ================================");
-        let erroneous_txs = th
-            .alice
-            .state
-            .read()
-            .await
-            .verify_transactions(&[tx.clone()], current_slot, true)
-            .await?;
-        assert!(erroneous_txs.is_empty());
-        // Gather new owncoins and apply the state transitions
-        for output in params.outputs {
-            th.alice.merkle_tree.append(MerkleNode::from(output.coin.inner()));
-            let note: MoneyNote = output.note.decrypt(&th.alice.keypair.secret)?;
-            let leaf_position = th.alice.merkle_tree.mark().unwrap();
-
-            let owncoin = OwnCoin {
-                coin: Coin::from(output.coin),
-                note: note.clone(),
-                secret: th.alice.keypair.secret,
-                nullifier: Nullifier::from(poseidon_hash([
-                    th.alice.keypair.secret.inner(),
-                    note.serial,
-                ])),
-                leaf_position,
-            };
-
-            owncoins.push(owncoin);
-        }
+        th.execute_transfer_tx(Holder::Alice, &tx, &params, current_slot, false).await?;
+
+        // Gather new owncoins
+        owncoins.append(&mut th.gather_multiple_owncoins(Holder::Alice, &params.outputs)?);
 
-        assert!(th.faucet.merkle_tree.root(0).unwrap() == th.alice.merkle_tree.root(0).unwrap());
+        th.assert_trees(&HOLDERS);
     }
 
     Ok(())
 }
 
 #[async_std::test]
-async fn alice2alice_random_amounts_multiplecoins() -> Result<()> {
+async fn alice2alice_multiplecoins_random_amounts() -> Result<()> {
     init_logger();
 
+    // Holders this test will use
+    const HOLDERS: [Holder; 2] = [Holder::Faucet, Holder::Alice];
+
     // Slot to verify against
     let current_slot = 0;
 
@@ -236,10 +136,7 @@ async fn alice2alice_random_amounts_multiplecoins() -> Result<()> {
     }
 
     // Initialize harness
-    let mut th = MoneyTestHarness::new().await?;
-    let (mint_pk, mint_zkbin) = th.proving_keys.get(&MONEY_CONTRACT_ZKAS_MINT_NS_V1).unwrap();
-    let (burn_pk, burn_zkbin) = th.proving_keys.get(&MONEY_CONTRACT_ZKAS_BURN_NS_V1).unwrap();
-    let contract_id = *MONEY_CONTRACT_ID;
+    let mut th = TestHarness::new(&["money".to_string()]).await?;
 
     // Mint 10 coins
     let mut token_ids = vec![];
@@ -250,50 +147,24 @@ async fn alice2alice_random_amounts_multiplecoins() -> Result<()> {
         info!(target: "money", "[Faucet] ===================================================");
         info!(target: "money", "[Faucet] Building Money::Mint params for Alice's mint for token {} and amount {}", i, amount);
         info!(target: "money", "[Faucet] ===================================================");
-        let (mint_tx, mint_params) =
-            th.mint_token(th.alice.keypair, amount, th.alice.keypair.public)?;
+        let (mint_tx, mint_params) = th.token_mint(amount, Holder::Alice, Holder::Alice)?;
 
         info!(target: "money", "[Faucet] =======================");
         info!(target: "money", "[Faucet] Executing Alice mint tx");
         info!(target: "money", "[Faucet] =======================");
-        let erroneous_txs = th
-            .faucet
-            .state
-            .read()
-            .await
-            .verify_transactions(&[mint_tx.clone()], current_slot, true)
-            .await?;
-        assert!(erroneous_txs.is_empty());
-        th.faucet.merkle_tree.append(MerkleNode::from(mint_params.output.coin.inner()));
+        th.execute_token_mint_tx(Holder::Faucet, &mint_tx, &mint_params, current_slot).await?;
+
         info!(target: "money", "[Alice] =======================");
         info!(target: "money", "[Alice] Executing Alice mint tx");
         info!(target: "money", "[Alice] =======================");
-        let erroneous_txs = th
-            .alice
-            .state
-            .read()
-            .await
-            .verify_transactions(&[mint_tx.clone()], current_slot, true)
-            .await?;
-        assert!(erroneous_txs.is_empty());
-        th.alice.merkle_tree.append(MerkleNode::from(mint_params.output.coin.inner()));
+        th.execute_token_mint_tx(Holder::Alice, &mint_tx, &mint_params, current_slot).await?;
 
-        assert!(th.faucet.merkle_tree.root(0).unwrap() == th.alice.merkle_tree.root(0).unwrap());
+        th.assert_trees(&HOLDERS);
 
         // Gather new owncoins
-        let leaf_position = th.alice.merkle_tree.mark().unwrap();
-        let note: MoneyNote = mint_params.output.note.decrypt(&th.alice.keypair.secret)?;
-        let token_id = note.token_id;
-        owncoins.push(vec![OwnCoin {
-            coin: Coin::from(mint_params.output.coin),
-            note: note.clone(),
-            secret: th.alice.keypair.secret,
-            nullifier: Nullifier::from(poseidon_hash([
-                th.alice.keypair.secret.inner(),
-                note.serial,
-            ])),
-            leaf_position,
-        }]);
+        let owncoin = th.gather_owncoin(Holder::Alice, mint_params.output, None)?;
+        let token_id = owncoin.note.token_id;
+        owncoins.push(vec![owncoin]);
         minted_amounts.push(amount);
         token_ids.push(token_id);
     }
@@ -310,6 +181,7 @@ async fn alice2alice_random_amounts_multiplecoins() -> Result<()> {
 
         // Generate a transaction for each coin
         let mut txs = vec![];
+        let mut txs_params = vec![];
         for index in sample {
             info!(target: "money", "[Alice] ===============================================");
             info!(target: "money", "[Alice] Building Money::Transfer params for coin {}", index);
@@ -323,92 +195,38 @@ async fn alice2alice_random_amounts_multiplecoins() -> Result<()> {
             let amount = rand::thread_rng().gen_range(1..mint_amount);
             info!(target: "money", "[Alice] Sending: {}", amount);
             info!(target: "money", "[Alice] ===============================================");
-            let call_debris = TransferCallBuilder {
-                keypair: th.alice.keypair,
-                recipient: th.alice.keypair.public,
-                value: amount,
-                token_id,
-                rcpt_spend_hook: pallas::Base::zero(),
-                rcpt_user_data: pallas::Base::zero(),
-                rcpt_user_data_blind: pallas::Base::random(&mut OsRng),
-                change_spend_hook: pallas::Base::zero(),
-                change_user_data: pallas::Base::zero(),
-                change_user_data_blind: pallas::Base::random(&mut OsRng),
-                coins: coins.clone(),
-                tree: th.alice.merkle_tree.clone(),
-                mint_zkbin: mint_zkbin.clone(),
-                mint_pk: mint_pk.clone(),
-                burn_zkbin: burn_zkbin.clone(),
-                burn_pk: burn_pk.clone(),
-                clear_input: false,
-            }
-            .build()?;
-            let (params, proofs, secret_keys, spent_coins) = (
-                call_debris.params,
-                call_debris.proofs,
-                call_debris.signature_secrets,
-                call_debris.spent_coins,
-            );
-
-            info!(target: "money", "[Alice] ============================");
-            info!(target: "money", "[Alice] Building payment tx to Alice");
-            info!(target: "money", "[Alice] ============================");
-            let mut data = vec![MoneyTransfer as u8];
-            params.encode(&mut data)?;
-            let calls = vec![ContractCall { contract_id, data }];
-            let proofs = vec![proofs];
-            let mut tx = Transaction { calls, proofs, signatures: vec![] };
-            let sigs = tx.create_sigs(&mut OsRng, &secret_keys)?;
-            tx.signatures = vec![sigs];
+            let (tx, params, spent_coins) =
+                th.transfer(amount, Holder::Alice, Holder::Alice, &coins, token_id)?;
 
             // Remove the owncoins we've spent
             for spent in spent_coins {
                 coins.retain(|x| x != &spent);
             }
 
-            // Store transaction
-            txs.push(tx.clone());
-
             // Gather new owncoins
-            for output in params.outputs {
-                th.faucet.merkle_tree.append(MerkleNode::from(output.coin.inner()));
-                th.alice.merkle_tree.append(MerkleNode::from(output.coin.inner()));
-                let note: MoneyNote = output.note.decrypt(&th.alice.keypair.secret)?;
-                let leaf_position = th.alice.merkle_tree.mark().unwrap();
-
-                let owncoin = OwnCoin {
-                    coin: Coin::from(output.coin),
-                    note: note.clone(),
-                    secret: th.alice.keypair.secret,
-                    nullifier: Nullifier::from(poseidon_hash([
-                        th.alice.keypair.secret.inner(),
-                        note.serial,
-                    ])),
-                    leaf_position,
-                };
-
-                coins.push(owncoin);
-            }
+            coins.append(&mut th.gather_multiple_owncoins(Holder::Alice, &params.outputs)?);
+
+            // Store transaction and its params
+            txs.push(tx);
+            txs_params.push(params);
 
             // Replace coins
             owncoins[index] = coins;
         }
 
-        // Verify transaction
-        info!(target: "money", "[Faucet] ================================");
-        info!(target: "money", "[Faucet] Executing Alice2Alice payment tx");
-        info!(target: "money", "[Faucet] ================================");
-        let erroneous_txs =
-            th.faucet.state.read().await.verify_transactions(&txs, current_slot, true).await?;
-        assert!(erroneous_txs.is_empty());
-        info!(target: "money", "[Alice] ================================");
-        info!(target: "money", "[Alice] Executing Alice2Alice payment tx");
-        info!(target: "money", "[Alice] ================================");
-        let erroneous_txs =
-            th.alice.state.read().await.verify_transactions(&txs, current_slot, true).await?;
-        assert!(erroneous_txs.is_empty());
+        info!(target: "money", "[Faucet] =================================");
+        info!(target: "money", "[Faucet] Executing Alice2Alice payment txs");
+        info!(target: "money", "[Faucet] =================================");
+        th.execute_multiple_transfer_txs(Holder::Faucet, &txs, &txs_params, current_slot, true)
+            .await?;
+
+        info!(target: "money", "[Alice] =================================");
+        info!(target: "money", "[Alice] Executing Alice2Alice payment txs");
+        info!(target: "money", "[Alice] =================================");
+        th.execute_multiple_transfer_txs(Holder::Alice, &txs, &txs_params, current_slot, false)
+            .await?;
 
-        assert!(th.faucet.merkle_tree.root(0).unwrap() == th.alice.merkle_tree.root(0).unwrap());
+        th.assert_trees(&HOLDERS);
     }
 
     Ok(())

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

@@ -104,7 +104,7 @@ impl TestHarness {
     pub async fn execute_erroneous_genesis_stake_txs(
         &mut self,
         holder: Holder,
-        txs: &Vec<Transaction>,
+        txs: &[Transaction],
         slot: u64,
         erroneous: usize,
     ) -> Result<()> {

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

@@ -115,7 +115,7 @@ impl TestHarness {
     pub async fn execute_erroneous_proposal_txs(
         &mut self,
         holder: Holder,
-        txs: &Vec<Transaction>,
+        txs: &[Transaction],
         slot: u64,
         erroneous: usize,
     ) -> Result<()> {

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

@@ -128,7 +128,7 @@ impl TestHarness {
     pub async fn execute_erroneous_unstake_request_txs(
         &mut self,
         holder: Holder,
-        txs: &Vec<Transaction>,
+        txs: &[Transaction],
         slot: u64,
         erroneous: usize,
     ) -> Result<()> {

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

@@ -43,8 +43,8 @@ use darkfi_money_contract::{
     MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1,
 };
 use darkfi_sdk::crypto::{
-    poseidon_hash, Keypair, MerkleTree, Nullifier, PublicKey, SecretKey, CONSENSUS_CONTRACT_ID,
-    DAO_CONTRACT_ID, MONEY_CONTRACT_ID,
+    poseidon_hash, Keypair, MerkleNode, MerkleTree, Nullifier, PublicKey, SecretKey,
+    CONSENSUS_CONTRACT_ID, DAO_CONTRACT_ID, MONEY_CONTRACT_ID,
 };
 use darkfi_serial::{deserialize, serialize};
 use log::{info, warn};
@@ -300,6 +300,39 @@ impl TestHarness {
         Ok(oc)
     }
 
+    /// This should be used after transfer call, so we can mark the merkle tree
+    /// before each output coin. Assumes using wallet secret key.
+    pub fn gather_multiple_owncoins(
+        &mut self,
+        holder: Holder,
+        outputs: &[Output],
+    ) -> Result<Vec<OwnCoin>> {
+        let wallet = self.holders.get_mut(&holder).unwrap();
+        let secret_key = wallet.keypair.secret;
+        let mut owncoins = vec![];
+        for output in outputs {
+            wallet.money_merkle_tree.append(MerkleNode::from(output.coin.inner()));
+            let leaf_position = wallet.money_merkle_tree.mark().unwrap();
+
+            let note: MoneyNote = output.note.decrypt(&secret_key)?;
+            let oc = OwnCoin {
+                coin: output.coin,
+                note: note.clone(),
+                secret: secret_key,
+                nullifier: Nullifier::from(poseidon_hash([
+                    wallet.keypair.secret.inner(),
+                    note.serial,
+                ])),
+                leaf_position,
+            };
+
+            wallet.unspent_money_coins.push(oc.clone());
+            owncoins.push(oc);
+        }
+
+        Ok(owncoins)
+    }
+
     pub fn gather_consensus_staked_owncoin(
         &mut self,
         holder: Holder,

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

@@ -104,7 +104,7 @@ impl TestHarness {
     pub async fn execute_erroneous_genesis_mint_tx(
         &mut self,
         holder: Holder,
-        txs: &Vec<Transaction>,
+        txs: &[Transaction],
         slot: u64,
         erroneous: usize,
     ) -> Result<()> {

+ 41 - 9
src/contract/test-harness/src/money_transfer.rs

@@ -25,7 +25,7 @@ use darkfi_money_contract::{
     MoneyFunction, MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
 };
 use darkfi_sdk::{
-    crypto::{MerkleNode, MONEY_CONTRACT_ID},
+    crypto::{MerkleNode, TokenId, MONEY_CONTRACT_ID},
     pasta::pallas,
     ContractCall,
 };
@@ -40,8 +40,9 @@ impl TestHarness {
         amount: u64,
         holder: Holder,
         recipient: Holder,
-        owncoin: &OwnCoin,
-    ) -> Result<(Transaction, MoneyTransferParamsV1)> {
+        owncoins: &[OwnCoin],
+        token_id: TokenId,
+    ) -> Result<(Transaction, MoneyTransferParamsV1, Vec<OwnCoin>)> {
         let wallet = self.holders.get(&holder).unwrap();
         let rcpt = self.holders.get(&recipient).unwrap().keypair.public;
         let (mint_pk, mint_zkbin) = self.proving_keys.get(&MONEY_CONTRACT_ZKAS_MINT_NS_V1).unwrap();
@@ -64,14 +65,14 @@ impl TestHarness {
             keypair: wallet.keypair,
             recipient: rcpt,
             value: amount,
-            token_id: owncoin.note.token_id,
+            token_id,
             rcpt_spend_hook,
             rcpt_user_data,
             rcpt_user_data_blind,
             change_spend_hook,
             change_user_data,
             change_user_data_blind,
-            coins: vec![owncoin.clone()],
+            coins: owncoins.to_owned(),
             tree: wallet.money_merkle_tree.clone(),
             mint_zkbin: mint_zkbin.clone(),
             mint_pk: mint_pk.clone(),
@@ -99,7 +100,7 @@ impl TestHarness {
         let size = std::mem::size_of_val(&*base58);
         tx_action_benchmark.broadcasted_sizes.push(size);
 
-        Ok((tx, debris.params))
+        Ok((tx, debris.params, debris.spent_coins))
     }
 
     pub async fn execute_transfer_tx(
@@ -108,6 +109,7 @@ impl TestHarness {
         tx: &Transaction,
         params: &MoneyTransferParamsV1,
         slot: u64,
+        append: bool,
     ) -> Result<()> {
         let wallet = self.holders.get_mut(&holder).unwrap();
         let tx_action_benchmark =
@@ -117,8 +119,38 @@ impl TestHarness {
         let erroneous_txs =
             wallet.state.read().await.verify_transactions(&[tx.clone()], slot, true).await?;
         assert!(erroneous_txs.is_empty());
-        wallet.money_merkle_tree.append(MerkleNode::from(params.outputs[0].coin.inner()));
-        wallet.money_merkle_tree.append(MerkleNode::from(params.outputs[1].coin.inner()));
+        if append {
+            for output in &params.outputs {
+                wallet.money_merkle_tree.append(MerkleNode::from(output.coin.inner()));
+            }
+        }
+        tx_action_benchmark.verify_times.push(timer.elapsed());
+
+        Ok(())
+    }
+
+    pub async fn execute_multiple_transfer_txs(
+        &mut self,
+        holder: Holder,
+        txs: &[Transaction],
+        txs_params: &Vec<MoneyTransferParamsV1>,
+        slot: u64,
+        append: bool,
+    ) -> Result<()> {
+        let wallet = self.holders.get_mut(&holder).unwrap();
+        let tx_action_benchmark =
+            self.tx_action_benchmarks.get_mut(&TxAction::MoneyTransfer).unwrap();
+        let timer = Instant::now();
+
+        let erroneous_txs = wallet.state.read().await.verify_transactions(txs, slot, true).await?;
+        assert!(erroneous_txs.is_empty());
+        if append {
+            for params in txs_params {
+                for output in &params.outputs {
+                    wallet.money_merkle_tree.append(MerkleNode::from(output.coin.inner()));
+                }
+            }
+        }
         tx_action_benchmark.verify_times.push(timer.elapsed());
 
         Ok(())
@@ -146,7 +178,7 @@ impl TestHarness {
     pub async fn execute_erroneous_transfer_tx(
         &mut self,
         holder: Holder,
-        txs: &Vec<Transaction>,
+        txs: &[Transaction],
         slot: u64,
         erroneous: usize,
     ) -> Result<()> {