diff --git a/.changelog/unreleased/improvements/547-multistore-refactor.md b/.changelog/unreleased/improvements/547-multistore-refactor.md new file mode 100644 index 0000000000..2aeca6ccfa --- /dev/null +++ b/.changelog/unreleased/improvements/547-multistore-refactor.md @@ -0,0 +1,2 @@ +- Extend Merkle tree storage to support multiple Merkle trees with a uniform + interface. ([#547](https://github.com/anoma/namada/pull/547)) \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 3811239693..748e68fbd0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -316,9 +316,9 @@ dependencies = [ [[package]] name = "async-std" -version = "1.12.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" +checksum = "d9f06685bad74e0570f5213741bea82158279a4103d988e57bfada11ad230341" dependencies = [ "async-channel", "async-global-executor", @@ -334,6 +334,7 @@ dependencies = [ "kv-log-macro", "log 0.4.17", "memchr", + "num_cpus", "once_cell", "pin-project-lite", "pin-utils", @@ -3053,8 +3054,7 @@ dependencies = [ "serde 1.0.145", "serde_json", "sha2 0.9.9", - "sparse-merkle-tree 0.3.1-pre (git+https://github.com/heliaxdev/sparse-merkle-tree?rev=04ad1eeb28901b57a7599bbe433b3822965dabe8)", - "sparse-merkle-tree 0.3.1-pre (git+https://github.com/heliaxdev/sparse-merkle-tree?rev=121c79424646cc3e917f8616e549fbddee775a0a)", + "sparse-merkle-tree", "tempfile", "tendermint", "tendermint-proto", @@ -3129,8 +3129,7 @@ dependencies = [ "serde_json", "sha2 0.9.9", "signal-hook", - "sparse-merkle-tree 0.3.1-pre (git+https://github.com/heliaxdev/sparse-merkle-tree?rev=04ad1eeb28901b57a7599bbe433b3822965dabe8)", - "sparse-merkle-tree 0.3.1-pre (git+https://github.com/heliaxdev/sparse-merkle-tree?rev=121c79424646cc3e917f8616e549fbddee775a0a)", + "sparse-merkle-tree", "sysinfo", "tar", "tempfile", @@ -4956,18 +4955,6 @@ dependencies = [ "sha2 0.9.9", ] -[[package]] -name = "sparse-merkle-tree" -version = "0.3.1-pre" -source = "git+https://github.com/heliaxdev/sparse-merkle-tree?rev=121c79424646cc3e917f8616e549fbddee775a0a#121c79424646cc3e917f8616e549fbddee775a0a" -dependencies = [ - "blake2b-rs", - "borsh", - "cfg-if 1.0.0", - "ics23", - "sha2 0.9.9", -] - [[package]] name = "spin" version = "0.5.2" diff --git a/apps/Cargo.toml b/apps/Cargo.toml index 12be2bb0f4..856b8a9e46 100644 --- a/apps/Cargo.toml +++ b/apps/Cargo.toml @@ -51,7 +51,7 @@ ark-serialize = "0.3.0" ark-std = "0.3.0" # branch = "bat/arse-merkle-tree" arse-merkle-tree = {package = "sparse-merkle-tree", git = "https://github.com/heliaxdev/sparse-merkle-tree", rev = "04ad1eeb28901b57a7599bbe433b3822965dabe8", features = ["std", "borsh"]} -async-std = {version = "1.9.0", features = ["unstable"]} +async-std = {version = "=1.9.0", features = ["unstable"]} async-trait = "0.1.51" base64 = "0.13.0" bech32 = "0.8.0" @@ -97,7 +97,6 @@ serde_bytes = "0.11.5" serde_json = {version = "1.0.62", features = ["raw_value"]} sha2 = "0.9.3" signal-hook = "0.3.9" -sparse-merkle-tree = {git = "https://github.com/heliaxdev/sparse-merkle-tree", rev = "121c79424646cc3e917f8616e549fbddee775a0a", features = ["borsh"]} # sysinfo with disabled multithread feature sysinfo = {version = "=0.21.1", default-features = false} tar = "0.4.37" diff --git a/apps/src/lib/node/ledger/shell/queries.rs b/apps/src/lib/node/ledger/shell/queries.rs index d50dd2405e..5b23078222 100644 --- a/apps/src/lib/node/ledger/shell/queries.rs +++ b/apps/src/lib/node/ledger/shell/queries.rs @@ -104,7 +104,7 @@ where let proof_ops = if is_proven { match self.storage.get_existence_proof( key, - value.clone(), + value.clone().into(), height, ) { Ok(proof) => Some(proof.into()), @@ -199,7 +199,7 @@ where for PrefixValue { key, value } in &values { match self.storage.get_existence_proof( key, - value.clone(), + value.clone().into(), height, ) { Ok(p) => { diff --git a/shared/Cargo.toml b/shared/Cargo.toml index 5178b46f90..a32da772da 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -87,7 +87,6 @@ serde = {version = "1.0.125", features = ["derive"]} serde_json = "1.0.62" sha2 = "0.9.3" # We switch off "blake2b" because it cannot be compiled to wasm -sparse-merkle-tree = {git = "https://github.com/heliaxdev/sparse-merkle-tree", rev = "121c79424646cc3e917f8616e549fbddee775a0a", default-features = false, features = ["std", "borsh"]} tempfile = {version = "3.2.0", optional = true} # temporarily using fork work-around for https://github.com/informalsystems/tendermint-rs/issues/971 tendermint = {git = "https://github.com/heliaxdev/tendermint-rs", rev = "95c52476bc37927218374f94ac8e2a19bd35bec9", features = ["secp256k1"]} diff --git a/shared/src/ledger/storage/ics23_specs.rs b/shared/src/ledger/storage/ics23_specs.rs new file mode 100644 index 0000000000..00691bd30e --- /dev/null +++ b/shared/src/ledger/storage/ics23_specs.rs @@ -0,0 +1,75 @@ +//! A module that contains + +use arse_merkle_tree::H256; +use ics23::{HashOp, LeafOp, LengthOp, ProofSpec}; + +use super::traits::StorageHasher; + +/// Get the leaf spec for the base tree. The key is stored after hashing, +/// but the stored value is the subtree's root without hashing. +pub fn base_leaf_spec() -> LeafOp { + LeafOp { + hash: H::hash_op().into(), + prehash_key: H::hash_op().into(), + prehash_value: HashOp::NoHash.into(), + length: LengthOp::NoPrefix.into(), + prefix: H256::zero().as_slice().to_vec(), + } +} + +/// Get the leaf spec for the subtree. Non-hashed values are used for the +/// verification with this spec because a subtree stores the key-value pairs +/// after hashing. +pub fn leaf_spec() -> LeafOp { + LeafOp { + hash: H::hash_op().into(), + prehash_key: H::hash_op().into(), + prehash_value: H::hash_op().into(), + length: LengthOp::NoPrefix.into(), + prefix: H256::zero().as_slice().to_vec(), + } +} + +/// Get the leaf spec for the ibc subtree. Non-hashed values are used for +/// the verification with this spec because a subtree stores the +/// key-value pairs after hashing. However, keys are also not hashed in +/// the backing store. +pub fn ibc_leaf_spec() -> LeafOp { + LeafOp { + hash: H::hash_op().into(), + prehash_key: HashOp::NoHash.into(), + prehash_value: HashOp::NoHash.into(), + length: LengthOp::NoPrefix.into(), + prefix: H256::zero().as_slice().to_vec(), + } +} + +/// Get the proof specs for ibc +#[allow(dead_code)] +pub fn ibc_proof_specs() -> Vec { + let spec = arse_merkle_tree::proof_ics23::get_spec(H::hash_op()); + let sub_tree_spec = ProofSpec { + leaf_spec: Some(ibc_leaf_spec::()), + ..spec.clone() + }; + let base_tree_spec = ProofSpec { + leaf_spec: Some(base_leaf_spec::()), + ..spec + }; + vec![sub_tree_spec, base_tree_spec] +} + +/// Get the proof specs +#[allow(dead_code)] +pub fn proof_specs() -> Vec { + let spec = arse_merkle_tree::proof_ics23::get_spec(H::hash_op()); + let sub_tree_spec = ProofSpec { + leaf_spec: Some(leaf_spec::()), + ..spec.clone() + }; + let base_tree_spec = ProofSpec { + leaf_spec: Some(base_leaf_spec::()), + ..spec + }; + vec![sub_tree_spec, base_tree_spec] +} diff --git a/shared/src/ledger/storage/merkle_tree.rs b/shared/src/ledger/storage/merkle_tree.rs index c7fec10126..9e64f0efe6 100644 --- a/shared/src/ledger/storage/merkle_tree.rs +++ b/shared/src/ledger/storage/merkle_tree.rs @@ -1,34 +1,29 @@ //! The merkle tree in the storage -use std::convert::{TryFrom, TryInto}; use std::fmt; -use std::marker::PhantomData; use std::str::FromStr; use arse_merkle_tree::default_store::DefaultStore; use arse_merkle_tree::error::Error as MtError; -use arse_merkle_tree::traits::{Hasher, Value}; use arse_merkle_tree::{ Hash as SmtHash, Key as TreeKey, SparseMerkleTree as ArseMerkleTree, H256, }; use borsh::{BorshDeserialize, BorshSerialize}; use ics23::commitment_proof::Proof as Ics23Proof; -use ics23::{ - CommitmentProof, ExistenceProof, HashOp, LeafOp, LengthOp, - NonExistenceProof, ProofSpec, -}; -use itertools::Either; +use ics23::{CommitmentProof, ExistenceProof, NonExistenceProof}; use prost::Message; -use sha2::{Digest, Sha256}; use tendermint::merkle::proof::{Proof, ProofOp}; use thiserror::Error; +use super::traits::{StorageHasher, SubTreeRead, SubTreeWrite}; use super::IBC_KEY_LIMIT; use crate::bytes::ByteBuf; +use crate::ledger::storage::ics23_specs::{self, ibc_leaf_spec}; use crate::ledger::storage::types; use crate::types::address::{Address, InternalAddress}; use crate::types::hash::Hash; use crate::types::storage::{ - DbKeySeg, Error as StorageError, Key, MerkleKey, StringKey, TreeBytes, + DbKeySeg, Error as StorageError, Key, MembershipProof, MerkleValue, + StringKey, TreeBytes, }; #[allow(missing_docs)] @@ -41,11 +36,17 @@ pub enum Error { #[error("Empty Key: {0}")] EmptyKey(String), #[error("Merkle Tree error: {0}")] - MerkleTree(MtError), + MerkleTree(String), #[error("Invalid store type: {0}")] StoreType(String), #[error("Non-existence proofs not supported for store type: {0}")] NonExistenceProof(String), + #[error("Invalid value given to sub-tree storage")] + InvalidValue, + #[error("ICS23 commitment proofs do not support multiple leaves")] + Ics23MultiLeaf, + #[error("A Tendermint proof can only be constructed from an ICS23 proof.")] + TendermintProof, } /// Result for functions that may fail @@ -244,7 +245,6 @@ impl MerkleTree { let account = Smt::new(stores.account.0.into(), stores.account.1); let ibc = Amt::new(stores.ibc.0.into(), stores.ibc.1); let pos = Smt::new(stores.pos.0.into(), stores.pos.1); - Self { base, account, @@ -253,42 +253,38 @@ impl MerkleTree { } } - fn tree(&self, store_type: &StoreType) -> Either<&Smt, &Amt> { + fn tree(&self, store_type: &StoreType) -> Box { + match store_type { + StoreType::Base => Box::new(&self.base), + StoreType::Account => Box::new(&self.account), + StoreType::Ibc => Box::new(&self.ibc), + StoreType::PoS => Box::new(&self.pos), + } + } + + fn tree_mut( + &mut self, + store_type: &StoreType, + ) -> Box { match store_type { - StoreType::Base => Either::Left(&self.base), - StoreType::Account => Either::Left(&self.account), - StoreType::Ibc => Either::Right(&self.ibc), - StoreType::PoS => Either::Left(&self.pos), + StoreType::Base => Box::new(&mut self.base), + StoreType::Account => Box::new(&mut self.account), + StoreType::Ibc => Box::new(&mut self.ibc), + StoreType::PoS => Box::new(&mut self.pos), } } fn update_tree( &mut self, store_type: &StoreType, - key: MerkleKey, - value: Either, + key: &Key, + value: MerkleValue, ) -> Result<()> { - let sub_root = match store_type { - StoreType::Account => self - .account - .update(key.try_into()?, value.unwrap_left()) - .map_err(Error::MerkleTree)?, - StoreType::Ibc => self - .ibc - .update(key.try_into()?, value.unwrap_right()) - .map_err(Error::MerkleTree)?, - StoreType::PoS => self - .pos - .update(key.try_into()?, value.unwrap_left()) - .map_err(Error::MerkleTree)?, - // base tree should not be directly updated - StoreType::Base => unreachable!(), - }; - + let sub_root = self.tree_mut(store_type).subtree_update(key, value)?; // update the base tree with the updated sub root without hashing if *store_type != StoreType::Base { let base_key = H::hash(&store_type.to_string()); - self.base.update(base_key.into(), Hash::from(sub_root))?; + self.base.update(base_key.into(), sub_root)?; } Ok(()) } @@ -296,41 +292,28 @@ impl MerkleTree { /// Check if the key exists in the tree pub fn has_key(&self, key: &Key) -> Result { let (store_type, sub_key) = StoreType::sub_key(key)?; - let value = match self.tree(&store_type) { - Either::Left(smt) => { - smt.get(&H::hash(sub_key.to_string()).into())?.is_zero() - } - Either::Right(amt) => { - let key = - StringKey::try_from_bytes(sub_key.to_string().as_bytes())?; - amt.get(&key)?.is_zero() - } - }; - Ok(!value) + self.tree(&store_type).subtree_has_key(&sub_key) } /// Update the tree with the given key and value - pub fn update(&mut self, key: &Key, value: impl AsRef<[u8]>) -> Result<()> { - let sub_key = StoreType::sub_key(key)?; - let store_type = sub_key.0; - let value = match store_type { - StoreType::Ibc => { - Either::Right(TreeBytes::from(value.as_ref().to_vec())) - } - _ => Either::Left(H::hash(value).into()), - }; - self.update_tree(&store_type, sub_key.into(), value) + pub fn update( + &mut self, + key: &Key, + value: impl Into, + ) -> Result<()> { + let (store_type, sub_key) = StoreType::sub_key(key)?; + self.update_tree(&store_type, &sub_key, value.into()) } /// Delete the value corresponding to the given key pub fn delete(&mut self, key: &Key) -> Result<()> { - let sub_key = StoreType::sub_key(key)?; - let store_type = sub_key.0; - let value = match store_type { - StoreType::Ibc => Either::Right(TreeBytes::zero()), - _ => Either::Left(H256::zero().into()), - }; - self.update_tree(&store_type, sub_key.into(), value) + let (store_type, sub_key) = StoreType::sub_key(key)?; + let sub_root = self.tree_mut(&store_type).subtree_delete(&sub_key)?; + if store_type != StoreType::Base { + let base_key = H::hash(&store_type.to_string()); + self.base.update(base_key.into(), sub_root)?; + } + Ok(()) } /// Get the root @@ -348,88 +331,71 @@ impl MerkleTree { } } - /// Get the existence proof - pub fn get_existence_proof( + /// Get the existence proof from a sub-tree + pub fn get_sub_tree_existence_proof( &self, - key: &Key, - value: Vec, - ) -> Result { - let (store_type, sub_key) = StoreType::sub_key(key)?; - let sub_proof = match self.tree(&store_type) { - Either::Left(smt) => { - let cp = smt - .membership_proof(&H::hash(&sub_key.to_string()).into())?; - // Replace the values and the leaf op for the verification - match cp.proof.expect("The proof should exist") { - Ics23Proof::Exist(ep) => CommitmentProof { - proof: Some(Ics23Proof::Exist(ExistenceProof { - key: sub_key.to_string().as_bytes().to_vec(), - value, - leaf: Some(self.leaf_spec()), - ..ep - })), - }, - // the proof should have an ExistenceProof - _ => unreachable!(), - } - } - Either::Right(amt) => { - let key = - StringKey::try_from_bytes(sub_key.to_string().as_bytes())?; - let cp = amt.membership_proof(&key)?; - - // Replace the values and the leaf op for the verification - match cp.proof.expect("The proof should exist") { - Ics23Proof::Exist(ep) => CommitmentProof { - proof: Some(Ics23Proof::Exist(ExistenceProof { - leaf: Some(self.ibc_leaf_spec()), - ..ep - })), - }, - _ => unreachable!(), - } + keys: &[Key], + values: Vec, + ) -> Result { + let first_key = keys.iter().next().ok_or_else(|| { + Error::InvalidMerkleKey( + "No keys provided for existence proof.".into(), + ) + })?; + let (store_type, sub_key) = StoreType::sub_key(first_key)?; + if !keys.iter().all(|k| { + if let Ok((s, _)) = StoreType::sub_key(k) { + s == store_type + } else { + false } - }; - self.get_proof(key, sub_proof) + }) { + return Err(Error::InvalidMerkleKey( + "Cannot construct inclusion proof for keys in separate \ + sub-trees." + .into(), + )); + } + self.tree(&store_type) + .subtree_membership_proof(std::array::from_ref(&sub_key), values) } /// Get the non-existence proof pub fn get_non_existence_proof(&self, key: &Key) -> Result { let (store_type, sub_key) = StoreType::sub_key(key)?; - let sub_proof = match self.tree(&store_type) { - Either::Left(_) => { - return Err(Error::NonExistenceProof(store_type.to_string())); - } - Either::Right(amt) => { - let key = - StringKey::try_from_bytes(sub_key.to_string().as_bytes())?; - let mut nep = amt.non_membership_proof(&key)?; - // Replace the values and the leaf op for the verification - if let Some(ref mut nep) = nep.proof { - match nep { - Ics23Proof::Nonexist(ref mut ep) => { - let NonExistenceProof { - ref mut left, - ref mut right, - .. - } = ep; - let ep = left.as_mut().or(right.as_mut()).expect( - "A left or right existence proof should exist.", - ); - ep.leaf = Some(self.ibc_leaf_spec()); - } - _ => unreachable!(), + if store_type != StoreType::Ibc { + return Err(Error::NonExistenceProof(store_type.to_string())); + } + + let string_key = + StringKey::try_from_bytes(sub_key.to_string().as_bytes())?; + let mut nep = self.ibc.non_membership_proof(&string_key)?; + // Replace the values and the leaf op for the verification + if let Some(ref mut nep) = nep.proof { + match nep { + Ics23Proof::Nonexist(ref mut ep) => { + let NonExistenceProof { + ref mut left, + ref mut right, + .. + } = ep; + if let Some(left) = left.as_mut() { + left.leaf = Some(ibc_leaf_spec::()); + } + if let Some(right) = right.as_mut() { + right.leaf = Some(ibc_leaf_spec::()); } } - nep + _ => unreachable!(), } - }; + } + // Get a proof of the sub tree - self.get_proof(key, sub_proof) + self.get_tendermint_proof(key, nep) } /// Get the Tendermint proof with the base proof - fn get_proof( + pub fn get_tendermint_proof( &self, key: &Key, sub_proof: CommitmentProof, @@ -454,7 +420,7 @@ impl MerkleTree { Ics23Proof::Exist(ep) => CommitmentProof { proof: Some(Ics23Proof::Exist(ExistenceProof { key: base_key.as_bytes().to_vec(), - leaf: Some(self.base_leaf_spec()), + leaf: Some(ics23_specs::base_leaf_spec::()), ..ep })), }, @@ -477,73 +443,6 @@ impl MerkleTree { ops: vec![sub_proof_op, base_proof_op], }) } - - /// Get the proof specs - pub fn proof_specs(&self) -> Vec { - let spec = arse_merkle_tree::proof_ics23::get_spec(H::hash_op()); - let sub_tree_spec = ProofSpec { - leaf_spec: Some(self.leaf_spec()), - ..spec.clone() - }; - let base_tree_spec = ProofSpec { - leaf_spec: Some(self.base_leaf_spec()), - ..spec - }; - vec![sub_tree_spec, base_tree_spec] - } - - /// Get the proof specs for ibc - pub fn ibc_proof_specs(&self) -> Vec { - let spec = arse_merkle_tree::proof_ics23::get_spec(H::hash_op()); - let sub_tree_spec = ProofSpec { - leaf_spec: Some(self.ibc_leaf_spec()), - ..spec.clone() - }; - let base_tree_spec = ProofSpec { - leaf_spec: Some(self.base_leaf_spec()), - ..spec - }; - vec![sub_tree_spec, base_tree_spec] - } - - /// Get the leaf spec for the base tree. The key is stored after hashing, - /// but the stored value is the subtree's root without hashing. - fn base_leaf_spec(&self) -> LeafOp { - LeafOp { - hash: H::hash_op().into(), - prehash_key: H::hash_op().into(), - prehash_value: HashOp::NoHash.into(), - length: LengthOp::NoPrefix.into(), - prefix: H256::zero().as_slice().to_vec(), - } - } - - /// Get the leaf spec for the subtree. Non-hashed values are used for the - /// verification with this spec because a subtree stores the key-value pairs - /// after hashing. - fn leaf_spec(&self) -> LeafOp { - LeafOp { - hash: H::hash_op().into(), - prehash_key: H::hash_op().into(), - prehash_value: H::hash_op().into(), - length: LengthOp::NoPrefix.into(), - prefix: H256::zero().as_slice().to_vec(), - } - } - - /// Get the leaf spec for the ibc subtree. Non-hashed values are used for - /// the verification with this spec because a subtree stores the - /// key-value pairs after hashing. However, keys are also not hashed in - /// the backing store. - fn ibc_leaf_spec(&self) -> LeafOp { - LeafOp { - hash: H::hash_op().into(), - prehash_key: HashOp::NoHash.into(), - prehash_value: HashOp::NoHash.into(), - length: LengthOp::NoPrefix.into(), - prefix: H256::zero().as_slice().to_vec(), - } - } } /// The root hash of the merkle tree as bytes @@ -568,45 +467,6 @@ impl fmt::Display for MerkleRoot { } } -impl From<(StoreType, Key)> for MerkleKey { - fn from((store, key): (StoreType, Key)) -> Self { - match store { - StoreType::Base | StoreType::Account | StoreType::PoS => { - MerkleKey::Sha256(key, PhantomData) - } - StoreType::Ibc => MerkleKey::Raw(key), - } - } -} - -impl TryFrom> for SmtHash { - type Error = Error; - - fn try_from(value: MerkleKey) -> Result { - match value { - MerkleKey::Sha256(key, _) => Ok(H::hash(key.to_string()).into()), - _ => Err(Error::InvalidMerkleKey( - "This key is for a sparse merkle tree".into(), - )), - } - } -} - -impl TryFrom> for StringKey { - type Error = Error; - - fn try_from(value: MerkleKey) -> Result { - match value { - MerkleKey::Raw(key) => { - Self::try_from_bytes(key.to_string().as_bytes()) - } - _ => Err(Error::InvalidMerkleKey( - "This is not an key for the IBC subtree".into(), - )), - } - } -} - /// The root and store pairs to restore the trees #[derive(Default)] pub struct MerkleTreeStoresRead { @@ -668,93 +528,6 @@ impl<'a> MerkleTreeStoresWrite<'a> { } } -impl TreeKey for StringKey { - type Error = Error; - - fn as_slice(&self) -> &[u8] { - &self.original.as_slice()[..self.length] - } - - fn try_from_bytes(bytes: &[u8]) -> Result { - let mut tree_key = [0u8; IBC_KEY_LIMIT]; - let mut original = [0u8; IBC_KEY_LIMIT]; - let mut length = 0; - for (i, byte) in bytes.iter().enumerate() { - if i >= IBC_KEY_LIMIT { - return Err(Error::InvalidMerkleKey( - "Input IBC key is too large".into(), - )); - } - original[i] = *byte; - tree_key[i] = byte.wrapping_add(1); - length += 1; - } - Ok(Self { - original, - tree_key: tree_key.into(), - length, - }) - } -} - -impl Value for TreeBytes { - fn as_slice(&self) -> &[u8] { - self.0.as_slice() - } - - fn zero() -> Self { - TreeBytes::zero() - } -} - -/// The storage hasher used for the merkle tree. -pub trait StorageHasher: Hasher + Default { - /// Hash the value to store - fn hash(value: impl AsRef<[u8]>) -> H256; -} - -/// The storage hasher used for the merkle tree. -#[derive(Default)] -pub struct Sha256Hasher(Sha256); - -impl Hasher for Sha256Hasher { - fn write_bytes(&mut self, h: &[u8]) { - self.0.update(h) - } - - fn finish(self) -> arse_merkle_tree::H256 { - let hash = self.0.finalize(); - let bytes: [u8; 32] = hash - .as_slice() - .try_into() - .expect("Sha256 output conversion to fixed array shouldn't fail"); - bytes.into() - } - - fn hash_op() -> ics23::HashOp { - ics23::HashOp::Sha256 - } -} - -impl StorageHasher for Sha256Hasher { - fn hash(value: impl AsRef<[u8]>) -> H256 { - let mut hasher = Sha256::new(); - hasher.update(value.as_ref()); - let hash = hasher.finalize(); - let bytes: [u8; 32] = hash - .as_slice() - .try_into() - .expect("Sha256 output conversion to fixed array shouldn't fail"); - bytes.into() - } -} - -impl fmt::Debug for Sha256Hasher { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Sha256Hasher") - } -} - impl From for Error { fn from(error: StorageError) -> Self { Error::InvalidKey(error) @@ -763,13 +536,15 @@ impl From for Error { impl From for Error { fn from(error: MtError) -> Self { - Error::MerkleTree(error) + Error::MerkleTree(error.to_string()) } } #[cfg(test)] mod test { use super::*; + use crate::ledger::storage::ics23_specs::{ibc_proof_specs, proof_specs}; + use crate::ledger::storage::traits::Sha256Hasher; use crate::types::storage::KeySeg; #[test] @@ -778,6 +553,7 @@ mod test { let key_prefix: Key = Address::Internal(InternalAddress::Ibc).to_db_key().into(); let ibc_key = key_prefix.push(&"test".to_string()).unwrap(); + let ibc_non_key = key_prefix.push(&"test2".to_string()).unwrap(); let key_prefix: Key = Address::Internal(InternalAddress::PoS).to_db_key().into(); let pos_key = key_prefix.push(&"test".to_string()).unwrap(); @@ -793,10 +569,65 @@ mod test { tree.update(&pos_key, [2u8; 8]).unwrap(); assert!(tree.has_key(&pos_key).unwrap()); + // update IBC tree + tree.update(&ibc_non_key, [2u8; 8]).unwrap(); + assert!(tree.has_key(&ibc_non_key).unwrap()); + assert!(tree.has_key(&ibc_key).unwrap()); + assert!(tree.has_key(&pos_key).unwrap()); // delete a value on IBC tree - tree.delete(&ibc_key).unwrap(); - assert!(!tree.has_key(&ibc_key).unwrap()); + tree.delete(&ibc_non_key).unwrap(); + assert!(!tree.has_key(&ibc_non_key).unwrap()); + assert!(tree.has_key(&ibc_key).unwrap()); assert!(tree.has_key(&pos_key).unwrap()); + + // get and verify non-existence proof for the deleted key + let nep = tree + .get_non_existence_proof(&ibc_non_key) + .expect("Test failed"); + let subtree_nep = nep.ops.get(0).expect("Test failed"); + let nep_commitment_proof = + CommitmentProof::decode(&*subtree_nep.data).expect("Test failed"); + let non_existence_proof = + match nep_commitment_proof.clone().proof.expect("Test failed") { + Ics23Proof::Nonexist(nep) => nep, + _ => unreachable!(), + }; + let subtree_root = if let Some(left) = &non_existence_proof.left { + ics23::calculate_existence_root(left).unwrap() + } else if let Some(right) = &non_existence_proof.right { + ics23::calculate_existence_root(right).unwrap() + } else { + unreachable!() + }; + let (store_type, sub_key) = + StoreType::sub_key(&ibc_non_key).expect("Test failed"); + let specs = ibc_proof_specs::(); + + let nep_verification_res = ics23::verify_non_membership( + &nep_commitment_proof, + &specs[0], + &subtree_root, + sub_key.to_string().as_bytes(), + ); + assert!(nep_verification_res); + let basetree_ep = nep.ops.get(1).unwrap(); + let basetree_ep_commitment_proof = + CommitmentProof::decode(&*basetree_ep.data).unwrap(); + let basetree_ics23_ep = + match basetree_ep_commitment_proof.clone().proof.unwrap() { + Ics23Proof::Exist(ep) => ep, + _ => unreachable!(), + }; + let basetree_root = + ics23::calculate_existence_root(&basetree_ics23_ep).unwrap(); + let basetree_verification_res = ics23::verify_membership( + &basetree_ep_commitment_proof, + &specs[1], + &basetree_root, + store_type.to_string().as_bytes(), + &subtree_root, + ); + assert!(basetree_verification_res); } #[test] @@ -840,9 +671,14 @@ mod test { let pos_val = [2u8; 8].to_vec(); tree.update(&pos_key, pos_val).unwrap(); - let specs = tree.ibc_proof_specs(); - let proof = - tree.get_existence_proof(&ibc_key, ibc_val.clone()).unwrap(); + let specs = ibc_proof_specs::(); + let MembershipProof::ICS23(proof) = tree + .get_sub_tree_existence_proof( + std::array::from_ref(&ibc_key), + vec![ibc_val.clone().into()], + ) + .unwrap(); + let proof = tree.get_tendermint_proof(&ibc_key, proof).unwrap(); let (store_type, sub_key) = StoreType::sub_key(&ibc_key).unwrap(); let paths = vec![sub_key.to_string(), store_type.to_string()]; let mut sub_root = ibc_val.clone(); @@ -890,9 +726,14 @@ mod test { let pos_val = [2u8; 8].to_vec(); tree.update(&pos_key, pos_val.clone()).unwrap(); - let specs = tree.proof_specs(); - let proof = - tree.get_existence_proof(&pos_key, pos_val.clone()).unwrap(); + let specs = proof_specs::(); + let MembershipProof::ICS23(proof) = tree + .get_sub_tree_existence_proof( + std::array::from_ref(&pos_key), + vec![pos_val.clone().into()], + ) + .unwrap(); + let proof = tree.get_tendermint_proof(&pos_key, proof).unwrap(); let (store_type, sub_key) = StoreType::sub_key(&pos_key).unwrap(); let paths = vec![sub_key.to_string(), store_type.to_string()]; let mut sub_root = pos_val.clone(); @@ -959,7 +800,7 @@ mod test { }; let (store_type, sub_key) = StoreType::sub_key(&ibc_non_key).expect("Test failed"); - let specs = tree.ibc_proof_specs(); + let specs = ibc_proof_specs::(); let nep_verification_res = ics23::verify_non_membership( &nep_commitment_proof, diff --git a/shared/src/ledger/storage/mod.rs b/shared/src/ledger/storage/mod.rs index 782ed17e27..2467e10cca 100644 --- a/shared/src/ledger/storage/mod.rs +++ b/shared/src/ledger/storage/mod.rs @@ -1,12 +1,15 @@ //! Ledger's state storage with key-value backed store and a merkle tree +mod ics23_specs; mod merkle_tree; #[cfg(any(test, feature = "testing"))] pub mod mockdb; +pub mod traits; pub mod types; pub mod write_log; use core::fmt::Debug; +use std::array; use tendermint::merkle::proof::Proof; use thiserror::Error; @@ -20,16 +23,16 @@ use crate::ledger::storage::merkle_tree::{ Error as MerkleTreeError, MerkleRoot, }; pub use crate::ledger::storage::merkle_tree::{ - MerkleTree, MerkleTreeStoresRead, MerkleTreeStoresWrite, Sha256Hasher, - StorageHasher, StoreType, + MerkleTree, MerkleTreeStoresRead, MerkleTreeStoresWrite, StoreType, }; +pub use crate::ledger::storage::traits::{Sha256Hasher, StorageHasher}; use crate::types::address::{Address, EstablishedAddressGen, InternalAddress}; use crate::types::chain::{ChainId, CHAIN_ID_LENGTH}; #[cfg(feature = "ferveo-tpke")] use crate::types::storage::TxQueue; use crate::types::storage::{ BlockHash, BlockHeight, Epoch, Epochs, Header, Key, KeySeg, - BLOCK_HASH_LENGTH, + MembershipProof, MerkleValue, BLOCK_HASH_LENGTH, }; use crate::types::time::DateTimeUtc; @@ -527,15 +530,32 @@ where pub fn get_existence_proof( &self, key: &Key, - value: Vec, + value: MerkleValue, height: BlockHeight, ) -> Result { if height >= self.get_block_height().0 { - Ok(self.block.tree.get_existence_proof(key, value)?) + let MembershipProof::ICS23(proof) = self + .block + .tree + .get_sub_tree_existence_proof(array::from_ref(key), vec![value]) + .map_err(Error::MerkleTreeError)?; + self.block + .tree + .get_tendermint_proof(key, proof) + .map_err(Error::MerkleTreeError) } else { match self.db.read_merkle_tree_stores(height)? { - Some(stores) => Ok(MerkleTree::::new(stores) - .get_existence_proof(key, value)?), + Some(stores) => { + let tree = MerkleTree::::new(stores); + let MembershipProof::ICS23(proof) = tree + .get_sub_tree_existence_proof( + array::from_ref(key), + vec![value], + ) + .map_err(Error::MerkleTreeError)?; + tree.get_tendermint_proof(key, proof) + .map_err(Error::MerkleTreeError) + } None => Err(Error::NoMerkleTree { height }), } } @@ -856,11 +876,9 @@ impl From for Error { /// Helpers for testing components that depend on storage #[cfg(any(test, feature = "testing"))] pub mod testing { - use merkle_tree::Sha256Hasher; - use super::mockdb::MockDB; use super::*; - + use crate::ledger::storage::traits::Sha256Hasher; /// Storage with a mock DB for testing pub type TestStorage = Storage; diff --git a/shared/src/ledger/storage/traits.rs b/shared/src/ledger/storage/traits.rs new file mode 100644 index 0000000000..e382f34d73 --- /dev/null +++ b/shared/src/ledger/storage/traits.rs @@ -0,0 +1,276 @@ +//! Traits needed to provide a uniform interface over +//! all the different Merkle trees used for storage +use std::convert::TryInto; +use std::fmt; + +use arse_merkle_tree::traits::{Hasher, Value}; +use arse_merkle_tree::{Key as TreeKey, H256}; +use ics23::commitment_proof::Proof as Ics23Proof; +use ics23::{CommitmentProof, ExistenceProof}; +use sha2::{Digest, Sha256}; + +use super::merkle_tree::{Amt, Error, Smt}; +use super::{ics23_specs, IBC_KEY_LIMIT}; +use crate::types::hash::Hash; +use crate::types::storage::{ + Key, MembershipProof, MerkleValue, StringKey, TreeBytes, +}; + +/// Trait for reading from a merkle tree that is a sub-tree +/// of the global merkle tree. +pub trait SubTreeRead { + /// Check if a key is present in the sub-tree + fn subtree_has_key(&self, key: &Key) -> Result; + /// Get a membership proof for various key-value pairs + fn subtree_membership_proof( + &self, + keys: &[Key], + values: Vec, + ) -> Result; +} + +/// Trait for updating a merkle tree that is a sub-tree +/// of the global merkle tree +pub trait SubTreeWrite { + /// Add a key-value pair to the sub-tree + fn subtree_update( + &mut self, + key: &Key, + value: MerkleValue, + ) -> Result; + /// Delete a key from the sub-tree + fn subtree_delete(&mut self, key: &Key) -> Result; +} + +impl<'a, H: StorageHasher + Default> SubTreeRead for &'a Smt { + fn subtree_has_key(&self, key: &Key) -> Result { + match self.get(&H::hash(key.to_string()).into()) { + Ok(hash) => Ok(!hash.is_zero()), + Err(e) => Err(Error::MerkleTree(e.to_string())), + } + } + + fn subtree_membership_proof( + &self, + keys: &[Key], + mut values: Vec, + ) -> Result { + if keys.len() != 1 || values.len() != 1 { + return Err(Error::Ics23MultiLeaf); + } + let key: &Key = &keys[0]; + let MerkleValue::Bytes(value) = values.remove(0); + let cp = self.membership_proof(&H::hash(key.to_string()).into())?; + // Replace the values and the leaf op for the verification + match cp.proof.expect("The proof should exist") { + Ics23Proof::Exist(ep) => Ok(CommitmentProof { + proof: Some(Ics23Proof::Exist(ExistenceProof { + key: key.to_string().as_bytes().to_vec(), + value, + leaf: Some(ics23_specs::leaf_spec::()), + ..ep + })), + } + .into()), + // the proof should have an ExistenceProof + _ => unreachable!(), + } + } +} + +impl<'a, H: StorageHasher + Default> SubTreeWrite for &'a mut Smt { + fn subtree_update( + &mut self, + key: &Key, + value: MerkleValue, + ) -> Result { + let value = match value { + MerkleValue::Bytes(bytes) => H::hash(bytes.as_slice()), + }; + self.update(H::hash(key.to_string()).into(), value.into()) + .map(Hash::from) + .map_err(|err| Error::MerkleTree(err.to_string())) + } + + fn subtree_delete(&mut self, key: &Key) -> Result { + let value = Hash::zero(); + self.update(H::hash(key.to_string()).into(), value) + .map(Hash::from) + .map_err(|err| Error::MerkleTree(err.to_string())) + } +} + +impl<'a, H: StorageHasher + Default> SubTreeRead for &'a Amt { + fn subtree_has_key(&self, key: &Key) -> Result { + let key = StringKey::try_from_bytes(key.to_string().as_bytes())?; + match self.get(&key) { + Ok(hash) => Ok(!hash.is_zero()), + Err(e) => Err(Error::MerkleTree(e.to_string())), + } + } + + fn subtree_membership_proof( + &self, + keys: &[Key], + _: Vec, + ) -> Result { + if keys.len() != 1 { + return Err(Error::Ics23MultiLeaf); + } + + let key = StringKey::try_from_bytes(keys[0].to_string().as_bytes())?; + let cp = self.membership_proof(&key)?; + // Replace the values and the leaf op for the verification + match cp.proof.expect("The proof should exist") { + Ics23Proof::Exist(ep) => Ok(CommitmentProof { + proof: Some(Ics23Proof::Exist(ExistenceProof { + leaf: Some(ics23_specs::ibc_leaf_spec::()), + ..ep + })), + } + .into()), + // the proof should have an ExistenceProof + _ => unreachable!(), + } + } +} + +impl<'a, H: StorageHasher + Default> SubTreeWrite for &'a mut Amt { + fn subtree_update( + &mut self, + key: &Key, + value: MerkleValue, + ) -> Result { + let key = StringKey::try_from_bytes(key.to_string().as_bytes())?; + let value = match value { + MerkleValue::Bytes(bytes) => TreeBytes::from(bytes), + }; + self.update(key, value) + .map(Into::into) + .map_err(|err| Error::MerkleTree(err.to_string())) + } + + fn subtree_delete(&mut self, key: &Key) -> Result { + let key = StringKey::try_from_bytes(key.to_string().as_bytes())?; + let value = TreeBytes::zero(); + self.update(key, value) + .map(Hash::from) + .map_err(|err| Error::MerkleTree(format!("{:?}", err))) + } +} + +impl TreeKey for StringKey { + type Error = Error; + + fn as_slice(&self) -> &[u8] { + &self.original.as_slice()[..self.length] + } + + fn try_from_bytes(bytes: &[u8]) -> Result { + let mut tree_key = [0u8; IBC_KEY_LIMIT]; + let mut original = [0u8; IBC_KEY_LIMIT]; + let mut length = 0; + for (i, byte) in bytes.iter().enumerate() { + if i >= IBC_KEY_LIMIT { + return Err(Error::InvalidMerkleKey( + "Input IBC key is too large".into(), + )); + } + original[i] = *byte; + tree_key[i] = byte.wrapping_add(1); + length += 1; + } + Ok(Self { + original, + tree_key: tree_key.into(), + length, + }) + } +} + +impl Value for Hash { + fn as_slice(&self) -> &[u8] { + self.0.as_slice() + } + + fn zero() -> Self { + Hash([0u8; 32]) + } +} + +impl From for H256 { + fn from(hash: Hash) -> Self { + hash.0.into() + } +} + +impl From for Hash { + fn from(hash: H256) -> Self { + Self(hash.into()) + } +} + +impl From<&H256> for Hash { + fn from(hash: &H256) -> Self { + let hash = hash.to_owned(); + Self(hash.into()) + } +} + +impl Value for TreeBytes { + fn as_slice(&self) -> &[u8] { + self.0.as_slice() + } + + fn zero() -> Self { + TreeBytes::zero() + } +} + +/// The storage hasher used for the merkle tree. +pub trait StorageHasher: Hasher + Default { + /// Hash the value to store + fn hash(value: impl AsRef<[u8]>) -> H256; +} + +/// The storage hasher used for the merkle tree. +#[derive(Default)] +pub struct Sha256Hasher(Sha256); + +impl Hasher for Sha256Hasher { + fn write_bytes(&mut self, h: &[u8]) { + self.0.update(h) + } + + fn finish(self) -> H256 { + let hash = self.0.finalize(); + let bytes: [u8; 32] = hash + .as_slice() + .try_into() + .expect("Sha256 output conversion to fixed array shouldn't fail"); + bytes.into() + } + + fn hash_op() -> ics23::HashOp { + ics23::HashOp::Sha256 + } +} + +impl StorageHasher for Sha256Hasher { + fn hash(value: impl AsRef<[u8]>) -> H256 { + let mut hasher = Sha256::new(); + hasher.update(value.as_ref()); + let hash = hasher.finalize(); + let bytes: [u8; 32] = hash + .as_slice() + .try_into() + .expect("Sha256 output conversion to fixed array shouldn't fail"); + bytes.into() + } +} + +impl fmt::Debug for Sha256Hasher { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Sha256Hasher") + } +} diff --git a/shared/src/types/hash.rs b/shared/src/types/hash.rs index 0e960ec01f..d2c27f96d4 100644 --- a/shared/src/types/hash.rs +++ b/shared/src/types/hash.rs @@ -4,7 +4,7 @@ use std::fmt::{self, Display}; use std::ops::Deref; use arse_merkle_tree::traits::Value; -use arse_merkle_tree::{Hash as TreeHash, H256}; +use arse_merkle_tree::Hash as TreeHash; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; @@ -110,37 +110,8 @@ impl From for TmHash { } } -impl From for Hash { - fn from(hash: H256) -> Self { - Hash(hash.into()) - } -} - -impl From<&H256> for Hash { - fn from(hash: &H256) -> Self { - let hash = *hash; - Hash(hash.into()) - } -} - -impl From for H256 { - fn from(hash: Hash) -> H256 { - hash.0.into() - } -} - impl From for TreeHash { fn from(hash: Hash) -> Self { Self::from(hash.0) } } - -impl Value for Hash { - fn as_slice(&self) -> &[u8] { - self.0.as_slice() - } - - fn zero() -> Self { - Hash([0u8; 32]) - } -} diff --git a/shared/src/types/storage.rs b/shared/src/types/storage.rs index c6b7f9dfbf..57d0f66c5d 100644 --- a/shared/src/types/storage.rs +++ b/shared/src/types/storage.rs @@ -2,7 +2,6 @@ use std::convert::{TryFrom, TryInto}; use std::fmt::Display; use std::io::Write; -use std::marker::PhantomData; use std::num::ParseIntError; use std::ops::{Add, Deref, Div, Mul, Rem, Sub}; use std::str::FromStr; @@ -10,13 +9,14 @@ use std::str::FromStr; use arse_merkle_tree::InternalKey; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use data_encoding::BASE32HEX_NOPAD; +use ics23::CommitmentProof; use serde::{Deserialize, Serialize}; use thiserror::Error; #[cfg(feature = "ferveo-tpke")] use super::transaction::WrapperTx; use crate::bytes::ByteBuf; -use crate::ledger::storage::{StorageHasher, IBC_KEY_LIMIT}; +use crate::ledger::storage::IBC_KEY_LIMIT; use crate::types::address::{self, Address}; use crate::types::hash::Hash; use crate::types::time::DateTimeUtc; @@ -34,6 +34,8 @@ pub enum Error { InvalidKeySeg(String), #[error("Error parsing key segment {0}")] ParseKeySeg(String), + #[error("Could not parse string: '{0}' into requested type: {1}")] + ParseError(String, String), } /// Result for functions that may fail @@ -58,6 +60,7 @@ pub const RESERVED_VP_KEY: &str = "?"; Copy, BorshSerialize, BorshDeserialize, + BorshSchema, PartialEq, Eq, PartialOrd, @@ -111,6 +114,12 @@ impl From for BlockHash { } } +impl From for BlockHeight { + fn from(height: u64) -> Self { + BlockHeight(height) + } +} + impl TryFrom for BlockHeight { type Error = String; @@ -237,14 +246,25 @@ impl FromStr for Key { } } -/// A type for converting an Anoma storage key -/// to that of the right type for the different -/// merkle trees used. -pub enum MerkleKey { - /// A key that needs to be hashed - Sha256(Key, PhantomData), - /// A key that can be given as raw bytes - Raw(Key), +/// An enum representing the different types of values +/// that can be passed into Anoma's storage. +/// +/// This is a multi-store organized as +/// several Merkle trees, each of which is +/// responsible for understanding how to parse +/// this value. +pub enum MerkleValue { + /// raw bytes + Bytes(Vec), +} + +impl From for MerkleValue +where + T: AsRef<[u8]>, +{ + fn from(bytes: T) -> Self { + Self::Bytes(bytes.as_ref().to_owned()) + } } /// Storage keys that are utf8 encoded strings @@ -326,6 +346,18 @@ impl From for Vec { } } +/// Type of membership proof from a merkle tree +pub enum MembershipProof { + /// ICS23 compliant membership proof + ICS23(CommitmentProof), +} + +impl From for MembershipProof { + fn from(proof: CommitmentProof) -> Self { + Self::ICS23(proof) + } +} + impl Key { /// Parses string and returns a key pub fn parse(string: impl AsRef) -> Result { diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index f69000550b..9b5fff114e 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -1519,8 +1519,7 @@ dependencies = [ "serde", "serde_json", "sha2 0.9.9", - "sparse-merkle-tree 0.3.1-pre (git+https://github.com/heliaxdev/sparse-merkle-tree?rev=04ad1eeb28901b57a7599bbe433b3822965dabe8)", - "sparse-merkle-tree 0.3.1-pre (git+https://github.com/heliaxdev/sparse-merkle-tree?rev=121c79424646cc3e917f8616e549fbddee775a0a)", + "sparse-merkle-tree", "tempfile", "tendermint", "tendermint-proto", @@ -2473,17 +2472,6 @@ dependencies = [ "sha2 0.9.9", ] -[[package]] -name = "sparse-merkle-tree" -version = "0.3.1-pre" -source = "git+https://github.com/heliaxdev/sparse-merkle-tree?rev=121c79424646cc3e917f8616e549fbddee775a0a#121c79424646cc3e917f8616e549fbddee775a0a" -dependencies = [ - "borsh", - "cfg-if 1.0.0", - "ics23", - "sha2 0.9.9", -] - [[package]] name = "stable_deref_trait" version = "1.2.0" diff --git a/wasm/checksums.json b/wasm/checksums.json index dbebc6943e..8767ca510c 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,18 +1,18 @@ { - "tx_bond.wasm": "tx_bond.cc16670ccd2b4a0de0e20de051196dda9058bab885b93c948e9d5dc9fba65e76.wasm", - "tx_ibc.wasm": "tx_ibc.bc1783aaa0fcb587e3963a03b7629fed3c59ef7dbcd099ce3101260b4fafb758.wasm", - "tx_init_account.wasm": "tx_init_account.8a70c8722e31af7d0875d2f13279da238269fa91053d0e9413c20fc9cd8e8662.wasm", - "tx_init_nft.wasm": "tx_init_nft.28872d99008b7b14593355c6ab6d62108b61a3d2956a0581d80c3d5816b13d4a.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.f704144e1bb74ebe9e937c4e6395148bfd31eaa74608f105f3c0496f62be3077.wasm", - "tx_init_validator.wasm": "tx_init_validator.dddaaf19e7a53eaa8b2b1376cdfdfaf3f776c4e5305cab0d4d2050d520853790.wasm", - "tx_mint_nft.wasm": "tx_mint_nft.272053e50688f04d7a258bc324c704c49a4833839b4e37dd6f691c80b38012e2.wasm", - "tx_transfer.wasm": "tx_transfer.baa846ba31c9e6f93c5d2facf4cec50518990924d1aa71216a6b4e48557f00b4.wasm", - "tx_unbond.wasm": "tx_unbond.86132bec595dfa726445a53a25900fccc2ab6457241d9ddf2ca3c4a8ec799f19.wasm", - "tx_update_vp.wasm": "tx_update_vp.28050dd60d9297b360ade93213a3b7677d424b703e13d3732c767e25da4bd6a1.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.3a698cb7f26d5c96c97efad860a33d123b439c071cb488d6f52be3dc0c170848.wasm", - "tx_withdraw.wasm": "tx_withdraw.600bfe69fc3fe60e96acb38f6cd5f69b0d4f182cb4eebe804cbf2a782e91d1ca.wasm", - "vp_nft.wasm": "vp_nft.736aef15807d2c8233bafbc842f3b87df0df48b1e319ce37358b73d21b73814b.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.ab70bd5fd3c5a6756f947ee86c4518611be7e6ab23b504a7f03681235f70d699.wasm", - "vp_token.wasm": "vp_token.6a8837598c2e9c380c4da1be54c4d18852213457d71f9e2d5fd4b13a1d4d8050.wasm", - "vp_user.wasm": "vp_user.86c1ca9c00248000e1bf071b86ad221cb1d1c4e75ea5ff9fc05ba90b531b4f9f.wasm" + "tx_bond.wasm": "tx_bond.f0c520b6562e23785df2471c326e0d387b57ecb3ec0313b4a845d464a43dd596.wasm", + "tx_ibc.wasm": "tx_ibc.a7cd11392c0f977a30effeb373c70de6d1365741da166cf7e871c92b166075dc.wasm", + "tx_init_account.wasm": "tx_init_account.844517c18297a87fbc5dccf47e21e4731db5f72ff3b8bfa49fda5037e56e7c93.wasm", + "tx_init_nft.wasm": "tx_init_nft.bf1e9e2e69044f551722c990fda480f45b38aca59a2f5f0f264ea5e198d36fda.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.668b6cfe3641980c6ed398ded6c323e52d5b3ad6d08ad1d67568585ddd72c8d8.wasm", + "tx_init_validator.wasm": "tx_init_validator.2af86bcf16eaa3551f1022cfb605c0e7d25ebcf22ca2df50e6020db331e91ab9.wasm", + "tx_mint_nft.wasm": "tx_mint_nft.17505172a619a49e1af50638023dd0a69ec4b3471c950e040f9e1859e7626327.wasm", + "tx_transfer.wasm": "tx_transfer.8b5b381c7d6d45a68222e3264f733be7005fb9251235b7055475d44c2a5cb146.wasm", + "tx_unbond.wasm": "tx_unbond.b96655ad430461abc787edabf93ba2ae503c8a5b4b64d496fd6a6c577a6138d3.wasm", + "tx_update_vp.wasm": "tx_update_vp.3b709f301e55cb970ec1df98c85c2486561c3243ab1c191d3e078ee345b8b93a.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.8e4d5f40390b9ddac71d8d6c6ae7245ed78103c5a2f835b7a337b6c75b1e3187.wasm", + "tx_withdraw.wasm": "tx_withdraw.54e9d9257c8e00c856b2f5680edc53f21357f5da5337e129e86fe57ee47c4a96.wasm", + "vp_nft.wasm": "vp_nft.03b0a64aa71f7d4e34d18c27d0fc821ebcdd4b5c3a5878c71b2c8a47923854f5.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.10211e52c82ca4377ef87db386c1a0a833327127ae7407ed891f7b84a228fdf7.wasm", + "vp_token.wasm": "vp_token.a955c30bb91c6480360b1d87197bd6df5655260f580747d07e6787b2d93dfe80.wasm", + "vp_user.wasm": "vp_user.2c1f06168ea914dc50ed52b57c6d697a684a8e2aa714c4d0a78458e9171fb2b8.wasm" } \ No newline at end of file diff --git a/wasm_for_tests/wasm_source/Cargo.lock b/wasm_for_tests/wasm_source/Cargo.lock index f77584d79a..88fd209412 100644 --- a/wasm_for_tests/wasm_source/Cargo.lock +++ b/wasm_for_tests/wasm_source/Cargo.lock @@ -1519,8 +1519,7 @@ dependencies = [ "serde", "serde_json", "sha2 0.9.9", - "sparse-merkle-tree 0.3.1-pre (git+https://github.com/heliaxdev/sparse-merkle-tree?rev=04ad1eeb28901b57a7599bbe433b3822965dabe8)", - "sparse-merkle-tree 0.3.1-pre (git+https://github.com/heliaxdev/sparse-merkle-tree?rev=121c79424646cc3e917f8616e549fbddee775a0a)", + "sparse-merkle-tree", "tempfile", "tendermint", "tendermint-proto", @@ -2467,17 +2466,6 @@ dependencies = [ "sha2 0.9.9", ] -[[package]] -name = "sparse-merkle-tree" -version = "0.3.1-pre" -source = "git+https://github.com/heliaxdev/sparse-merkle-tree?rev=121c79424646cc3e917f8616e549fbddee775a0a#121c79424646cc3e917f8616e549fbddee775a0a" -dependencies = [ - "borsh", - "cfg-if 1.0.0", - "ics23", - "sha2 0.9.9", -] - [[package]] name = "stable_deref_trait" version = "1.2.0"