瀏覽代碼

contract/money: Working darkotc integration tests.

parazyd 3 年之前
父節點
當前提交
2854beb2b2

+ 6 - 1
src/consensus/validator.rs

@@ -912,6 +912,11 @@ impl ValidatorState {
             // move on with verification. First we verify the signatures as that's
             // move on with verification. First we verify the signatures as that's
             // cheaper, and then finally we verify the ZK proofs.
             // cheaper, and then finally we verify the ZK proofs.
             info!("Verifying signatures for transaction {}", tx_hash);
             info!("Verifying signatures for transaction {}", tx_hash);
+            if sig_table.len() != tx.signatures.len() {
+                error!("Incorrect number of signatures in tx {}", tx_hash);
+                return Err(Error::InvalidSignature)
+            }
+
             match tx.verify_sigs(sig_table) {
             match tx.verify_sigs(sig_table) {
                 Ok(()) => info!("Signatures verification for tx {} successful", tx_hash),
                 Ok(()) => info!("Signatures verification for tx {} successful", tx_hash),
                 Err(e) => {
                 Err(e) => {
@@ -928,7 +933,7 @@ impl ValidatorState {
             match tx.verify_zkps(self.verifying_keys.clone(), zkp_table).await {
             match tx.verify_zkps(self.verifying_keys.clone(), zkp_table).await {
                 Ok(()) => info!("ZK proof verification for tx {} successful", tx_hash),
                 Ok(()) => info!("ZK proof verification for tx {} successful", tx_hash),
                 Err(e) => {
                 Err(e) => {
-                    error!("ZK proof verrification for tx {} failed: {}", tx_hash, e);
+                    error!("ZK proof verification for tx {} failed: {}", tx_hash, e);
                     return Err(e.into())
                     return Err(e.into())
                 }
                 }
             };
             };

+ 2 - 2
src/contract/dao/src/entrypoint.rs

@@ -329,7 +329,7 @@ fn process_update(cid: ContractId, ix: &[u8]) -> ContractResult {
             let roots_db = db_lookup(cid, DAO_ROOTS_TREE)?;
             let roots_db = db_lookup(cid, DAO_ROOTS_TREE)?;
 
 
             let node = MerkleNode::from(update.dao_bulla.inner());
             let node = MerkleNode::from(update.dao_bulla.inner());
-            merkle_add(bulla_db, roots_db, &serialize(&DAO_MERKLE_TREE), &node)?;
+            merkle_add(bulla_db, roots_db, &serialize(&DAO_MERKLE_TREE), &[node])?;
 
 
             Ok(())
             Ok(())
         }
         }
@@ -346,7 +346,7 @@ fn process_update(cid: ContractId, ix: &[u8]) -> ContractResult {
                 proposal_tree_db,
                 proposal_tree_db,
                 proposal_root_db,
                 proposal_root_db,
                 &serialize(&DAO_PROPOSAL_MERKLE_TREE),
                 &serialize(&DAO_PROPOSAL_MERKLE_TREE),
-                &node,
+                &[node],
             )?;
             )?;
 
 
             let pv = ProposalVotes::default();
             let pv = ProposalVotes::default();

+ 7 - 2
src/contract/money/Makefile

@@ -28,8 +28,13 @@ money_contract.wasm: $(ZKAS_BIN) $(WASM_SRC)
 	$(CARGO) build --release --package darkfi-money-contract --target wasm32-unknown-unknown
 	$(CARGO) build --release --package darkfi-money-contract --target wasm32-unknown-unknown
 	cp -f ../../../target/wasm32-unknown-unknown/release/darkfi_money_contract.wasm $@
 	cp -f ../../../target/wasm32-unknown-unknown/release/darkfi_money_contract.wasm $@
 
 
-test: all
-	$(CARGO) test --release --features=no-entrypoint,client --package darkfi-money-contract
+test-otc: all
+	$(CARGO) test --release --features=no-entrypoint,client --package darkfi-money-contract --test otcswap
+
+test-transfer: all
+	$(CARGO) test --release --features=no-entrypoint,client --package darkfi-money-contract --test transfer
+
+test: test-otc test-transfer
 
 
 clean:
 clean:
 	rm -f $(ZKAS_BIN) $(WASM_BIN)
 	rm -f $(ZKAS_BIN) $(WASM_BIN)

+ 40 - 21
src/contract/money/src/client.rs

@@ -477,7 +477,8 @@ fn create_transfer_burn_proof(
 /// * `token_id_send` - Token ID to send
 /// * `token_id_send` - Token ID to send
 /// * `value_recv` - Amount to receive
 /// * `value_recv` - Amount to receive
 /// * `token_id_recv` - Token ID to receive
 /// * `token_id_recv` - Token ID to receive
-/// * `value_blinds` - Value blinds used to calculate remainder blind
+/// * `value_blinds` - Value blinds to use if we're the second half
+/// * `token_blinds` - Token blinds to use if we're the second half
 /// * `coins` - Set of coins we're able to spend
 /// * `coins` - Set of coins we're able to spend
 /// * `tree` - Current Merkle tree of coins
 /// * `tree` - Current Merkle tree of coins
 /// * `mint_zkbin` - ZkBinary of the mint circuit
 /// * `mint_zkbin` - ZkBinary of the mint circuit
@@ -491,13 +492,21 @@ pub fn build_half_swap_tx(
     value_recv: u64,
     value_recv: u64,
     token_id_recv: TokenId,
     token_id_recv: TokenId,
     value_blinds: &[ValueBlind],
     value_blinds: &[ValueBlind],
+    token_blinds: &[ValueBlind],
     coins: &[OwnCoin],
     coins: &[OwnCoin],
     tree: &BridgeTree<MerkleNode, MERKLE_DEPTH>,
     tree: &BridgeTree<MerkleNode, MERKLE_DEPTH>,
     mint_zkbin: &ZkBinary,
     mint_zkbin: &ZkBinary,
     mint_pk: &ProvingKey,
     mint_pk: &ProvingKey,
     burn_zkbin: &ZkBinary,
     burn_zkbin: &ZkBinary,
     burn_pk: &ProvingKey,
     burn_pk: &ProvingKey,
-) -> Result<(MoneyTransferParams, Vec<Proof>, Vec<SecretKey>, Vec<OwnCoin>, Vec<ValueBlind>)> {
+) -> Result<(
+    MoneyTransferParams,
+    Vec<Proof>,
+    Vec<SecretKey>,
+    Vec<OwnCoin>,
+    Vec<ValueBlind>,
+    Vec<ValueBlind>,
+)> {
     debug!("Building OTC swap transaction half");
     debug!("Building OTC swap transaction half");
     assert!(value_send != 0);
     assert!(value_send != 0);
     assert!(value_recv != 0);
     assert!(value_recv != 0);
@@ -533,26 +542,37 @@ pub fn build_half_swap_tx(
     // We now fill this with necessary stuff
     // We now fill this with necessary stuff
     let mut params = MoneyTransferParams { clear_inputs: vec![], inputs: vec![], outputs: vec![] };
     let mut params = MoneyTransferParams { clear_inputs: vec![], inputs: vec![], outputs: vec![] };
 
 
-    let mut ret_blinds = vec![];
-
-    let value_send_blind = ValueBlind::random(&mut OsRng);
-
-    // If we got a non-empty value_blinds passed into this function, we're making the last
-    // output so we use those blinds to calculate the remainder. The slice should have two
-    // elements, 0 being the input blind, and 1 being the output blind.
-    // BUG: This doesn't work properly, and needs to be fixed.
-    let value_recv_blind = if value_blinds.is_empty() {
-        ValueBlind::random(&mut OsRng)
-    } else {
-        compute_remainder_blind(&[], &[], &[value_blinds[0]])
+    let val_blinds: Vec<ValueBlind>;
+    let tok_blinds: Vec<ValueBlind>;
+
+    // If we got non-empty `value_blinds` passed into this function, we use them here.
+    // They should be sent to the second party by the swap initiator.
+    let (value_send_blind, value_recv_blind) = {
+        if value_blinds.is_empty() {
+            let value_send_blind = ValueBlind::random(&mut OsRng);
+            let value_recv_blind = ValueBlind::random(&mut OsRng);
+            val_blinds = vec![value_send_blind, value_recv_blind];
+            (value_send_blind, value_recv_blind)
+        } else {
+            val_blinds = vec![value_blinds[1], value_blinds[0]];
+            (value_blinds[1], value_blinds[0])
+        }
     };
     };
-    ret_blinds.push(value_recv_blind);
-    ret_blinds.push(value_send_blind);
-    debug!("RET BLINDS: {:?}", ret_blinds);
 
 
-    let token_send_blind = ValueBlind::random(&mut OsRng);
-    let token_recv_blind = ValueBlind::random(&mut OsRng);
+    // The same goes for token blinds
+    let (token_send_blind, token_recv_blind) = {
+        if token_blinds.is_empty() {
+            let token_send_blind = ValueBlind::random(&mut OsRng);
+            let token_recv_blind = ValueBlind::random(&mut OsRng);
+            tok_blinds = vec![token_send_blind, token_recv_blind];
+            (token_send_blind, token_recv_blind)
+        } else {
+            tok_blinds = vec![token_blinds[1], token_blinds[0]];
+            (token_blinds[1], token_blinds[0])
+        }
+    };
 
 
+    // The ephemeral secret key we're using here.
     let signature_secret = SecretKey::random(&mut OsRng);
     let signature_secret = SecretKey::random(&mut OsRng);
 
 
     // Disable composability for this old obsolete API
     // Disable composability for this old obsolete API
@@ -641,8 +661,7 @@ pub fn build_half_swap_tx(
 
 
     // Now we should have all the params, zk proofs, and signature secrets.
     // Now we should have all the params, zk proofs, and signature secrets.
     // We return it all and let the caller deal with it.
     // We return it all and let the caller deal with it.
-
-    Ok((params, zk_proofs, vec![signature_secret], spent_coins, ret_blinds))
+    Ok((params, zk_proofs, vec![signature_secret], spent_coins, val_blinds, tok_blinds))
 }
 }
 
 
 /// Build money contract transfer transaction parameters with the given data:
 /// Build money contract transfer transaction parameters with the given data:

+ 12 - 33
src/contract/money/src/lib.rs

@@ -142,6 +142,7 @@ fn init_contract(cid: ContractId, ix: &[u8]) -> ContractResult {
             // Add a Merkle tree to the info db:
             // Add a Merkle tree to the info db:
             let coin_tree = MerkleTree::new(100);
             let coin_tree = MerkleTree::new(100);
             let mut coin_tree_data = vec![];
             let mut coin_tree_data = vec![];
+
             coin_tree_data.write_u32(0)?;
             coin_tree_data.write_u32(0)?;
             coin_tree.encode(&mut coin_tree_data)?;
             coin_tree.encode(&mut coin_tree_data)?;
 
 
@@ -271,17 +272,14 @@ fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
                 valcom_total += pedersen_commitment_u64(input.value, input.value_blind);
                 valcom_total += pedersen_commitment_u64(input.value, input.value_blind);
             }
             }
 
 
-            let mut new_coin_roots = vec![];
-            let mut new_nullifiers = vec![];
+            let mut new_nullifiers = Vec::with_capacity(params.inputs.len());
 
 
             msg!("[Transfer] Iterating over anonymous inputs");
             msg!("[Transfer] Iterating over anonymous inputs");
             for (i, input) in params.inputs.iter().enumerate() {
             for (i, input) in params.inputs.iter().enumerate() {
                 // The Merkle root is used to know whether this is a coin that existed
                 // The Merkle root is used to know whether this is a coin that existed
                 // in a previous state.
                 // in a previous state.
-                if new_coin_roots.contains(&input.merkle_root) ||
-                    db_contains_key(coin_roots_db, &serialize(&input.merkle_root))?
-                {
-                    msg!("[Transfer] Error: Duplicate Merkle root found in input {}", i);
+                if !db_contains_key(coin_roots_db, &serialize(&input.merkle_root))? {
+                    msg!("[Transfer] Error: Merkle root not found in previous state (input {})", i);
                     return Err(ContractError::Custom(21))
                     return Err(ContractError::Custom(21))
                 }
                 }
 
 
@@ -293,9 +291,7 @@ fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
                     return Err(ContractError::Custom(22))
                     return Err(ContractError::Custom(22))
                 }
                 }
 
 
-                new_coin_roots.push(input.merkle_root);
                 new_nullifiers.push(input.nullifier);
                 new_nullifiers.push(input.nullifier);
-
                 valcom_total += input.value_commit;
                 valcom_total += input.value_commit;
             }
             }
 
 
@@ -310,7 +306,6 @@ fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
 
 
                 // FIXME: Needs some work on types and their place within all these libraries
                 // FIXME: Needs some work on types and their place within all these libraries
                 new_coins.push(Coin::from(output.coin));
                 new_coins.push(Coin::from(output.coin));
-
                 valcom_total -= output.value_commit;
                 valcom_total -= output.value_commit;
             }
             }
 
 
@@ -363,24 +358,17 @@ fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
             assert!(params.inputs.len() == 2);
             assert!(params.inputs.len() == 2);
             assert!(params.outputs.len() == 2);
             assert!(params.outputs.len() == 2);
 
 
-            let mut new_coin_roots = vec![];
-            let mut new_nullifiers = vec![];
+            let mut new_nullifiers = Vec::with_capacity(params.inputs.len());
 
 
             // inputs[0] is being swapped to outputs[1]
             // inputs[0] is being swapped to outputs[1]
             // inputs[1] is being swapped to outputs[0]
             // inputs[1] is being swapped to outputs[0]
             // So that's how we check the value and token commitments
             // So that's how we check the value and token commitments
-            let mut valcom_total = pallas::Point::identity();
-
-            valcom_total += params.inputs[0].value_commit;
-            valcom_total -= params.outputs[1].value_commit;
-            if valcom_total != pallas::Point::identity() {
+            if params.inputs[0].value_commit != params.outputs[1].value_commit {
                 msg!("[OtcSwap] Error: Value commitments for input 0 and output 1 do not match");
                 msg!("[OtcSwap] Error: Value commitments for input 0 and output 1 do not match");
                 return Err(ContractError::Custom(24))
                 return Err(ContractError::Custom(24))
             }
             }
 
 
-            valcom_total += params.inputs[1].value_commit;
-            valcom_total -= params.outputs[0].value_commit;
-            if valcom_total != pallas::Point::identity() {
+            if params.inputs[1].value_commit != params.outputs[0].value_commit {
                 msg!("[OtcSwap] Error: Value commitments for input 1 and output 0 do not match");
                 msg!("[OtcSwap] Error: Value commitments for input 1 and output 0 do not match");
                 return Err(ContractError::Custom(24))
                 return Err(ContractError::Custom(24))
             }
             }
@@ -399,10 +387,8 @@ fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
             for (i, input) in params.inputs.iter().enumerate() {
             for (i, input) in params.inputs.iter().enumerate() {
                 // The Merkle root is used to know whether this is a coin that
                 // The Merkle root is used to know whether this is a coin that
                 // existed in a previous state.
                 // existed in a previous state.
-                if new_coin_roots.contains(&input.merkle_root) ||
-                    db_contains_key(coin_roots_db, &serialize(&input.merkle_root))?
-                {
-                    msg!("[OtcSwap] Error: Duplicate Merkle root found in input {}", i);
+                if !db_contains_key(coin_roots_db, &serialize(&input.merkle_root))? {
+                    msg!("[OtcSwap] Error: Merkle root not found in previous state (input {})", i);
                     return Err(ContractError::Custom(21))
                     return Err(ContractError::Custom(21))
                 }
                 }
 
 
@@ -414,7 +400,6 @@ fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
                     return Err(ContractError::Custom(22))
                     return Err(ContractError::Custom(22))
                 }
                 }
 
 
-                new_coin_roots.push(input.merkle_root);
                 new_nullifiers.push(input.nullifier);
                 new_nullifiers.push(input.nullifier);
             }
             }
 
 
