Parcourir la source

contract/money: Clean up and merge integration tests

parazyd il y a 3 ans
Parent
commit
1381ce7729

+ 866 - 0
src/contract/money/tests/drop_pay_swap.rs

@@ -0,0 +1,866 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2022 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/>.
+ */
+
+//! Integration test for payments between Alice and Bob.
+//!
+//! We first airdrop them different tokens, and then they send them to each
+//! other a couple of times.
+//!
+//! With this test, we want to confirm the money contract transfer state
+//! transitions work between multiple parties and are able to be verified.
+//! We also test atomic swaps with some of the coins that have been produced.
+//!
+//! TODO: Malicious cases
+
+use darkfi::{tx::Transaction, Result};
+use darkfi_sdk::{
+    crypto::{poseidon_hash, MerkleNode, Nullifier, TokenId},
+    incrementalmerkletree::Tree,
+    pasta::{group::ff::Field, pallas},
+    tx::ContractCall,
+};
+use darkfi_serial::Encodable;
+use log::info;
+use rand::rngs::OsRng;
+
+use darkfi_money_contract::{
+    client::{build_half_swap_tx, build_transfer_tx, Coin, EncryptedNote, OwnCoin},
+    state::MoneyTransferParams,
+    MoneyFunction,
+};
+
+mod harness;
+use harness::MoneyTestHarness;
+
+#[async_std::test]
+async fn money_contract_transfer() -> Result<()> {
+    let mut cfg = simplelog::ConfigBuilder::new();
+    cfg.add_filter_ignore("sled".to_string());
+    simplelog::TermLogger::init(
+        //simplelog::LevelFilter::Debug,
+        simplelog::LevelFilter::Info,
+        cfg.build(),
+        simplelog::TerminalMode::Mixed,
+        simplelog::ColorChoice::Auto,
+    )?;
+
+    // Some numbers we want to assert
+    const ALICE_INITIAL: u64 = 100;
+    const BOB_INITIAL: u64 = 200;
+
+    // Alice = 50 ALICE
+    // Bob = 200 BOB + 50 ALICE
+    const ALICE_FIRST_SEND: u64 = ALICE_INITIAL - 50;
+    // Alice = 50 ALICE + 180 BOB
+    // Bob = 20 BOB + 50 ALICE
+    const BOB_FIRST_SEND: u64 = BOB_INITIAL - 20;
+
+    let mut th = MoneyTestHarness::new().await?;
+
+    // The faucet will now mint some tokens for Alice and Bob
+    let alice_token_id = TokenId::from(pallas::Base::random(&mut OsRng));
+    let bob_token_id = TokenId::from(pallas::Base::random(&mut OsRng));
+
+    let mut alice_owncoins = vec![];
+    let mut bob_owncoins = vec![];
+
+    info!("[Faucet] ===================================================");
+    info!("[Faucet] Building Money::Transfer params for Alice's airdrop");
+    info!("[Faucet] ===================================================");
+    let (alice_params, alice_proofs, alicedrop_secret_keys, _spent_coins) = build_transfer_tx(
+        &th.faucet_kp,
+        &th.alice_kp.public,
+        ALICE_INITIAL,
+        alice_token_id,
+        &[],
+        &th.faucet_merkle_tree,
+        &th.mint_zkbin,
+        &th.mint_pk,
+        &th.burn_zkbin,
+        &th.burn_pk,
+        true,
+    )?;
+
+    info!("[Faucet] =================================================");
+    info!("[Faucet] Building Money::Transfer params for Bob's airdrop");
+    info!("[Faucet] =================================================");
+    let (bob_params, bob_proofs, bobdrop_secret_keys, _spent_coins) = build_transfer_tx(
+        &th.faucet_kp,
+        &th.bob_kp.public,
+        BOB_INITIAL,
+        bob_token_id,
+        &[],
+        &th.faucet_merkle_tree,
+        &th.mint_zkbin,
+        &th.mint_pk,
+        &th.burn_zkbin,
+        &th.burn_pk,
+        true,
+    )?;
+
+    info!("[Faucet] =====================================");
+    info!("[Faucet] Building airdrop tx with Alice params");
+    info!("[Faucet] =====================================");
+    let mut data = vec![MoneyFunction::Transfer as u8];
+    alice_params.encode(&mut data)?;
+    let calls = vec![ContractCall { contract_id: th.money_contract_id, data }];
+    let proofs = vec![alice_proofs];
+    let mut alicedrop_tx = Transaction { calls, proofs, signatures: vec![] };
+    let sigs = alicedrop_tx.create_sigs(&mut OsRng, &alicedrop_secret_keys)?;
+    alicedrop_tx.signatures = vec![sigs];
+
+    info!("[Faucet] ===================================");
+    info!("[Faucet] Building airdrop tx with Bob params");
+    info!("[Faucet] ===================================");
+    let mut data = vec![MoneyFunction::Transfer as u8];
+    bob_params.encode(&mut data)?;
+    let calls = vec![ContractCall { contract_id: th.money_contract_id, data }];
+    let proofs = vec![bob_proofs];
+    let mut bobdrop_tx = Transaction { calls, proofs, signatures: vec![] };
+    let sigs = bobdrop_tx.create_sigs(&mut OsRng, &bobdrop_secret_keys)?;
+    bobdrop_tx.signatures = vec![sigs];
+
+    info!("[Faucet] ==========================");
+    info!("[Faucet] Executing Alice airdrop tx");
+    info!("[Faucet] ==========================");
+    th.faucet_state.read().await.verify_transactions(&[alicedrop_tx.clone()], true).await?;
+    th.faucet_merkle_tree.append(&MerkleNode::from(alice_params.outputs[0].coin));
+
+    info!("[Faucet] ========================");
+    info!("[Faucet] Executing Bob airdrop tx");
+    info!("[Faucet] ========================");
+    th.faucet_state.read().await.verify_transactions(&[bobdrop_tx.clone()], true).await?;
+    th.faucet_merkle_tree.append(&MerkleNode::from(bob_params.outputs[0].coin));
+
+    info!("[Alice] ==========================");
+    info!("[Alice] Executing Alice airdrop tx");
+    info!("[Alice] ==========================");
+    th.alice_state.read().await.verify_transactions(&[alicedrop_tx.clone()], true).await?;
+    th.alice_merkle_tree.append(&MerkleNode::from(alice_params.outputs[0].coin));
+    // Alice has to witness this coin because it's hers.
+    let alice_leaf_pos = th.alice_merkle_tree.witness().unwrap();
+
+    info!("[Alice] ========================");
+    info!("[Alice] Executing Bob airdrop tx");
+    info!("[Alice] ========================");
+    th.alice_state.read().await.verify_transactions(&[bobdrop_tx.clone()], true).await?;
+    th.alice_merkle_tree.append(&MerkleNode::from(bob_params.outputs[0].coin));
+
+    info!("[Bob] ==========================");
+    info!("[Bob] Executing Alice airdrop tx");
+    info!("[Bob] ==========================");
+    th.bob_state.read().await.verify_transactions(&[alicedrop_tx.clone()], true).await?;
+    th.bob_merkle_tree.append(&MerkleNode::from(alice_params.outputs[0].coin));
+
+    info!("[Bob] ========================");
+    info!("[Bob] Executing Bob airdrop tx");
+    info!("[Bob] ========================");
+    th.bob_state.read().await.verify_transactions(&[bobdrop_tx.clone()], true).await?;
+    th.bob_merkle_tree.append(&MerkleNode::from(bob_params.outputs[0].coin));
+    let bob_leaf_pos = th.bob_merkle_tree.witness().unwrap();
+
+    assert!(th.alice_merkle_tree.root(0).unwrap() == th.bob_merkle_tree.root(0).unwrap());
+    assert!(th.faucet_merkle_tree.root(0).unwrap() == th.bob_merkle_tree.root(0).unwrap());
+
+    // Alice builds an `OwnCoin` from her airdrop
+    let ciphertext = alice_params.outputs[0].ciphertext.clone();
+    let ephem_public = alice_params.outputs[0].ephem_public;
+    let e_note = EncryptedNote { ciphertext, ephem_public };
+    let note = e_note.decrypt(&th.alice_kp.secret)?;
+    let alice_oc = OwnCoin {
+        coin: Coin::from(alice_params.outputs[0].coin),
+        note: note.clone(),
+        secret: th.alice_kp.secret, // <-- What should this be?
+        nullifier: Nullifier::from(poseidon_hash([th.alice_kp.secret.inner(), note.serial])),
+        leaf_position: alice_leaf_pos,
+    };
+    alice_owncoins.push(alice_oc);
+
+    // Bob too
+    let ciphertext = bob_params.outputs[0].ciphertext.clone();
+    let ephem_public = bob_params.outputs[0].ephem_public;
+    let e_note = EncryptedNote { ciphertext, ephem_public };
+    let note = e_note.decrypt(&th.bob_kp.secret)?;
+    let bob_oc = OwnCoin {
+        coin: Coin::from(bob_params.outputs[0].coin),
+        note: note.clone(),
+        secret: th.bob_kp.secret, // <-- What should this be?
+        nullifier: Nullifier::from(poseidon_hash([th.bob_kp.secret.inner(), note.serial])),
+        leaf_position: bob_leaf_pos,
+    };
+    bob_owncoins.push(bob_oc);
+
+    // Now Alice can send a little bit of funds to Bob
+    info!("[Alice] ====================================================");
+    info!("[Alice] Building Money::Transfer params for a payment to Bob");
+    info!("[Alice] ====================================================");
+    let (alice2bob_params, alice2bob_proofs, alice2bob_secret_keys, alice2bob_spent_coins) =
+        build_transfer_tx(
+            &th.alice_kp,
+            &th.bob_kp.public,
+            ALICE_FIRST_SEND,
+            alice_token_id,
+            &alice_owncoins,
+            &th.alice_merkle_tree,
+            &th.mint_zkbin,
+            &th.mint_pk,
+            &th.burn_zkbin,
+            &th.burn_pk,
+            false,
+        )?;
+
+    assert!(alice2bob_params.inputs.len() == 1);
+    assert!(alice2bob_params.outputs.len() == 2);
+    assert!(alice2bob_spent_coins.len() == 1);
+    alice_owncoins.retain(|x| x != &alice2bob_spent_coins[0]);
+    assert!(alice_owncoins.is_empty());
+
+    info!("[Alice] ==========================");
+    info!("[Alice] Building payment tx to Bob");
+    info!("[Alice] ==========================");
+    let mut data = vec![MoneyFunction::Transfer as u8];
+    alice2bob_params.encode(&mut data)?;
+    let calls = vec![ContractCall { contract_id: th.money_contract_id, data }];
+    let proofs = vec![alice2bob_proofs];
+    let mut alice2bob_tx = Transaction { calls, proofs, signatures: vec![] };
+    let sigs = alice2bob_tx.create_sigs(&mut OsRng, &alice2bob_secret_keys)?;
+    alice2bob_tx.signatures = vec![sigs];
+
+    info!("[Faucet] ==============================");
+    info!("[Faucet] Executing Alice2Bob payment tx");
+    info!("[Faucet] ==============================");
+    th.faucet_state.read().await.verify_transactions(&[alice2bob_tx.clone()], true).await?;
+    th.faucet_merkle_tree.append(&MerkleNode::from(alice2bob_params.outputs[0].coin));
+    th.faucet_merkle_tree.append(&MerkleNode::from(alice2bob_params.outputs[1].coin));
+
+    info!("[Alice] ==============================");
+    info!("[Alice] Executing Alice2Bob payment tx");
+    info!("[Alice] ==============================");
+    th.alice_state.read().await.verify_transactions(&[alice2bob_tx.clone()], true).await?;
+    th.alice_merkle_tree.append(&MerkleNode::from(alice2bob_params.outputs[0].coin));
+    let alice_leaf_pos = th.alice_merkle_tree.witness().unwrap();
+    th.alice_merkle_tree.append(&MerkleNode::from(alice2bob_params.outputs[1].coin));
+
+    info!("[Bob] ==============================");
+    info!("[Bob] Executing Alice2Bob payment tx");
+    info!("[Bob] ==============================");
+    th.bob_state.read().await.verify_transactions(&[alice2bob_tx.clone()], true).await?;
+    th.bob_merkle_tree.append(&MerkleNode::from(alice2bob_params.outputs[0].coin));
+    th.bob_merkle_tree.append(&MerkleNode::from(alice2bob_params.outputs[1].coin));
+    let bob_leaf_pos = th.bob_merkle_tree.witness().unwrap();
+
+    assert!(th.alice_merkle_tree.root(0).unwrap() == th.bob_merkle_tree.root(0).unwrap());
+    assert!(th.faucet_merkle_tree.root(0).unwrap() == th.bob_merkle_tree.root(0).unwrap());
+
+    // Alice should now have one OwnCoin with the change from the above transaction.
+    let ciphertext = alice2bob_params.outputs[0].ciphertext.clone();
+    let ephem_public = alice2bob_params.outputs[0].ephem_public;
+    let e_note = EncryptedNote { ciphertext, ephem_public };
+    let note = e_note.decrypt(&th.alice_kp.secret)?;
+    let alice_oc = OwnCoin {
+        coin: Coin::from(alice2bob_params.outputs[0].coin),
+        note: note.clone(),
+        secret: th.alice_kp.secret, // <-- What should this be?
+        nullifier: Nullifier::from(poseidon_hash([th.alice_kp.secret.inner(), note.serial])),
+        leaf_position: alice_leaf_pos,
+    };
+    alice_owncoins.push(alice_oc);
+
+    // Bob should have his old one, and this new one.
+    let ciphertext = alice2bob_params.outputs[1].ciphertext.clone();
+    let ephem_public = alice2bob_params.outputs[1].ephem_public;
+    let e_note = EncryptedNote { ciphertext, ephem_public };
+    let note = e_note.decrypt(&th.bob_kp.secret)?;
+    let bob_oc = OwnCoin {
+        coin: Coin::from(alice2bob_params.outputs[1].coin),
+        note: note.clone(),
+        secret: th.bob_kp.secret, // <-- What should this be?
+        nullifier: Nullifier::from(poseidon_hash([th.bob_kp.secret.inner(), note.serial])),
+        leaf_position: bob_leaf_pos,
+    };
+    bob_owncoins.push(bob_oc);
+
+    assert!(alice_owncoins.len() == 1);
+    assert!(bob_owncoins.len() == 2);
+
+    // Bob can send a little bit to Alice as well
+    info!("[Bob] ======================================================");
+    info!("[Bob] Building Money::Transfer params for a payment to Alice");
+    info!("[Bob] ======================================================");
+    let mut bob_owncoins_tmp = bob_owncoins.clone();
+    bob_owncoins_tmp.retain(|x| x.note.token_id == bob_token_id);
+    let (bob2alice_params, bob2alice_proofs, bob2alice_secret_keys, bob2alice_spent_coins) =
+        build_transfer_tx(
+            &th.bob_kp,
+            &th.alice_kp.public,
+            BOB_FIRST_SEND,
+            bob_token_id,
+            &bob_owncoins_tmp,
+            &th.bob_merkle_tree,
+            &th.mint_zkbin,
+            &th.mint_pk,
+            &th.burn_zkbin,
+            &th.burn_pk,
+            false,
+        )?;
+
+    assert!(bob2alice_params.inputs.len() == 1);
+    assert!(bob2alice_params.outputs.len() == 2);
+    assert!(bob2alice_spent_coins.len() == 1);
+    bob_owncoins.retain(|x| x != &bob2alice_spent_coins[0]);
+    assert!(bob_owncoins.len() == 1);
+
+    info!("[Bob] ============================");
+    info!("[Bob] Building payment tx to Alice");
+    info!("[Bob] ============================");
+    let mut data = vec![MoneyFunction::Transfer as u8];
+    bob2alice_params.encode(&mut data)?;
+    let calls = vec![ContractCall { contract_id: th.money_contract_id, data }];
+    let proofs = vec![bob2alice_proofs];
+    let mut bob2alice_tx = Transaction { calls, proofs, signatures: vec![] };
+    let sigs = bob2alice_tx.create_sigs(&mut OsRng, &bob2alice_secret_keys)?;
+    bob2alice_tx.signatures = vec![sigs];
+
+    info!("[Faucet] ==============================");
+    info!("[Faucet] Executing Bob2Alice payment tx");
+    info!("[Faucet] ==============================");
+    th.faucet_state.read().await.verify_transactions(&[bob2alice_tx.clone()], true).await?;
+    th.faucet_merkle_tree.append(&MerkleNode::from(bob2alice_params.outputs[0].coin));
+    th.faucet_merkle_tree.append(&MerkleNode::from(bob2alice_params.outputs[1].coin));
+
+    info!("[Alice] ==============================");
+    info!("[Alice] Executing Bob2Alice payment tx");
+    info!("[Alice] ==============================");
+    th.alice_state.read().await.verify_transactions(&[bob2alice_tx.clone()], true).await?;
+    th.alice_merkle_tree.append(&MerkleNode::from(bob2alice_params.outputs[0].coin));
+    th.alice_merkle_tree.append(&MerkleNode::from(bob2alice_params.outputs[1].coin));
+    let alice_leaf_pos = th.alice_merkle_tree.witness().unwrap();
+
+    info!("[Bob] ==================+===========");
+    info!("[Bob] Executing Bob2Alice payment tx");
+    info!("[Bob] ==================+===========");
+    th.bob_state.read().await.verify_transactions(&[bob2alice_tx.clone()], true).await?;
+    th.bob_merkle_tree.append(&MerkleNode::from(bob2alice_params.outputs[0].coin));
+    let bob_leaf_pos = th.bob_merkle_tree.witness().unwrap();
+    th.bob_merkle_tree.append(&MerkleNode::from(bob2alice_params.outputs[1].coin));
+
+    // Alice should now have two OwnCoins
+    let ciphertext = bob2alice_params.outputs[1].ciphertext.clone();
+    let ephem_public = bob2alice_params.outputs[1].ephem_public;
+    let e_note = EncryptedNote { ciphertext, ephem_public };
+    let note = e_note.decrypt(&th.alice_kp.secret)?;
+    let alice_oc = OwnCoin {
+        coin: Coin::from(bob2alice_params.outputs[1].coin),
+        note: note.clone(),
+        secret: th.alice_kp.secret, // <-- What should this be?
+        nullifier: Nullifier::from(poseidon_hash([th.alice_kp.secret.inner(), note.serial])),
+        leaf_position: alice_leaf_pos,
+    };
+    alice_owncoins.push(alice_oc);
+
+    // Bob should have two with the change from the above tx
+    let ciphertext = bob2alice_params.outputs[0].ciphertext.clone();
+    let ephem_public = bob2alice_params.outputs[0].ephem_public;
+    let e_note = EncryptedNote { ciphertext, ephem_public };
+    let note = e_note.decrypt(&th.bob_kp.secret)?;
+    let bob_oc = OwnCoin {
+        coin: Coin::from(bob2alice_params.outputs[0].coin),
+        note: note.clone(),
+        secret: th.bob_kp.secret, // <-- What should this be?
+        nullifier: Nullifier::from(poseidon_hash([th.bob_kp.secret.inner(), note.serial])),
+        leaf_position: bob_leaf_pos,
+    };
+    bob_owncoins.push(bob_oc);
+
+    assert!(alice_owncoins.len() == 2);
+    assert!(bob_owncoins.len() == 2);
+    assert!(th.alice_merkle_tree.root(0).unwrap() == th.bob_merkle_tree.root(0).unwrap());
+    assert!(th.faucet_merkle_tree.root(0).unwrap() == th.bob_merkle_tree.root(0).unwrap());
+
+    assert!(alice_owncoins[0].note.value == ALICE_INITIAL - ALICE_FIRST_SEND);
+    assert!(alice_owncoins[0].note.token_id == alice_token_id);
+    assert!(alice_owncoins[1].note.value == BOB_FIRST_SEND);
+    assert!(alice_owncoins[1].note.token_id == bob_token_id);
+
+    assert!(bob_owncoins[0].note.value == ALICE_FIRST_SEND);
+    assert!(bob_owncoins[0].note.token_id == alice_token_id);
+    assert!(bob_owncoins[1].note.value == BOB_INITIAL - BOB_FIRST_SEND);
+    assert!(bob_owncoins[1].note.token_id == bob_token_id);
+
+    // Alice and Bob decide to swap back their tokens so Alice gets back her initial
+    // tokens and Bob gets his.
+    info!("[Alice] Building OtcSwap half");
+    let (
+        alice_swap_params,
+        alice_swap_proofs,
+        alice_swap_secret_keys,
+        alice_swap_spent_coins,
+        alice_value_blinds,
+        alice_token_blinds,
+    ) = build_half_swap_tx(
+        &th.alice_kp.public,
+        BOB_FIRST_SEND,
+        bob_token_id,
+        ALICE_FIRST_SEND,
+        alice_token_id,
+        &[],
+        &[],
+        &[alice_owncoins[1].clone()],
+        &th.alice_merkle_tree,
+        &th.mint_zkbin,
+        &th.mint_pk,
+        &th.burn_zkbin,
+        &th.burn_pk,
+    )?;
+
+    assert!(alice_swap_params.inputs.len() == 1);
+    assert!(alice_swap_params.outputs.len() == 1);
+    assert!(alice_swap_spent_coins.len() == 1);
+    alice_owncoins.retain(|x| x != &alice_swap_spent_coins[0]);
+    assert!(alice_owncoins.len() == 1);
+
+    // Alice sends Bob necessary data and he builds his half.
+    info!("[Bob] Building OtcSwap half");
+    let (
+        bob_swap_params,
+        bob_swap_proofs,
+        bob_swap_secret_keys,
+        bob_swap_spent_coins,
+        _bob_value_blinds,
+        _bob_token_blinds,
+    ) = build_half_swap_tx(
+        &th.bob_kp.public,
+        ALICE_FIRST_SEND,
+        alice_token_id,
+        BOB_FIRST_SEND,
+        bob_token_id,
+        &alice_value_blinds,
+        &alice_token_blinds,
+        &[bob_owncoins[0].clone()],
+        &th.bob_merkle_tree,
+        &th.mint_zkbin,
+        &th.mint_pk,
+        &th.burn_zkbin,
+        &th.burn_pk,
+    )?;
+
+    assert!(bob_swap_params.inputs.len() == 1);
+    assert!(bob_swap_params.outputs.len() == 1);
+    assert!(bob_swap_spent_coins.len() == 1);
+    bob_owncoins.retain(|x| x != &bob_swap_spent_coins[0]);
+    assert!(bob_owncoins.len() == 1);
+
+    // Then he combines the halves
+    let swap_full_params = MoneyTransferParams {
+        clear_inputs: vec![],
+        inputs: vec![alice_swap_params.inputs[0].clone(), bob_swap_params.inputs[0].clone()],
+        outputs: vec![alice_swap_params.outputs[0].clone(), bob_swap_params.outputs[0].clone()],
+    };
+
+    let swap_full_proofs = vec![
+        alice_swap_proofs[0].clone(),
+        bob_swap_proofs[0].clone(),
+        alice_swap_proofs[1].clone(),
+        bob_swap_proofs[1].clone(),
+    ];
+
+    // And signs the transaction
+    let mut data = vec![MoneyFunction::OtcSwap as u8];
+    swap_full_params.encode(&mut data)?;
+    let mut alicebob_swap_tx = Transaction {
+        calls: vec![ContractCall { contract_id: th.money_contract_id, data }],
+        proofs: vec![swap_full_proofs],
+        signatures: vec![],
+    };
+    let sigs = alicebob_swap_tx.create_sigs(&mut OsRng, &bob_swap_secret_keys)?;
+    alicebob_swap_tx.signatures = vec![sigs];
+
+    // Alice gets the partially signed transaction and adds her signature
+    let sigs = alicebob_swap_tx.create_sigs(&mut OsRng, &alice_swap_secret_keys)?;
+    alicebob_swap_tx.signatures[0].insert(0, sigs[0]);
+
+    info!("[Faucet] ==========================");
+    info!("[Faucet] Executing AliceBob swap tx");
+    info!("[Faucet] ==========================");
+    th.faucet_state.read().await.verify_transactions(&[alicebob_swap_tx.clone()], true).await?;
+    th.faucet_merkle_tree.append(&MerkleNode::from(swap_full_params.outputs[0].coin));
+    th.faucet_merkle_tree.append(&MerkleNode::from(swap_full_params.outputs[1].coin));
+
+    info!("[Alice] ==========================");
+    info!("[Alice] Executing AliceBob swap tx");
+    info!("[Alice] ==========================");
+    th.alice_state.read().await.verify_transactions(&[alicebob_swap_tx.clone()], true).await?;
+    th.alice_merkle_tree.append(&MerkleNode::from(swap_full_params.outputs[0].coin));
+    let alice_leaf_pos = th.alice_merkle_tree.witness().unwrap();
+    th.alice_merkle_tree.append(&MerkleNode::from(swap_full_params.outputs[1].coin));
+
+    info!("[Bob] ==========================");
+    info!("[Bob] Executing AliceBob swap tx");
+    info!("[Bob] ==========================");
+    th.bob_state.read().await.verify_transactions(&[alicebob_swap_tx.clone()], true).await?;
+    th.bob_merkle_tree.append(&MerkleNode::from(swap_full_params.outputs[0].coin));
+    th.bob_merkle_tree.append(&MerkleNode::from(swap_full_params.outputs[1].coin));
+    let bob_leaf_pos = th.bob_merkle_tree.witness().unwrap();
+
+    assert!(th.alice_merkle_tree.root(0).unwrap() == th.bob_merkle_tree.root(0).unwrap());
+    assert!(th.faucet_merkle_tree.root(0).unwrap() == th.bob_merkle_tree.root(0).unwrap());
+
+    // Alice should now have two OwnCoins with the same token ID (ALICE)
+    let ciphertext = swap_full_params.outputs[0].ciphertext.clone();
+    let ephem_public = swap_full_params.outputs[0].ephem_public;
+    let e_note = EncryptedNote { ciphertext, ephem_public };
+    let note = e_note.decrypt(&th.alice_kp.secret)?;
+    let alice_oc = OwnCoin {
+        coin: Coin::from(swap_full_params.outputs[0].coin),
+        note: note.clone(),
+        secret: th.alice_kp.secret, // <-- What should this be?
+        nullifier: Nullifier::from(poseidon_hash([th.alice_kp.secret.inner(), note.serial])),
+        leaf_position: alice_leaf_pos,
+    };
+    alice_owncoins.push(alice_oc);
+
+    assert!(alice_owncoins.len() == 2);
+    assert!(alice_owncoins[0].note.token_id == alice_token_id);
+    assert!(alice_owncoins[1].note.token_id == alice_token_id);
+
+    // Same for Bob with BOB tokens
+    let ciphertext = swap_full_params.outputs[1].ciphertext.clone();
+    let ephem_public = swap_full_params.outputs[1].ephem_public;
+    let e_note = EncryptedNote { ciphertext, ephem_public };
+    let note = e_note.decrypt(&th.bob_kp.secret)?;
+    let bob_oc = OwnCoin {
+        coin: Coin::from(swap_full_params.outputs[1].coin),
+        note: note.clone(),
+        secret: th.bob_kp.secret, // <-- What should this be?
+        nullifier: Nullifier::from(poseidon_hash([th.bob_kp.secret.inner(), note.serial])),
+        leaf_position: bob_leaf_pos,
+    };
+    bob_owncoins.push(bob_oc);
+
+    assert!(bob_owncoins.len() == 2);
+    assert!(bob_owncoins[0].note.token_id == bob_token_id);
+    assert!(bob_owncoins[1].note.token_id == bob_token_id);
+
+    // Now Alice will create a new coin for herself to combine the two owncoins.
+    info!("[Alice] ======================================================");
+    info!("[Alice] Building Money::Transfer params for a payment to Alice");
+    info!("[Alice] =======================================================");
+    let (alice2alice_params, alice2alice_proofs, alice2alice_secret_keys, alice2alice_spent_coins) =
+        build_transfer_tx(
+            &th.alice_kp,
+            &th.alice_kp.public,
+            ALICE_INITIAL,
+            alice_token_id,
+            &alice_owncoins,
+            &th.alice_merkle_tree,
+            &th.mint_zkbin,
+            &th.mint_pk,
+            &th.burn_zkbin,
+            &th.burn_pk,
+            false,
+        )?;
+
+    for coin in alice2alice_spent_coins {
+        alice_owncoins.retain(|x| x != &coin);
+    }
+    assert!(alice_owncoins.is_empty());
+    assert!(alice2alice_params.inputs.len() == 2);
+    assert!(alice2alice_params.outputs.len() == 1);
+
+    info!("[Alice] ============================");
+    info!("[Alice] Building payment tx to Alice");
+    info!("[Alice] ============================");
+    let mut data = vec![MoneyFunction::Transfer as u8];
+    alice2alice_params.encode(&mut data)?;
+    let calls = vec![ContractCall { contract_id: th.money_contract_id, data }];
+    let proofs = vec![alice2alice_proofs];
+    let mut alice2alice_tx = Transaction { calls, proofs, signatures: vec![] };
+    let sigs = alice2alice_tx.create_sigs(&mut OsRng, &alice2alice_secret_keys)?;
+    alice2alice_tx.signatures = vec![sigs];
+
+    info!("[Faucet] ================================");
+    info!("[Faucet] Executing Alice2Alice payment tx");
+    info!("[Faucet] ================================");
+    th.faucet_state.read().await.verify_transactions(&[alice2alice_tx.clone()], true).await?;
+    th.faucet_merkle_tree.append(&MerkleNode::from(alice2alice_params.outputs[0].coin));
+
+    info!("[Alice] ================================");
+    info!("[Alice] Executing Alice2Alice payment tx");
+    info!("[Alice] ================================");
+    th.alice_state.read().await.verify_transactions(&[alice2alice_tx.clone()], true).await?;
+    th.alice_merkle_tree.append(&MerkleNode::from(alice2alice_params.outputs[0].coin));
+    let alice_leaf_pos = th.alice_merkle_tree.witness().unwrap();
+
+    info!("[Bob] ================================");
+    info!("[Bob] Executing Alice2Alice payment tx");
+    info!("[Bob] ================================");
+    th.bob_state.read().await.verify_transactions(&[alice2alice_tx.clone()], true).await?;
+    th.bob_merkle_tree.append(&MerkleNode::from(alice2alice_params.outputs[0].coin));
+
+    assert!(th.alice_merkle_tree.root(0).unwrap() == th.bob_merkle_tree.root(0).unwrap());
+    assert!(th.faucet_merkle_tree.root(0).unwrap() == th.bob_merkle_tree.root(0).unwrap());
+
+    // Alice should now have a single OwnCoin with her initial airdrop
+    let ciphertext = alice2alice_params.outputs[0].ciphertext.clone();
+    let ephem_public = alice2alice_params.outputs[0].ephem_public;
+    let e_note = EncryptedNote { ciphertext, ephem_public };
+    let note = e_note.decrypt(&th.alice_kp.secret)?;
+    let alice_oc = OwnCoin {
+        coin: Coin::from(alice2alice_params.outputs[0].coin),
+        note: note.clone(),
+        secret: th.alice_kp.secret, // <-- What should this be?
+        nullifier: Nullifier::from(poseidon_hash([th.alice_kp.secret.inner(), note.serial])),
+        leaf_position: alice_leaf_pos,
+    };
+    alice_owncoins.push(alice_oc);
+
+    assert!(alice_owncoins.len() == 1);
+    assert!(alice_owncoins[0].note.value == ALICE_INITIAL);
+    assert!(alice_owncoins[0].note.token_id == alice_token_id);
+
+    // Bob does the same
+    info!("[Bob] ====================================================");
+    info!("[Bob] Building Money::Transfer params for a payment to Bob");
+    info!("[Bob] ====================================================");
+    let (bob2bob_params, bob2bob_proofs, bob2bob_secret_keys, bob2bob_spent_coins) =
+        build_transfer_tx(
+            &th.bob_kp,
+            &th.bob_kp.public,
+            BOB_INITIAL,
+            bob_token_id,
+            &bob_owncoins,
+            &th.bob_merkle_tree,
+            &th.mint_zkbin,
+            &th.mint_pk,
+            &th.burn_zkbin,
+            &th.burn_pk,
+            false,
+        )?;
+
+    for coin in bob2bob_spent_coins {
+        bob_owncoins.retain(|x| x != &coin);
+    }
+    assert!(bob_owncoins.is_empty());
+    assert!(bob2bob_params.inputs.len() == 2);
+    assert!(bob2bob_params.outputs.len() == 1);
+
+    info!("[Bob] ==========================");
+    info!("[Bob] Building payment tx to Bob");
+    info!("[Bob] ==========================");
+    let mut data = vec![MoneyFunction::Transfer as u8];
+    bob2bob_params.encode(&mut data)?;
+    let calls = vec![ContractCall { contract_id: th.money_contract_id, data }];
+    let proofs = vec![bob2bob_proofs];
+    let mut bob2bob_tx = Transaction { calls, proofs, signatures: vec![] };
+    let sigs = bob2bob_tx.create_sigs(&mut OsRng, &bob2bob_secret_keys)?;
+    bob2bob_tx.signatures = vec![sigs];
+
+    info!("[Faucet] ============================");
+    info!("[Faucet] Executing Bob2Bob payment tx");
+    info!("[Faucet] ============================");
+    th.faucet_state.read().await.verify_transactions(&[bob2bob_tx.clone()], true).await?;
+    th.faucet_merkle_tree.append(&MerkleNode::from(bob2bob_params.outputs[0].coin));
+
+    info!("[Alice] ============================");
+    info!("[Alice] Executing Bob2Bob payment tx");
+    info!("[Alice] ============================");
+    th.alice_state.read().await.verify_transactions(&[bob2bob_tx.clone()], true).await?;
+    th.alice_merkle_tree.append(&MerkleNode::from(bob2bob_params.outputs[0].coin));
+
+    info!("[Bob] ============================");
+    info!("[Bob] Executing Bob2Bob payment tx");
+    info!("[Bob] ============================");
+    th.bob_state.read().await.verify_transactions(&[bob2bob_tx.clone()], true).await?;
+    th.bob_merkle_tree.append(&MerkleNode::from(bob2bob_params.outputs[0].coin));
+    let bob_leaf_pos = th.bob_merkle_tree.witness().unwrap();
+
+    assert!(th.alice_merkle_tree.root(0).unwrap() == th.bob_merkle_tree.root(0).unwrap());
+    assert!(th.faucet_merkle_tree.root(0).unwrap() == th.bob_merkle_tree.root(0).unwrap());
+
+    // Bob should now have a single OwnCoin with her initial airdrop
+    let ciphertext = bob2bob_params.outputs[0].ciphertext.clone();
+    let ephem_public = bob2bob_params.outputs[0].ephem_public;
+    let e_note = EncryptedNote { ciphertext, ephem_public };
+    let note = e_note.decrypt(&th.bob_kp.secret)?;
+    let bob_oc = OwnCoin {
+        coin: Coin::from(bob2bob_params.outputs[0].coin),
+        note: note.clone(),
+        secret: th.bob_kp.secret, // <-- What should this be?
+        nullifier: Nullifier::from(poseidon_hash([th.bob_kp.secret.inner(), note.serial])),
+        leaf_position: bob_leaf_pos,
+    };
+    bob_owncoins.push(bob_oc);
+
+    assert!(bob_owncoins.len() == 1);
+    assert!(bob_owncoins[0].note.value == BOB_INITIAL);
+    assert!(bob_owncoins[0].note.token_id == bob_token_id);
+
+    // Now they decide to swap all of their tokens
+    info!("[Alice] Building OtcSwap half");
+    let (
+        alice_swap_params,
+        alice_swap_proofs,
+        alice_swap_secret_keys,
+        alice_swap_spent_coins,
+        alice_value_blinds,
+        alice_token_blinds,
+    ) = build_half_swap_tx(
+        &th.alice_kp.public,
+        ALICE_INITIAL,
+        alice_token_id,
+        BOB_INITIAL,
+        bob_token_id,
+        &[],
+        &[],
+        &alice_owncoins,
+        &th.alice_merkle_tree,
+        &th.mint_zkbin,
+        &th.mint_pk,
+        &th.burn_zkbin,
+        &th.burn_pk,
+    )?;
+
+    assert!(alice_swap_params.inputs.len() == 1);
+    assert!(alice_swap_params.outputs.len() == 1);
+    assert!(alice_swap_spent_coins.len() == 1);
+    alice_owncoins.retain(|x| x != &alice_swap_spent_coins[0]);
+    assert!(alice_owncoins.is_empty());
+
+    info!("[Bob] Building OtcSwap half");
+    let (
+        bob_swap_params,
+        bob_swap_proofs,
+        bob_swap_secret_keys,
+        bob_swap_spent_coins,
+        _bob_value_blinds,
+        _bob_token_blinds,
+    ) = build_half_swap_tx(
+        &th.bob_kp.public,
+        BOB_INITIAL,
+        bob_token_id,
+        ALICE_INITIAL,
+        alice_token_id,
+        &alice_value_blinds,
+        &alice_token_blinds,
+        &bob_owncoins,
+        &th.bob_merkle_tree,
+        &th.mint_zkbin,
+        &th.mint_pk,
+        &th.burn_zkbin,
+        &th.burn_pk,
+    )?;
+
+    assert!(bob_swap_params.inputs.len() == 1);
+    assert!(bob_swap_params.outputs.len() == 1);
+    assert!(bob_swap_spent_coins.len() == 1);
+    bob_owncoins.retain(|x| x != &bob_swap_spent_coins[0]);
+    assert!(bob_owncoins.is_empty());
+
+    let swap_full_params = MoneyTransferParams {
+        clear_inputs: vec![],
+        inputs: vec![alice_swap_params.inputs[0].clone(), bob_swap_params.inputs[0].clone()],
+        outputs: vec![alice_swap_params.outputs[0].clone(), bob_swap_params.outputs[0].clone()],
+    };
+
+    let swap_full_proofs = vec![
+        alice_swap_proofs[0].clone(),
+        bob_swap_proofs[0].clone(),
+        alice_swap_proofs[1].clone(),
+        bob_swap_proofs[1].clone(),
+    ];
+
+    // And signs the transaction
+    let mut data = vec![MoneyFunction::OtcSwap as u8];
+    swap_full_params.encode(&mut data)?;
+    let mut alicebob_swap_tx = Transaction {
+        calls: vec![ContractCall { contract_id: th.money_contract_id, data }],
+        proofs: vec![swap_full_proofs],
+        signatures: vec![],
+    };
+    let sigs = alicebob_swap_tx.create_sigs(&mut OsRng, &bob_swap_secret_keys)?;
+    alicebob_swap_tx.signatures = vec![sigs];
+
+    // Alice gets the partially signed transaction and adds her signature
+    let sigs = alicebob_swap_tx.create_sigs(&mut OsRng, &alice_swap_secret_keys)?;
+    alicebob_swap_tx.signatures[0].insert(0, sigs[0]);
+
+    info!("[Faucet] ==========================");
+    info!("[Faucet] Executing AliceBob swap tx");
+    info!("[Faucet] ==========================");
+    th.faucet_state.read().await.verify_transactions(&[alicebob_swap_tx.clone()], true).await?;
+    th.faucet_merkle_tree.append(&MerkleNode::from(swap_full_params.outputs[0].coin));
+    th.faucet_merkle_tree.append(&MerkleNode::from(swap_full_params.outputs[1].coin));
+
+    info!("[Alice] ==========================");
+    info!("[Alice] Executing AliceBob swap tx");
+    info!("[Alice] ==========================");
+    th.alice_state.read().await.verify_transactions(&[alicebob_swap_tx.clone()], true).await?;
+    th.alice_merkle_tree.append(&MerkleNode::from(swap_full_params.outputs[0].coin));
+    let alice_leaf_pos = th.alice_merkle_tree.witness().unwrap();
+    th.alice_merkle_tree.append(&MerkleNode::from(swap_full_params.outputs[1].coin));
+
+    info!("[Bob] ==========================");
+    info!("[Bob] Executing AliceBob swap tx");
+    info!("[Bob] ==========================");
+    th.bob_state.read().await.verify_transactions(&[alicebob_swap_tx.clone()], true).await?;
+    th.bob_merkle_tree.append(&MerkleNode::from(swap_full_params.outputs[0].coin));
+    th.bob_merkle_tree.append(&MerkleNode::from(swap_full_params.outputs[1].coin));
+    let bob_leaf_pos = th.bob_merkle_tree.witness().unwrap();
+
+    assert!(th.alice_merkle_tree.root(0).unwrap() == th.bob_merkle_tree.root(0).unwrap());
+    assert!(th.faucet_merkle_tree.root(0).unwrap() == th.bob_merkle_tree.root(0).unwrap());
+
+    // Alice should now have Bob's BOB tokens
+    let ciphertext = swap_full_params.outputs[0].ciphertext.clone();
+    let ephem_public = swap_full_params.outputs[0].ephem_public;
+    let e_note = EncryptedNote { ciphertext, ephem_public };
+    let note = e_note.decrypt(&th.alice_kp.secret)?;
+    let alice_oc = OwnCoin {
+        coin: Coin::from(swap_full_params.outputs[0].coin),
+        note: note.clone(),
+        secret: th.alice_kp.secret, // <-- What should this be?
+        nullifier: Nullifier::from(poseidon_hash([th.alice_kp.secret.inner(), note.serial])),
+        leaf_position: alice_leaf_pos,
+    };
+    alice_owncoins.push(alice_oc);
+
+    assert!(alice_owncoins.len() == 1);
+    assert!(alice_owncoins[0].note.value == BOB_INITIAL);
+    assert!(alice_owncoins[0].note.token_id == bob_token_id);
+
+    // And Bob should have Alice's ALICE tokens
+    let ciphertext = swap_full_params.outputs[1].ciphertext.clone();
+    let ephem_public = swap_full_params.outputs[1].ephem_public;
+    let e_note = EncryptedNote { ciphertext, ephem_public };
+    let note = e_note.decrypt(&th.bob_kp.secret)?;
+    let bob_oc = OwnCoin {
+        coin: Coin::from(swap_full_params.outputs[1].coin),
+        note: note.clone(),
+        secret: th.bob_kp.secret, // <-- What should this be?
+        nullifier: Nullifier::from(poseidon_hash([th.bob_kp.secret.inner(), note.serial])),
+        leaf_position: bob_leaf_pos,
+    };
+    bob_owncoins.push(bob_oc);
+
+    assert!(bob_owncoins.len() == 1);
+    assert!(bob_owncoins[0].note.value == ALICE_INITIAL);
+    assert!(bob_owncoins[0].note.token_id == alice_token_id);
+
+    // Thanks for reading
+    Ok(())
+}

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

