Browse Source

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

assert_trees() might seem placed randomly, but its not! Since we add stuff to each holder's merkle tree asyncronously, we have to ensure that everyone has added everything(along with gathering their coin) before asserting. Ignore any typos, its getting late...
aggstam 3 years ago
parent
commit
19b70a8d4b

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

@@ -169,17 +169,19 @@ async fn genesis_mint() -> Result<()> {
     info!(target: "money", "[Faucet] ==============================");
     info!(target: "money", "[Faucet] ==============================");
     info!(target: "money", "[Faucet] Executing Alice2Bob payment tx");
     info!(target: "money", "[Faucet] Executing Alice2Bob payment tx");
     info!(target: "money", "[Faucet] ==============================");
     info!(target: "money", "[Faucet] ==============================");
-    th.execute_transfer_tx(Holder::Faucet, &transfer_tx, &transfer_params, current_slot).await?;
+    th.execute_transfer_tx(Holder::Faucet, &transfer_tx, &transfer_params, current_slot, true)
+        .await?;
 
 
     info!(target: "money", "[Alice] ==============================");
     info!(target: "money", "[Alice] ==============================");
     info!(target: "money", "[Alice] Executing Alice2Bob payment tx");
     info!(target: "money", "[Alice] Executing Alice2Bob payment tx");
     info!(target: "money", "[Alice] ==============================");
     info!(target: "money", "[Alice] ==============================");
-    th.execute_transfer_tx(Holder::Alice, &transfer_tx, &transfer_params, current_slot).await?;
+    th.execute_transfer_tx(Holder::Alice, &transfer_tx, &transfer_params, current_slot, true)
+        .await?;
 
 
     info!(target: "money", "[Bob] ==============================");
     info!(target: "money", "[Bob] ==============================");
     info!(target: "money", "[Bob] Executing Alice2Bob payment tx");
     info!(target: "money", "[Bob] Executing Alice2Bob payment tx");
     info!(target: "money", "[Bob] ==============================");
     info!(target: "money", "[Bob] ==============================");
-    th.execute_transfer_tx(Holder::Bob, &transfer_tx, &transfer_params, current_slot).await?;
+    th.execute_transfer_tx(Holder::Bob, &transfer_tx, &transfer_params, current_slot, true).await?;
 
 
     th.assert_trees(&HOLDERS);
     th.assert_trees(&HOLDERS);
 
 
@@ -211,17 +213,19 @@ async fn genesis_mint() -> Result<()> {
     info!(target: "money", "[Faucet] ==============================");
     info!(target: "money", "[Faucet] ==============================");
     info!(target: "money", "[Faucet] Executing Bob2Alice payment tx");
     info!(target: "money", "[Faucet] Executing Bob2Alice payment tx");
     info!(target: "money", "[Faucet] ==============================");
     info!(target: "money", "[Faucet] ==============================");
-    th.execute_transfer_tx(Holder::Faucet, &transfer_tx, &transfer_params, current_slot).await?;
+    th.execute_transfer_tx(Holder::Faucet, &transfer_tx, &transfer_params, current_slot, true)
+        .await?;
 
 
     info!(target: "money", "[Alice] ==============================");
     info!(target: "money", "[Alice] ==============================");
     info!(target: "money", "[Alice] Executing Bob2Alice payment tx");
     info!(target: "money", "[Alice] Executing Bob2Alice payment tx");
     info!(target: "money", "[Alice] ==============================");
     info!(target: "money", "[Alice] ==============================");
-    th.execute_transfer_tx(Holder::Alice, &transfer_tx, &transfer_params, current_slot).await?;
+    th.execute_transfer_tx(Holder::Alice, &transfer_tx, &transfer_params, current_slot, true)
+        .await?;
 
 
     info!(target: "money", "[Bob] ==================+===========");
     info!(target: "money", "[Bob] ==================+===========");
     info!(target: "money", "[Bob] Executing Bob2Alice payment tx");
     info!(target: "money", "[Bob] Executing Bob2Alice payment tx");
     info!(target: "money", "[Bob] ==================+===========");
     info!(target: "money", "[Bob] ==================+===========");
-    th.execute_transfer_tx(Holder::Bob, &transfer_tx, &transfer_params, current_slot).await?;
+    th.execute_transfer_tx(Holder::Bob, &transfer_tx, &transfer_params, current_slot, true).await?;
 
 
     th.assert_trees(&HOLDERS);
     th.assert_trees(&HOLDERS);
 
 

+ 0 - 256
src/contract/money/tests/harness.rs

@@ -1,256 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::collections::HashMap;
-
-use darkfi::{
-    consensus::{
-        ValidatorState, ValidatorStatePtr, TESTNET_BOOTSTRAP_TIMESTAMP, TESTNET_GENESIS_HASH_BYTES,
-        TESTNET_GENESIS_TIMESTAMP, TESTNET_INITIAL_DISTRIBUTION,
-    },
-    runtime::vm_runtime::SMART_CONTRACT_ZKAS_DB_NAME,
-    tx::Transaction,
-    wallet::{WalletDb, WalletPtr},
-    zk::{empty_witnesses, halo2::Field, ProvingKey, ZkCircuit},
-    zkas::ZkBinary,
-    Result,
-};
-use darkfi_sdk::{
-    crypto::{Keypair, MerkleTree, PublicKey, DARK_TOKEN_ID, MONEY_CONTRACT_ID},
-    pasta::pallas,
-    ContractCall,
-};
-use darkfi_serial::{deserialize, serialize, Encodable};
-use log::info;
-use rand::rngs::OsRng;
-
-use darkfi_money_contract::{
-    client::{
-        token_freeze_v1::TokenFreezeCallBuilder, token_mint_v1::TokenMintCallBuilder,
-        transfer_v1::TransferCallBuilder, OwnCoin,
-    },
-    model::{MoneyTokenFreezeParamsV1, MoneyTokenMintParamsV1, MoneyTransferParamsV1},
-    MoneyFunction, MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
-    MONEY_CONTRACT_ZKAS_TOKEN_FRZ_NS_V1, MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1,
-};
-
-pub fn init_logger() {
-    let mut cfg = simplelog::ConfigBuilder::new();
-    cfg.add_filter_ignore("sled".to_string());
-    cfg.add_filter_ignore("blockchain::contractstore".to_string());
-
-    // We check this error so we can execute same file tests in parallel,
-    // otherwise second one fails to init logger here.
-    if let Err(_) = simplelog::TermLogger::init(
-        simplelog::LevelFilter::Info,
-        //simplelog::LevelFilter::Debug,
-        //simplelog::LevelFilter::Trace,
-        cfg.build(),
-        simplelog::TerminalMode::Mixed,
-        simplelog::ColorChoice::Auto,
-    ) {
-        info!(target: "money_harness", "Logger already initialized");
-    }
-}
-
-pub struct Wallet {
-    pub keypair: Keypair,
-    pub state: ValidatorStatePtr,
-    pub merkle_tree: MerkleTree,
-    pub wallet: WalletPtr,
-    pub coins: Vec<OwnCoin>,
-    pub spent_coins: Vec<OwnCoin>,
-}
-
-impl Wallet {
-    async fn new(keypair: Keypair, faucet_pubkeys: &[PublicKey]) -> Result<Self> {
-        let wallet = WalletDb::new("sqlite::memory:", "foo").await?;
-        let sled_db = sled::Config::new().temporary(true).open()?;
-
-        let state = ValidatorState::new(
-            &sled_db,
-            *TESTNET_BOOTSTRAP_TIMESTAMP,
-            *TESTNET_GENESIS_TIMESTAMP,
-            *TESTNET_GENESIS_HASH_BYTES,
-            *TESTNET_INITIAL_DISTRIBUTION,
-            wallet.clone(),
-            faucet_pubkeys.to_vec(),
-            false,
-            false,
-        )
-        .await?;
-
-        let merkle_tree = MerkleTree::new(100);
-
-        let coins = vec![];
-        let spent_coins = vec![];
-
-        Ok(Self { keypair, state, merkle_tree, wallet, coins, spent_coins })
-    }
-}
-
-pub struct MoneyTestHarness {
-    pub faucet: Wallet,
-    pub alice: Wallet,
-    pub bob: Wallet,
-    pub charlie: Wallet,
-    pub proving_keys: HashMap<&'static str, (ProvingKey, ZkBinary)>,
-}
-
-impl MoneyTestHarness {
-    pub async fn new() -> Result<Self> {
-        let faucet_kp = Keypair::random(&mut OsRng);
-        let faucet_pubkeys = vec![faucet_kp.public];
-        let faucet = Wallet::new(faucet_kp, &faucet_pubkeys).await?;
-
-        let alice_kp = Keypair::random(&mut OsRng);
-        let alice = Wallet::new(alice_kp, &faucet_pubkeys).await?;
-
-        let bob_kp = Keypair::random(&mut OsRng);
-        let bob = Wallet::new(bob_kp, &faucet_pubkeys).await?;
-
-        let charlie_kp = Keypair::random(&mut OsRng);
-        let charlie = Wallet::new(charlie_kp, &faucet_pubkeys).await?;
-
-        // Get the zkas circuits and build proving keys
-        let mut proving_keys = HashMap::new();
-        let alice_sled = alice.state.read().await.blockchain.sled_db.clone();
-        let db_handle = alice.state.read().await.blockchain.contracts.lookup(
-            &alice_sled,
-            &MONEY_CONTRACT_ID,
-            SMART_CONTRACT_ZKAS_DB_NAME,
-        )?;
-
-        macro_rules! mkpk {
-            ($ns:expr) => {
-                let zkas_bytes = db_handle.get(&serialize(&$ns))?.unwrap();
-                let (zkbin, _): (Vec<u8>, Vec<u8>) = deserialize(&zkas_bytes)?;
-                let zkbin = ZkBinary::decode(&zkbin)?;
-                let witnesses = empty_witnesses(&zkbin);
-                let circuit = ZkCircuit::new(witnesses, zkbin.clone());
-                let pk = ProvingKey::build(13, &circuit);
-                proving_keys.insert($ns, (pk, zkbin));
-            };
-        }
-
-        mkpk!(MONEY_CONTRACT_ZKAS_MINT_NS_V1);
-        mkpk!(MONEY_CONTRACT_ZKAS_BURN_NS_V1);
-        mkpk!(MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1);
-        mkpk!(MONEY_CONTRACT_ZKAS_TOKEN_FRZ_NS_V1);
-
-        Ok(Self { faucet, alice, bob, charlie, proving_keys })
-    }
-
-    pub fn airdrop_native(
-        &self,
-        value: u64,
-        recipient: PublicKey,
-    ) -> Result<(Transaction, MoneyTransferParamsV1)> {
-        let (mint_pk, mint_zkbin) = self.proving_keys.get(&MONEY_CONTRACT_ZKAS_MINT_NS_V1).unwrap();
-        let (burn_pk, burn_zkbin) = self.proving_keys.get(&MONEY_CONTRACT_ZKAS_BURN_NS_V1).unwrap();
-
-        let builder = TransferCallBuilder {
-            keypair: self.faucet.keypair,
-            recipient,
-            value,
-            token_id: *DARK_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: vec![],
-            tree: self.faucet.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: true,
-        };
-
-        let debris = builder.build()?;
-
-        let mut data = vec![MoneyFunction::TransferV1 as u8];
-        debris.params.encode(&mut data)?;
-        let calls = vec![ContractCall { contract_id: *MONEY_CONTRACT_ID, data }];
-        let proofs = vec![debris.proofs];
-        let mut tx = Transaction { calls, proofs, signatures: vec![] };
-        let sigs = tx.create_sigs(&mut OsRng, &debris.signature_secrets)?;
-        tx.signatures = vec![sigs];
-
-        Ok((tx, debris.params))
-    }
-
-    pub fn mint_token(
-        &self,
-        mint_authority: Keypair,
-        amount: u64,
-        recipient: PublicKey,
-    ) -> Result<(Transaction, MoneyTokenMintParamsV1)> {
-        let (token_mint_pk, token_mint_zkbin) =
-            self.proving_keys.get(&MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1).unwrap();
-
-        let builder = TokenMintCallBuilder {
-            mint_authority,
-            recipient,
-            amount,
-            spend_hook: pallas::Base::zero(),
-            user_data: pallas::Base::zero(),
-            token_mint_zkbin: token_mint_zkbin.clone(),
-            token_mint_pk: token_mint_pk.clone(),
-        };
-
-        let debris = builder.build()?;
-
-        let mut data = vec![MoneyFunction::TokenMintV1 as u8];
-        debris.params.encode(&mut data)?;
-        let calls = vec![ContractCall { contract_id: *MONEY_CONTRACT_ID, data }];
-        let proofs = vec![debris.proofs];
-        let mut tx = Transaction { calls, proofs, signatures: vec![] };
-        let sigs = tx.create_sigs(&mut OsRng, &[mint_authority.secret])?;
-        tx.signatures = vec![sigs];
-
-        Ok((tx, debris.params))
-    }
-
-    pub fn freeze_token(
-        &self,
-        mint_authority: Keypair,
-    ) -> Result<(Transaction, MoneyTokenFreezeParamsV1)> {
-        let (token_freeze_pk, token_freeze_zkbin) =
-            self.proving_keys.get(&MONEY_CONTRACT_ZKAS_TOKEN_FRZ_NS_V1).unwrap();
-
-        let builder = TokenFreezeCallBuilder {
-            mint_authority,
-            token_freeze_zkbin: token_freeze_zkbin.clone(),
-            token_freeze_pk: token_freeze_pk.clone(),
-        };
-        let debris = builder.build()?;
-
-        let mut data = vec![MoneyFunction::TokenFreezeV1 as u8];
-        debris.params.encode(&mut data)?;
-        let calls = vec![ContractCall { contract_id: *MONEY_CONTRACT_ID, data }];
-        let proofs = vec![debris.proofs];
-        let mut tx = Transaction { calls, proofs, signatures: vec![] };
-        let sigs = tx.create_sigs(&mut OsRng, &[mint_authority.secret])?;
-        tx.signatures = vec![sigs];
-
-        Ok((tx, debris.params))
-    }
-}

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

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

File diff suppressed because it is too large
+ 108 - 736
src/contract/money/tests/mint_pay_swap.rs


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

@@ -130,21 +130,24 @@ async fn txs_verification() -> Result<()> {
     info!(target: "money", "[Faucet] ==============================");
     info!(target: "money", "[Faucet] ==============================");
     th.execute_erroneous_transfer_tx(Holder::Faucet, &transactions, current_slot, duplicates - 1)
     th.execute_erroneous_transfer_tx(Holder::Faucet, &transactions, current_slot, duplicates - 1)
         .await?;
         .await?;
-    th.execute_transfer_tx(Holder::Faucet, &transactions[0], &txs_params[0], current_slot).await?;
+    th.execute_transfer_tx(Holder::Faucet, &transactions[0], &txs_params[0], current_slot, true)
+        .await?;
 
 
     info!(target: "money", "[Alice] ==============================");
     info!(target: "money", "[Alice] ==============================");
     info!(target: "money", "[Alice] Executing Alice2Bob payment tx");
     info!(target: "money", "[Alice] Executing Alice2Bob payment tx");
     info!(target: "money", "[Alice] ==============================");
     info!(target: "money", "[Alice] ==============================");
     th.execute_erroneous_transfer_tx(Holder::Alice, &transactions, current_slot, duplicates - 1)
     th.execute_erroneous_transfer_tx(Holder::Alice, &transactions, current_slot, duplicates - 1)
         .await?;
         .await?;
-    th.execute_transfer_tx(Holder::Alice, &transactions[0], &txs_params[0], current_slot).await?;
+    th.execute_transfer_tx(Holder::Alice, &transactions[0], &txs_params[0], current_slot, true)
+        .await?;
 
 
     info!(target: "money", "[Bob] ==============================");
     info!(target: "money", "[Bob] ==============================");
     info!(target: "money", "[Bob] Executing Alice2Bob payment tx");
     info!(target: "money", "[Bob] Executing Alice2Bob payment tx");
     info!(target: "money", "[Bob] ==============================");
     info!(target: "money", "[Bob] ==============================");
     th.execute_erroneous_transfer_tx(Holder::Bob, &transactions, current_slot, duplicates - 1)
     th.execute_erroneous_transfer_tx(Holder::Bob, &transactions, current_slot, duplicates - 1)
         .await?;
         .await?;
-    th.execute_transfer_tx(Holder::Bob, &transactions[0], &txs_params[0], current_slot).await?;
+    th.execute_transfer_tx(Holder::Bob, &transactions[0], &txs_params[0], current_slot, true)
+        .await?;
 
 
     th.assert_trees(&HOLDERS);
     th.assert_trees(&HOLDERS);
 
 
@@ -159,6 +162,9 @@ async fn txs_verification() -> Result<()> {
     assert!(alice_owncoins.len() == 1);
     assert!(alice_owncoins.len() == 1);
     assert!(bob_owncoins.len() == 1);
     assert!(bob_owncoins.len() == 1);
 
 
+    // Statistics
+    th.statistics();
+
     // Thanks for reading
     // Thanks for reading
     Ok(())
     Ok(())
 }
 }

+ 8 - 0
src/contract/money/tests/verification_bench.rs

@@ -110,6 +110,10 @@ async fn alice2alice_random_amounts() -> Result<()> {
         th.assert_trees(&HOLDERS);
         th.assert_trees(&HOLDERS);
     }
     }
 
 
+    // Statistics
+    th.statistics();
+
+    // Thanks for reading
     Ok(())
     Ok(())
 }
 }
 
 
