Forráskód Böngészése

dao2: working merkle_add()

x 3 éve
szülő
commit
f8d2790bb7

+ 16 - 10
example/dao2/contract/dao/src/lib.rs

@@ -1,21 +1,18 @@
 use darkfi_sdk::{
 use darkfi_sdk::{
-    crypto::{ContractId, constants::MERKLE_DEPTH, MerkleNode, Nullifier},
+    crypto::{constants::MERKLE_DEPTH, ContractId, MerkleNode, MerkleTree, Nullifier},
     db::{db_get, db_init, db_lookup, db_set},
     db::{db_get, db_init, db_lookup, db_set},
     define_contract,
     define_contract,
     error::ContractResult,
     error::ContractResult,
-    msg,
     merkle::merkle_add,
     merkle::merkle_add,
+    msg,
     pasta::pallas,
     pasta::pallas,
     tx::ContractCall,
     tx::ContractCall,
     util::{get_object_bytes, get_object_size, put_object_bytes, set_return_data},
     util::{get_object_bytes, get_object_size, put_object_bytes, set_return_data},
-    incrementalmerkletree::{bridgetree::BridgeTree, Tree}
 };
 };
 use darkfi_serial::{
 use darkfi_serial::{
     deserialize, serialize, Encodable, ReadExt, SerialDecodable, SerialEncodable, WriteExt,
     deserialize, serialize, Encodable, ReadExt, SerialDecodable, SerialEncodable, WriteExt,
 };
 };
 
 
-type MerkleTree = BridgeTree<MerkleNode, { MERKLE_DEPTH }>;
-
 #[derive(Clone, SerialEncodable, SerialDecodable)]
 #[derive(Clone, SerialEncodable, SerialDecodable)]
 pub struct DaoBulla(pub pallas::Base);
 pub struct DaoBulla(pub pallas::Base);
 
 
@@ -52,11 +49,14 @@ define_contract!(
 );
 );
 
 
 fn init_contract(cid: ContractId, _ix: &[u8]) -> ContractResult {
 fn init_contract(cid: ContractId, _ix: &[u8]) -> ContractResult {
-    let db_handle = db_init(cid, "info")?;
+    let info_db = db_init(cid, "info")?;
+    let roots_db = db_init(cid, "dao_roots")?;
 
 
     let dao_tree = MerkleTree::new(100);
     let dao_tree = MerkleTree::new(100);
-    let dao_tree_data = serialize(&dao_tree);
-    db_set(db_handle, &serialize(&"dao_tree".to_string()), &dao_tree_data)?;
+    let mut dao_tree_data = Vec::new();
+    dao_tree_data.write_u32(0)?;
+    dao_tree.encode(&mut dao_tree_data)?;
+    db_set(info_db, &serialize(&"dao_tree".to_string()), &dao_tree_data)?;
 
 
     Ok(())
     Ok(())
 }
 }