@@ -0,0 +1,156 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2022 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::{
+        constants::{TESTNET_GENESIS_HASH_BYTES, TESTNET_GENESIS_TIMESTAMP},
+        ValidatorState, ValidatorStatePtr,
+    },
+    wallet::WalletDb,
+    zk::{proof::ProvingKey, vm::ZkCircuit, vm_stack::empty_witnesses},
+    zkas::ZkBinary,
+    Result,
+};
+use darkfi_sdk::{
+    crypto::{constants::MERKLE_DEPTH, ContractId, Keypair, MerkleNode, PublicKey},
+    db::ZKAS_DB_NAME,
+    incrementalmerkletree::bridgetree::BridgeTree,
+    pasta::{group::ff::PrimeField, pallas},
+};
+use darkfi_serial::serialize;
+use log::info;
+use rand::rngs::OsRng;
+
+use darkfi_money_contract::{ZKAS_BURN_NS, ZKAS_MINT_NS};
+
+pub struct MoneyTestHarness {
+    pub faucet_kp: Keypair,
+    pub alice_kp: Keypair,
+    pub bob_kp: Keypair,
+    pub faucet_pubkeys: Vec<PublicKey>,
+    pub faucet_state: ValidatorStatePtr,
+    pub alice_state: ValidatorStatePtr,
+    pub bob_state: ValidatorStatePtr,
+    pub money_contract_id: ContractId,
+    pub proving_keys: HashMap<[u8; 32], Vec<(&'static str, ProvingKey)>>,
+    pub mint_zkbin: ZkBinary,
+    pub burn_zkbin: ZkBinary,
+    pub mint_pk: ProvingKey,
+    pub burn_pk: ProvingKey,
+    pub faucet_merkle_tree: BridgeTree<MerkleNode, MERKLE_DEPTH>,
+    pub alice_merkle_tree: BridgeTree<MerkleNode, MERKLE_DEPTH>,
+    pub bob_merkle_tree: BridgeTree<MerkleNode, MERKLE_DEPTH>,
+}
+
+impl MoneyTestHarness {
+    pub async fn new() -> Result<Self> {
+        let faucet_kp = Keypair::random(&mut OsRng);
+        let alice_kp = Keypair::random(&mut OsRng);
+        let bob_kp = Keypair::random(&mut OsRng);
+        let faucet_pubkeys = vec![faucet_kp.public];
+
+        let faucet_wallet = WalletDb::new("sqlite::memory:", "foo").await?;
+        let alice_wallet = WalletDb::new("sqlite::memory:", "foo").await?;
+        let bob_wallet = WalletDb::new("sqlite::memory:", "foo").await?;
+
+        let faucet_sled_db = sled::Config::new().temporary(true).open()?;
+        let alice_sled_db = sled::Config::new().temporary(true).open()?;
+        let bob_sled_db = sled::Config::new().temporary(true).open()?;
+
+        let faucet_state = ValidatorState::new(
+            &faucet_sled_db,
+            *TESTNET_GENESIS_TIMESTAMP,
+            *TESTNET_GENESIS_HASH_BYTES,
+            faucet_wallet,
+            faucet_pubkeys.clone(),
+            false,
+        )
+        .await?;
+
+        let alice_state = ValidatorState::new(
+            &alice_sled_db,
+            *TESTNET_GENESIS_TIMESTAMP,
+            *TESTNET_GENESIS_HASH_BYTES,
+            alice_wallet,
+            faucet_pubkeys.clone(),
+            false,
+        )
+        .await?;
+
+        let bob_state = ValidatorState::new(
+            &bob_sled_db,
+            *TESTNET_GENESIS_TIMESTAMP,
+            *TESTNET_GENESIS_HASH_BYTES,
+            bob_wallet,
+            faucet_pubkeys.clone(),
+            false,
+        )
+        .await?;
+
+        let money_contract_id = ContractId::from(pallas::Base::from(u64::MAX - 420));
+
+        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,
+            ZKAS_DB_NAME,
+        )?;
+
+        let mint_zkbin = db_handle.get(&serialize(&ZKAS_MINT_NS))?.unwrap();
+        let burn_zkbin = db_handle.get(&serialize(&ZKAS_BURN_NS))?.unwrap();
+        info!("Decoding bincode");
+        let mint_zkbin = ZkBinary::decode(&mint_zkbin.clone())?;
+        let burn_zkbin = ZkBinary::decode(&burn_zkbin.clone())?;
+        let mint_witnesses = empty_witnesses(&mint_zkbin);
+        let burn_witnesses = empty_witnesses(&burn_zkbin);
+        let mint_circuit = ZkCircuit::new(mint_witnesses, mint_zkbin.clone());
+        let burn_circuit = ZkCircuit::new(burn_witnesses, burn_zkbin.clone());
+
+        info!("Creating zk proving keys");
+        let k = 13;
+        let mut proving_keys = HashMap::<[u8; 32], Vec<(&str, ProvingKey)>>::new();
+        let mint_pk = ProvingKey::build(k, &mint_circuit);
+        let burn_pk = ProvingKey::build(k, &burn_circuit);
+        let pks = vec![(ZKAS_MINT_NS, mint_pk.clone()), (ZKAS_BURN_NS, burn_pk.clone())];
+        proving_keys.insert(money_contract_id.inner().to_repr(), pks);
+
+        let faucet_merkle_tree = BridgeTree::<MerkleNode, MERKLE_DEPTH>::new(100);
+        let alice_merkle_tree = BridgeTree::<MerkleNode, MERKLE_DEPTH>::new(100);
+        let bob_merkle_tree = BridgeTree::<MerkleNode, MERKLE_DEPTH>::new(100);
+
+        Ok(Self {
+            faucet_kp,
+            alice_kp,
+            bob_kp,
+            faucet_pubkeys,
+            faucet_state,
+            alice_state,
+            bob_state,
+            money_contract_id,
+            proving_keys,
+            mint_pk: mint_pk.clone(),
+            burn_pk: burn_pk.clone(),
+            mint_zkbin,
+            burn_zkbin,
+            faucet_merkle_tree,
+            alice_merkle_tree,
+            bob_merkle_tree,
+        })
+    }
+}