@@ -229,5 +233,9 @@ async fn alice2alice_multiplecoins_random_amounts() -> Result<()> {
         th.assert_trees(&HOLDERS);
         th.assert_trees(&HOLDERS);
     }
     }
 
 
+    // Statistics
+    th.statistics();
+
+    // Thanks for reading
     Ok(())
     Ok(())
 }
 }

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

@@ -61,6 +61,7 @@ mod consensus_unstake;
 mod consensus_unstake_request;
 mod consensus_unstake_request;
 mod money_airdrop;
 mod money_airdrop;
 mod money_genesis_mint;
 mod money_genesis_mint;
+mod money_otc_swap;
 mod money_token;
 mod money_token;
 mod money_transfer;
 mod money_transfer;
 
 
@@ -333,6 +334,40 @@ impl TestHarness {
         Ok(owncoins)
         Ok(owncoins)
     }
     }
 
 
+    pub fn gather_owncoin_at_index(
+        &mut self,
+        holder: Holder,
+        outputs: &[Output],
+        index: usize,
+    ) -> Result<OwnCoin> {
+        let wallet = self.holders.get_mut(&holder).unwrap();
+        let secret_key = wallet.keypair.secret;
+        let mut owncoin = None;
+        for (i, output) in outputs.iter().enumerate() {
+            wallet.money_merkle_tree.append(MerkleNode::from(output.coin.inner()));
+            if i == index {
+                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());
+                owncoin = Some(oc);
+            }
+        }
+
+        Ok(owncoin.unwrap())
+    }
+
     pub fn gather_consensus_staked_owncoin(
     pub fn gather_consensus_staked_owncoin(
         &mut self,
         &mut self,
         holder: Holder,
         holder: Holder,

+ 180 - 0
src/contract/test-harness/src/money_otc_swap.rs

@@ -0,0 +1,180 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2023 Dyne.org foundation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+use std::time::Instant;
+
+use darkfi::{tx::Transaction, zk::halo2::Field, Result};
+use darkfi_money_contract::{
+    client::{swap_v1::SwapCallBuilder, OwnCoin},
+    model::MoneyTransferParamsV1,
+    MoneyFunction, MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
+};
+use darkfi_sdk::{
+    crypto::{MerkleNode, ValueBlind, MONEY_CONTRACT_ID},
+    pasta::pallas,
+    ContractCall,
+};
+use darkfi_serial::{serialize, Encodable};
+use rand::rngs::OsRng;
+
+use super::{Holder, TestHarness, TxAction};
+
+impl TestHarness {
+    pub fn otc_swap(
+        &mut self,
+        holder0: Holder,
+        owncoin0: OwnCoin,
+        holder1: Holder,
+        owncoin1: OwnCoin,
+    ) -> Result<(Transaction, MoneyTransferParamsV1)> {
+        let wallet0 = self.holders.get(&holder0).unwrap();
+        let wallet1 = self.holders.get(&holder1).unwrap();
+        let (mint_pk, mint_zkbin) = self.proving_keys.get(&MONEY_CONTRACT_ZKAS_MINT_NS_V1).unwrap();
+        let (burn_pk, burn_zkbin) = self.proving_keys.get(&MONEY_CONTRACT_ZKAS_BURN_NS_V1).unwrap();
+        let tx_action_benchmark =
+            self.tx_action_benchmarks.get_mut(&TxAction::MoneyOtcSwap).unwrap();
+        let timer = Instant::now();
+
+        // We're just going to be using a zero spend-hook and user-data
+        let rcpt_spend_hook = pallas::Base::zero();
+        let rcpt_user_data = pallas::Base::zero();
+        let rcpt_user_data_blind = pallas::Base::random(&mut OsRng);
+
+        // Generating  swap blinds
+        let value_send_blind = ValueBlind::random(&mut OsRng);
+        let value_recv_blind = ValueBlind::random(&mut OsRng);
+        let token_send_blind = ValueBlind::random(&mut OsRng);
+        let token_recv_blind = ValueBlind::random(&mut OsRng);
+
+        // Builder first holder part
+        let builder = SwapCallBuilder {
+            pubkey: wallet0.keypair.public,
+            value_send: owncoin0.note.value,
+            token_id_send: owncoin0.note.token_id,
+            value_recv: owncoin1.note.value,
+            token_id_recv: owncoin1.note.token_id,
+            user_data_blind_send: rcpt_user_data_blind,
+            spend_hook_recv: rcpt_spend_hook,
+            user_data_recv: rcpt_user_data,
+            value_blinds: [value_send_blind, value_recv_blind],
+            token_blinds: [token_send_blind, token_recv_blind],
+            coin: owncoin0.clone(),
+            tree: wallet0.money_merkle_tree.clone(),
+            mint_zkbin: mint_zkbin.clone(),
+            mint_pk: mint_pk.clone(),
+            burn_zkbin: burn_zkbin.clone(),
+            burn_pk: burn_pk.clone(),
+        };
+
+        let debris0 = builder.build()?;
+
+        assert!(debris0.params.inputs.len() == 1);
+        assert!(debris0.params.outputs.len() == 1);
+
+        // Builder second holder part
+        let builder = SwapCallBuilder {
+            pubkey: wallet1.keypair.public,
+            value_send: owncoin1.note.value,
+            token_id_send: owncoin1.note.token_id,
+            value_recv: owncoin0.note.value,
+            token_id_recv: owncoin0.note.token_id,
+            user_data_blind_send: rcpt_user_data_blind,
+            spend_hook_recv: rcpt_spend_hook,
+            user_data_recv: rcpt_user_data,
+            value_blinds: [value_recv_blind, value_send_blind],
+            token_blinds: [token_recv_blind, token_send_blind],
+            coin: owncoin1,
+            tree: wallet1.money_merkle_tree.clone(),
+            mint_zkbin: mint_zkbin.clone(),
+            mint_pk: mint_pk.clone(),
+            burn_zkbin: burn_zkbin.clone(),
+            burn_pk: burn_pk.clone(),
+        };
+
+        let debris1 = builder.build()?;
+
+        assert!(debris1.params.inputs.len() == 1);
+        assert!(debris1.params.outputs.len() == 1);
+
+        // Then second holder combines the halves
+        let swap_full_params = MoneyTransferParamsV1 {
+            clear_inputs: vec![],
+            inputs: vec![debris0.params.inputs[0].clone(), debris1.params.inputs[0].clone()],
+            outputs: vec![debris0.params.outputs[0].clone(), debris1.params.outputs[0].clone()],
+        };
+
+        let swap_full_proofs = vec![
+            debris0.proofs[0].clone(),
+            debris1.proofs[0].clone(),
+            debris0.proofs[1].clone(),
+            debris1.proofs[1].clone(),
+        ];
+
+        // And signs the transaction
+        let mut data = vec![MoneyFunction::OtcSwapV1 as u8];
+        swap_full_params.encode(&mut data)?;
+        let mut tx = Transaction {
+            calls: vec![ContractCall { contract_id: *MONEY_CONTRACT_ID, data }],
+            proofs: vec![swap_full_proofs],
+            signatures: vec![],
+        };
+        let sigs = tx.create_sigs(&mut OsRng, &[debris1.signature_secret])?;
+        tx.signatures = vec![sigs];
+
+        // First holder gets the partially signed transaction and adds their signature
+        let sigs = tx.create_sigs(&mut OsRng, &[debris0.signature_secret])?;
+        tx.signatures[0].insert(0, sigs[0]);
+        tx_action_benchmark.creation_times.push(timer.elapsed());
+
+        // Calculate transaction sizes
+        let encoded: Vec<u8> = serialize(&tx);
+        let size = std::mem::size_of_val(&*encoded);
+        tx_action_benchmark.sizes.push(size);
+        let base58 = bs58::encode(&encoded).into_string();
+        let size = std::mem::size_of_val(&*base58);
+        tx_action_benchmark.broadcasted_sizes.push(size);
+
+        Ok((tx, swap_full_params))
+    }
+
+    pub async fn execute_otc_swap_tx(
+        &mut self,
+        holder: Holder,
+        tx: &Transaction,
+        params: &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::MoneyOtcSwap).unwrap();
+        let timer = Instant::now();
+
+        let erroneous_txs =
+            wallet.state.read().await.verify_transactions(&[tx.clone()], slot, true).await?;
+        assert!(erroneous_txs.is_empty());
+        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(())
+    }
+}

Some files were not shown because too many files changed in this diff