@@ -105,9 +105,15 @@ fn process_update(cid: ContractId, update_data: &[u8]) -> ContractResult {
             let data = &update_data[1..];
             let data = &update_data[1..];
             let update: DaoMintUpdate = deserialize(data)?;
             let update: DaoMintUpdate = deserialize(data)?;
 
 
-            let db_handle = db_lookup(cid, "info")?;
+            let db_info = db_lookup(cid, "info")?;
+            let db_roots = db_lookup(cid, "dao_roots")?;
             let node = MerkleNode::new(update.dao_bulla.0);
             let node = MerkleNode::new(update.dao_bulla.0);
-            merkle_add(db_handle, &serialize(&"dao_tree".to_string()), &node)?;
+            merkle_add(
+                db_info,
+                db_roots,
+                &serialize(&"dao_tree".to_string()),
+                &node,
+            )?;
         }
         }
         DaoFunction::Foo => {
         DaoFunction::Foo => {
             unimplemented!();
             unimplemented!();

+ 14 - 9
example/dao2/src/main.rs

@@ -14,7 +14,7 @@ use darkfi::{
     Result,
     Result,
 };
 };
 use darkfi_sdk::{
 use darkfi_sdk::{
-    crypto::{constants::MERKLE_DEPTH, ContractId, MerkleNode},
+    crypto::{constants::MERKLE_DEPTH, ContractId, MerkleNode, MerkleTree},
     tx::ContractCall,
     tx::ContractCall,
 };
 };
 use darkfi_serial::{deserialize, serialize, Decodable, Encodable, WriteExt};
 use darkfi_serial::{deserialize, serialize, Decodable, Encodable, WriteExt};
@@ -50,14 +50,19 @@ mod tx;
 mod util;
 mod util;
 
 
 fn show_dao_state(chain: &Blockchain, contract_id: &ContractId) -> Result<()> {
 fn show_dao_state(chain: &Blockchain, contract_id: &ContractId) -> Result<()> {
-    let db = chain.contracts.lookup(&chain.sled_db, contract_id, "info")?;
-    match db.get(&serialize(&"dao_tree".to_string())).expect("dao_tree") {
-        Some(value) => {
-            debug!(target: "demo", "DAO tree: {} bytes", value.len());
-        }
-        None => {
-            error!(target: "demo", "Missing DAO tree");
-        }
+    let db_info = chain.contracts.lookup(&chain.sled_db, contract_id, "info")?;
+    let value = db_info.get(&serialize(&"dao_tree".to_string())).expect("dao_tree").unwrap();
+    let mut decoder = Cursor::new(&value);
+    let set_size: u32 = Decodable::decode(&mut decoder)?;
+    let tree: MerkleTree = Decodable::decode(decoder)?;
+    debug!(target: "demo", "DAO tree: {} bytes", value.len());
+    debug!(target: "demo", "set size: {}", set_size);
+
+    let db_roots = chain.contracts.lookup(&chain.sled_db, contract_id, "dao_roots")?;
+    for i in 0..set_size {
+        let root = db_roots.get(&serialize(&i)).expect("dao_roots").unwrap();
+        let root: MerkleNode = deserialize(&root)?;
+        debug!(target: "demo", "root {}: {:?}", i, root);
     }
     }
 
 
     Ok(())
     Ok(())

+ 8 - 0
example/dao2/src/tx.rs

@@ -0,0 +1,8 @@
+use darkfi::crypto::{schnorr::Signature, Proof};
+use darkfi_sdk::tx::ContractCall;
+
+pub struct Transaction {
+    pub calls: Vec<ContractCall>,
+    pub proofs: Vec<Vec<Proof>>,
+    pub signatures: Vec<Vec<Signature>>,
+}

+ 34 - 32
example/less_than.rs

@@ -1,27 +1,30 @@
 use halo2_proofs::{
 use halo2_proofs::{
     arithmetic::FieldExt,
     arithmetic::FieldExt,
-    circuit::{AssignedCell, Chip, Layouter, Region, Value},
+    circuit::{floor_planner, AssignedCell, Chip, Layouter, Region, Value},
+    dev::{CircuitLayout, MockProver},
     pasta::pallas,
     pasta::pallas,
-    plonk::{Circuit, Advice, Column, ConstraintSystem, Error, Expression, Selector, TableColumn},
+    plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Expression, Selector, TableColumn},
     poly::Rotation,
     poly::Rotation,
-    circuit::{floor_planner},
-    dev::{CircuitLayout, MockProver},
 };
 };
 
 
-
 use darkfi::{
 use darkfi::{
-    consensus::{utils::{fbig2base}, types::Float10},
-    crypto::{Proof, proof::{ProvingKey, VerifyingKey}, },
-    zk::gadget::{native_range_check::{NativeRangeCheckChip},
-                 less_than::{LessThanChip, LessThanConfig}},
-    VerifyFailed
+    consensus::{types::Float10, utils::fbig2base},
+    crypto::{
+        proof::{ProvingKey, VerifyingKey},
+        Proof,
+    },
+    zk::gadget::{
+        less_than::{LessThanChip, LessThanConfig},
+        native_range_check::NativeRangeCheckChip,
+    },
+    VerifyFailed,
 };
 };
-use log::{info, error};
+use log::{error, info};
 use rand::rngs::OsRng;
 use rand::rngs::OsRng;
 
 
-const WINDOW_SIZE : usize= 3;
-const NUM_BITS : usize = 253;
-const NUM_WINDOWS : usize =85;
+const WINDOW_SIZE: usize = 3;
+const NUM_BITS: usize = 253;
+const NUM_WINDOWS: usize = 85;
 
 
 #[derive(Default)]
 #[derive(Default)]
 struct LessThanCircuit {
 struct LessThanCircuit {
@@ -30,8 +33,7 @@ struct LessThanCircuit {
 }
 }
 
 
 impl Circuit<pallas::Base> for LessThanCircuit {
 impl Circuit<pallas::Base> for LessThanCircuit {
-    type Config =
-        (LessThanConfig<WINDOW_SIZE, NUM_BITS, NUM_WINDOWS>, Column<Advice>);
+    type Config = (LessThanConfig<WINDOW_SIZE, NUM_BITS, NUM_WINDOWS>, Column<Advice>);
     type FloorPlanner = floor_planner::V1;
     type FloorPlanner = floor_planner::V1;
 
 
     fn without_witnesses(&self) -> Self {
     fn without_witnesses(&self) -> Self {
@@ -73,9 +75,7 @@ impl Circuit<pallas::Base> for LessThanCircuit {
         mut layouter: impl Layouter<pallas::Base>,
         mut layouter: impl Layouter<pallas::Base>,
     ) -> Result<(), Error> {
     ) -> Result<(), Error> {
         let less_than_chip =
         let less_than_chip =
-            LessThanChip::<WINDOW_SIZE, NUM_BITS, NUM_WINDOWS>::construct(
-                config.0.clone(),
-            );
+            LessThanChip::<WINDOW_SIZE, NUM_BITS, NUM_WINDOWS>::construct(config.0.clone());
 
 
         NativeRangeCheckChip::<WINDOW_SIZE, NUM_BITS, NUM_WINDOWS>::load_k_table(
         NativeRangeCheckChip::<WINDOW_SIZE, NUM_BITS, NUM_WINDOWS>::load_k_table(
             &mut layouter,
             &mut layouter,
@@ -93,16 +93,16 @@ impl Circuit<pallas::Base> for LessThanCircuit {
     }
     }
 }
 }
 
 
-fn simple_lessthan (k: u32) -> Result<(), halo2_proofs::plonk::Error>{
-    let y : pallas::Base = pallas::Base::zero();
-    let t : pallas::Base = pallas::Base::one();
+fn simple_lessthan(k: u32) -> Result<(), halo2_proofs::plonk::Error> {
+    let y: pallas::Base = pallas::Base::zero();
+    let t: pallas::Base = pallas::Base::one();
     let circuit = LessThanCircuit { a: Value::known(y), b: Value::known(t) };
     let circuit = LessThanCircuit { a: Value::known(y), b: Value::known(t) };
 
 
     let prover = MockProver::run(k, &circuit, vec![]).unwrap();
     let prover = MockProver::run(k, &circuit, vec![]).unwrap();
     prover.assert_satisfied();
     prover.assert_satisfied();
     assert!(prover.verify().is_ok());
     assert!(prover.verify().is_ok());
 
 
-    let public_inputs : Vec<pallas::Base> = vec![];
+    let public_inputs: Vec<pallas::Base> = vec![];
     let pk = ProvingKey::build(k, &LessThanCircuit::default());
     let pk = ProvingKey::build(k, &LessThanCircuit::default());
     let vk = VerifyingKey::build(k, &LessThanCircuit::default());
     let vk = VerifyingKey::build(k, &LessThanCircuit::default());
     let proof = Proof::create(&pk, &[circuit], &public_inputs, &mut OsRng)?;
     let proof = Proof::create(&pk, &[circuit], &public_inputs, &mut OsRng)?;
@@ -110,7 +110,7 @@ fn simple_lessthan (k: u32) -> Result<(), halo2_proofs::plonk::Error>{
         Ok(()) => {
         Ok(()) => {
             info!("proof verified");
             info!("proof verified");
             Ok(())
             Ok(())
-        },
+        }
         Err(e) => {
         Err(e) => {
             error!("verification failed: {}", e);
             error!("verification failed: {}", e);
             return Err(e)
             return Err(e)
@@ -118,11 +118,13 @@ fn simple_lessthan (k: u32) -> Result<(), halo2_proofs::plonk::Error>{
     }
     }
 }
 }
 
 
-fn fullrange_lessthan (k: u32) -> Result<(), halo2_proofs::plonk::Error> {
-    let y_str : &'static str = "0x057eaec1c805d808f70c4e2d2f173c72d091e9c9f78b11dddf52d072c30951ad";
-    let t_str : &'static str = "0x2cb8d8aec6766dc83595602e3050b0b908191bbe59dcc3d1e2b7020a37339a14";
-    let y : pallas::Base = fbig2base(Float10::from_str_native(y_str).unwrap().with_precision(74).value());
-    let t : pallas::Base = fbig2base(Float10::from_str_native(t_str).unwrap().with_precision(74).value());
+fn fullrange_lessthan(k: u32) -> Result<(), halo2_proofs::plonk::Error> {
+    let y_str: &'static str = "0x057eaec1c805d808f70c4e2d2f173c72d091e9c9f78b11dddf52d072c30951ad";
+    let t_str: &'static str = "0x2cb8d8aec6766dc83595602e3050b0b908191bbe59dcc3d1e2b7020a37339a14";
+    let y: pallas::Base =
+        fbig2base(Float10::from_str_native(y_str).unwrap().with_precision(74).value());
+    let t: pallas::Base =
+        fbig2base(Float10::from_str_native(t_str).unwrap().with_precision(74).value());
 
 
     let circuit = LessThanCircuit { a: Value::known(y), b: Value::known(t) };
     let circuit = LessThanCircuit { a: Value::known(y), b: Value::known(t) };
 
 
@@ -130,7 +132,7 @@ fn fullrange_lessthan (k: u32) -> Result<(), halo2_proofs::plonk::Error> {
     prover.assert_satisfied();
     prover.assert_satisfied();
     assert!(prover.verify().is_ok());
     assert!(prover.verify().is_ok());
 
 
-    let public_inputs : Vec<pallas::Base> = vec![];
+    let public_inputs: Vec<pallas::Base> = vec![];
     let pk = ProvingKey::build(k, &LessThanCircuit::default());
     let pk = ProvingKey::build(k, &LessThanCircuit::default());
     let vk = VerifyingKey::build(k, &LessThanCircuit::default());
     let vk = VerifyingKey::build(k, &LessThanCircuit::default());
     let proof = Proof::create(&pk, &[circuit], &public_inputs, &mut OsRng)?;
     let proof = Proof::create(&pk, &[circuit], &public_inputs, &mut OsRng)?;
@@ -138,7 +140,7 @@ fn fullrange_lessthan (k: u32) -> Result<(), halo2_proofs::plonk::Error> {
         Ok(()) => {
         Ok(()) => {
             info!("proof verified");
             info!("proof verified");
             Ok(())
             Ok(())
-        },
+        }
         Err(e) => {
         Err(e) => {
             error!("verification failed: {}", e);
             error!("verification failed: {}", e);
             return Err(e)
             return Err(e)
@@ -146,7 +148,7 @@ fn fullrange_lessthan (k: u32) -> Result<(), halo2_proofs::plonk::Error> {
     }
     }
 }
 }
 
 
-fn main ()  {
+fn main() {
     env_logger::init();
     env_logger::init();
     let k = 11;
     let k = 11;
     let res_simple = simple_lessthan(k).unwrap();
     let res_simple = simple_lessthan(k).unwrap();

+ 162 - 0
src/runtime/import/merkle.rs

@@ -0,0 +1,162 @@
+use darkfi_sdk::{
+    crypto::{constants::MERKLE_DEPTH, MerkleNode},
+    incrementalmerkletree::{bridgetree::BridgeTree, Tree},
+};
+use darkfi_serial::{deserialize, serialize, Decodable, Encodable, ReadExt, WriteExt};
+use log::{debug, error};
+use std::io::Cursor;
+use wasmer::{FunctionEnvMut, WasmPtr};
+
+use crate::{
+    runtime::vm_runtime::{ContractSection, Env},
+    Result,
+};
+
+type MerkleTree = BridgeTree<MerkleNode, { MERKLE_DEPTH }>;
+
+pub(crate) fn merkle_add(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i32 {
+    let env = ctx.data();
+    match env.contract_section {
+        ContractSection::Update => {
+            let memory_view = env.memory_view(&ctx);
+
+            let Ok(mem_slice) = ptr.slice(&memory_view, len) else {
+                error!(target: "wasm_runtime::merkle_add", "Failed to make slice from ptr");
+                return -2
+            };
+
+            let mut buf = vec![0_u8; len as usize];
+            if let Err(e) = mem_slice.read_slice(&mut buf) {
+                error!(target: "wasm_runtime:merkle_add", "Failed to read from memory slice: {}", e);
+                return -2
+            };
+
+            let mut buf_reader = Cursor::new(buf);
+
+            // FIXME: There's a type DbHandle=u32, but this should maybe be renamed
+            let db_info: u32 = match Decodable::decode(&mut buf_reader) {
+                Ok(v) => v,
+                Err(e) => {
+                    error!(target: "wasm_runtime::merkle_add", "Failed to decode db_info DbHandle: {}", e);
+                    return -2
+                }
+            };
+            let db_info = db_info as usize;
+
+            let db_roots: u32 = match Decodable::decode(&mut buf_reader) {
+                Ok(v) => v,
+                Err(e) => {
+                    error!(target: "wasm_runtime::merkle_add", "Failed to decode db_roots DbHandle: {}", e);
+                    return -2
+                }
+            };
+            let db_roots = db_roots as usize;
+
+            let key: Vec<u8> = match Decodable::decode(&mut buf_reader) {
+                Ok(v) => v,
+                Err(e) => {
+                    error!(target: "wasm_runtime::merkle_add", "Failed to decode key vec: {}", e);
+                    return -2
+                }
+            };
+
+            let coin: MerkleNode = match Decodable::decode(&mut buf_reader) {
+                Ok(v) => v,
+                Err(e) => {
+                    error!(target: "wasm_runtime::merkle_add", "Failed to decode MerkleNode: {}", e);
+                    return -2
+                }
+            };
+
+            // 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
+
+            let ret = match db_info.get(&key) {
+                Ok(v) => v,
+                Err(e) => {
+                    error!(target: "wasm_runtime::merkle_add", "Internal error getting from tree");
+                    return -2
+                }
+            };
+
+            let Some(return_data) = ret else {
+                error!(target: "wasm_runtime::merkle_add", "Return data is empty");
+                return -2
+            };
+
+            debug!(
+                target: "wasm_runtime::merkle_add",
+                "Serialized tree: {} bytes",
+                return_data.len()
+            );
+            debug!(
+                target: "wasm_runtime::merkle_add",
+                "                 {:02x?}",
+                return_data
+            );
+
+            let mut decoder = Cursor::new(&return_data);
+            let set_size: u32 = match Decodable::decode(&mut decoder) {
+                Ok(v) => v,
+                Err(e) => {
+                    error!(target: "wasm_runtime::merkle_add", "Unable to read set size");
+                    return -2
+                }
+            };
+            let mut tree: MerkleTree = match Decodable::decode(&mut decoder) {
+                Ok(v) => v,
+                Err(e) => {
+                    error!(target: "wasm_runtime::merkle_add", "Unable to deserialize tree");
+                    return -2
+                }
+            };
+
+            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;
+            };
+
+            if db_info.contract_id != env.contract_id {
+                error!(target: "wasm_runtime::merkle_add", "Unauthorized to write to DbHandle");
+                return -2
+            }
+
+            let mut tree_data = Vec::new();
+            if tree_data.write_u32(set_size + 1).is_err() || tree.encode(&mut tree_data).is_err() {
+                error!(target: "wasm_runtime::merkle_add", "Couldn't reserialize modified tree");
+                return -2
+            }
+            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);
+
+            0
+        }
+        _ => -1,
+    }
+}

+ 5 - 3
src/sdk/src/crypto/merkle_node.rs

@@ -21,7 +21,7 @@ use std::{io, iter};
 
 
 use darkfi_serial::{SerialDecodable, SerialEncodable};
 use darkfi_serial::{SerialDecodable, SerialEncodable};
 use halo2_gadgets::sinsemilla::primitives::HashDomain;
 use halo2_gadgets::sinsemilla::primitives::HashDomain;
-use incrementalmerkletree::{Altitude, Hashable};
+use incrementalmerkletree::{bridgetree::BridgeTree, Altitude, Hashable, Tree};
 use lazy_static::lazy_static;
 use lazy_static::lazy_static;
 use pasta_curves::{
 use pasta_curves::{
     group::ff::{PrimeField, PrimeFieldBits},
     group::ff::{PrimeField, PrimeFieldBits},
@@ -31,15 +31,17 @@ use subtle::{Choice, ConditionallySelectable};
 
 
 use crate::crypto::constants::{
 use crate::crypto::constants::{
     sinsemilla::{i2lebsp_k, L_ORCHARD_MERKLE, MERKLE_CRH_PERSONALIZATION},
     sinsemilla::{i2lebsp_k, L_ORCHARD_MERKLE, MERKLE_CRH_PERSONALIZATION},
-    MERKLE_DEPTH_ORCHARD,
+    MERKLE_DEPTH,
 };
 };
 
 
+pub type MerkleTree = BridgeTree<MerkleNode, { MERKLE_DEPTH }>;
+
 lazy_static! {
 lazy_static! {
     static ref UNCOMMITTED_ORCHARD: pallas::Base = pallas::Base::from(2);
     static ref UNCOMMITTED_ORCHARD: pallas::Base = pallas::Base::from(2);
     static ref EMPTY_ROOTS: Vec<MerkleNode> = {
     static ref EMPTY_ROOTS: Vec<MerkleNode> = {
         iter::empty()
         iter::empty()
             .chain(Some(MerkleNode::empty_leaf()))
             .chain(Some(MerkleNode::empty_leaf()))
-            .chain((0..MERKLE_DEPTH_ORCHARD).scan(MerkleNode::empty_leaf(), |state, l| {
+            .chain((0..MERKLE_DEPTH).scan(MerkleNode::empty_leaf(), |state, l| {
                 let l = l as u8;
                 let l = l as u8;
                 *state = MerkleNode::combine(l.into(), state, state);
                 *state = MerkleNode::combine(l.into(), state, state);
                 Some(*state)
                 Some(*state)

+ 1 - 1
src/sdk/src/crypto/mod.rs

@@ -36,7 +36,7 @@ pub use contract_id::ContractId;
 
 
 /// Merkle node definitions
 /// Merkle node definitions
 pub mod merkle_node;
 pub mod merkle_node;
-pub use merkle_node::MerkleNode;
+pub use merkle_node::{MerkleNode, MerkleTree};
 
 
 /// Nullifier definitions
 /// Nullifier definitions
 pub mod nullifier;
 pub mod nullifier;

+ 38 - 0
src/sdk/src/merkle.rs

@@ -0,0 +1,38 @@
+use super::{
+    crypto::{ContractId, MerkleNode},
+    db::DbHandle,
+    error::{ContractError, GenericResult},
+    util::{get_object_bytes, get_object_size},
+};
+use darkfi_serial::Encodable;
+
+pub fn merkle_add(
+    db_info: DbHandle,
+    db_roots: DbHandle,
+    key: &[u8],
+    coin: &MerkleNode,
+) -> GenericResult<()> {
+    #[cfg(target_arch = "wasm32")]
+    {
+        let mut buf = vec![];
+        let mut len = 0;
+        len += db_info.encode(&mut buf)?;
+        len += db_roots.encode(&mut buf)?;
+        len += key.to_vec().encode(&mut buf)?;
+        len += coin.encode(&mut buf)?;
+        return match unsafe { merkle_add_(buf.as_ptr(), len as u32) } {
+            0 => Ok(()),
+            -1 => Err(ContractError::CallerAccessDenied),
+            -2 => Err(ContractError::DbSetFailed),
+            _ => unreachable!(),
+        }
+    }
+
+    #[cfg(not(target_arch = "wasm32"))]
+    unimplemented!()
+}
+
+#[cfg(target_arch = "wasm32")]
+extern "C" {
+    fn merkle_add_(ptr: *const u8, len: u32) -> i32;
+}