+ 0 - 485
src/contract/money/tests/otcswap.rs

@@ -1,485 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2022 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::{
-        constants::{TESTNET_GENESIS_HASH_BYTES, TESTNET_GENESIS_TIMESTAMP},
-        ValidatorState,
-    },
-    tx::Transaction,
-    util::parse::decode_base10,
-    wallet::WalletDb,
-    zk::{proof::ProvingKey, vm::ZkCircuit, vm_stack::empty_witnesses},
-    zkas::ZkBinary,
-    Result,
-};
-use darkfi_sdk::{
-    crypto::{
-        constants::MERKLE_DEPTH, poseidon_hash, ContractId, Keypair, MerkleNode, Nullifier, TokenId,
-    },
-    db::ZKAS_DB_NAME,
-    incrementalmerkletree::{bridgetree::BridgeTree, Tree},
-    pasta::{
-        group::ff::{Field, PrimeField},
-        pallas,
-    },
-    tx::ContractCall,
-};
-use darkfi_serial::{deserialize, serialize, Encodable};
-use log::info;
-use rand::rngs::OsRng;
-
-use darkfi_money_contract::{
-    client::{build_half_swap_tx, build_transfer_tx, Coin, EncryptedNote, OwnCoin},
-    state::MoneyTransferParams,
-    MoneyFunction, ZKAS_BURN_NS, ZKAS_MINT_NS,
-};
-
-#[async_std::test]
-async fn money_contract_swap() -> Result<()> {
-    // Debug log configuration
-    let mut cfg = simplelog::ConfigBuilder::new();
-    cfg.add_filter_ignore("sled".to_string());
-    simplelog::TermLogger::init(
-        simplelog::LevelFilter::Debug,
-        cfg.build(),
-        simplelog::TerminalMode::Mixed,
-        simplelog::ColorChoice::Auto,
-    )?;
-
-    // A keypair we can use for the faucet whitelist
-    let faucet_kp = Keypair::random(&mut OsRng);
-
-    // A keypair we'll use for Alice
-    let alice_kp = Keypair::random(&mut OsRng);
-
-    // A keypair we'll use for Bob
-    let bob_kp = Keypair::random(&mut OsRng);
-
-    // The faucet's pubkey is allowed to make clear inputs
-    let faucet_pubkeys = vec![faucet_kp.public];
-
-    // The wallets are just noops to get around the ValidatorState API
-    let faucet_wallet = WalletDb::new("sqlite::memory:", "foo").await?;
-    let alice_wallet = WalletDb::new("sqlite::memory:", "foo").await?;
-    let bob_wallet = WalletDb::new("sqlite::memory:", "foo").await?;
-
-    // Our main sled database references which live in memory during this test.
-    info!("Initializing ValidatorState");
-    let faucet_sled_db = sled::Config::new().temporary(true).open()?;
-    let alice_sled_db = sled::Config::new().temporary(true).open()?;
-    let bob_sled_db = sled::Config::new().temporary(true).open()?;
-
-    let faucet_state = ValidatorState::new(
-        &faucet_sled_db,
-        *TESTNET_GENESIS_TIMESTAMP,
-        *TESTNET_GENESIS_HASH_BYTES,
-        faucet_wallet,
-        faucet_pubkeys.clone(),
-        false,
-    )
-    .await?;
-
-    let alice_state = ValidatorState::new(
-        &alice_sled_db,
-        *TESTNET_GENESIS_TIMESTAMP,
-        *TESTNET_GENESIS_HASH_BYTES,
-        alice_wallet,
-        faucet_pubkeys.clone(),
-        false,
-    )
-    .await?;
-
-    let bob_state = ValidatorState::new(
-        &bob_sled_db,
-        *TESTNET_GENESIS_TIMESTAMP,
-        *TESTNET_GENESIS_HASH_BYTES,
-        bob_wallet,
-        faucet_pubkeys.clone(),
-        false,
-    )
-    .await?;
-
-    // In a hacky way, we just generate the proving keys for the circuits used.
-    let contract_id = ContractId::from(pallas::Base::from(u64::MAX - 420));
-
-    let alice_sled = &alice_state.read().await.blockchain.sled_db;
-    let db_handle = alice_state.read().await.blockchain.contracts.lookup(
-        alice_sled,
-        &contract_id,
-        ZKAS_DB_NAME,
-    )?;
-
-    let mint_zkbin = db_handle.get(&serialize(&ZKAS_MINT_NS))?.unwrap();
-    let burn_zkbin = db_handle.get(&serialize(&ZKAS_BURN_NS))?.unwrap();
-    let mint_zkbin = ZkBinary::decode(&mint_zkbin.clone())?;
-    let burn_zkbin = ZkBinary::decode(&burn_zkbin.clone())?;
-    let mint_witnesses = empty_witnesses(&mint_zkbin);
-    let burn_witnesses = empty_witnesses(&burn_zkbin);
-    let mint_circuit = ZkCircuit::new(mint_witnesses, mint_zkbin.clone());
-    let burn_circuit = ZkCircuit::new(burn_witnesses, burn_zkbin.clone());
-
-    info!("Creating ZK proving keys");
-    let k = 13;
-    let mut proving_keys = HashMap::<[u8; 32], Vec<(&str, ProvingKey)>>::new();
-    let mint_pk = ProvingKey::build(k, &mint_circuit);
-    let burn_pk = ProvingKey::build(k, &burn_circuit);
-    let pks = vec![(ZKAS_MINT_NS, mint_pk.clone()), (ZKAS_BURN_NS, burn_pk.clone())];
-    proving_keys.insert(contract_id.inner().to_repr(), pks);
-
-    // We also have to initialize the Merkle trees used for coins.
-    let mut faucet_merkle_tree = BridgeTree::<MerkleNode, MERKLE_DEPTH>::new(100);
-    let mut alice_merkle_tree = BridgeTree::<MerkleNode, MERKLE_DEPTH>::new(100);
-    let mut bob_merkle_tree = BridgeTree::<MerkleNode, MERKLE_DEPTH>::new(100);
-
-    // The faucet will now mint some tokens for Alice and for Bob
-    let alice_token_id = TokenId::from(pallas::Base::random(&mut OsRng));
-    let bob_token_id = TokenId::from(pallas::Base::random(&mut OsRng));
-    let alice_amount = decode_base10("42.69", 8, true)?;
-    let bob_amount = decode_base10("69.42", 8, true)?;
-
-    info!("[Faucet] Building Money::Transfer tx for Alice's airdrop");
-    let (params, proofs, secret_keys, _spent_coins) = build_transfer_tx(
-        &faucet_kp,
-        &alice_kp.public,
-        alice_amount,
-        alice_token_id,
-        &[],
-        &faucet_merkle_tree,
-        &mint_zkbin,
-        &mint_pk,
-        &burn_zkbin,
-        &burn_pk,
-        true,
-    )?;
-
-    // Build transaction
-    let mut data = vec![MoneyFunction::Transfer as u8];
-    params.encode(&mut data)?;
-    let mut tx = Transaction {
-        calls: vec![ContractCall { contract_id, data }],
-        proofs: vec![proofs],
-        signatures: vec![],
-    };
-    let sigs = tx.create_sigs(&mut OsRng, &secret_keys)?;
-    tx.signatures = vec![sigs];
-
-    // Let's first execute this transaction for the faucet to see if it passes.
-    // Then Alice gets the tx and also executes it.
-    info!("[Faucet] Verifying Alice's airdrop tx");
-    faucet_state.read().await.verify_transactions(&[tx.clone()], true).await?;
-    faucet_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
-
-    info!("[Alice] Verifying Alice's airdrop tx");
-    alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
-    alice_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
-
-    info!("[Bob] Verifying Alice's airdrop tx");
-    bob_state.read().await.verify_transactions(&[tx.clone()], true).await?;
-    bob_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
-
-    let params: MoneyTransferParams = deserialize(&tx.calls[0].data[1..])?;
-    let output = &params.outputs[0];
-    let ciphertext = params.outputs[0].ciphertext.clone();
-    let ephem_public = params.outputs[0].ephem_public;
-    let encrypted_note = EncryptedNote { ciphertext, ephem_public };
-    let note = encrypted_note.decrypt(&alice_kp.secret)?;
-
-    let alice_owncoin = OwnCoin {
-        coin: Coin::from(output.coin),
-        note: note.clone(),
-        secret: alice_kp.secret, // <-- What should this be?
-        nullifier: Nullifier::from(poseidon_hash([alice_kp.secret.inner(), note.serial])),
-        leaf_position: alice_merkle_tree.witness().unwrap(),
-    };
-
-    info!("[Faucet] Building Money::Transfer tx for Bob's airdrop");
-    let (params, proofs, secret_keys, _spent_coins) = build_transfer_tx(
-        &faucet_kp,
-        &bob_kp.public,
-        bob_amount,
-        bob_token_id,
-        &[],
-        &faucet_merkle_tree,
-        &mint_zkbin,
-        &mint_pk,
-        &burn_zkbin,
-        &burn_pk,
-        true,
-    )?;
-
-    // Build transaction
-    let mut data = vec![MoneyFunction::Transfer as u8];
-    params.encode(&mut data)?;
-    let mut tx = Transaction {
-        calls: vec![ContractCall { contract_id, data }],
-        proofs: vec![proofs],
-        signatures: vec![],
-    };
-    let sigs = tx.create_sigs(&mut OsRng, &secret_keys)?;
-    tx.signatures = vec![sigs];
-
-    // Let's first execute this transaction for the faucet to see if it passes.
-    // Then Alice gets the tx and also executes it.
-    info!("[Faucet] Verifying Bob's airdrop tx");
-    faucet_state.read().await.verify_transactions(&[tx.clone()], true).await?;
-    faucet_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
-
-    info!("[Alice] Verifying Bob's airdrop tx");
-    alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
-    alice_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
-
-    info!("[Bob] Verifying Bob's airdrop tx");
-    bob_state.read().await.verify_transactions(&[tx.clone()], true).await?;
-    bob_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
-
-    let params: MoneyTransferParams = deserialize(&tx.calls[0].data[1..])?;
-    let ciphertext = params.outputs[0].ciphertext.clone();
-    let ephem_public = params.outputs[0].ephem_public;
-    let encrypted_note = EncryptedNote { ciphertext, ephem_public };
-    let note = encrypted_note.decrypt(&bob_kp.secret)?;
-
-    let bob_owncoin = OwnCoin {
-        coin: Coin::from(output.coin),
-        note: note.clone(),
-        secret: bob_kp.secret, // <-- What should this be?
-        nullifier: Nullifier::from(poseidon_hash([bob_kp.secret.inner(), note.serial])),
-        leaf_position: bob_merkle_tree.witness().unwrap(),
-    };
-
-    // Now Alice and Bob should have their tokens. They can attempt to swap them.
-    // Alice will create a transaction half, and send it to Bob, which he can inspect
-    // and add his half, sign it, and return to Alice. The Alice can do the inspection
-    // and sign with her key, and broadcast the transaction.
-    info!("[Alice] Building swap tx half");
-    let (
-        alice_half_params,
-        alice_half_proofs,
-        alice_half_keys,
-        _alice_half_spent_coins,
-        alice_value_blinds,
-        alice_token_blinds,
-    ) = build_half_swap_tx(
-        &alice_kp.public,
-        alice_amount,
-        alice_token_id,
-        bob_amount,
-        bob_token_id,
-        &[],
-        &[],
-        &[alice_owncoin],
-        &alice_merkle_tree,
-        &mint_zkbin,
-        &mint_pk,
-        &burn_zkbin,
-        &burn_pk,
-    )?;
-
-    info!("[Bob] Building swap tx half");
-    let (
-        bob_half_params,
-        bob_half_proofs,
-        bob_half_keys,
-        _bob_half_spent_coins,
-        _bob_value_blinds,
-        _bob_token_blinds,
-    ) = build_half_swap_tx(
-        &bob_kp.public,
-        bob_amount,
-        bob_token_id,
-        alice_amount,
-        alice_token_id,
-        &alice_value_blinds,
-        &alice_token_blinds,
-        &[bob_owncoin],
-        &bob_merkle_tree,
-        &mint_zkbin,
-        &mint_pk,
-        &burn_zkbin,
-        &burn_pk,
-    )?;
-
-    // Ordering is important
-    let bob_full_params = MoneyTransferParams {
-        clear_inputs: vec![],
-        inputs: vec![alice_half_params.inputs[0].clone(), bob_half_params.inputs[0].clone()],
-        outputs: vec![alice_half_params.outputs[0].clone(), bob_half_params.outputs[0].clone()],
-    };
-
-    let bob_full_proofs = vec![
-        alice_half_proofs[0].clone(),
-        bob_half_proofs[0].clone(),
-        alice_half_proofs[1].clone(),
-        bob_half_proofs[1].clone(),
-    ];
-
-    let mut data = vec![MoneyFunction::OtcSwap as u8];
-    bob_full_params.encode(&mut data)?;
-    let mut tx = Transaction {
-        calls: vec![ContractCall { contract_id, data }],
-        proofs: vec![bob_full_proofs],
-        signatures: vec![],
-    };
-    info!("[Bob] Signing swap transaction");
-    let sigs = tx.create_sigs(&mut OsRng, &bob_half_keys)?;
-    tx.signatures = vec![sigs];
-
-    // This tx finds its way back to Alice.
-    // She can try broadcasting the tx without signing, but this should fail to verify.
-    //info!("[Alice] Verifying half-signed swap transaction (should fail)");
-    //assert!(alice_state.read().await.verify_transactions(&[tx.clone()], false).await.is_err());
-
-    // So she signs it. Important to note that the signature goes into the same vec.
-    // As well as placing it in the right place. So if Alice was first, her signature
-    // should be the first in line.
-    info!("[Alice] Signing swap transaction");
-    let sigs = tx.create_sigs(&mut OsRng, &alice_half_keys)?;
-    tx.signatures[0].insert(0, sigs[0]);
-
-    info!("[Alice] Verifying signed swap transaction");
-    // Now the transaction is signed by both parties.
-    // Let's execute it on Alice's chain state.
-    alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
-    // Alice's received coin is in outputs[0]
-    alice_merkle_tree.append(&MerkleNode::from(bob_full_params.outputs[0].coin));
-    let alice_leaf_position = alice_merkle_tree.witness().unwrap();
-    // This is Bob's received coin
-    alice_merkle_tree.append(&MerkleNode::from(bob_full_params.outputs[1].coin));
-
-    info!("[Bob] Verifying signed swap transaction");
-    bob_state.read().await.verify_transactions(&[tx.clone()], true).await?;
-    bob_merkle_tree.append(&MerkleNode::from(bob_full_params.outputs[0].coin));
-    bob_merkle_tree.append(&MerkleNode::from(bob_full_params.outputs[1].coin));
-    let bob_leaf_position = bob_merkle_tree.witness().unwrap();
-
-    info!("[Faucet] Verifying signed swap transaction");
-    faucet_state.read().await.verify_transactions(&[tx.clone()], true).await?;
-    faucet_merkle_tree.append(&MerkleNode::from(bob_full_params.outputs[0].coin));
-    faucet_merkle_tree.append(&MerkleNode::from(bob_full_params.outputs[1].coin));
-
-    let encrypted_note = EncryptedNote {
-        ciphertext: bob_full_params.outputs[0].ciphertext.clone(),
-        ephem_public: bob_full_params.outputs[0].ephem_public,
-    };
-    let alice_note = encrypted_note.decrypt(&alice_kp.secret)?;
-
-    let encrypted_note = EncryptedNote {
-        ciphertext: bob_full_params.outputs[1].ciphertext.clone(),
-        ephem_public: bob_full_params.outputs[1].ephem_public,
-    };
-    let bob_note = encrypted_note.decrypt(&bob_kp.secret)?;
-
-    // Alice and Bob save their new coins
-    let alice_owncoin = OwnCoin {
-        coin: Coin::from(bob_full_params.outputs[0].coin),
-        note: alice_note.clone(),
-        secret: alice_kp.secret, // <-- What should this be?
-        nullifier: Nullifier::from(poseidon_hash([alice_kp.secret.inner(), alice_note.serial])),
-        leaf_position: alice_leaf_position,
-    };
-
-    let bob_owncoin = OwnCoin {
-        coin: Coin::from(bob_full_params.outputs[1].coin),
-        note: bob_note.clone(),
-        secret: bob_kp.secret, // <-- What should this be?
-        nullifier: Nullifier::from(poseidon_hash([bob_kp.secret.inner(), bob_note.serial])),
-        leaf_position: bob_leaf_position,
-    };
-
-    // Bob was nice to Alice, so she decides to send him all the money back.
-    // This makes sure our coins work after the swap.
-    info!("[Alice] Building Money::Transfer tx for Bob");
-    let (params, proofs, secret_keys, _spent_coins) = build_transfer_tx(
-        &alice_kp,
-        &bob_kp.public,
-        bob_amount,
-        bob_token_id,
-        &[alice_owncoin],
-        &alice_merkle_tree,
-        &mint_zkbin,
-        &mint_pk,
-        &burn_zkbin,
-        &burn_pk,
-        false,
-    )?;
-
-    let mut data = vec![MoneyFunction::Transfer as u8];
-    params.encode(&mut data)?;
-    let mut tx = Transaction {
-        calls: vec![ContractCall { contract_id, data }],
-        proofs: vec![proofs],
-        signatures: vec![],
-    };
-    info!("[Alice] Signing transfer transaction");
-    let sigs = tx.create_sigs(&mut OsRng, &secret_keys)?;
-    tx.signatures = vec![sigs];
-
-    info!("[Faucet] Verifying Alice's Money::Transfer tx");
-    faucet_state.read().await.verify_transactions(&[tx.clone()], true).await?;
-    faucet_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
-
-    info!("[Alice] Verifying Alice's Money::Transfer tx");
-    alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
-    alice_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
-
-    info!("Executing transaction on Bob's blockchain db");
-    bob_state.read().await.verify_transactions(&[tx.clone()], true).await?;
-    bob_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
-
-    // Bob thanks Alice, but he doesn't want to accept the gift, so he sends
-    // her back her money that they initially swapped, effectively going back
-    // to square one.
-    info!("Building transfer tx for Alice from Bob");
-    let (params, proofs, secret_keys, _spent_coins) = build_transfer_tx(
-        &bob_kp,
-        &alice_kp.public,
-        alice_amount,
-        alice_token_id,
-        &[bob_owncoin],
-        &bob_merkle_tree,
-        &mint_zkbin,
-        &mint_pk,
-        &burn_zkbin,
-        &burn_pk,
-        false,
-    )?;
-
-    let mut data = vec![MoneyFunction::Transfer 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];
-
-    info!("Executing transaction on the faucet's blockchain db");
-    faucet_state.read().await.verify_transactions(&[tx.clone()], true).await?;
-    faucet_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
-
-    info!("Executing transaction on Alice's blockchain db");
-    alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
-    alice_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
-
-    info!("Executing transaction on Bob's blockchain db");
-    bob_state.read().await.verify_transactions(&[tx.clone()], true).await?;
-    bob_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
-
-    // Thanks for reading.
-    Ok(())
-}