@@ -459,15 +444,9 @@ fn process_update(cid: ContractId, update_data: &[u8]) -> ContractResult {
                 db_set(nullifiers_db, &serialize(&nullifier), &[])?;
                 db_set(nullifiers_db, &serialize(&nullifier), &[])?;
             }
             }
 
 
-            for coin in update.coins {
-                // TODO: merkle_add() should take a list of coins and batch add them for efficiency
-                merkle_add(
-                    info_db,
-                    coin_roots_db,
-                    &serialize(&COIN_MERKLE_TREE.to_string()),
-                    &MerkleNode::from(coin.inner()),
-                )?;
-            }
+            msg!("Adding coins {:?} to Merkle tree", update.coins);
+            let coins: Vec<_> = update.coins.iter().map(|x| MerkleNode::from(x.inner())).collect();
+            merkle_add(info_db, coin_roots_db, &serialize(&COIN_MERKLE_TREE.to_string()), &coins)?;
 
 
             Ok(())
             Ok(())
         }
         }

+ 198 - 66
src/contract/money/tests/otcswap.rs

@@ -117,7 +117,6 @@ async fn money_contract_swap() -> Result<()> {
     .await?;
     .await?;
 
 
     // In a hacky way, we just generate the proving keys for the circuits used.
     // 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 contract_id = ContractId::from(pallas::Base::from(u64::MAX - 420));
 
 
     let alice_sled = &alice_state.read().await.blockchain.sled_db;
     let alice_sled = &alice_state.read().await.blockchain.sled_db;
@@ -129,7 +128,6 @@ async fn money_contract_swap() -> Result<()> {
 
 
     let mint_zkbin = db_handle.get(&serialize(&ZKAS_MINT_NS))?.unwrap();
     let mint_zkbin = db_handle.get(&serialize(&ZKAS_MINT_NS))?.unwrap();
     let burn_zkbin = db_handle.get(&serialize(&ZKAS_BURN_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 mint_zkbin = ZkBinary::decode(&mint_zkbin.clone())?;
     let burn_zkbin = ZkBinary::decode(&burn_zkbin.clone())?;
     let burn_zkbin = ZkBinary::decode(&burn_zkbin.clone())?;
     let mint_witnesses = empty_witnesses(&mint_zkbin);
     let mint_witnesses = empty_witnesses(&mint_zkbin);
@@ -137,7 +135,7 @@ async fn money_contract_swap() -> Result<()> {
     let mint_circuit = ZkCircuit::new(mint_witnesses, mint_zkbin.clone());
     let mint_circuit = ZkCircuit::new(mint_witnesses, mint_zkbin.clone());
     let burn_circuit = ZkCircuit::new(burn_witnesses, burn_zkbin.clone());
     let burn_circuit = ZkCircuit::new(burn_witnesses, burn_zkbin.clone());
 
 
-    info!("Creating zk proving keys");
+    info!("Creating ZK proving keys");
     let k = 13;
     let k = 13;
     let mut proving_keys = HashMap::<[u8; 32], Vec<(&str, ProvingKey)>>::new();
     let mut proving_keys = HashMap::<[u8; 32], Vec<(&str, ProvingKey)>>::new();
     let mint_pk = ProvingKey::build(k, &mint_circuit);
     let mint_pk = ProvingKey::build(k, &mint_circuit);
@@ -146,7 +144,6 @@ async fn money_contract_swap() -> Result<()> {
     proving_keys.insert(contract_id.inner().to_repr(), pks);
     proving_keys.insert(contract_id.inner().to_repr(), pks);
 
 
     // We also have to initialize the Merkle trees used for coins.
     // 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 faucet_merkle_tree = BridgeTree::<MerkleNode, MERKLE_DEPTH>::new(100);
     let mut alice_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);
     let mut bob_merkle_tree = BridgeTree::<MerkleNode, MERKLE_DEPTH>::new(100);
@@ -157,7 +154,7 @@ async fn money_contract_swap() -> Result<()> {
     let alice_amount = decode_base10("42.69", 8, true)?;
     let alice_amount = decode_base10("42.69", 8, true)?;
     let bob_amount = decode_base10("69.42", 8, true)?;
     let bob_amount = decode_base10("69.42", 8, true)?;
 
 
-    info!("Building transfer tx for Alice's airdrop");
+    info!("[Faucet] Building Money::Transfer tx for Alice's airdrop");
     let (params, proofs, secret_keys, _spent_coins) = build_transfer_tx(
     let (params, proofs, secret_keys, _spent_coins) = build_transfer_tx(
         &faucet_kp,
         &faucet_kp,
         &alice_kp.public,
         &alice_kp.public,
@@ -175,45 +172,44 @@ async fn money_contract_swap() -> Result<()> {
     // Build transaction
     // Build transaction
     let mut data = vec![MoneyFunction::Transfer as u8];
     let mut data = vec![MoneyFunction::Transfer as u8];
     params.encode(&mut data)?;
     params.encode(&mut data)?;
-    let calls = vec![ContractCall { contract_id, data }];
-    let proofs = vec![proofs];
-    let mut tx = Transaction { calls, proofs, signatures: vec![] };
+    let mut tx = Transaction {
+        calls: vec![ContractCall { contract_id, data }],
+        proofs: vec![proofs],
+        signatures: vec![],
+    };
     let sigs = tx.create_sigs(&mut OsRng, &secret_keys)?;
     let sigs = tx.create_sigs(&mut OsRng, &secret_keys)?;
     tx.signatures = vec![sigs];
     tx.signatures = vec![sigs];
 
 
     // Let's first execute this transaction for the faucet to see if it passes.
     // Let's first execute this transaction for the faucet to see if it passes.
     // Then Alice gets the tx and also executes it.
     // Then Alice gets the tx and also executes it.
-    info!("Executing transaction on the faucet's blockchain db");
+    info!("[Faucet] Verifying Alice's airdrop tx");
     faucet_state.read().await.verify_transactions(&[tx.clone()], true).await?;
     faucet_state.read().await.verify_transactions(&[tx.clone()], true).await?;
     faucet_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
     faucet_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
 
 
-    info!("Executing transaction on Alice's blockchain db");
+    info!("[Alice] Verifying Alice's airdrop tx");
     alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
     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));
     alice_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
-    let leaf_position = alice_merkle_tree.witness().unwrap();
 
 
-    info!("Executing transaction on Bob's blockchain db");
+    info!("[Bob] Verifying Alice's airdrop tx");
     bob_state.read().await.verify_transactions(&[tx.clone()], true).await?;
     bob_state.read().await.verify_transactions(&[tx.clone()], true).await?;
     bob_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
     bob_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
 
 
     let params: MoneyTransferParams = deserialize(&tx.calls[0].data[1..])?;
     let params: MoneyTransferParams = deserialize(&tx.calls[0].data[1..])?;
     let output = &params.outputs[0];
     let output = &params.outputs[0];
-    let encrypted_note =
-        EncryptedNote { ciphertext: output.ciphertext.clone(), ephem_public: output.ephem_public };
+    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 note = encrypted_note.decrypt(&alice_kp.secret)?;
 
 
-    let mut alice_owncoins = vec![];
-    let owncoin = OwnCoin {
+    let alice_owncoin = OwnCoin {
         coin: Coin::from(output.coin),
         coin: Coin::from(output.coin),
         note: note.clone(),
         note: note.clone(),
         secret: alice_kp.secret, // <-- What should this be?
         secret: alice_kp.secret, // <-- What should this be?
         nullifier: Nullifier::from(poseidon_hash([alice_kp.secret.inner(), note.serial])),
         nullifier: Nullifier::from(poseidon_hash([alice_kp.secret.inner(), note.serial])),
-        leaf_position,
+        leaf_position: alice_merkle_tree.witness().unwrap(),
     };
     };
-    alice_owncoins.push(owncoin);
 
 
-    info!("Building transfer tx for Bob's airdrop");
+    info!("[Faucet] Building Money::Transfer tx for Bob's airdrop");
     let (params, proofs, secret_keys, _spent_coins) = build_transfer_tx(
     let (params, proofs, secret_keys, _spent_coins) = build_transfer_tx(
         &faucet_kp,
         &faucet_kp,
         &bob_kp.public,
         &bob_kp.public,
@@ -231,53 +227,54 @@ async fn money_contract_swap() -> Result<()> {
     // Build transaction
     // Build transaction
     let mut data = vec![MoneyFunction::Transfer as u8];
     let mut data = vec![MoneyFunction::Transfer as u8];
     params.encode(&mut data)?;
     params.encode(&mut data)?;
-    let calls = vec![ContractCall { contract_id, data }];
-    let proofs = vec![proofs];
-    let mut tx = Transaction { calls, proofs, signatures: vec![] };
+    let mut tx = Transaction {
+        calls: vec![ContractCall { contract_id, data }],
+        proofs: vec![proofs],
+        signatures: vec![],
+    };
     let sigs = tx.create_sigs(&mut OsRng, &secret_keys)?;
     let sigs = tx.create_sigs(&mut OsRng, &secret_keys)?;
     tx.signatures = vec![sigs];
     tx.signatures = vec![sigs];
 
 
     // Let's first execute this transaction for the faucet to see if it passes.
     // Let's first execute this transaction for the faucet to see if it passes.
     // Then Alice gets the tx and also executes it.
     // Then Alice gets the tx and also executes it.
-    info!("Executing transaction on the faucet's blockchain db");
+    info!("[Faucet] Verifying Bob's airdrop tx");
     faucet_state.read().await.verify_transactions(&[tx.clone()], true).await?;
     faucet_state.read().await.verify_transactions(&[tx.clone()], true).await?;
     faucet_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
     faucet_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
 
 
-    info!("Executing transaction on Alice's blockchain db");
+    info!("[Alice] Verifying Bob's airdrop tx");
     alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
     alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
     alice_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
     alice_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
 
 
-    info!("Executing transaction on Bob's blockchain db");
+    info!("[Bob] Verifying Bob's airdrop tx");
     bob_state.read().await.verify_transactions(&[tx.clone()], true).await?;
     bob_state.read().await.verify_transactions(&[tx.clone()], true).await?;
     bob_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
     bob_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
-    let leaf_position = bob_merkle_tree.witness().unwrap();
 
 
     let params: MoneyTransferParams = deserialize(&tx.calls[0].data[1..])?;
     let params: MoneyTransferParams = deserialize(&tx.calls[0].data[1..])?;
-    let output = &params.outputs[0];
-    let encrypted_note =
-        EncryptedNote { ciphertext: output.ciphertext.clone(), ephem_public: output.ephem_public };
+    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 note = encrypted_note.decrypt(&bob_kp.secret)?;
 
 
-    let mut bob_owncoins = vec![];
-    let owncoin = OwnCoin {
+    let bob_owncoin = OwnCoin {
         coin: Coin::from(output.coin),
         coin: Coin::from(output.coin),
         note: note.clone(),
         note: note.clone(),
         secret: bob_kp.secret, // <-- What should this be?
         secret: bob_kp.secret, // <-- What should this be?
         nullifier: Nullifier::from(poseidon_hash([bob_kp.secret.inner(), note.serial])),
         nullifier: Nullifier::from(poseidon_hash([bob_kp.secret.inner(), note.serial])),
-        leaf_position,
+        leaf_position: bob_merkle_tree.witness().unwrap(),
     };
     };
-    bob_owncoins.push(owncoin);
 
 
     // Now Alice and Bob should have their tokens. They can attempt to swap them.
     // 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
     // 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 add his half, sign it, and return to Alice. The Alice can do the inspection
     // and sign with her key, and broadcast the transaction.
     // and sign with her key, and broadcast the transaction.
+    info!("[Alice] Building swap tx half");
     let (
     let (
         alice_half_params,
         alice_half_params,
         alice_half_proofs,
         alice_half_proofs,
         alice_half_keys,
         alice_half_keys,
         _alice_half_spent_coins,
         _alice_half_spent_coins,
         alice_value_blinds,
         alice_value_blinds,
+        alice_token_blinds,
     ) = build_half_swap_tx(
     ) = build_half_swap_tx(
         &alice_kp.public,
         &alice_kp.public,
         alice_amount,
         alice_amount,
@@ -285,7 +282,8 @@ async fn money_contract_swap() -> Result<()> {
         bob_amount,
         bob_amount,
         bob_token_id,
         bob_token_id,
         &[],
         &[],
-        &alice_owncoins,
+        &[],
+        &[alice_owncoin],
         &alice_merkle_tree,
         &alice_merkle_tree,
         &mint_zkbin,
         &mint_zkbin,
         &mint_pk,
         &mint_pk,
@@ -293,61 +291,195 @@ async fn money_contract_swap() -> Result<()> {
         &burn_pk,
         &burn_pk,
     )?;
     )?;
 
 
-    let (bob_half_params, bob_half_proofs, bob_half_keys, _bob_half_spent_coins, _bob_value_blinds) =
-        build_half_swap_tx(
-            &bob_kp.public,
-            bob_amount,
-            bob_token_id,
-            alice_amount,
-            alice_token_id,
-            &alice_value_blinds,
-            &bob_owncoins,
-            &bob_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 {
     let bob_full_params = MoneyTransferParams {
         clear_inputs: vec![],
         clear_inputs: vec![],
         inputs: vec![alice_half_params.inputs[0].clone(), bob_half_params.inputs[0].clone()],
         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()],
         outputs: vec![alice_half_params.outputs[0].clone(), bob_half_params.outputs[0].clone()],
     };
     };
 
 
-    assert!(bob_full_params.inputs.len() == 2);
-    assert!(bob_full_params.outputs.len() == 2);
-
-    let mut bob_full_proofs = vec![];
-    bob_full_proofs.extend_from_slice(&alice_half_proofs);
-    bob_full_proofs.extend_from_slice(&bob_half_proofs);
+    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];
     let mut data = vec![MoneyFunction::OtcSwap as u8];
     bob_full_params.encode(&mut data)?;
     bob_full_params.encode(&mut data)?;
-    let calls = vec![ContractCall { contract_id, data }];
-    let proofs = vec![bob_full_proofs];
-    let mut tx = Transaction { calls, proofs, signatures: vec![] };
+    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)?;
     let sigs = tx.create_sigs(&mut OsRng, &bob_half_keys)?;
     tx.signatures = vec![sigs];
     tx.signatures = vec![sigs];
 
 
     // This tx finds its way back to Alice.
     // This tx finds its way back to Alice.
     // She can try broadcasting the tx without signing, but this should fail to verify.
     // 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());
+    //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.
+    // 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)?;
     let sigs = tx.create_sigs(&mut OsRng, &alice_half_keys)?;
-    tx.signatures.push(sigs);
+    tx.signatures[0].insert(0, sigs[0]);
 
 
     info!("[Alice] Verifying signed swap transaction");
     info!("[Alice] Verifying signed swap transaction");
     // Now the transaction is signed by both parties.
     // Now the transaction is signed by both parties.
     // Let's execute it on Alice's chain state.
     // Let's execute it on Alice's chain state.
     alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
     alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
     // Alice's received coin is in outputs[0]
     // Alice's received coin is in outputs[0]
-    alice_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
-    let leaf_position = alice_merkle_tree.witness().unwrap();
+    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
     // This is Bob's received coin
-    alice_merkle_tree.append(&MerkleNode::from(params.outputs[1].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(())
     Ok(())
 }
 }

+ 4 - 4
src/contract/money/tests/transfer.rs

@@ -110,16 +110,15 @@ async fn money_contract_transfer() -> Result<()> {
     info!("Looking up zkas circuits from DB");
     info!("Looking up zkas circuits from DB");
     let contract_id = ContractId::from(pallas::Base::from(u64::MAX - 420));
     let contract_id = ContractId::from(pallas::Base::from(u64::MAX - 420));
 
 
-    let zkas_mint_ns = String::from("Mint");
-    let zkas_burn_ns = String::from("Burn");
     let alice_sled = &alice_state.read().await.blockchain.sled_db;
     let alice_sled = &alice_state.read().await.blockchain.sled_db;
     let db_handle = alice_state.read().await.blockchain.contracts.lookup(
     let db_handle = alice_state.read().await.blockchain.contracts.lookup(
         alice_sled,
         alice_sled,
         &contract_id,
         &contract_id,
         ZKAS_DB_NAME,
         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 = db_handle.get(&serialize(&ZKAS_MINT_NS))?.unwrap();
+    let burn_zkbin = db_handle.get(&serialize(&ZKAS_BURN_NS))?.unwrap();
     info!("Decoding bincode");
     info!("Decoding bincode");
     let mint_zkbin = ZkBinary::decode(&mint_zkbin.clone())?;
     let mint_zkbin = ZkBinary::decode(&mint_zkbin.clone())?;
     let burn_zkbin = ZkBinary::decode(&burn_zkbin.clone())?;
     let burn_zkbin = ZkBinary::decode(&burn_zkbin.clone())?;
@@ -237,6 +236,7 @@ async fn money_contract_transfer() -> Result<()> {
     info!("Executing transaction on Alice's blockchain db");
     info!("Executing transaction on Alice's blockchain db");
     alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
     alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
     // TODO: FIXME: Actually have a look at the `merkle_add` calls
     // 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));
     alice_merkle_tree.append(&MerkleNode::from(params.outputs[0].coin));
     let leaf_position = alice_merkle_tree.witness().unwrap();
     let leaf_position = alice_merkle_tree.witness().unwrap();
 
 

+ 60 - 37
src/runtime/import/merkle.rs

@@ -47,8 +47,12 @@ pub(crate) fn merkle_add(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -
                 return -2
                 return -2
             };
             };
 
 
+            // The buffer should deserialize into:
+            // - db_info
+            // - db_roots
+            // - key (as Vec<u8>) (key being the name of the sled tree where the Merkle tree is)
+            // - coins (as Vec<MerkleNode>) (the coins being added into the Merkle tree)
             let mut buf_reader = Cursor::new(buf);
             let mut buf_reader = Cursor::new(buf);
-
             // FIXME: There's a type DbHandle=u32, but this should maybe be renamed
             // FIXME: There's a type DbHandle=u32, but this should maybe be renamed
             let db_info: u32 = match Decodable::decode(&mut buf_reader) {
             let db_info: u32 = match Decodable::decode(&mut buf_reader) {
                 Ok(v) => v,
                 Ok(v) => v,
@@ -57,7 +61,6 @@ pub(crate) fn merkle_add(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -
                     return -2
                     return -2
                 }
                 }
             };
             };
-            let db_info = db_info as usize;
 
 
             let db_roots: u32 = match Decodable::decode(&mut buf_reader) {
             let db_roots: u32 = match Decodable::decode(&mut buf_reader) {
                 Ok(v) => v,
                 Ok(v) => v,
@@ -66,8 +69,31 @@ pub(crate) fn merkle_add(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -
                     return -2
                     return -2
                 }
                 }
             };
             };
+
+            let db_info = db_info as usize;
             let db_roots = db_roots as usize;
             let db_roots = db_roots as usize;
+            let db_handles = env.db_handles.borrow();
+            let mut db_batches = env.db_batches.borrow_mut();
+            let n_dbs = db_handles.len();
+            let n_bat = db_batches.len();
+
+            if n_dbs <= db_info || n_bat <= db_info || n_dbs <= db_roots || n_bat <= db_roots {
+                error!(target: "wasm_runtime::merkle_add", "Requested DbHandle that is out of bounds");
+                return -2
+            }
+
+            let info_handle_idx = db_info;
+            let db_info = &db_handles[info_handle_idx];
+
+            let roots_handle_idx = db_roots;
+            let db_roots = &db_handles[roots_handle_idx];
+
+            if db_info.contract_id != env.contract_id || db_roots.contract_id != env.contract_id {
+                error!(target: "wasm_runtime::merkle_add", "Unauthorized to write to DbHandle");
+                return -2
+            }
 
 
+            // This `key` represents the sled database tree name
             let key: Vec<u8> = match Decodable::decode(&mut buf_reader) {
             let key: Vec<u8> = match Decodable::decode(&mut buf_reader) {
                 Ok(v) => v,
                 Ok(v) => v,
                 Err(e) => {
                 Err(e) => {
@@ -76,7 +102,8 @@ pub(crate) fn merkle_add(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -
                 }
                 }
             };
             };
 
 
-            let coin: MerkleNode = match Decodable::decode(&mut buf_reader) {
+            // This `coin` represents the leaf we're adding to the Merkle tree
+            let coins: Vec<MerkleNode> = match Decodable::decode(&mut buf_reader) {
                 Ok(v) => v,
                 Ok(v) => v,
                 Err(e) => {
                 Err(e) => {
                     error!(target: "wasm_runtime::merkle_add", "Failed to decode MerkleNode: {}", e);
                     error!(target: "wasm_runtime::merkle_add", "Failed to decode MerkleNode: {}", e);
@@ -86,26 +113,7 @@ pub(crate) fn merkle_add(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -
 
 
             // TODO: Ensure we've read the entire buffer above.
             // TODO: Ensure we've read the entire buffer above.
 
 
-            let db_handles = env.db_handles.borrow();
-            let mut db_batches = env.db_batches.borrow_mut();
-
-            if db_handles.len() <= db_info || db_batches.len() <= db_info {
-                error!(target: "wasm_runtime::merkle_add", "Requested db_info DbHandle that is out of bounds");
-                return -2
-            }
-            if db_handles.len() <= db_roots || db_batches.len() <= db_roots {
-                error!(target: "wasm_runtime::merkle_add", "Requested db_roots DbHandle that is out of bounds");
-                return -2
-            }
-
-            let handle_idx = db_info;
-            let db_info = &db_handles[handle_idx];
-            let db_info_batch = &mut db_batches[handle_idx];
-            let handle_idx = db_roots;
-            //let db_roots = &db_handles[handle_idx];
-
             // Read the current tree
             // Read the current tree
-
             let ret = match db_info.get(&key) {
             let ret = match db_info.get(&key) {
                 Ok(v) => v,
                 Ok(v) => v,
                 Err(e) => {
                 Err(e) => {
@@ -131,6 +139,7 @@ pub(crate) fn merkle_add(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -
             );
             );
 
 
             let mut decoder = Cursor::new(&return_data);
             let mut decoder = Cursor::new(&return_data);
+
             let set_size: u32 = match Decodable::decode(&mut decoder) {
             let set_size: u32 = match Decodable::decode(&mut decoder) {
                 Ok(v) => v,
                 Ok(v) => v,
                 Err(e) => {
                 Err(e) => {
@@ -138,6 +147,7 @@ pub(crate) fn merkle_add(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -
                     return -2
                     return -2
                 }
                 }
             };
             };
+
             let mut tree: MerkleTree = match Decodable::decode(&mut decoder) {
             let mut tree: MerkleTree = match Decodable::decode(&mut decoder) {
                 Ok(v) => v,
                 Ok(v) => v,
                 Err(e) => {
                 Err(e) => {
@@ -146,30 +156,43 @@ pub(crate) fn merkle_add(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -
                 }
                 }
             };
             };
 
 
-            tree.append(&coin);
-            let Some(root) = tree.root(0) else {
-                error!(target: "wasm_runtime::merkle_add", "Unable to read the root of tree");
-                return -2;
-            };
+            // Here we add the new coins into the tree.
+            let mut new_roots = vec![];
 
 
-            if db_info.contract_id != env.contract_id {
-                error!(target: "wasm_runtime::merkle_add", "Unauthorized to write to DbHandle");
-                return -2
+            for coin in coins {
+                tree.append(&coin);
+                let Some(root) = tree.root(0) else {
+                    error!(target: "wasm_runtime::merkle_add", "Unable to read the root of tree");
+                    return -2;
+                };
+                new_roots.push(root);
             }
             }
 
 
+            // And we serialize the tree back to bytes
             let mut tree_data = Vec::new();
             let mut tree_data = Vec::new();
-            if tree_data.write_u32(set_size + 1).is_err() || tree.encode(&mut tree_data).is_err() {
+            if tree_data.write_u32(set_size + new_roots.len() as u32).is_err() ||
+                tree.encode(&mut tree_data).is_err()
+            {
                 error!(target: "wasm_runtime::merkle_add", "Couldn't reserialize modified tree");
                 error!(target: "wasm_runtime::merkle_add", "Couldn't reserialize modified tree");
                 return -2
                 return -2
             }
             }
+            let db_info_batch = &mut db_batches[info_handle_idx];
             db_info_batch.insert(key, tree_data);
             db_info_batch.insert(key, tree_data);
 
 
-            let db_roots_batch = &mut db_batches[handle_idx];
-            let root_index: Vec<u8> = serialize(&(set_size as u32));
-            assert_eq!(root_index.len(), 4);
-            let root_value: Vec<u8> = serialize(&root);
-            assert_eq!(root_value.len(), 32);
-            db_roots_batch.insert(root_index, root_value);
+            // Here we add the Merkle root to our set of roots
+            // TODO: We should probably make sure that this root isn't in the set
+            let db_roots_batch = &mut db_batches[roots_handle_idx];
+            for root in new_roots.iter() {
+                // FIXME: Why were we writing the set size here?
+                //let root_index: Vec<u8> = serialize(&(set_size as u32));
+                //assert_eq!(root_index.len(), 4);
+                debug!(target: "wasm_runtime::merkle_add", "Appending Merkle root to db: {:?}", root);
+                let root_value: Vec<u8> = serialize(root);
+                // FIXME: This assert can be used to DoS nodes from contracts
+                assert_eq!(root_value.len(), 32);
+                //db_roots_batch.insert(root_index, root_value);
+                db_roots_batch.insert(root_value, &[]);
+            }
 
 
             0
             0
         }
         }

+ 2 - 2
src/sdk/src/merkle.rs

@@ -28,14 +28,14 @@ pub fn merkle_add(
     db_info: DbHandle,
     db_info: DbHandle,
     db_roots: DbHandle,
     db_roots: DbHandle,
     key: &[u8],
     key: &[u8],
-    coin: &MerkleNode,
+    coins: &[MerkleNode],
 ) -> GenericResult<()> {
 ) -> GenericResult<()> {
     let mut buf = vec![];
     let mut buf = vec![];
     let mut len = 0;
     let mut len = 0;
     len += db_info.encode(&mut buf)?;
     len += db_info.encode(&mut buf)?;
     len += db_roots.encode(&mut buf)?;
     len += db_roots.encode(&mut buf)?;
     len += key.to_vec().encode(&mut buf)?;
     len += key.to_vec().encode(&mut buf)?;
-    len += coin.encode(&mut buf)?;
+    len += coins.to_vec().encode(&mut buf)?;
 
 
     match unsafe { merkle_add_(buf.as_ptr(), len as u32) } {
     match unsafe { merkle_add_(buf.as_ptr(), len as u32) } {
         0 => Ok(()),
         0 => Ok(()),

+ 5 - 2
src/tx/mod.rs

@@ -75,10 +75,13 @@ impl Transaction {
                         // We have a verifying key for this
                         // We have a verifying key for this
                         debug!("public inputs: {:#?}", public_vals);
                         debug!("public inputs: {:#?}", public_vals);
                         if let Err(e) = proof.verify(&vk.1, public_vals) {
                         if let Err(e) = proof.verify(&vk.1, public_vals) {
-                            error!("Failed verifying ZK proof: {:#?}", e);
+                            error!(
+                                "Failed verifying {}::{} ZK proof: {:#?}",
+                                call.contract_id, zk_ns, e
+                            );
                             return Err(VerifyFailed::ProofVerifyFailed(e.to_string()).into())
                             return Err(VerifyFailed::ProofVerifyFailed(e.to_string()).into())
                         }
                         }
-                        debug!("Successfully verified {}:{} ZK proof", call.contract_id, zk_ns);
+                        debug!("Successfully verified {}::{} ZK proof", call.contract_id, zk_ns);
                         continue
                         continue
                     }
                     }
                 }
                 }

+ 1 - 0
src/zk/vm_stack.rs

@@ -77,6 +77,7 @@ pub enum StackVar {
     Uint64(Value<u64>),
     Uint64(Value<u64>),
 }
 }
 
 
+// TODO: Make this not panic (try_from)
 macro_rules! impl_from {
 macro_rules! impl_from {
     ($variant:ident, $fortype:ty) => {
     ($variant:ident, $fortype:ty) => {
         impl From<StackVar> for $fortype {
         impl From<StackVar> for $fortype {