+ 0 - 298
src/contract/money/tests/transfer.rs

@@ -1,298 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2022 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/>.
- */
-
-//! In this test module we make sure execution of the contract works as it is
-//! intended to. We initialize a state, deploy the contract, create a clear
-//! input, and then we try to spend it.
-//! Let's see if we manage.
-use std::collections::HashMap;
-
-use darkfi::{
-    consensus::{
-        constants::{TESTNET_GENESIS_HASH_BYTES, TESTNET_GENESIS_TIMESTAMP},
-        ValidatorState,
-    },
-    tx::Transaction,
-    util::parse::decode_base10,
-    wallet::WalletDb,
-    zk::{proof::ProvingKey, vm::ZkCircuit, vm_stack::empty_witnesses},
-    zkas::ZkBinary,
-    Result,
-};
-use darkfi_sdk::{
-    crypto::{
-        constants::MERKLE_DEPTH, poseidon_hash, ContractId, Keypair, MerkleNode, Nullifier, TokenId,
-    },
-    db::ZKAS_DB_NAME,
-    incrementalmerkletree::{bridgetree::BridgeTree, Tree},
-    pasta::{
-        group::ff::{Field, PrimeField},
-        pallas,
-    },
-    tx::ContractCall,
-};
-use darkfi_serial::{deserialize, serialize, Encodable};
-use log::info;
-use rand::rngs::OsRng;
-
-use darkfi_money_contract::{
-    client::{build_transfer_tx, Coin, EncryptedNote, OwnCoin},
-    state::MoneyTransferParams,
-    MoneyFunction, ZKAS_BURN_NS, ZKAS_MINT_NS,
-};
-
-#[async_std::test]
-async fn money_contract_transfer() -> Result<()> {
-    // Debug log configuration
-    let mut cfg = simplelog::ConfigBuilder::new();
-    cfg.add_filter_ignore("sled".to_string());
-    simplelog::TermLogger::init(
-        simplelog::LevelFilter::Debug,
-        //simplelog::LevelFilter::Info,
-        cfg.build(),
-        simplelog::TerminalMode::Mixed,
-        simplelog::ColorChoice::Auto,
-    )?;
-
-    // A keypair we can use for the faucet whitelist
-    let faucet_kp = Keypair::random(&mut OsRng);
-
-    // A keypair we'll use for Alice
-    let alice_kp = Keypair::random(&mut OsRng);
-
-    // The faucet's pubkey is allowed to make clear inputs
-    let faucet_pubkeys = vec![faucet_kp.public];
-
-    // The wallets are just noops to get around the ValidatorState API
-    let faucet_wallet = WalletDb::new("sqlite::memory:", "foo").await?;
-    let alice_wallet = WalletDb::new("sqlite::memory:", "foo").await?;
-
-    // Our main sled database references which live in memory during this test.
-    info!("Initializing ValidatorState");
-    let faucet_sled_db = sled::Config::new().temporary(true).open()?;
-    let alice_sled_db = sled::Config::new().temporary(true).open()?;
-    let faucet_state = ValidatorState::new(
-        &faucet_sled_db,
-        *TESTNET_GENESIS_TIMESTAMP,
-        *TESTNET_GENESIS_HASH_BYTES,
-        faucet_wallet,
-        faucet_pubkeys.clone(),
-        false,
-    )
-    .await?;
-
-    let alice_state = ValidatorState::new(
-        &alice_sled_db,
-        *TESTNET_GENESIS_TIMESTAMP,
-        *TESTNET_GENESIS_HASH_BYTES,
-        alice_wallet,
-        faucet_pubkeys.clone(),
-        false,
-    )
-    .await?;
-
-    // In a hacky way, we just generate the proving keys for the circuits used.
-    info!("Looking up zkas circuits from DB");
-    let contract_id = ContractId::from(pallas::Base::from(u64::MAX - 420));
-
-    let alice_sled = &alice_state.read().await.blockchain.sled_db;
-    let db_handle = alice_state.read().await.blockchain.contracts.lookup(
-        alice_sled,
-        &contract_id,
-        ZKAS_DB_NAME,
-    )?;
-
-    let mint_zkbin = db_handle.get(&serialize(&ZKAS_MINT_NS))?.unwrap();
-    let burn_zkbin = db_handle.get(&serialize(&ZKAS_BURN_NS))?.unwrap();
-    info!("Decoding bincode");
-    let mint_zkbin = ZkBinary::decode(&mint_zkbin.clone())?;
-    let burn_zkbin = ZkBinary::decode(&burn_zkbin.clone())?;
-    let mint_witnesses = empty_witnesses(&mint_zkbin);
-    let burn_witnesses = empty_witnesses(&burn_zkbin);
-    let mint_circuit = ZkCircuit::new(mint_witnesses, mint_zkbin.clone());
-    let burn_circuit = ZkCircuit::new(burn_witnesses, burn_zkbin.clone());
-
-    info!("Creating zk proving keys");
-    let k = 13;
-    let mut proving_keys = HashMap::<[u8; 32], Vec<(&str, ProvingKey)>>::new();
-    let mint_pk = ProvingKey::build(k, &mint_circuit);
-    let burn_pk = ProvingKey::build(k, &burn_circuit);
-    let pks = vec![(ZKAS_MINT_NS, mint_pk.clone()), (ZKAS_BURN_NS, burn_pk.clone())];
-    proving_keys.insert(contract_id.inner().to_repr(), pks);
-
-    // We also have to initialize the Merkle trees used for coins.
-    info!("Initializing Merkle trees");
-    let mut faucet_merkle_tree = BridgeTree::<MerkleNode, MERKLE_DEPTH>::new(100);
-    let mut alice_merkle_tree = BridgeTree::<MerkleNode, MERKLE_DEPTH>::new(100);
-
-    // The faucet will now mint some tokens for Alice.
-    let token_id = TokenId::from(pallas::Base::random(&mut OsRng));
-    let amount = decode_base10("42.69", 8, true)?;
-
-    info!("Building transfer tx for clear inputs");
-    let (params, proofs, secret_keys, _spent_coins) = build_transfer_tx(
-        &faucet_kp,
-        &alice_kp.public,
-        amount,
-        token_id,
-        &[],
-        &faucet_merkle_tree,
-        &mint_zkbin,
-        &mint_pk,
-        &burn_zkbin,
-        &burn_pk,
-        true,
-    )?;
-
-    // Build transaction
-    let mut data = vec![MoneyFunction::Transfer 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's first execute this transaction for the faucet to see if it passes.
-    // Then Alice gets the tx and also executes it.
-    info!("Executing transaction on the faucet's blockchain db");
-    faucet_state.read().await.verify_transactions(&[tx.clone()], true).await?;
-    info!("Adding coin to faucet's Merkle tree");
-    faucet_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
-
-    info!("Executing transaction on Alice's blockchain db");
-    alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
-    // TODO: FIXME: Actually have a look at the `merkle_add` calls
-    alice_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
-    let leaf_position = alice_merkle_tree.witness().unwrap();
-
-    // If the above succeeded, the state has been written, so Alice should have
-    // the minted coin. In practice, Alice's node should get the transaction, scan
-    // it, and add it to her wallet. In this test unit, we abstract that away for
-    // simplicity reasons. I should make some kind of API for this though.
-    info!("Deserializing params");
-    let params: MoneyTransferParams = deserialize(&tx.calls[0].data[1..])?;
-    let output = &params.outputs[0];
-    info!("Decrypting output note");
-    let encrypted_note =
-        EncryptedNote { ciphertext: output.ciphertext.clone(), ephem_public: output.ephem_public };
-    let note = encrypted_note.decrypt(&alice_kp.secret)?;
-
-    // Now since Alice got an output and a note to decrypt, we make the coin
-    // metadata so we can spend it.
-    let owncoin = OwnCoin {
-        coin: Coin::from(output.coin),
-        note: note.clone(),
-        secret: alice_kp.secret, // <-- What should this be?
-        nullifier: Nullifier::from(poseidon_hash([alice_kp.secret.inner(), note.serial])),
-        leaf_position,
-    };
-
-    // Alice can spend the coin and send another one to herself
-    info!("Building transfer tx for Alice from Alice");
-    let (params, proofs, secret_keys, _spent_coins) = build_transfer_tx(
-        &alice_kp,
-        &alice_kp.public,
-        amount,
-        token_id,
-        &[owncoin],
-        &alice_merkle_tree,
-        &mint_zkbin,
-        &mint_pk,
-        &burn_zkbin,
-        &burn_pk,
-        false,
-    )?;
-
-    // Build transaction
-    let mut data = vec![MoneyFunction::Transfer 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];
-
-    info!("Executing transaction on the faucet's blockchain db");
-    faucet_state.read().await.verify_transactions(&[tx.clone()], true).await?;
-    info!("Adding coin to faucet's Merkle tree");
-    faucet_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
-
-    info!("Executing transaction on Alice's blockchain db");
-    alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
-    // TODO: FIXME: Actually have a look at the `merkle_add` calls
-    //              We might want to witness in there to avoid maintaining two trees.
-    alice_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
-    let leaf_position = alice_merkle_tree.witness().unwrap();
-
-    // And again
-
-    info!("Deserializing params");
-    let params: MoneyTransferParams = deserialize(&tx.calls[0].data[1..])?;
-    let output = &params.outputs[0];
-    info!("Decrypting output note");
-    let encrypted_note =
-        EncryptedNote { ciphertext: output.ciphertext.clone(), ephem_public: output.ephem_public };
-    let note = encrypted_note.decrypt(&alice_kp.secret)?;
-
-    // Now since Alice got an output and a note to decrypt, we make the coin
-    // metadata so we can spend it.
-    let owncoin = OwnCoin {
-        coin: Coin::from(output.coin),
-        note: note.clone(),
-        secret: alice_kp.secret, // <-- What should this be?
-        nullifier: Nullifier::from(poseidon_hash([alice_kp.secret.inner(), note.serial])),
-        leaf_position,
-    };
-
-    // Alice can spend the coin and send another one to herself
-    info!("Building transfer tx for Alice from Alice");
-    let (params, proofs, secret_keys, _spent_coins) = build_transfer_tx(
-        &alice_kp,
-        &alice_kp.public,
-        amount,
-        token_id,
-        &[owncoin],
-        &alice_merkle_tree,
-        &mint_zkbin,
-        &mint_pk,
-        &burn_zkbin,
-        &burn_pk,
-        false,
-    )?;
-
-    // Build transaction
-    let mut data = vec![MoneyFunction::Transfer 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];
-
-    info!("Executing transaction on the faucet's blockchain db");
-    faucet_state.read().await.verify_transactions(&[tx.clone()], true).await?;
-    info!("Adding coin to faucet's Merkle tree");
-    faucet_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
-    info!("Executing transaction on Alice's blockchain db");
-    alice_state.read().await.verify_transactions(&[tx], true).await?;
-    // TODO: FIXME: Actually have a look at the `merkle_add` calls
-    alice_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
-
-    Ok(())
-}