From 62ab0f556ee622d3eaaa313d0e884d5aab5f26bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 16 Dec 2022 19:29:28 +0100 Subject: [PATCH 01/18] core/storage: add a warning to `iter_prefix` --- core/src/ledger/storage/mod.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/core/src/ledger/storage/mod.rs b/core/src/ledger/storage/mod.rs index 45f145ec870..1e64fc72479 100644 --- a/core/src/ledger/storage/mod.rs +++ b/core/src/ledger/storage/mod.rs @@ -301,6 +301,10 @@ pub trait DBIter<'iter> { /// The concrete type of the iterator type PrefixIter: Debug + Iterator, u64)>; + /// WARNING: This only works for values that have been committed to DB. + /// To be able to see values written or deleted, but not yet committed, + /// use the `StorageWithWriteLog`. + /// /// Read account subspace key value pairs with the given prefix from the DB, /// ordered by the storage keys. fn iter_prefix(&'iter self, prefix: &Key) -> Self::PrefixIter; @@ -503,7 +507,11 @@ where } } - /// Returns a prefix iterator, ordered by storage keys, and the gas cost + /// WARNING: This only works for values that have been committed to DB. + /// To be able to see values written or deleted, but not yet committed, + /// use the `StorageWithWriteLog`. + /// + /// Returns a prefix iterator, ordered by storage keys, and the gas cost. pub fn iter_prefix( &self, prefix: &Key, From 077291f988dd32a656ac7792b1fbbe4221282983 Mon Sep 17 00:00:00 2001 From: brentstone Date: Sat, 24 Dec 2022 21:32:34 -0500 Subject: [PATCH 02/18] move write_log --- core/src/ledger/storage/mod.rs | 1 + {shared => core}/src/ledger/storage/write_log.rs | 4 ++-- shared/src/ledger/storage/mod.rs | 4 +--- 3 files changed, 4 insertions(+), 5 deletions(-) rename {shared => core}/src/ledger/storage/write_log.rs (99%) diff --git a/core/src/ledger/storage/mod.rs b/core/src/ledger/storage/mod.rs index 1e64fc72479..b6f95685ea8 100644 --- a/core/src/ledger/storage/mod.rs +++ b/core/src/ledger/storage/mod.rs @@ -6,6 +6,7 @@ pub mod merkle_tree; pub mod mockdb; pub mod traits; pub mod types; +pub mod write_log; use core::fmt::Debug; use std::collections::BTreeMap; diff --git a/shared/src/ledger/storage/write_log.rs b/core/src/ledger/storage/write_log.rs similarity index 99% rename from shared/src/ledger/storage/write_log.rs rename to core/src/ledger/storage/write_log.rs index 6e78612f569..098204b707b 100644 --- a/shared/src/ledger/storage/write_log.rs +++ b/core/src/ledger/storage/write_log.rs @@ -682,12 +682,12 @@ mod tests { /// Helpers for testing with write log. #[cfg(any(test, feature = "testing"))] pub mod testing { - use namada_core::types::address::testing::arb_address; - use namada_core::types::storage::testing::arb_key; use proptest::collection; use proptest::prelude::{any, prop_oneof, Just, Strategy}; use super::*; + use crate::types::address::testing::arb_address; + use crate::types::storage::testing::arb_key; /// Generate an arbitrary tx write log of [`HashMap`]. diff --git a/shared/src/ledger/storage/mod.rs b/shared/src/ledger/storage/mod.rs index d55a96db390..2b30ecf3129 100644 --- a/shared/src/ledger/storage/mod.rs +++ b/shared/src/ledger/storage/mod.rs @@ -1,7 +1,5 @@ //! Ledger's state storage with key-value backed store and a merkle tree -pub mod write_log; - #[cfg(any(test, feature = "testing"))] pub use namada_core::ledger::storage::mockdb; -pub use namada_core::ledger::storage::{traits, *}; +pub use namada_core::ledger::storage::{traits, write_log, *}; From a1284af9434b810b096580e0d336346a80c2817f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 16 Dec 2022 19:29:53 +0100 Subject: [PATCH 03/18] shared: Add WlStorage, combining WriteLog with Storage --- apps/src/lib/node/ledger/storage/mod.rs | 9 +- core/src/ledger/storage/mod.rs | 24 +- core/src/ledger/storage/wl_storage.rs | 324 ++++++++++++++++++++++++ shared/src/ledger/storage/mod.rs | 4 +- 4 files changed, 357 insertions(+), 4 deletions(-) create mode 100644 core/src/ledger/storage/wl_storage.rs diff --git a/apps/src/lib/node/ledger/storage/mod.rs b/apps/src/lib/node/ledger/storage/mod.rs index 1bd1446c519..843f49560bc 100644 --- a/apps/src/lib/node/ledger/storage/mod.rs +++ b/apps/src/lib/node/ledger/storage/mod.rs @@ -52,8 +52,9 @@ fn new_blake2b() -> Blake2b { mod tests { use borsh::BorshSerialize; use itertools::Itertools; + use namada::ledger::storage::testing::TestWlStorage; use namada::ledger::storage::types; - use namada::ledger::storage_api; + use namada::ledger::storage_api::{self, StorageWrite}; use namada::types::chain::ChainId; use namada::types::storage::{BlockHash, BlockHeight, Key}; use namada::types::{address, storage}; @@ -353,12 +354,16 @@ mod tests { fn test_persistent_storage_prefix_iter() { let db_path = TempDir::new().expect("Unable to create a temporary DB directory"); - let mut storage = PersistentStorage::open( + let storage = PersistentStorage::open( db_path.path(), ChainId::default(), address::nam(), None, ); + let mut storage = TestWlStorage { + storage, + write_log: Default::default(), + }; let prefix = storage::Key::parse("prefix").unwrap(); let mismatched_prefix = storage::Key::parse("different").unwrap(); diff --git a/core/src/ledger/storage/mod.rs b/core/src/ledger/storage/mod.rs index b6f95685ea8..28e3552537d 100644 --- a/core/src/ledger/storage/mod.rs +++ b/core/src/ledger/storage/mod.rs @@ -6,6 +6,7 @@ pub mod merkle_tree; pub mod mockdb; pub mod traits; pub mod types; +mod wl_storage; pub mod write_log; use core::fmt::Debug; @@ -29,6 +30,9 @@ use rayon::iter::{ use rayon::prelude::ParallelSlice; use thiserror::Error; pub use traits::{Sha256Hasher, StorageHasher}; +pub use wl_storage::{ + iter_prefix_post, iter_prefix_pre, PrefixIter, WlStorage, +}; use crate::ledger::gas::MIN_STORAGE_GAS; use crate::ledger::parameters::{self, EpochDuration, Parameters}; @@ -1124,7 +1128,15 @@ pub mod testing { use super::*; use crate::ledger::storage::traits::Sha256Hasher; use crate::types::address; - /// Storage with a mock DB for testing + + /// `WlStorage` with a mock DB for testing + pub type TestWlStorage = WlStorage; + + /// Storage with a mock DB for testing. + /// + /// Prefer to use [`TestWlStorage`], which implements + /// `storage_api::StorageRead + StorageWrite` with properly working + /// `prefix_iter`. pub type TestStorage = Storage; impl Default for TestStorage { @@ -1159,6 +1171,16 @@ pub mod testing { } } } + + #[allow(clippy::derivable_impls)] + impl Default for TestWlStorage { + fn default() -> Self { + Self { + write_log: Default::default(), + storage: Default::default(), + } + } + } } #[cfg(test)] diff --git a/core/src/ledger/storage/wl_storage.rs b/core/src/ledger/storage/wl_storage.rs new file mode 100644 index 00000000000..a5c559936c3 --- /dev/null +++ b/core/src/ledger/storage/wl_storage.rs @@ -0,0 +1,324 @@ +//! Storage with write log. + +use std::iter::Peekable; + +use crate::ledger::storage::write_log::{self, WriteLog}; +use crate::ledger::storage::{DBIter, Storage, StorageHasher, DB}; +use crate::ledger::storage_api::{ResultExt, StorageRead, StorageWrite}; +use crate::ledger::{gas, storage_api}; +use crate::types::address::Address; +use crate::types::storage; + +/// Storage with write log that allows to implement prefix iterator that works +/// with changes not yet committed to the DB. +#[derive(Debug)] +pub struct WlStorage +where + D: DB + for<'iter> DBIter<'iter>, + H: StorageHasher, +{ + /// Write log + pub write_log: WriteLog, + /// Storage provides access to DB + pub storage: Storage, +} + +impl WlStorage +where + D: 'static + DB + for<'iter> DBIter<'iter>, + H: StorageHasher, +{ + /// Combine storage with write-log + pub fn new(write_log: WriteLog, storage: Storage) -> Self { + Self { write_log, storage } + } + + /// Commit the genesis state to DB. This should only be used before any + /// blocks are produced. + pub fn commit_genesis(&mut self) -> storage_api::Result<()> { + self.write_log + .commit_genesis(&mut self.storage) + .into_storage_result() + } + + /// Commit the current transaction's write log to the block when it's + /// accepted by all the triggered validity predicates. Starts a new + /// transaction write log. + pub fn commit_tx(&mut self) { + self.write_log.commit_tx() + } + + /// Drop the current transaction's write log when it's declined by any of + /// the triggered validity predicates. Starts a new transaction write log. + pub fn drop_tx(&mut self) { + self.write_log.drop_tx() + } + + /// Commit the current block's write log to the storage and commit the block + /// to DB. Starts a new block write log. + pub fn commit_block(&mut self) -> storage_api::Result<()> { + self.write_log + .commit_block(&mut self.storage) + .into_storage_result()?; + self.storage.commit_block().into_storage_result() + } +} + +/// Prefix iterator for [`WlStorage`]. +#[derive(Debug)] +pub struct PrefixIter<'iter, D> +where + D: DB + DBIter<'iter>, +{ + /// Peekable storage iterator + pub storage_iter: Peekable<>::PrefixIter>, + /// Peekable write log iterator + pub write_log_iter: Peekable, +} + +/// Iterate write-log storage items prior to a tx execution, matching the +/// given prefix. Returns the iterator and gas cost. +pub fn iter_prefix_pre<'iter, D, H>( + // We cannot use e.g. `&'iter WlStorage`, because it doesn't live long + // enough - the lifetime of the `PrefixIter` must depend on the lifetime of + // references to the `WriteLog` and `Storage`. + write_log: &'iter WriteLog, + storage: &'iter Storage, + prefix: &storage::Key, +) -> (PrefixIter<'iter, D>, u64) +where + D: DB + for<'iter_> DBIter<'iter_>, + H: StorageHasher, +{ + let storage_iter = storage.db.iter_prefix(prefix).peekable(); + let write_log_iter = write_log.iter_prefix_pre(prefix).peekable(); + ( + PrefixIter { + storage_iter, + write_log_iter, + }, + gas::MIN_STORAGE_GAS, + ) +} + +/// Iterate write-log storage items posterior to a tx execution, matching the +/// given prefix. Returns the iterator and gas cost. +pub fn iter_prefix_post<'iter, D, H>( + // We cannot use e.g. `&'iter WlStorage`, because it doesn't live long + // enough - the lifetime of the `PrefixIter` must depend on the lifetime of + // references to the `WriteLog` and `Storage`. + write_log: &'iter WriteLog, + storage: &'iter Storage, + prefix: &storage::Key, +) -> (PrefixIter<'iter, D>, u64) +where + D: DB + for<'iter_> DBIter<'iter_>, + H: StorageHasher, +{ + let storage_iter = storage.db.iter_prefix(prefix).peekable(); + let write_log_iter = write_log.iter_prefix_post(prefix).peekable(); + ( + PrefixIter { + storage_iter, + write_log_iter, + }, + gas::MIN_STORAGE_GAS, + ) +} + +impl<'iter, D> Iterator for PrefixIter<'iter, D> +where + D: DB + DBIter<'iter>, +{ + type Item = (String, Vec, u64); + + fn next(&mut self) -> Option { + enum Next { + ReturnWl { advance_storage: bool }, + ReturnStorage, + } + loop { + let what: Next; + { + let storage_peeked = self.storage_iter.peek(); + let wl_peeked = self.write_log_iter.peek(); + match (storage_peeked, wl_peeked) { + (None, None) => return None, + (None, Some(_)) => { + what = Next::ReturnWl { + advance_storage: false, + }; + } + (Some(_), None) => { + what = Next::ReturnStorage; + } + (Some((storage_key, _, _)), Some((wl_key, _))) => { + let wl_key = wl_key.to_string(); + if &wl_key <= storage_key { + what = Next::ReturnWl { + advance_storage: &wl_key == storage_key, + }; + } else { + what = Next::ReturnStorage; + } + } + } + } + match what { + Next::ReturnWl { advance_storage } => { + if advance_storage { + let _ = self.storage_iter.next(); + } + + if let Some((key, modification)) = + self.write_log_iter.next() + { + match modification { + write_log::StorageModification::Write { value } + | write_log::StorageModification::Temp { value } => { + let gas = value.len() as u64; + return Some((key.to_string(), value, gas)); + } + write_log::StorageModification::InitAccount { + vp, + } => { + let gas = vp.len() as u64; + return Some((key.to_string(), vp, gas)); + } + write_log::StorageModification::Delete => { + continue; + } + } + } + } + Next::ReturnStorage => { + if let Some(next) = self.storage_iter.next() { + return Some(next); + } + } + } + } + } +} + +impl StorageRead for WlStorage +where + D: DB + for<'iter> DBIter<'iter>, + H: StorageHasher, +{ + type PrefixIter<'iter> = PrefixIter<'iter, D> where Self: 'iter; + + fn read_bytes( + &self, + key: &storage::Key, + ) -> storage_api::Result>> { + // try to read from the write log first + let (log_val, _gas) = self.write_log.read(key); + match log_val { + Some(&write_log::StorageModification::Write { ref value }) => { + Ok(Some(value.clone())) + } + Some(&write_log::StorageModification::Delete) => Ok(None), + Some(&write_log::StorageModification::InitAccount { + ref vp, + .. + }) => Ok(Some(vp.clone())), + Some(&write_log::StorageModification::Temp { ref value }) => { + Ok(Some(value.clone())) + } + None => { + // when not found in write log, try to read from the storage + self.storage.db.read_subspace_val(key).into_storage_result() + } + } + } + + fn has_key(&self, key: &storage::Key) -> storage_api::Result { + // try to read from the write log first + let (log_val, _gas) = self.write_log.read(key); + match log_val { + Some(&write_log::StorageModification::Write { .. }) + | Some(&write_log::StorageModification::InitAccount { .. }) + | Some(&write_log::StorageModification::Temp { .. }) => Ok(true), + Some(&write_log::StorageModification::Delete) => { + // the given key has been deleted + Ok(false) + } + None => { + // when not found in write log, try to check the storage + self.storage.block.tree.has_key(key).into_storage_result() + } + } + } + + fn iter_prefix<'iter>( + &'iter self, + prefix: &storage::Key, + ) -> storage_api::Result> { + let (iter, _gas) = + iter_prefix_post(&self.write_log, &self.storage, prefix); + Ok(iter) + } + + fn iter_next<'iter>( + &'iter self, + iter: &mut Self::PrefixIter<'iter>, + ) -> storage_api::Result)>> { + Ok(iter.next().map(|(key, val, _gas)| (key, val))) + } + + fn get_chain_id(&self) -> std::result::Result { + Ok(self.storage.chain_id.to_string()) + } + + fn get_block_height( + &self, + ) -> std::result::Result { + Ok(self.storage.block.height) + } + + fn get_block_hash( + &self, + ) -> std::result::Result { + Ok(self.storage.block.hash.clone()) + } + + fn get_block_epoch( + &self, + ) -> std::result::Result { + Ok(self.storage.block.epoch) + } + + fn get_tx_index( + &self, + ) -> std::result::Result { + Ok(self.storage.tx_index) + } + + fn get_native_token(&self) -> storage_api::Result
{ + Ok(self.storage.native_token.clone()) + } +} + +impl StorageWrite for WlStorage +where + D: DB + for<'iter> DBIter<'iter>, + H: StorageHasher, +{ + fn write_bytes( + &mut self, + key: &storage::Key, + val: impl AsRef<[u8]>, + ) -> storage_api::Result<()> { + let _ = self + .write_log + .write(key, val.as_ref().to_vec()) + .into_storage_result(); + Ok(()) + } + + fn delete(&mut self, key: &storage::Key) -> storage_api::Result<()> { + let _ = self.write_log.delete(key).into_storage_result(); + Ok(()) + } +} diff --git a/shared/src/ledger/storage/mod.rs b/shared/src/ledger/storage/mod.rs index 2b30ecf3129..0578a8286c6 100644 --- a/shared/src/ledger/storage/mod.rs +++ b/shared/src/ledger/storage/mod.rs @@ -2,4 +2,6 @@ #[cfg(any(test, feature = "testing"))] pub use namada_core::ledger::storage::mockdb; -pub use namada_core::ledger::storage::{traits, write_log, *}; +pub use namada_core::ledger::storage::{ + traits, write_log, PrefixIter, WlStorage, *, +}; From 59d69f41b6a3cd351bd0dcf627d404fb6b67c166 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 20 Dec 2022 19:10:34 +0100 Subject: [PATCH 04/18] write_log: add prefix iter --- core/src/ledger/storage/write_log.rs | 53 +++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/core/src/ledger/storage/write_log.rs b/core/src/ledger/storage/write_log.rs index 098204b707b..2ec90a08989 100644 --- a/core/src/ledger/storage/write_log.rs +++ b/core/src/ledger/storage/write_log.rs @@ -3,6 +3,7 @@ use std::collections::{BTreeSet, HashMap, HashSet}; +use itertools::Itertools; use thiserror::Error; use crate::ledger; @@ -71,6 +72,21 @@ pub struct WriteLog { ibc_event: Option, } +/// Write log prefix iterator +#[derive(Debug)] +pub struct PrefixIter { + /// The concrete iterator for modifications sorted by storage keys + pub iter: std::vec::IntoIter<(storage::Key, StorageModification)>, +} + +impl Iterator for PrefixIter { + type Item = (storage::Key, StorageModification); + + fn next(&mut self) -> Option { + self.iter.next() + } +} + impl Default for WriteLog { fn default() -> Self { Self { @@ -285,7 +301,7 @@ impl WriteLog { pub fn get_partitioned_keys( &self, ) -> (BTreeSet<&storage::Key>, HashSet<&Address>) { - use itertools::{Either, Itertools}; + use itertools::Either; self.tx_write_log.iter().partition_map(|(key, value)| { match (key.is_validity_predicate(), value) { (Some(address), StorageModification::InitAccount { .. }) => { @@ -424,6 +440,41 @@ impl WriteLog { } (verifiers, changed_keys) } + + /// Iterate modifications prior to the current transaction, whose storage + /// key matches the given prefix, sorted by their storage key. + pub fn iter_prefix_pre(&self, prefix: &storage::Key) -> PrefixIter { + let mut matches = HashMap::new(); + for (key, modification) in &self.block_write_log { + if key.split_prefix(prefix).is_some() { + matches.insert(key.clone(), modification.clone()); + } + } + let iter = matches + .into_iter() + .sorted_unstable_by_key(|(key, _val)| key.clone()); + PrefixIter { iter } + } + + /// Iterate modifications posterior of the current tx, whose storage key + /// matches the given prefix, sorted by their storage key. + pub fn iter_prefix_post(&self, prefix: &storage::Key) -> PrefixIter { + let mut matches = HashMap::new(); + for (key, modification) in &self.block_write_log { + if key.split_prefix(prefix).is_some() { + matches.insert(key.clone(), modification.clone()); + } + } + for (key, modification) in &self.tx_write_log { + if key.split_prefix(prefix).is_some() { + matches.insert(key.clone(), modification.clone()); + } + } + let iter = matches + .into_iter() + .sorted_unstable_by_key(|(key, _val)| key.clone()); + PrefixIter { iter } + } } #[cfg(test)] From 42d6df433aa0babb583b30d78807d3702aede00b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Thu, 22 Dec 2022 19:17:21 +0100 Subject: [PATCH 05/18] write_log: allow to commit genesis txs --- core/src/ledger/storage/write_log.rs | 47 ++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/core/src/ledger/storage/write_log.rs b/core/src/ledger/storage/write_log.rs index 2ec90a08989..e7bf72b1d00 100644 --- a/core/src/ledger/storage/write_log.rs +++ b/core/src/ledger/storage/write_log.rs @@ -338,6 +338,53 @@ impl WriteLog { self.ibc_event.as_ref() } + /// Commit the current genesis tx's write log to the storage. + pub fn commit_genesis( + &mut self, + storage: &mut Storage, + ) -> Result<()> + where + DB: 'static + + ledger::storage::DB + + for<'iter> ledger::storage::DBIter<'iter>, + H: StorageHasher, + { + // This hole function is almost the same as `commit_block`, except that + // we commit the state directly from `tx_write_log` + let mut batch = Storage::::batch(); + for (key, entry) in self.tx_write_log.iter() { + match entry { + StorageModification::Write { value } => { + storage + .batch_write_subspace_val( + &mut batch, + key, + value.clone(), + ) + .map_err(Error::StorageError)?; + } + StorageModification::Delete => { + storage + .batch_delete_subspace_val(&mut batch, key) + .map_err(Error::StorageError)?; + } + StorageModification::InitAccount { vp } => { + storage + .batch_write_subspace_val(&mut batch, key, vp.clone()) + .map_err(Error::StorageError)?; + } + // temporary value isn't persisted + StorageModification::Temp { .. } => {} + } + } + storage.exec_batch(batch).map_err(Error::StorageError)?; + if let Some(address_gen) = self.address_gen.take() { + storage.address_gen = address_gen + } + self.tx_write_log.clear(); + Ok(()) + } + /// Commit the current transaction's write log to the block when it's /// accepted by all the triggered validity predicates. Starts a new /// transaction write log. From 671710812b8c95616d534f7985538bf615afabb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Thu, 22 Dec 2022 19:21:10 +0100 Subject: [PATCH 06/18] storage: rename `fn commit` to `commit_block` --- apps/src/lib/node/ledger/shell/mod.rs | 2 +- apps/src/lib/node/ledger/storage/mod.rs | 6 +++--- core/src/ledger/storage/mod.rs | 2 +- tests/src/vm_host_env/mod.rs | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index 40a3f5d1156..07e6c2fbc66 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -551,7 +551,7 @@ where .commit_block(&mut self.storage) .expect("Expected committing block write log success"); // store the block's data in DB - self.storage.commit().unwrap_or_else(|e| { + self.storage.commit_block().unwrap_or_else(|e| { tracing::error!( "Encountered a storage error while committing a block {:?}", e diff --git a/apps/src/lib/node/ledger/storage/mod.rs b/apps/src/lib/node/ledger/storage/mod.rs index 843f49560bc..76579baa65c 100644 --- a/apps/src/lib/node/ledger/storage/mod.rs +++ b/apps/src/lib/node/ledger/storage/mod.rs @@ -133,7 +133,7 @@ mod tests { storage .write(&key, value_bytes.clone()) .expect("write failed"); - storage.commit().expect("commit failed"); + storage.commit_block().expect("commit failed"); // save the last state and drop the storage let root = storage.merkle_root().0; @@ -188,7 +188,7 @@ mod tests { .expect("write failed"); expected.push((key.to_string(), value_bytes)); } - storage.commit().expect("commit failed"); + storage.commit_block().expect("commit failed"); let (iter, gas) = storage.iter_prefix(&prefix); assert_eq!(gas, prefix.len() as u64); @@ -303,7 +303,7 @@ mod tests { } else { storage.delete(&key)?; } - storage.commit()?; + storage.commit_block()?; } // 2. We try to read from these heights to check that we get back diff --git a/core/src/ledger/storage/mod.rs b/core/src/ledger/storage/mod.rs index 28e3552537d..49274d5d2b2 100644 --- a/core/src/ledger/storage/mod.rs +++ b/core/src/ledger/storage/mod.rs @@ -438,7 +438,7 @@ where } /// Persist the current block's state to the database - pub fn commit(&mut self) -> Result<()> { + pub fn commit_block(&mut self) -> Result<()> { let state = BlockStateWrite { merkle_tree_stores: self.block.tree.stores(), header: self.header.as_ref(), diff --git a/tests/src/vm_host_env/mod.rs b/tests/src/vm_host_env/mod.rs index 4ab7b6eca76..ba9258efa9c 100644 --- a/tests/src/vm_host_env/mod.rs +++ b/tests/src/vm_host_env/mod.rs @@ -169,7 +169,7 @@ mod tests { let value = i.try_to_vec().unwrap(); env.storage.write(&key, value).unwrap(); } - env.storage.commit().unwrap(); + env.storage.commit_block().unwrap(); }); // Then try to iterate over their prefix @@ -377,7 +377,7 @@ mod tests { let value = i.try_to_vec().unwrap(); tx_env.storage.write(&key, value).unwrap(); } - tx_env.storage.commit().unwrap(); + tx_env.storage.commit_block().unwrap(); // In a transaction, write override the existing key's value and add // another key-value From 21ecedf78d45f09fad463614b490d8ca7bf468ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Thu, 22 Dec 2022 19:23:08 +0100 Subject: [PATCH 07/18] storage: extend iter_prefix test --- apps/src/lib/node/ledger/storage/mod.rs | 70 ++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 7 deletions(-) diff --git a/apps/src/lib/node/ledger/storage/mod.rs b/apps/src/lib/node/ledger/storage/mod.rs index 76579baa65c..d7db889ada3 100644 --- a/apps/src/lib/node/ledger/storage/mod.rs +++ b/apps/src/lib/node/ledger/storage/mod.rs @@ -50,7 +50,6 @@ fn new_blake2b() -> Blake2b { #[cfg(test)] mod tests { - use borsh::BorshSerialize; use itertools::Itertools; use namada::ledger::storage::testing::TestWlStorage; use namada::ledger::storage::types; @@ -368,18 +367,15 @@ mod tests { let prefix = storage::Key::parse("prefix").unwrap(); let mismatched_prefix = storage::Key::parse("different").unwrap(); // We'll write sub-key in some random order to check prefix iter's order - let sub_keys = [2_i32, 1, i32::MAX, -1, 260, -2, i32::MIN, 5, 0]; + let sub_keys = [2_i32, -1, 260, -2, 5, 0]; for i in sub_keys.iter() { let key = prefix.push(i).unwrap(); - let value = i.try_to_vec().unwrap(); - storage.write(&key, value).unwrap(); + storage.write(&key, i).unwrap(); let key = mismatched_prefix.push(i).unwrap(); - let value = (i / 2).try_to_vec().unwrap(); - storage.write(&key, value).unwrap(); + storage.write(&key, i / 2).unwrap(); } - storage.commit().unwrap(); // Then try to iterate over their prefix let iter = storage_api::iter_prefix(&storage, &prefix) @@ -391,6 +387,66 @@ mod tests { .iter() .sorted() .map(|i| (prefix.push(i).unwrap(), *i)); + itertools::assert_equal(iter, expected.clone()); + + // Commit genesis state + storage.commit_genesis().unwrap(); + + // Again, try to iterate over their prefix + let iter = storage_api::iter_prefix(&storage, &prefix) + .unwrap() + .map(Result::unwrap); + itertools::assert_equal(iter, expected); + + let more_sub_keys = [1_i32, i32::MIN, -10, 123, i32::MAX, 10]; + debug_assert!( + !more_sub_keys.iter().any(|x| sub_keys.contains(x)), + "assuming no repetition" + ); + for i in more_sub_keys.iter() { + let key = prefix.push(i).unwrap(); + storage.write(&key, i).unwrap(); + + let key = mismatched_prefix.push(i).unwrap(); + storage.write(&key, i / 2).unwrap(); + } + + let iter = storage_api::iter_prefix(&storage, &prefix) + .unwrap() + .map(Result::unwrap); + + // The order has to be sorted by sub-key value + let merged = itertools::merge(sub_keys.iter(), more_sub_keys.iter()); + let expected = merged + .clone() + .sorted() + .map(|i| (prefix.push(i).unwrap(), *i)); + itertools::assert_equal(iter, expected); + + // Delete some keys + let delete_keys = [2, 0, -10, 123]; + for i in delete_keys.iter() { + let key = prefix.push(i).unwrap(); + storage.delete(&key).unwrap() + } + + // Check that iter_prefix doesn't return deleted keys anymore + let iter = storage_api::iter_prefix(&storage, &prefix) + .unwrap() + .map(Result::unwrap); + let expected = merged + .filter(|x| !delete_keys.contains(x)) + .sorted() + .map(|i| (prefix.push(i).unwrap(), *i)); + itertools::assert_equal(iter, expected.clone()); + + // Commit genesis state + storage.commit_genesis().unwrap(); + + // And check again + let iter = storage_api::iter_prefix(&storage, &prefix) + .unwrap() + .map(Result::unwrap); itertools::assert_equal(iter, expected); } } From b71e22b09952df028d81d1f14ce2143674c946f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 11 Jan 2023 16:37:03 +0100 Subject: [PATCH 08/18] test/vm_host_env: fix expected keys in VP iter prefix The expected keys previously omitted a newly written value, because iter prefix wasn't able to find it --- tests/src/vm_host_env/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/src/vm_host_env/mod.rs b/tests/src/vm_host_env/mod.rs index ba9258efa9c..1ef6c9bc220 100644 --- a/tests/src/vm_host_env/mod.rs +++ b/tests/src/vm_host_env/mod.rs @@ -411,7 +411,10 @@ mod tests { .map(|item| item.unwrap()); // The order in post also has to be sorted - let expected_post = sub_keys.iter().sorted().map(|i| { + let mut expected_keys = sub_keys.to_vec(); + // Add value from `new_key` + expected_keys.push(11); + let expected_post = expected_keys.iter().sorted().map(|i| { let val = if *i == 5 { 100 } else { *i }; (prefix.push(i).unwrap(), val) }); From 326ebee20e8bf46b62445a49f1ea4ed71b578da9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 11 Jan 2023 16:38:30 +0100 Subject: [PATCH 09/18] vp_host_fns: fix has_key_pre to see keys from prev txs (not yet in DB) --- shared/src/ledger/vp_host_fns.rs | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/shared/src/ledger/vp_host_fns.rs b/shared/src/ledger/vp_host_fns.rs index c678744ccf1..336981b2afc 100644 --- a/shared/src/ledger/vp_host_fns.rs +++ b/shared/src/ledger/vp_host_fns.rs @@ -157,16 +157,32 @@ pub fn read_temp( pub fn has_key_pre( gas_meter: &mut VpGasMeter, storage: &Storage, + write_log: &WriteLog, key: &Key, ) -> EnvResult where DB: storage::DB + for<'iter> storage::DBIter<'iter>, H: StorageHasher, { - let (present, gas) = - storage.has_key(key).map_err(RuntimeError::StorageError)?; + // Try to read from the write log first + let (log_val, gas) = write_log.read_pre(key); add_gas(gas_meter, gas)?; - Ok(present) + match log_val { + Some(&write_log::StorageModification::Write { .. }) => Ok(true), + Some(&write_log::StorageModification::Delete) => { + // The given key has been deleted + Ok(false) + } + Some(&write_log::StorageModification::InitAccount { .. }) => Ok(true), + Some(&write_log::StorageModification::Temp { .. }) => Ok(true), + None => { + // When not found in write log, try to check the storage + let (present, gas) = + storage.has_key(key).map_err(RuntimeError::StorageError)?; + add_gas(gas_meter, gas)?; + Ok(present) + } + } } /// Storage `has_key` in posterior state (after tx execution). It will try to From ff7e247ddff31e7bb11d1961579ae09efc082ed7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 11 Jan 2023 16:39:20 +0100 Subject: [PATCH 10/18] make -C wasm_for_tests/wasm_source --- wasm_for_tests/tx_memory_limit.wasm | Bin 133402 -> 133419 bytes wasm_for_tests/tx_mint_tokens.wasm | Bin 350309 -> 356325 bytes wasm_for_tests/tx_no_op.wasm | Bin 25554 -> 25554 bytes wasm_for_tests/tx_proposal_code.wasm | Bin 203888 -> 205824 bytes wasm_for_tests/tx_read_storage_key.wasm | Bin 150016 -> 152605 bytes wasm_for_tests/tx_write_storage_key.wasm | Bin 224094 -> 228614 bytes wasm_for_tests/vp_always_false.wasm | Bin 156489 -> 156524 bytes wasm_for_tests/vp_always_true.wasm | Bin 156489 -> 156524 bytes wasm_for_tests/vp_eval.wasm | Bin 157426 -> 157461 bytes wasm_for_tests/vp_memory_limit.wasm | Bin 158937 -> 158972 bytes wasm_for_tests/vp_read_storage_key.wasm | Bin 168269 -> 170868 bytes 11 files changed, 0 insertions(+), 0 deletions(-) diff --git a/wasm_for_tests/tx_memory_limit.wasm b/wasm_for_tests/tx_memory_limit.wasm index 3a83b28632070bdb8b54f73b809ecabf4bc4c365..8950a6708890421229ec123658a70bc1d0c4ab01 100755 GIT binary patch delta 1320 zcmah}T}&KR6u#%q%4RvN^rgdLuAh*8@ zI;!s8UB0)%7iiCVL)G;SjZMkso_ud#|3KLr_E8w%+#0ptQ!atCj%(k7v9PknP>h7=DDs7pHj|ncg)Qf^%f_>=M)2{739MEGBwx{&*(pT3!O8{+n$S?^{ z8kY&<-hRRblZuskck_pzpgQg;3s2V+iA#M%hp}kMN_cO%A%H{{svNCCDQ3AY znQ_kkCHyIn1Grk70t0`jUAA8k*I{?b0wy%+DN?db2xU4L!H3N+pcY%}juO6HXAxG% z&OojGdaR+O|7QFTS2)VNs)2L)M^ka#w|cJ zo0^llb8BY09PmhWW>D=FL_+eXGt`(n1VXQtTq;7x(@pn+jyIa(;KARTLWFv9ERyAE z(oT(W?p#lOa)`X*loPj)C&K{sxYT@{P)q#?_4a0J6`%%}TZF%c^r=$Ft#c%{2G?7b zA&t}N{YE-qkZ14eDp+_qJ&-o_s7tuzJTEpgCO@)9eN?H-r+LwI2{`EE0*FiteaNQ@ zO}H?cDUUkNMqQG5fm{%ziqiirhf>x9nJX1;nk$$#l_(c(XSer&^KAtqt^LlJ9%+?4 z6LWl|BbaarHQ3^)4o0_Q6oG$YnoJkz)nLXq_+{HC5W`p7_f&F=O0^VYCdkkU89HgI zl8yOT72awepczbL8zGMOWy8)`9?K?)ypp{@gO=^c(V)$AJOU8Lul7AjO)hjk(HAkI zj7*z{VvJ@sV37R(wtENjP=;h#&I~W>yLjv4KW}`v{*oIvJD-6BK9;K@d_Fe;3Hx^L z8uusU#_}4IZmKbY34Fi%CWYR5jY1k7;-k<)&-Vdq*ksj?8^O~2;m`RhT+EM?y4(5L z{~y6qy(^l}8YMGfWOmlan1L4#p0dY>x;Q2agWG=qr*rg2c&c!E2mh;ZzelB$4V7+% z!QlxGV|Z+Mox=g_85wt$7e}sgeV2XD=&#_c-DB4Sc-O=({O+O9v+;=!;uG-u+kXRO CD?dj7 delta 1361 zcmah}U1(fI6rMBpcXRhI(EMk!b8oWQO?Efgq{*h)q)Dbp^UoHMiX<#YBl%rGofiqX>c$r647OQmikY+2BK-ybs@*bLPz4 z?|kRn``)T~Vbxqd+(s7{$&{9t51E2IEKU(JrAim_Q_5n&=+Z5$jKX4N);?+~LxT!6 z0aUQ4M^%AP-h_1 z)eRk02TDrY%6x%zg}1yq+RzwJB#-nD^z`wfW%m>eP)df~50on)?S%FtSlgF23ExEs z_KI%fnSK6sqrdF6%W}(6e1uU(xtU|Q2=)lSnbP<6#DG}`HSV-IQMh%}vjWgA9L-kAX*QHAX5; z3zPt_s8^B@2YC5BFJEv}PO@sA5l#HsK7bP$PdBi7)C+iZ4jW>t46qfphLlj>YBV^! z^XWL$i?`BiIEtB$436SL$CH3#@WY|gI0mDg&$fpxo1lnGkgFq%1h9a81F_HZV1~S% zh=-joK}@`usYE=VnS_}8AoDTx$N2X8CgBFAS;3h2^zc_0dgmgBBrhL^)_Q&lkgP+h zq%6T2oIzFm(_1ONtDitZ4|?bRe=c9|yR7-hFjfv@b8|j|U(D~rLwkP&**E+{^o+c) z&;M-XG54dvZfdIdR!b#ZWCouX&ggMUM#Y)YJCuxzV`CH2TphbXw~O7$@%#AI6ua5% UCjs&2@y3_alQ+|o@YlQl0!*Mw%m4rY diff --git a/wasm_for_tests/tx_mint_tokens.wasm b/wasm_for_tests/tx_mint_tokens.wasm index cb7822f98c258074a7f6006afa2fd1a47e152d16..593db7f98290574c7044e7f84a7f4e6de9b9fc09 100755 GIT binary patch delta 71783 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 delta 66836 zcmeFa33wGn)<4`;efKRl`=0c@36MZmmV{N(?1-!aq9THTXh3!q6}drB5kZzl3jrmH z3oa-cG^nT%w>YBWHsXv6DmtjB=(wPR`2U^idv6+KW_0F#eV*_AxX|f7r>ag>opb8c zsimtwen0i&t*I-Ey)42a<82ZAlvN$ev-pAqY{7W8KpAgafS)p6QO2taB8aqVe!eFX z0Y;4gzv_)0x`k}YFvnPBVLt?VYYj@`&Mu!q@p_9u3> z?Q!;JR@Am+d?$VI&|%|-k2rPoIp@vGZGF*BR@lHUysVk6V7Ie5&$CVJ7IrK4zb=z3fBw5%aw8qH&V?0$;qX!EJLew!{e9D+%gte~!DZ-LzBD+v)p_MAsR% zh8uZzk(kFyjjb-5QSWjYkGed@gRbf0!nPVMtSbZj$+)qgJKW08e2HE?OJ|x*3kN?X z9PjH7YFy8#)eo=Psgx^9H$?#)tku5-T&yV75_Z+<2dwbEHO2yWH@d<~IJ}2`9QKL$#Q{O-)!rp$jF`W7|PIWR8sH_IoOC{wqqD(JBDF4{b+{SbTDkN zuUqQc)jL7nh9WwvxZ?8>&)mjMzJb>I|0Ze|1iD3zp@vGhQHiiq=e1ye$OSN^7BUPm z&0Vb6LRp#<5)Ro3@`VaCRp0Nf4O6NdidLU%*bpfNL}*L<}aG=xM_P_6EPj5NCu3grGL ztRPLiJJ43QYk^`lT9GEOBA|KWa5G@9=840*0eduO9Nq)iZ=@!4a=1$sJ>)iqC8Tz- zQ?M-i-`%w)aS8nckR&akMX0DGETL-w`5SIdfQ@n)cLWQH?Wj6(kLT2EqmrmPN)X5G z2F_!A6D*JTG_{DYbcX_nama@*1c9!s6}1NLgNkWzSi0o$#00_@aWv0Su3 z6|W;k`>Ob17w&0Q{1DyMVs*2vTHTGjjrv3rb}wLSG4}wr7IQaXyYXyFx88ou6{oTtSme)eFn&lIn(jdm8h4xz+o9ZU&1IaG-aA2RYMcXZN^iqFMmVEKgiLl!3D+hZrBwP#z%GjtE&*(}C}|yFOT!~&TN|iW522zO zyEE*m;(MI**h^b-XV_oG-yjH@x|{CM)Ll+(s8rQvAoM0@Z8!m(ij`(NV3e+<YdywPLV&0P+EU z`opUi1;{KeDZr5CW zWyP$%z#YiRvp~tYq%z%d^R12Q_c#>K<#w9v?y5+$gg$v{7`eaU<~6)Va|hra+P#p3 zIyl}T!-Glu&pep%c~K9hcf{>uUfqVK$Mrfc{|w9yJ0V%8Zkwyy(C-J3jaOITSE%G6 zY$aBq55Jf0(ACYj>$f>;Lv|H>EkcRI?iQy3WlZ{4Wz78NZq@~Xu(2*bGv3#8={ab8 zOE2xJUg~0>OS%xmC$y9|s5HoZw;N6Xt3?x%#UwlA@2VVj5f{NnKk?D+!KYR-|SujcEe_*D2xntQMx95Y=a)Q%{etzB+YMD$VonJ8u@Dwi?6RgMe}KX%?W>&{5)dMRtO0VAuE)_d`5Ms8~)A- z^DSTA=+kl zt-jGHuk9d~okrwe<4b#^nX2iSE9BST0hE zkj?b?9xEt73EnU0!QmL@6^>>uao>EN)&n{1CTFXN+rrRtl8h7TZkrmmliTQ&Rv01V zK#-4RgRcdbRQl0Qqq#7zm&K3%Eq-Lo;^{uA<<7o-)fn%~ji|^q#k6`c0hvCl<|6f1 zG}yza3ZkZ970L!aKv+~to92bAP&^V<4sIkW(1wgPG+z>xUE>;B_5j@MTTEJutI>9N zG4JCIxo8klGgWgMyIO_HGJ4hFr;UKhAzy`9i@p>Ypw%Kh5I)?1aN)*`UpA%NZBXqD zqq_AN-gKR@s&!ww?P*;RK_M6l!Hd8TIjBflveMG9ad*)TUS)_M0+L43*wMbom{UB8-DbQ{Jds^vl(sEx6NvU3$$C+( z=Fpqb)_v4}f+5LpTYze!=5H))yACOG+I5G%pWd!4=!QCS9a75xpW*r&msI2!4eiEt z%J4b7T2M=_4Rg&=tk&C!6sPVemKCS0VwrHt#BcN~SxUpO*Gf*O23T79OWzFX0&tm4 z$7vxaic?e@;-nTRa@c0(KNZhXr@(uT0Z0h4*E>!PJN*ibMust~EGr`;B$dOVCBinV zs8PMWR!a=tW^5`un_Xe3xPb0B#ma^8DsXlk%x2k|l9P zIbmE}p|N~pZN-JzF`_N?1-WQ_iz@3YoS^OdcU9_aR;e?pWE`rT%asAf^!6R?0o?`t zxU>B*2=h?;Iq0-huinIT7aA8#j7@V#nU5^pEtQIm4#*>oidG6j#)5m`(6>X{v4XoX zlm%)lDFEq&jD<$x&^H5>5Jfq9Ev_>@>)-)PcDL&j2+k>VZ5sH1xhEv(3+;FGf;#WAY090Ud@}0rEYVFtYh~2h zP`Ty~`Jq=BH)vt5^L~D~My2qnZ5lEu3W0)N4=dYN^P^*n4vroATALGRv?bMs zOMrv-Y>uHF(Cyo+_O$b8JOr1)3%A0Lwh+QV4&=S0&$1hCZIgE}BVcTD(HB zr*`hiLdHFv3;P66LP&iotYv`A36clO*|=~}4^InmJreTq8fqNmgJS$ZW;h)!LkoV+ zLOJN#F{9x#lDp`U002H0mPf5c;|T?P3R$J-MAGiRKZVgp3w-alCJYwrx77wOMekbP z-*59d$^Su{YYlHB)S5?3Zvw>+K`S)7zW>UdxMc&FA6AB;PlC6E`JDi)`0HJ=10l^G zN+NB$k=wf%74P4BTzDaBSF8>j=4X1HPg)-7C8Nsd*unw5PBr$|^fo+w(%3>H)F;;% zujhxMh;uQG;l@8d>gq;aaiOkm#Gi(Cfk8{3*2eRCs_I9xeqWCm-TMYvz!=>37Bx@H zG`{Yei9v<8UlvNyx?ek3oe}-U1U;Iq7Zo{J%{#*7@EDKw>rmi<#<`(oHYyPQ)=Uj9 zVPFDeKBgBNPEs|3{WC|PFeH-z$ppTSGTF zTAuOsfcyds2iB@+f(f!2qK)W^R;2TRen%GBHcZ2Mj1B`UN^b*q=pPk%JjtdXYK4*I zBzV(sG`lL8{?w3jA?Wyxl>;lYu7-aEwP$2CnZ6YUiaH?4*}o1PDb1>ltQC?sE49I^ zCHu_;)K2`yu#;1rZnO(Oo~NDMBSJlDI;A&x>DJOgRqD4}OKnkacT~OITDF#t$WisC zsQ^;%RGJi!>YYmJ9gM0sh)Ut6wy1Y%Uw;`a3w4Mnh3)hr-Wl#b8W5`89SlY7GECt? zki}{S3D08+59197)YwM_j0q1Q)fF`cHYz}_uI0jdcMruLp&n6FjHjC zT3ya&lUQ}RTtZ^hx1sZ^Qlvpm)Pf*ra>Wk=MAY1Rs@1xf$l_Y9tNBz}t93JB+72kd%#{!wZW&;Qlsy zZ84YLqUj{T71eiGaO6w*jb+1YA;%wvS9Xtiat{3v+G=cu)}bGyjtSi}QFo9#y9CqH z`^k++AmyxcU=(3Y8gUw?!$`@rY)oL=amN(Q0lH%lxR>r;}9f-Y%H*q*zxbJ`;2fJ(6 zw{wWKnS=V_)3OHvXU35a+j$smltw^sduc!gB?hNxUIgaCRBs7G?7nCVkT7h3&vQp$Y@QEmc3NmRJ44iyg=TtA$4pC`RfUE(= zWthh?#A#HSM^6j=>&hIAR;Ko!S0?56R>ss%syzBWYEQfhtdz5#A@VI2j`>wB?;cYY zqFNTKsHM{D7q+OEZ3OIV&b7yWz(%JDCgp9?8yUJblK~oxpJzBDBt+JLG{u zBpc_^;3bhcq!|JTjX^~)$)KSW-_qSxDXX%OV5frX*SbI!>mQvJILP-u9N8y zpwZ98WV09l65tTPZ6enR4uO}3F$mDfoWM!rb3;kIM)Oc%%mFeAU_3gu7liur*s``B zGS|?SgB*ie+`r}U)-jO=3Dcwz>^J%$O!L4w8Ff|}1h@FCfk^uDS=o9_Fwh4ilv>8+ zTz*u+q-AlnD44V>t`-Gz8*RrGfYy+4*~a;k6O4J|1~`2vC!9p%`Eiuj_v20tPA1|z zc-TkxCLT6En3kRny^q4gTr+nk>NRIlJLAE#b91D%hZ_mu+TqDzf?Kagx5tBBL3_dl z84)W1GkwTO?kOtmhOnX1(wI;aNs3no?Sm|y8G^>)jRKGyDU+EW{Nt&5{$N|gY?Txg zoMDzdBwNsS4&_5;9^en8O?SuZKnB1#Yl_Pc)dR_BYcM8E$Tz+l9Rv@tPKitU4Ny`7qYcK>uw_Cq zgXrEmCyX%APSX7?R>@s6Cves35J;14<7S-6H6dp@1&g6 zm{oD9R6UZFEjZceKB*(f&zh8l=CEwi0JSTIYM_u7(>ZvX()yv=E;L-%tcOVVF;@tRj}?6>F{5}&wlU(|Zk@WKMVbmATfdYPQo0~}A`n8R z)McC8##AM&B{X5@xn1oZNL`Xg#WSW1?hiuFsrpAYEGQs}Q{QKU9+@=g<|2{HP<0BC!HcoSrKm@n39ofDKKTok-~U)N(PzOAE!WDJVxGmnH3&=Efy}FS}HLIiw@E@ zU~LXtBC60s!vjY{DpG7P>+n&LXv%q4k>JnEfGnNom*k`AnEei9DfN(AJ0`)TtiU_; zP2gQLj(i}iVMNX!fjS?@`2OSk(E-dRQ|pj6fNRFr6lk1djc;>!^}ozkZxRZf%j}#JTxpRyXUxdR29OAuWyX^;@| zG<3fZ(>`IF@ytb~%w~Ld(Mc$7$BRRGC@xK&(b$~^6l+yXaMHXfhg%6dJ?G-W$nTw_ zmwjB8oM6V%0qA#*lK;<^GZKzn&uBHTFSs{#-Z_Pf4O*1iY@=0Py$;O-`mi04z7};? z_01$Y_CHn7POEMRny~esgpvTcmf4m0M=`@Xwu8rJSNZXEePg?Sp z6G$}Ah`9l598eI|Z5+HZWh{C#%!s0or7;n8Z4?KSC>TOg7m1lck^@;*>x)*~DomRN`z-%m)GxWo7R%AUe0)w%L)HvIsX?XMC{1Ftkg)cq-v0Xgl#7#w@M0=2j;F}(}-Td9bz4G^1YiwmhQH}+hS z*XkhF)v?t8`Op|`5AHhdJCI*5Ud`_%{oZ?xk-unCA~;mU_qj0|$9+GBe8!qZJ?&Cf zM&i@#`O4Cw_--t2bK zhOQ#M%N3&Co<+Rb1v3|bM$;xKw^4ItyJ!VwUfB^jAqFZ4F~|zKjfbv$%ow^j2yI?? z)u_zq0CO(27i`T4*`iI)W>jC=b|g&!gwZ5rO)wV5=$H6_Q7knw+75vE42%z<-m;O= z{IwH?GupanvKt#%piXjO8*Ms*?L8Mcz zToyNWUtP>L8$VrLNh_G8OC}7nY)+siZ{1@|4yAREF`Vo8hU4hUJQ_)g=IXT$L+*N2 zZUQjgTax9C87X+EhI&oM$(C(hlBi`{ms4L7-{aLnG#Nx!6H#beY-d3<6b2|6=0~pm zJLCJmD5nkA6k=QUQ`Z!_NtRL`#+TRR*ug`uoqC<`SM}!gH%ZKyt0m^f)!mG4mj%&2Dicv^zYWxq%@!wdN!Im%&)$ifW0e>f!9w8E#3x8%3*@AnyM)p zLl5q&)ps-?#vAw({J?nm`swX0FbiSfol=oALvuGYyTc{Mw0tmNbFqVP`VF}m2P6d+ zR%K5nrwNcOxYyWpL#4wEH2!i!E7to#F^Zv`G@Q0}xQ*>F?%Gg;zn{s!?QWvyY4YzD z`S(ltxBO&V}oG;Nbidg`XuWMeMdbcnxmHTL^t8rR)c zLXSIcqs=8^YJ_JQmA97^L^W70SL)wlkB`3j7Kk_*T|B1^M=ZF@sJneURp`UpsX`51 z?l_O+Vr;r|B8kVf`3h$5?KSKkW0otWVbfg)R0>_SWe9lL@WGaDtoNan)Sp4h*f6i} zj{}8Revbn=wR&?LNU7Dg#{sC`_Lauj_xIwsZMuJgW63I%(j{g-z#y`%X^~}!syjga z@QNlSI7D9^gCXZAYAvD2nTL&yqHZD-yl;%lW2if03AH$C3!%^rV*xuBYb&A9ony{f zqIhtK(YW;us`T;)hS2TZ2l~*h;K3ehgALP3t~BGa2VW-CpoSV5uc4LGkB-G1R*lCR zu(@J@pLX(6_iD#_8pZTSUXIvCEzIrUp!*wia zIP{j7`!6`08J$6K>{KiT{HmKMJUP9)VZWiv%hdsXwef60xB|C z_Vk!YBFlvOkxvF+pbGklJ(B&fYY1B=YeVqkH1||mB!=%HYnmXPi-=|mJ4;YgFlLCY zCJCHDg+f?pK|zRNPNVbVp|SMJiG)fOuJ@b;7r-%hHk=JrpE(dFnC9<%Ku|JOM&x?Z zTuh!HRAy;-RY;AenH|O=Pu(+0KP0I5lAJ)UZl(kdVV%J>$Y~JYIqs z&zS#2S$j8C6=vHJEJIf=ixqNU;S?KRnU|bKFgjLR*bhgr+<51Sf?!KgF=?)%R0&UJ zxUhCj1;M`2C)2xP!vhKgZn*WcY0T^Zw=_Sohh$9!U z&B}~jPlj2!@zs+(S+P;xRF&=4Z}f^gA#kb+%=8Auv)Vef@e*OU`-hg71SBvtYk z)*ovnw9kO%-q7TyqS-y(R2cN@yB})8%orR{1tszuUpFO>YzLj;!FJ@8y21|ZkfbFM zN3b2q!Z{VoUeFqwmO?X{*g~UtgBlo;fNdx$hInfi!A@0}2%X%~B@$tbd1_#OKcK0( z9a@Pvoh1PG5&HA(m6XV6dg=&j_vFqyFnmAv=@R@kp6<>{jonZ8!Qa$phU4#)XWAR< zpV1PFW$|Qn$m`;@#!Jt9i4;4YEymwZp6#q+XNM7bPOA#Y(WOhb({3HFegvMTZpWr6 zK-|w^3p~0?TN<$*6%5PB(B*(}@pJi^S6shSf##SknG#0FU;*R)=hmYzgPy;dB^a+i zKcC;a))?MAFk$t&*dAL*ciB4Qu4dU|ebQW1Q->g3MZVAhkN)kAQHVC6dh~U%Kx|us zYlB4-ecRe-3^Wd`8?0>@^1?Zc*RL`j{8P6+&r|YJ5F=}bHBM~TA}Urai($VYLb13Q z%bRh(yuqk^F(u=v8!UnDSm2r)jI&`Kp00EZH>sxAF!@|tN2 zh6Dme-X$+>hnd%3p2-)lGg4pC_-$*A>Q{1&Z(a`ZLpQcy#*83F6`P9~(l>g= z3Zuh(%L=2neBTPAtNfc4ZUx`!M&pB5N=6W;tz>Vt!XSIM6$aS{tuV+wYK1}eNg|tL zT(&zng2ZJd{hbvC>7T7INMC6s1=7o{Fi5X9N&g7aP1d9kMY9zK>D^Wsr1w~1klt&B zLHgshveXy98sgj68tYz_a@g@|=?KyoYaM>J!pP`KD#Z;{T4!c-+H1iP zq;^))yR9%t@3F!lz1IqZ^nNQ0(q9N!hc`iby)_8z*<^)5`W`C`(hXJ^q#v`wAl)QM zpM&8;sq`=yZQEt~muPa<8hmeskd#x}?Z?nQ6{m2as54@4h zU{GIr^L+fR{_`mOUH<365>sM0-|C!XvA#6(p)(#N^u^c3#}gfEY4ZVkBAUknMSVLuk19?SF(ZXC>CF;>GSRbw z;#J@Q>{xt5!%Ke|#nc<{E;oi5E^JZStzd4{(BCP}vY9?AqYc$>Dg$Zr0hTyXI-9Zh zo$81k&U+X;pMyDYf-zTBh?}E|6J*Y`Wx(Ht1E>LkdA3vFJEM5r0qvE(b7utGF-#y{ z*=Ziq4#oxLf<_6w{)}1qB5+F1@(=?Ra{?&j)rLqLg88;iM?| zN3Zaq+pi5PgV9;qXqP?KbK#JHcbM^F*y6$TPBcNubYq#(^Sv z=OOi>Nf3a|@AqwIhq}=oLM0*U%;^V7w+r-@#HB1Lk)HrQ$D;w{n&2~Bhx;@ zJMAb|Fsy8<&GyrVBuhc={gwH-@V~jzc_hhFxT>JTM20viz7w0(O zah{B$Cc1tVhGz&OF3j4PbyRCb;u4-}JoQB#>mW+*VSN+e=F94{U^`v$z9O~?K7o~r znKSq%GzIGF;+^gY=zmK4h zH^0U9OBs?H4FLct%<^t=sYdX-K9iwFPQ8xDhdKy%=nLV*V?l2qMx_kERLz0C_@@4) zke=E#*q9W$Lji+w+A2CqAuX(>^s%86S}w+GkA3$*Dh=_mi62If!fFPfnP}Wt^nGs( zk{g=8*LmA&V5r4l3KK0Ayo|mr0082rAgCxD^1xDi*0Yb%*7_!SwHcMn6K+eDEJ=Ca#IB|z?kS(L9WyL^EAv1VlPY_ zNiW*dGzj)vxW#pOENw!VjBgXrD15Lxo@=ItGcmLwx_e4LPxQ7`2(J{nJ8si<_i?=32J?d%JB331v7ORY1 zOKFKH{-LozB%Bj3aXlJRp_VO6jIJ0^YCJ257I0b=CZ=o#oZdpMSjFl9r_w?c!C5mBTmLmEU zu#k8hFST~sizKD-Bq%4~mW0rIz%9w4@qiOWWdTbSyFOw$l}8rIvAQ``AVcZIxQEmpBq94+{dgB@z%j0q>NNvKhOHpdm91D8 z4O$$tBrVBW5=5iMRLB{LSqO)ggSOUP!;E2+2RN7~`jkRmo!L%e?V=7-X&T<&LW} zG`Q5!C%5>Zn59GF2T+7L;-Ji$x! zERqw!6IvDJMB)ifu#`W_n30Qi!kGQf7_)>Ey!;J{E3IAW30D19R?#%oi5B`_x6nVp zp#Jw+$hJ{_Lg2rlZTt^0`-CBa1Nz_d^8W?}+5C!6G`~MYBP6qFdhA5!_U}43$*NW- zSoMeK<^Lg5MpNJ?TGwxCU4avV&QO&8#}1RVP>_ zSI^Cb>HpqgIbo~tviNUX2|Zz}ejlq&*sA|O*(%vOwEyorkiVfG;>@#Ix9r$vMcS?& z-|LCjWpSjXc;IYyYvawFRkBq1?zn>O;B@dut=>jcrw03oeJXp6^%adab{=k;9Ut}U zE9&iRF>b9KY>GH_Jod|C@9YJQ%g3{y@Y*r9Q`4BR?RVMB81K)MQ? zXo45&f_McLRmV#qZ_q1kfE#a_!f-^<;1KPp4bndI8*b$N#q?Ti9W6ee`Pg7lbv{0W zH@GpYmic(<5R77R2qaE<9D)t3^tF#baERzO4WHZ_B%Ytf`a9cWXR{;NUZh^YE?}Z@ z(FNFC%zBCMrsD)>bI-LF-E%DpX0V&t8Di%Q*3EfF0ygz~_X^ug_I!YLJY&1mv*=r*HhPIOqA7NfJd5>Y*NRhTu}&z$HM3YQwlv>4iXv@742a>}&UES);dH4qTRa^)jYq;1hKtx6BW_ zdL3!}@t6DSK7H?XWl=a;%N6eVtS!qC9p|$}pt*fM6evr)J)cbkvd86Ylq1Y&GoV;^ zIqPLNA3tq*?7V=Tp&rx<#Vtozve82XKU=fkX^ytgu$oWf<48HSFjN(0z}~= zb|yPXyt;_B6~A1GCepA7D$<=0BZ2>FF&i!}UJUkh7wxWMoyEECT)3`^ zMO|wPWB0Lm_9}LzN)N-YX5&z{tyeQQ!~M}EtO{i;M2R*-Rw2YVbPapk9ik#~2vNMZ zlnwI|hz-xQ<5B3>vZsAuo|(c2jBgoR9K>rfIK~qk*-Wn}Xa!>PGN^S}e60&cm&>qiD;# zTU4xMIe2L%zA04hP{%72@o2O|-6t+s$+9Do|D=bs4V^OR9HU00D`J2fKv4{UDi^C- zwK^W^7R#iCJYG_cWkfn%BR|1K!Ut2#t*_J=yUNX(T}8qwcK6^R`nFG>!ka;08qC^l zc*Z#lJ7XxE;1ESUO(^IUsvJW-8$;pH2Q!v%tY(+77sPdCyxLu<5LvW+v4*i3up2hc zrgy^^aqwmiH9zy698j#YnE0b0uW3?1qlUKfAB}9K8R6(^keuI}v)Ggu_=76~)i#H5mF3jHU$V&?{SH zGSNGoxs{#7=8AP&**UoVyp`Px3%UsBbjzbLU_qDQ*|ea|F6DIMx{r%h9r*_Fegn%? z`}i0^<>K8^zF3zsz)7ZY860KEwWFXG8Jz2NLmu+%cu}#9jc5HES8ZcM1Nro>JI>+K zo4gL76(yw3j^tWO>5nv zWzRf5I_ns;&|f%8iaE!GN2%o=gBGh^VkucI49q_^oHUN_afq*8V5go?IWX#k%Ej!- z|DwvRVeQ3{0URR%p$ncO>IU-U#wB??nW;~Dyv8lAG;u~IZw1${E|Zt$$xnaa6dd}P z1%@s3ZW$eZ0t*Qrz#(wz#Sk`0WM%PH_|lY1*9Q7=ej%dQ$YZxDI(=q_zQ0K0nS&Vo z;aC}YFtU7UaKA*vKY79Ry%J#_olG(CnF9s$d3)%PrImOs81J*VYJ)w<`PzzOcH|-1 zdJ__H(KefRD|O-JJ9-@yUEoS5-WjE`)Ny=f!Ym42AT>+!2Hs1wilNg-wVMD+)YZT1Gxlkv{GH8LmNx+Za4i?aWGD(mDT|tiYgToKV|)b zm9=}W3@`IL3^RR9~R)!Jzxt-bgWFBh{iG2%10zvYcLe#X`%mQ~J~ z6RO0gw#w&d+8mUKYsGW$_EPQ~J@eu@p<=u!ltTlhe60)-%fy7kEZJExM+?mfRfx+D zv+}euGS-D!IUUMWs8x{Y3bjhnbeLr&(m1q;POsARca~uE)>(Xh7>>*&(e`K7ssb-_ z`QX`Nm?qB_#fNC{S+-Iw7@P?FN=Q7YmC9$lt4COI<=a(d>KZqs z@I4F+zc7vO;_CDHCy9eE<6{~4g1p##1Ouusk1&UXnvbwb@xu}J0Grf!*DtJ)u^!jI z%nz~x@g(C z-Jy1LuXl$^#155z(z8@c?)BZquRlKWv+>-Gkt;R(A}bgw(^7g}v2uA`{V!W~?onj0 z1q_vo4{SVTY=xHA>#L<}K3R9qy{j{1q!x%|W+i!S4(+v!f0aWAG2G5m#&?AFW-Y?^ zmh4*gqEIq!Ig4<9mR&1cgb&f!wN{I84xwFZy@;j{v^INRP!G6g%#dhWeynIcv{2M!bc?_3+eNF6W}|AIqfb7qYxK#hbr<7Y zJYVgpoz%F*#mjMSZoX)A^G%+x7R@Ov#(VfxcsccN9^RAXi*{Z-7CIEntb-iQ%tc+&lLb)}sJ+(fp|s+>95PV%SC;ASNHWQ<4R;c^{*`SAh5Hky01v?X z%F-Ti{W+Y~JrL;TP;K~RaoRgpe za+K-OLg=SY{8Y$CF<;}bR{S&;ppz>!eC8bI?CJH%je`TcMD91nuJUduzYP0I|sZfK7=z@I93_}HkM zj~BH}yjjIZvQeUKHNOml%RT?V;F1P?7+k)CXACYML5Nt>+>2}C(+-r{H|KDPEgC-g2nRn%X_ zePVQHK0+nO`JT>vxLU)Qz=)zVpummoSr9$Tab&Ii{MAP@COC&j-+{jvMF-69(-(c!hLrcA1My@ z;Mtgt@twqHYyYk?jW?gfJJ`B5<7}W}EMDxow_&@qq zf;u;L>CbTr{=pTZd@$yMSK_!+=*lsk@vP%H%U7SwKjg;?_+>DEUMuz~y&nAH!22sU z|5P{3n*l%3Tsx2_sL<*M%wVvUw^vkH@~{06L+PYxrp2O=B47|MZ67PztpTlG}pf_;uktr zQc=A}l?FwLiMZ!V@Z$r@ zM5U8!+a>LzXEM_;EC;Bh(Ups(SMkf#VzfHf)qFUlA~gw0|F}4C4cFN|QMQzqPWa?5 zq^3D9oSS<9&%q(m{1kOn5+L3OW1$H+gUbwMdWt-_Nue)-(dwMuct7SEaRR{oISjH>D(Yz%| zGjBag1xI$7B(7Y>uVsfs;_{a0vLgHMri$osxh}p(vSs3OezD0`sRcpN?>e|D?M3+t zeu?9|dr?ADZsPqFvY&YWQW*T7SHKp?1s=_Plqf>t<(1qk_;qL!?+X0^xED>3o8#aL z5OSEZ`JSFhvml$pc#g`Z@r6}<0P~@xto!hppI&%u*IK72TZ2)j?1NIpV1p;bJE9#1 z(pim5kr;YCuSJ(~+agep&R1^6GpN6GJ>PU(Dr9lBOR-I95r9OiLP z(LjsNSi=dR>&2!yyz_61{OvlS&gDI`od&sZw@`|CyGML@o1P5#b$ zG5jKa5`P0^F5+Dip0fr%v0nUzQaoy<-MC(4T?}M{6}ff2ID?S)0U6Kmj`d<4A#bP3RYX_uKe zbfmfBuFLo^G(5~RWqZg#N?R`*V$tz!`LIj8eb#ZtN}1MYnOa?bcJL%gtn z_oaIhLu5*qYg6 z@4o5i9y|Y^5r0ee;W$xk)smL}|DO|=oqCM8IA=Y4z=JoNS&0{^!;eG78~A{{e`DKM zY~T~I&EfAG_^F9IVD`yBF`JK~?+~Zm#H(-;*>yL;!DAc6-8b`W#|p&KgW~?1d79(M zeRxik&+IF41i@^tyg6Q`kR&tKufHNRzTLY6b zmHllIU9pSc?HJB1x}6vQhG=^cO+2=l_w~x%JoG2{f$-kNFFgve@-BW_W8@ADCR{e; z=CvuhaNfb^Iq=~_EQ*RB@8bQqavFv`niu0{kiKiamL&96H3M#`DMcISP zjVlH(VTzI|VNv-Yv?WQL@*qFxI&a1WlgnpMxJ1;qRx2AXY~b^mXUB~}L{yZK%Nzf( zjZd7!k{Zv>RR3XPSWeATPqQZ>Y@KM#gNvRhe#uks7f&@@Ma=vKGX`HtL;X zTL-m}*Dn$)^VGD|o=D-tWy3WB*QvNhippX&Sq$r-`rM-ckH&SHc(zEbc2_J8Dj5I= z2(0}VxQ|J(w^XeNtwj{*g)(+!P#J`KH@l*w;vVD14pCXA7O*j5R2eFMy10p;GsIK4 zWuA$6|Dr_W%hl%M0T9dp#M_E14Oa|agJ&vQOKnLM;_eDHi-h`Ih1xzh2uDVJljg=oMYlm1WdSDC5qd%Bq-yb2l#wk zy~LIdYCG<_MrxH9&|a-MtIst-B_ClEqjzhe#sF`HYXP5%CNyNm^vPEIRN()quqNhy z3(`XoWf|BVI)>yJ@oIZDS8OX*yS87n)KuH`xaT6?0^CVLab5Yfw6oi)S@zwC*Rioj zTQ!@d#q>A4EU0j#J^!4EO2=hldOP)rz_-Auz)hYrS5cOWt|jV-szJ+x(r}E#o#GF? zVCJ;*C!RmIs|HO0&I4TEIddnRGwu92=S`kOXl4F#v9m<&$Ddy=Jf&(cHcbpJRok!& z#D%45hv0O?=reiZj7gJwO`dpOhxVd-xte2Nd|gn<60JI@yZIm{wxh|OhOouBC{7H%8qetji+4Mz)uN`8 zS|EH?Y6(|%GaQSdCT%WHB zD(~TX9aj^so7ViVT@guq88WyOS1qnHaP`O430D!WEL?V6KN>;hOI&Z`dIr}+xbDQY z2G?|4Rk(6-IdJ`iN_>s$Z@AvV)r9L#TsPuchHDc%jM0~+bQPdY8kdF@o zTFRJu2yLVX6pY`;n8Fu=`?kZg6y>#-fO-;`Ai_x3<4>MaD>Ghnw{Xt(g~B(^pB828%O2_& zV?X{1wHCgNr1Yf&f9lZ#*#4HSC_`RQ6vtJ}R&b}H{B%%0EBx<>Vq1qwegjPFF)U(R zm4*K7YXqMQft`-TYRydaxr8|jQdRg*Bw#^QKO2s=i-5d02~WaR=k-vO;H!#Kcq-H( z3;jkl!V5p-6hRi}VbYvgR4{=*-mbSO%Hj7E#W}^TGPUWth@w353cxOYre?QOl)rre z;9(b2=ij6#YyS%1Di7lyQti$}Mb$fpDa!PBfL`uF7QiM$V|aHw*|uJ#DEnVS|541i z7g*n|3FrU_QL0j0{)!)emJtIUfT`5Kg57>M!qnT)d%ye<0Ju5)gLqC0`Y6h?`+z=& zxIpQjrK-$>;3YSFi#I9CTX4|=^FW^aLEN{rUQwQZ3#gHlBZfElL%gz0@2e=UAdfb^ z37+v^nNGXg$>di>K6 z5Up9KD0d)YT9Tcqp{JnYXhdlb*ccxiC9SACt4y@^l#Qu2^e&W*){Ja6!+WG`w8*8% z=cu5*ixj2y8wBk>HYA=1X~0AbDN&9cC9s#El$_vZM*#{SNvaZ{NS(VV%Dac)i`F@q z*=}yzYGNX(!y%z=*D1sJw#D{`BYJ|3z=>o?}7X& zLB`R~|AJ7QjwR!$34oXt+YP5H3MqI_F|$5L5C^=pz4>4?cqvp=HjUu};R2#D?Nu>GQD&Ne!aGp<`LHE2{XnGuHW*Dm9qH9GH!8|JH1m9E7f~qLHiiVC z*+?mWu++-N7th7B*w9bSjEqL0@9id7kzE)a-K8?yi`fyvb3#fjC+T1IO+D)j5NT4d$!M>BQzC(F(U_y$ZTDF^2hFX|E*%N*Dl%hNb z$=I50uB*_aL2(t^3$}!Wxd4KoojQWM6_;U$b`=4%y=W_S-GZ4aelcH3Tu+zLM^X?o1?*Y8Dw=WhfwyvHkd6jVF373Jxx)TP_$wv zQ#;+HD1Sm5P52y6Ngo)8sRXP;$eVDS9;9dwp?5j8Oi`|(kbUTV;~^0W`lq=noDpNC zReD-O(U3mnRPf5QEVgUA!PP>!z2g|q0}Ff)U9TvYppxEu$(j*YeVr#N3c1AI1B5CB z``_#0boHCmcZ0GZ5yyz@6(F zkJwNi#W~%bn(`H54g(MvNwy5g{V`eq_zvcG|J8zUmv9KdRg4;+hj9Bup5+RH8+8C| z-?Q|HO9iwRP{K#d?Ya)^*-F4do|3u{Kr;Zg$n7}8Vied`&E3}md+uf!bCI(Y`>{t# z9po;>e%>$Ko(HV(0|Vha+kNKDjEu#e$aels0|@&h?sm_$6qpd8gAY(kK@ze2#SFm{ zH!BL|kv@~T?8ss!WSu^XCA+Q!l4dZ|FND)R4?sNtDVIGE#dZQ>a@h^wH_9afLb2a( z7fd|Ge=V1Y@@v8LceV6y&I1gX#9bDK9A+O3@C3MMZT1!r(OtP zEr6V538{ZU)wa@eNm9xoa46CYAiH(I^NEFv+3k~3jsV_EVO^3_JRoq0p7mCqZ>;gk z$nLpvpr%1oBYh!Dbp4FDMFa@eJ@r!nH30H;xAPr5$A0;REWx!Mh%pfgRhch!Bsek? zK<+yU2|Ava;JNTL=2kpmJjH&w`_b*etUY5PMmaerAW7_(o1msUJr+daM_B21SPI^H zLZbUeNjBR4vP#C{+ga;`$XV?VUuWXB~IM7W#Vk!pe4^mjYm7C1D8xgED# z${O02>!5K@?3WW6nMAJ|$A8%)Qygg)*wMWlAJvKnD~{>K?qmzBy(tTRG>viTznO-t zf0T$PY$;jxc-PTHCuaXHTm{|VKCtRt{21|LJ*LhMr11&X*%fRV_0As)O8=3#;5y3K+&zKEqKj)AKi`xW%zzO-u*8v7OW z=gDc8_KAUtQg8-$r(JC#O#T-xc8Al-`n5zUyq=|GY&p3Fk@u-TvEBklwJz-@OGcoQ zo>)D&B@r?z0hIQG71x&s5d-*Hix7 z1%0eIa=MyUkn$0pn-MecO{!-y&5%TYfp5xEGCQG*wTtRA)VBPPT}(VfEoDKm>I|%2 z(cmOoY&}D59m#xtwk1nf*;PG8&Jy{zucrYBCrmeAl1m4FiufUhIj&31M@INdioACl8lp2 zak5dzc$}rcSh>VEK7A_Uh&#_z`{$>2Mgg}%Sb@h9eh;Jg+cQMvG_`Mj>IP)E8@fmtE<}d=@f^=k5r@xGD=Sq@7*_(nM_EXNtqB5dO{$CUZeyYD6rCtfRm6+3ZyYf z2t^#4QWONsUW%HqYhfu0iVQ^{c2H4RT}Az=h`Wl4E2tmRcq8q(6rPndX?}@@o_HS7T*N>;r5Kh_#VyU z*C=uJ&tOuHqih^zp5G#T&MdxJ#}kA#pN}!im}rro&qpR_qkE>&+;Lzh_!0m}#oX~& z7Gt?#;`uo|QGAilyPF-SLCY-BvVgDju0kfF+%cjHbPqsGkIs%;LEb?MPsxcIj_`4W zqvh5Xv+V1O`l%2W*N#&F(k)QGG0hz(fOG=FNh9Lz&w$9K2s@Xc3)nr#wM*u5_<3l4 z8JX!5S#CaM%t<5?5fVA=%`_qhU?3lYuz|c7;YQ?3WiA7`<1RF%UVP%_Gt7=EFk+V& zSI9er*)O2dBUZ7bkoSzWH$$d#geBi;w}ad?@m3*U;mv`#1N_JZVDMuWh5h_kg>W!G zHc}yjAIAU~B-0+bL2_?|8%Zt(l<41*03Qs7h!2bSZQeCV3=pvkfI-9&3j2wm#eOgm zGz0{Q$Od2#k&CcF#43aviD;0y48CFXeni?Pv8$MmOFxZ7<*3PFDFE9n&4Sc89Y8>h zhu}pX-f6V+3E0)n7?zapl%&=(8T$Z6Kv|3~W!j085dxM1cyF)1EvV#TSIajZ#Jr3Y zOHlWMF^Q^MxskEg#ZDlOtjErgaxx4%IoFdU&y?`Vae>}~%wcsC$0CQwoyN1&BQavt zG(N+dG!8U}QxC5-D@oM{Fc3l1%0W=DYca+Vh)5Z2QIdYRm9ZMiwmnQ~@mUAhW@_|M z3yqMsBNN{N;^%_V$|XAKqR~qIH%NWc1N-QtqA9@#{8EO8uFxi;DmqOsgZmj2Q(dd8=z>p|MG;9ft z!|?Gnsn5Rvsl}M%@Y+0Vp^~IZXj>=<5wGjH)i0JuHR~;bBxHR|OR|JP_Pd&~u1wji zrPz~D&5>p}p~;G2mN+Dx1fc9jN?W8ba9gHSpcf>H_zIqnH?-zd@B(Fs_@si5>7Gl2 z!6D5Vbpd8{766Tw;7olJ?U3h6!NVZ0TelnvE{ZC7zsNNpEaPD%DQYWNw#~P_lFw6y zkHA2t#)AZH%b(FL!SM9kp;=jIr#3aiAM+_?T8Ij?OBe)6D(NSc;Cf7Tb2dOP#9R*_ zY_`{+qJ!d~hu;|Immz z5Bc-DuFxZDdwO@p^7C~tUm~`e=is%a(bYUFGA9Np{S=+<2_mhEcM!X)x!sjyYw**a zPckVf z6In^MFY?#EG*Ek__-74wrVg0~;|ldm$4{>heFh5#3dd1xQe7nx0IcME~@p()VU9 zAFB*lia4d(+QRXAe+yYiF=JsOZ#GYAA@d~0 zAVSY9nQv zYmIkw8UTwOCq}O0$=hg+-;4O z^;QB1)Voxip3BFC3|4C0iD^`F{O#Q1r8zBhME?v?oq>gtCUR;xb!+2OrY0cR=l~$~ zhmYLD*g`T!g*X*5j{I@US>c^)q1ez-^)Yl0_YB7Npxdd-a27T0TE;5SPgU z3>N~GJP1O7VB9R=e4x%ZD*@jERr(gl&X;sDA5&xW(#*-!L+Ch)JLiFZ^uNr`ijp*Q z6VB$nATKkOGNAXeq!)MiGb}=epCKGN+s~X&>C{)8A48HdYCB@)A}wM|0elXDvg0`z zD(!{!I+VE#=`*Q;55ich8hTPvJ#610m^^+R8g>zVzB|&r9FI|{6yl2@b~1S!KH;FU zVFhE$zXv!#QE5IHk=PN_1d14nxpme;Os5GL51_~nzZ#6&GUc&#*uf%zQqBVBq96S! ziy`%L0+7PAyW3-6vjQjYqiANd{vuhl9*TMf!~}$HXhdZ>I=?t`2Y)xt(K-u?iYocY zc2V}uSn1hC^F_Q*f5(T{L*A4y-fAgsi^D5~Q#SCF@SOmRkB>*4jys@X^C|13NVzCk zBSy^UE!y1!)PMQ`(P;Ew(9uLQ=RZbtJTRZX6Xzgb--FIZI*vR2bac1@Lk=Z?j)kZ? z8iT=A=%B;MbR-*ewZZ-0g4H;}xt3wrNADKkO74e(3) zMh6%-FMz8P6(vgE84f?Op8!z%Lzoc^*k0ZYf9;dVCMkIg3?Fa`(B+hb;P%=$#@(}l zQmHS&)0n9Eucd;0A2}GE2I9=0D%vlY`H>%_`5%g=-i^r)IsnB7hm%3xMgqI>5+Djf0M?URvE536bJ zVQO05+YL7SC?U8u6v0|_x3>tvP>G;{5DdElf(gT*B~*T5X!)9aapt7;^EiI`PzvsC!xXn02wxER8|v=X{{n$iHgJr}DQVklDd0Yv+G6fUT|izxW7 z1&6E3Fj)EHgTwd0&euSVDgQs>aX#n|>t(_J%MdYK_rpGG;vIgPS`dX#L2spC!{Pk3 zx7<&ZOxj)Sr?)qe2(^W;`E~xN6SPN*HiLP(`|WWT{Kn9@7`We7-jCZY-oNXBc0EQT zbXQ6Uf0VHwpa>LB8^hR%SM~6WJK^{SO+!3?3KboF3E`ept90HSk$?^cUd4=6w6-gB zN8Xkb6m7~@7*`adaQkIo2B8>>Qgn{&H7$4l<<@2}J!1pk6c8Gj<+h zXuE7hJ8}!0yKM-kDq1q^)J+tQ3kf%|_Isaa49^6!b^{%j?%)w5-tOjj%YS0JVg3og z)WsAVI*AUCZ6|kPszepEHbISSW$EzxAut(WDE%R% zgf!+4rg$4$c6D@jO#(5{Q`xdjPr>jLKrMTvFJmhWwihmjTZqB-tCtB$@dl%VT8Gsz z_Ab~PzTcz-T!gW;07jEMqRB(}j{pPq>N1QbuVULsooZl%S$elfC!3N$LAU*kee)V9 z7>+smTa`GwA%J|0kKT(J*Fk?u!OhT80+3>RV23WT@aE*NtE+J<%s%k`5HL<0j~P5P zJ8UC$Jal8m%VF*^Ao?)xdWyq>p7BQpVZ5Mh*HRR^3*(QcVqAsc2>Sum2Lt-y1>fI4 z81pQ`Qc)Fzg{gZn5#nqAgnD0DO+hLIB+-9bGE{PU!&;bqOS7VB!*q zlxs1iabk(KjDQP3_eK_DWdz)0Z0~7t4oDWPOt?b{4ukohBUXJG998cG7a$J(Ge*>H znT%~!e`Mm$RXoG{7c25@CJtsV0n;&{wf=!D?q|e|;rkftfmYx&5K)ANe+c;pa$zW; zx*Di>UUSfcyjdTD-m3!GE<$Fl{onM=oxNRPvkf3L48(b$Up*BL>~!yK9H6ex6LGW& z6lj;+d6)?8EqxQ|lfu7=(c4RZyG}v#fMXDNr+SGc%kf9MH5bx>@r~%@Rh$d687l(n zh&;tPcNaz@8lXV`i$C&}zZ}Wz)=q;n=AKTrLAsKmH z{6ush5MsMUpDKWK+ZdC~1UI57%RN4kjv!t^xcuz&~Li*L82u4Vh0~#LDDf*w&FZ zWN!Tv2H_)sa6!gaz6?9#usX3rXtJ_W)(Clr)#)ZHMYp9LFnIKm0BL z`q!ioQ^7G7k<*5;yFiX(pKO3CZ(C6NoR1)dPIa7^zxBV9u@AsIr&CcWF5)8W675kq zm!O#QGqCwRN^YlUv?Df|ig^@mG%DT$HhGzPQ?uu-*v+H$GBv5TnM=u6tmK&bn6|1FoXTi!!dj-u^@X{8G%6#g1Y zLpM>{;CFZp@@ERoi=)*({V`1v(HbxR`v8itH0PM9vi&G2CRsD?=tQ2os!~1k} zs7PIk-jiBncWgoOHiWSgZK*@}7=_98lD4AHImC-=c)wJA&p93?<~rn-a5jbgC+S1t zv;3zMD*>zZge()K-CGdYLsA(Nf1FUrJ*cs)GtjCd#3WX`eyt#Bp93V z2gRv%ymy$L-++Cn1d+L(w;wKtHQK&UU0{;oGz!nq!@Vh7W`*jSAw8NBq!=uf!}Gfs zs%VG0o5xO5f_hQ6o+rU#%iXf9WVDHHZFF12)YqGo?S@_}%Vs%#M%nq|{CXVmu5n@u z{cZmd?>5o%A)e=zm;E-PV*bE&7zi;c#i!%GLbD6$#ByZmtB4wdS@E($OtoHx{gBY~K3JW- zu3Lj!urO26&B#opn~=4y6@LcfPsGEqSSUnd!^2MY3ZJ?UW#Y_Buv-Fqq~2&j8M<*3 z+ZtH8i;UmxBuUw~wtx@ncr)lMT8B$F$QIU?Snx#z^pF4JCg4S)`A#ZzE09BN^-uOg zJJx*Em&_0GsO^Y92{D)-<}hUdqMHM#<1HL?5|$1iI?`luBb1Q2yJ%hD3zrNaP3+PT$Y2UJhM!!TYdfL#Rags0hAwEYMHrB=s#2%n^IU6O?k z`p9H7qq`UP14tB;>v``siR4>heAfNR@xj+uEV#ZBf6^v))$_&X4!2~`e$KTUa40+k zn6xhx#j+oma>X4R_;rc15NG}??<@1I(t&i`@DlHD;H{+FLDHJ7e&*0@Ng5(&CvpO_ zwI|K>f3XumvlPck!mxt3k>X%28pV-_a6la7QXLS-5CDcaauE(lV-^91G*%H{NMj=b zt^DF>Aiyt?w7`+&EKw z;-;X8*c0~5&!HH`7yG4^fk_4k3@3&F7Ly<4y&}_qF(B*SV#A|+fH!_oFwa-<^!V|? zJRfGU#)P0tz>OA`Kt7ivfX$2o35!5%F%j!lk`vOpV%Pydl*V@lV0^KKO23#8QY+!- z;SI%Jq&Pw&N|YEN2vv;LJnKq|VN?V{NH0Q}iZYU^YlzMuruL&!f0(ODkMpkH#L2-N zTpk`u+mW#3PvGF%2-ZdONyMvPFAU;P0?Zql>YFt&v@GO!9&DZovKY7j4`Z^i|H^2wM9ishSm^T;)5 zt<$A6i`t8_+r*B|{IIv4rd9W$30N-cABJ_M?$ltc5q`pU`d$8c+aN0jnTfOYd0QJg zsKS0Zc%vpFLEVmdfo|-$oR1w#)6&=`F;#zcb~!O*_ZTQ3D0haHON2ov5xnz$J4`a`hi<2;M3txNfx8F z@(z&)QAuD>JT3>tUi>g<+DwITTHwF;LAMXk4E;0Y^6YMGbRELh<^U_rk1lQH@!pjs zSm|O`k2s}Cf#_j4{sjR@w30uG@c|yt)T8xl$l=&gnLCITIw!=wK-K>5N5?Xd zvMUW?KgmZZd%OHx=0eF80$*5SrDD>qfeJx_b zj}9VEzEhL10}CoBbvQPtgV(2Pz{42_F|cFRh`eo}W9nf1v9*J=I=qGUr#^{SAXM^` z=hdrWMVleY0VK${7|SGe8LgF8KF-*kZ(=8;0o!izo#7o!z*^d;o;;GVIRs6^x=@>n z_3KeRAIi#wc+3Vpduyz{5l0I!`G+1r@X8kuESJB4U}^dSf(0)iL}YBlel|>z zcr6z6z-FmLlj)rnRldja1iEd@H}D=3y~nciNhaTfur5@zZav_D^EwhvBfs2Ie#VP6 z)O!^55tel<=q(BIGNiX8n$4Fymi|3?R%D?q7;VcPsQECu_#+@At(PB|4z$t-=*4W9 zEKm@+9V~ff2xG4A0Ia2z;{x!N|5(Xb1B4YB=anpxo6Z@8_{c*z4}b)sju1KW(U$^* zcmkA&9_O%K8xACo5Rxf(Vb23nvqeypeBfmva>?@%JOctdO1X^JVr&br1sV+C8~}*D z;F|s!M#^|f#&*zReFWSwKHDKn^N@Et5JCL75m=fa*?6sPNo+?f*3H34j#`{RsJ}Q; zPWAIM7_9q)l@YDOKyyE8Y&P=9s1At2*AM?-mgsL6{Uy7L7a7|JJ)kWFwE`wi?G75c zZbEmXmA(SMf^HO};Q9?b7N{iKgaqT(-Y-AH%@5@r3Fio{{;AQloj@v!-;vnx47|F= zpjIVY(xBs@$0-aPr59jDF!ngEjN+fvp$yapb157UU~nYv6d7a@9M8{}NB#Dv`nprMTjdzR0S zJcdXu$!ePdnoqY84?fEedLP<@A)8dH8&yu*l@YBle0+}Xpl(786oo~Udaz?fWIah@ z(FcEdRg$_-vtBiND&M6kA%K2V(#-Lsj*r7d7sdUW5efP=#jC5OtaAGZb}Ow0h?jjBeT1bpSa=nD0al-gWT>3_a84@bdBQsJF+ z6l?5t6z+-FW}b$@qHwwrlIM5kXZ+%AI3TAQZ-)yXs;~F@tEWhAyP2^Ps%Ac1`nqCp zr0^PrPrn`7LgD3bv}nu&e@*Oz(=8bFQ$?l!`G}>MJW<4FaKdN-J*4m{B{-gGHN`l( zpoB;~LEKc0yt{E3&{w&Of2Oqj&vtkSQTIt#EE_-O@IN~Q1AUjDXXTD}sG+4^4EhnGuDWvv-25@+}G^sMG&iVyH>S}aA)=Fx*AUd70<4Plf1 zWCNeQ1{>Ai>jnMrHqpr4IF@FQK zH$L;9aaRC+G9Bfp((}MEA_&J`bkSKqNF0-1dknXkpoD~j=G(?+UT3wo0*+aJ91eu* zgK)HgaQc1+{F4-uQO4kib2Z#DFEa5#1HZ<5kPws%jpz&nrws(waexxrBPD-m#7zk2 zAZ#@8S~p|Ae1B!shrq8xFoi^JTL|3v%uiUYbSwZ8(08!(-+W{0CNl^X$ z`{DKW!yXx-}wZ!>UOzXNp4U){eDK4+w$w(na( z)#FJ{o~Cl@(FK+aa@z3%@6&nBZ1S1malh_r$~gEJ5g zFEA;gDZepWFam5DphE4dME%*!uzOL#Nc2UjAZ#F(aCo#Z=6xLVtvWKT-z7d8H3zQxblS?q4PQWjm}H zG*#;ZmWL1*lHv`iXk9*LfN%K=T|MRM=t5F{i|{Zh52fz66hfI0;zA_;`$+KqnrdAY z9r{tgYgQXQN>^HP?9_%V*OitWJAW<33+4XZh5mW0$T>COqW&Yc0AW zaUaHFz6oO(r5Ak%byyFH#iO~htnV>y4Ac#+oX+8zcW9ZP0G4p0%l-DEt zdG;Y@WJpY;=kgd=lbCIYDer`p0eyO9EikbUMO1+p4HA^!e(~=^{2JfH*ZAp{zKN&! zgJH^pzHh(h%@l8z5~IYLa}vuVd1PPPHKu_{*!fFgU7{&^f^DK{Jf?fGSMy492jOJh(b9oe%ZFyNF$Z2(X6y<9za)0Cr zyiJxOrvAv=iTWS8)!Xj!oIP#e8C;Ssr_vvvep!A=t}KxeXaXAG=X(bdS0f)YgQO?< z*wIUzZ4^u=qt+mqVs&D~8jO(jb-s)eo$l;Fjk1C+F-qIWI1`VIPDqa2aV;9wwUJDq zF_G}P`L_JTkE+7;3pa_Me#UjOc|UWz_{-1uS%YOibEn8W&pV3uf96Rd_dMV4Ei9|f z@>DU*b;|PaGagk0U}afS?e^qVS9l1-B0VoZzoyI|t|=+6b~R?kq`Wk5c14XuV@B4} z(u#aP%KVD5vWjv7!tjiayQ-#C=CV*_l@v{n9+XuSP!Q9eKvA@#(o>P|uBsBRT;Q$A z>9nfSo$qqbs&<#_xy6MGJX0}=^o!gZ3ks{ImsGlHi%Uw~60g{Ok*B8Qm&saCoZ3SP zF+|5qnZ@_@MZP_pZ@tJxokd&f>tfXwslMGdZMMnRFGicL`gX=??mLTq(tYwG={WPbT zHBd_t(POnp-?ILA!B|f|@~UR?J$0?NI#oP2M2q&VpQ^RyVo`zC#y6@^+mS3bd$hK` z(uLZ&Xy1zaHE)>kEzv4{10T}f;J$J7+Fr$X{a>_x4)Jo97AyAqv@YV}Qf-{5Y^zy( zPwdh%Ov1ch`%XOW(K?8y8niH9%>iw>CLVkfXeYj;t&I?ibF>s+*-`Clv+vROwGE0T zKd&+mzuz%i9CezK#qtle1ET8xw0A`9N7^XgcOPj36<@zkw2#cfHO1uct@=V6m(l6i z4kgO}a}Sj@Q}Jsu?y6!ZnQHpTT<4Jbc<{qP?35%GjCSCwB3Wi$Ex^Ks>$1C5agyO4Pf-ZrED|l zX$3|Q+3M%HIFo8x+QEyQH~EJgmIi)9CnV_&2um%L5MFrp4LG9}DtRV<0d|BkgUCEY zkdc@9HXzbnPQN20_0k2Mlox3rXZ8=I&CDw;DaflXK@%B0NC_gPlfBF3DRNaniHQ2=5k5nryBrQab9^rDa73a1^`O$n%RZ@0upshT_Ix} z+LKvH7RUdk^=bbRYN^7{F1gusaF{qCA6BY@YBU7NO{yebqW2pzcY(A4->M?IkLd=H z`<*tZ4Z~CV!CYgPKyg7u&D2u23!^T&=4>vWZ*O`@b}{@c6_RzTvgfgjX8fd@se_q* z)27rd7IZK*cV5Dd3iV%I290)ai%MvYdEW!UI)dKw*v+UM6APH(s*?OFMh=mq@@mN{ zwF4vaD?DySzg(kRBr*^9u4R!JjI5T3425 zuXNQ^R_a~SRaH_{mWRs&q9Mc7F=-KmOqx%-tvb$bedaNoaEkH^T2DK@L>uUdQsEYa ziOK>~dcricLK0Wq*e&NVtH{nUrHLaawPu6qjaAtXFtP=8gxx?Xjh=>r*Gs0~Ks*n0 ziueF2pkz4rUCK1IRuU5fMuKfei=`gX^(}EaT(gg(_fIPd3#(-B9KalC73`2S+(Fie z-qEe}xLpM_!7A77Go5M;F0GiFSIX$8jLO~af+`ov7I`Xar7Ed)s_)HCrpZdL2uOvD ziX0~12QHmq-62zYh(ots^pLr(VqEysEP)De0a7uOLSHE_T${dcGGrn=W-! zn@{n*IN5Z>s?M7s9xOBsQuoaew-lLLi}Qu1cs0_a13imO393s1ebj9p-|`|8eg>sQ TjkvGa)JLtT@f|KUC5HV!=O44@ diff --git a/wasm_for_tests/tx_no_op.wasm b/wasm_for_tests/tx_no_op.wasm index 105a68cd1b7b12afd3382e087fea275772fba9a7..3e0b1ef9970ebb8846c675a59676c86aed95bd39 100755 GIT binary patch delta 253 zcmca~obl3e#tF(w_4N!1Om%e(39R)Xz*wKaSkG9;%*(^d!pP3Z#K6op(KVfom5rT~ zi+kcGZKiskiT7=}A2c3daNGbS1UM$EGp3{PXEHjAp8!iKG4nDgFexxOa%3s6DliFf zPX5ejzy^_@V*+3WY*830} z74b4Ka8H)us@Qyk-G*uMIyP5`ogjw*-SCgC5{2Ky9>&SSpuot@!r;g-c{10n$*tT% Oll{3RHc#iajsO4|rb*TS delta 248 zcmca~obl3e#tF(wb#)90O!f5)35@lObqTEXAi!A9%*(^V%E-pZ#LmDx(KVfolZ%^` zonzuAZ6*efiT7=}H#8n#a6ADd1lT64Gp3{PXEHjAKLAT9G4nDgFexxOa%3s6DliGK zPX5ejzy^_*{m##ybK-+jE;=n zMMb;}4BV6BINc#uf$Rrby@9h5g>S+YwpoSUhKYlPL4lE*g~4(18n%kbQ@CzU4&~O` JJe}J*0sxSCMNt3% diff --git a/wasm_for_tests/tx_proposal_code.wasm b/wasm_for_tests/tx_proposal_code.wasm index e32dbb1fb379c05f584720982efed5ed22bdd556..2c7db93b860769324b3dad135cfb35f9a26c6275 100755 GIT binary patch delta 57666 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

ohj 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 delta 24523 zcmch92Ygh;_W#V>yDfFINj7CeSVBuDASLuH(mPUBGzlS)gcg%fRB(f$Afl8J7X%HU zXlPQR1}qR1kfwqKc~9&OeWF;O&+`0#&%L{w8a9Bbu*b7oVx!bP~EaKD{!57hk4PC0)!c8)rC-e1k5Fg&9+to3-ox6-2<-BI}7-fVz z(UsJ-MMhS3yPWoYW1PeKM-T7bvv;1S&wzo01`io}-Srd4O}IAX?cce4g&*Y0>Q)E{ zULn^g?fEi!TqzN3pzNsTWFOtpz}hj^oiVrKv6p!o0H-n5MT4znp1nqRmt3Xx5NkHe zQ|crC)0P`eTFduL>;LC1qvdnvG?M$7xg9AUZ~0DCZ<68G!T4Gg*-74KZ6ogAC~t^N zl&7p&(R;TwFi#h^@T}owo;8~QoRoH3v%eyErcD#P);8y_v>q8kt?O)zCIXE$n=+ZOpQ z?iD0mpJP3~88iT{+`9W?pMa$w?ms}PV!57u7i^}DEgM7m=l?3*TzR{?) z>5^E}*coEY(z zgxM{YJ=wrg1?w!rM4p?uIU+Tz%yXy?e9qy+K3>`qXS6rKEtF6XHFAqo5!P9-Fdm9F zgy}4-Zh@gIsyr*? z*6j3ft49Sj>f6?*yyuRPQKOymVq3HPx$R5h`>nD?np@nzMZVfGN%FL0alQ#AKdsj_ z^;?4(ijigqj4(3TOGY@p%=4-dc9(hHGQzE))>ip-T7q|}5e>Ghj4;?fXoSIblMx2n ztwtDZcLw1wTc{_?!|mo2)-T-dW}bJpH0{ABfW7{t%gFc_j0BMNgAs06y08y$6LTFj5o-dJgQW@)v_*Oh>ndjnW*`<9O-b>!tzK8f`vs~Z) zd-__{;Xr)Pe&LMrD(}X|P*UwIMgn?nlPtZey};M{tFEWS^{=b;h`pP%L{+;9iRz+Q z{D?f*DTO~NKkn4Zb^oKt;0_il^4zSSn&;syOq8yf2Tfac&TWHyY#F5HfirrHvRzZz z-NHmFr?yZ--KyJER=23Ln|qIAg7sjSc_@FrK?|9mhMy za_ZR3a0D|s-%KA?xnZo+YIAvnM{(Qa{;?ytO-77sooEepo5+1}_=0(s+(`yuZj;+8 zhm9L-&0JnB5jnwJ6JE%yYmyltRGlSt!aRj?aJa<{!^V&|O>USzhQ1-du1(!7%pPrCMr4*z`v5=gZx*24yR3nM))d-VBmpO% zo9K&G;1@ov1!9g`t;~Ot@aOx;4H@7iriG#o-dYf6pn^PQzvy+VxFTV4Z7e-DuX$(q=;fn zhLt8-Je+$GEpmCOGpdK?P8xM&c#JvnliXWejm2P8zkd8>>77!DarD8IN94J(gg>F* zJ+%h3qe}Y8kH+^E=Qqk((_)qWaQa=P&Hsd)!r+WNSy}?7oys;}mT7ajeZWAjagz^} z^u@4yyQG!uTi#4Ansy>4jP-f&!$UtGd-miSOK6}!BWAYMqD<3S?(6@kTt6e70!@B8 zqpvu(J*Zj#nK>HanYv{&BY2Q%`+xy{D*1a!gq&I4kDroHmS-isMwUdffh9c=_Dpz0 zcv8EuNH_Bwl)sg?NPZs6F@O5erjVxq2dDZSvd2x$`C&Qnrp^3hov8SP^XKIG%ABZ! zI$Nzpq&?GfSf*7yz?aL0s$iWa(t*0wlFz;!hFdfn3+>@`7iMqce6w6T*JWM}SsuH5 zc&-y`=+kpu{0DgfpP$mF`HelObZC_?O3yqOua`H>bBQPR$mClR%@0$p-c-unsJ&ah zjL%l9^U>XC5Jl|FPg7CnTq{@J?7{%vfmU~EgyNT7 za^|gx{5#2Saq%x?vs+x^Jq;z^&}*KTHvzYM7qJzawJ)>W6=Zpj*6=s^8;HKysS$m+ zQ+_o!k-sWO-ReS$I*89j^l7f&iDuziAGd4h$K^Yyd{F-D)*{!M=TJ!>lu{ye$5`VPT{%EX0Z>FcJ2Q;_e@E6+sC*ULn(%EDcd7V*L6)G+6&pmBtQs-u8?+!5Y|(bs4BuY@CTWc-RTP)c zYNv1UWX&ino!PEvRz-2U%8H_PRX3LxSGFshJh`H{vNF4(G7mgD)~V9PodJ8Tim@{R zcBPYw*rPGB)BXFSCdb;D0#tEj6)UH*%v;^uG#{lzndjRnm{We^>yUN1E>=1Jq>FOo zn#G9qvuk?u@pbWQrwKW2UAp(nH+g9G zS=N$^{Fj4BmKhMy=M@LDy{X9Thgye^oD26-508f|x5|QtlDtP@CCwzSgp3XFz_)mn zv=!ql_~;8mDf=;I9bt&>4^3Q&fx}x7@HFgv`)J0hL1$WH=E|%*#vURd1G|G8Fgb4{ zvR75|!$H_k#D;>|iCGw5P{Z8Hi!DUu?Q}mF0^UY|iD76iRB|x6OB5nLLu2MMRt{d4 z9kc<-L@272VOS=gIS6DeR5Ak8jf2EMqNMD`(Ei~u#;mDUguqe73tU|f#^yW@T#BaV zr!ct4Ctb&bySg)uu~TRfaTg$e#Ynh7{End~qCgi~y9gtGFJqBYX%F?xZ18*nP}gwM z?mAdQv`15wq1Pe18MCKxaSgCxH-0h3-qLULiOO z-3j8q3qb45n!dV27r?8SlW@_edV#T;iZP5mE_-cA@ZO0^<#ay`#cjy`w_-ba+Rey$ zTL4_-p|?WTQ7TV|^TVtg7;Cl_&=!TObyFDoma1;&N;m@jQL1pj3p&GHV0P^$ z#_l2DJ+7X1p>ug#L7t3HFu?bKFsZ1ACR%%?!7&}5T;<=?* zhHYHWE991q&Ahi61}I<2ZOrnq5z~x^TW$xlz{S7d8I~#oBIHgUVJS8u7V>cWfxdwT zh@wZh!@k{s2%XGLQB!n;?6=9`Wg;331VWi92iGxnEg*FvIkFwOdto|bls46)Gz{?1 zlnc3j)G0zGf?jdLmFG~W{s1F(%VX?m_+Nck;YK@%n(sC+cG61BRO|0xYUc=R^_UtN zc>z!=7&xyHHf9Zj74nGP2{p@_2w(zLtrF=rq6=Kkhs==}#%)P;|D_&R9kvr_5V#zl znd5CQ7#cagG>2O{Ha77x$3M(*wzrIJv{YGVY(ylF84y~kWKC213DxlzQhNmD_HuMj z&+T&e`c~fmv&QX|P6to<_wV=;y}jQKTHUTD*RGt<4dyH7d%B zp&hu?V^#`rEz$jf&HTlFHYcsNRYo>1lW1zDO$>8T6rvQUqZk{HNDBE2_Aur{P*;~0oEdj}@e>$U($vv9?`kg*@B<{wrsSLR*C z*i?cZMAaEAE*BBqQz}=ww`J^3s`(gmsIi8LvE-#6&}H=v7p58H#MsfWJp6^o6{(1` zp!JZIM9nCSo>KHUyii#Bkg5FFl<-L+-V$jDcd?VVvZNXik-frg=4%ZIdwLY~32?xq zGv5~ZF0wg-f1g&8Y%UH#M1p)`OPkENj%B(aOB^^L3FwzLacxJ``-sQuu`i@BR=aAeGbQ#_ zlo}wx@@;I$hU5S}!P&9VZpO~a+^renBA3Nmv%FUH(xPyuts80rmnD*CTqZ3zZVW18 zYpEMeYh_8~NEljp!&dqgNIf4znYfJ`gAD2qRsKc~!Y2T075&pbB=Li3{d%oyvPA4pG7%@?w@dTa26^0BH%EjI;RDE+^|>lm?Y;% zDANr=%t&YKlTfc3<=Dl|@o_Jj?2^CLRgQTdIFhDup}=b70wBO859I)t0+buMRBKci zD%$1rwM0slhn~F2?7YyLWORQj-5mQH6geuVJ=Mh#n+$@B#GxoLHWoU?%O{_@*P9Dh z2m3q$K%mcbHx=mf5|oX7rWHd_+fx7n+G3#;)HVm@Mr{iKMUM$+%gjfh7%tiH^o`yX zs0-?_3qU}Jqg3|W{e6^;I{ZY90(MWeHtH||<$%SOpxmg#UX3cK1G6lI3!uY8_2W{{ zqmG|8#ag|oK-moO=>`yt&pheco8^t)i;ade6vW^1xSI+XTS(+*c$n+GNZ#McgXqJu{|@ zn|gRmO^hsQ#aJ$Yo=)&?m6__moQ=M5N%6R14cX&sJoIm)*7CP(CMH&S-p%4a)?VGd+(QHFG?uEpL0 z&Bd+f!`z?1(&}T-U&DMrL`l#{9f#@hH6l9CgNng0rkeAM7%P*}`v;g~Cqy!~K^E-q z?{G$8rMDMSTi=o5EC%|N+`In>eLay~# zuC&BT_&AlLNh8{GOOUdme|B(khg4DX0Tfc_lrWZE3%}QL^)8t4?Bgiw`*PCrQgrh6 z^-!Jak07G;OKbeE0%|974^9}Ie5=3NDfYk!=qRO=ua4|wIKU5T9Md0vTZBA!) z21e$p!%41OV3G#=C&`fj0-{SIwMlZs;le-8#VxVWeO?|pd}q?$XfV9T=FlXb0uYpV zJ{l5pWz7*+bkM?@yRyeq3BQW843>#_QMe3LCiwGuHL6zYW7MDkOk<)~N$y?O*cO{5qYn zaRi_S)^FU041<}-1tGK#Nr1NrbOk^!%&4?^8A6+vz53`vHFn>Q(S=eCT>mh}^#NoG zWf<P$=Exa^{UY6t97#~NwhI3XwfF(EckdYM7CHT zZQj)y_i;z$ugB88wN{`fk)-Nbc%&d0Yw^97UB837#9XRKi^rw|$gd_2g0= z)vi4t@oWe__8>1Q{c^Ej1JvOlky4g71)^RXFo)2aliN{$eMp5iBH%c>I^2NPU_$ek zIE|bp&f!C?9S)Vi!G!z4*~nD~!P->F^5{%9T5vdvn61tjxjZ1r_}rdwj(vw zG>xbL^3U%Oja`~ld)q^TOP_*VzXK7*{2wRBWusudN z%@5u~_hNF{g`59lz-b%SFLu*6tgYCvR;~n5E_=x*yjg; zS%JM3g(l&I20)9Dlwy$_-iHfN#g%6#Gq#n~MvdRf6<6aSR~=AMTs4+!G3CswgM6yc z0li!`jj`YT4HDt&$MiB2w7d*uQ~k1&Nt543`pp!=dFaLK{KB=WH^=(*!XacPrc*Ea zb@ih@q#YtnKQB|ZAq}>L&dDEQL-PCi*mm3rU^II3MSJW460j(oD{byV%?<#Qac6Q6 zdS65|B}W3-QGk^i?45iO8s)df0~l&^9~d+NURsGUK@5nsRWg?LB;v0LqyZ=L85=(j}y?#jo=Y*KD7}j_Ven~!wWt#F;DPs!) zUHLW-d=TndCk;KucB%n_p?a7R59d=Eqe9y@y!E~fjNOIQ(Y8094OQNO)QCLPHixTw zCP5^kq-}JQvi|`kg^fBAZD+rTVSY^@8pf=`N|`VTZ~znew7D%Vc@Fr_CdR&h4l8!; zIbc^0mIF@!!gIjThcT{89s@9tyl=uj%u0gh!NtZ$gq5(RnH;BE^dQe%XE!Y)HFUie5w-C_u)>t@zLRP*^KpnmTsQ4AfgQb?dD4T zsy}1bKZ^N!7#9yIz?MKtSSw_JX9zoz$bt31X4-_M&K-~Ozx*H;m04W9Z3rao*2<{& z!s0qnI?UV`7Hi*D(@&cL>8YmjVeNC z8?NrQW3^AsDv+j2fQK<+Z@CnYapbQ+Fpwp+%>1twc>E37PK4Pn0~q^Cpq$3l%i#4S zrS8C((kPdrQ{9ksmG5W4?X_q-4fZ%cjIrGW3?X~bGFoYQ9pqm}xOSKom{8P_jl#q6 z_~hp{j6FcWT&{WzfZloK>uK1Ih9a&Agm~4B$XjrQYXC+co=39Zpr`9~uKWb^zOe`8 zeaL(%>oJ+28?G_fcu?NLDk=gNa|zA`6+)lAQ}>b!X@mNy4Y(0x0dF^GiBn1}3B<~x z&WvqDgQS!|&Qy=%)>f~C3cTyqSnURO1(p3x#zgfmH&R1ClPPr01_UaqV=b6>PG5^4+t(&dfNZdVH&k{@=t<^dJFVO>G>0Z|UR zVReF^b0V3~-;{3G&pTwz?$-J(x;+}<>fbj`m0!P=YmQYqU~@=jolW&>>xBYPxE7kU zay6B2(#vb8JOk#|&#ml&55>z6r;MA71vy5OJ-To>7gPzW%l)F)WM=&CyelkZ+1pXqG|7FG!>L|LYln=(u z-)5l3peH$4b65gEtBs(3)L--S62>+^q608ha^ueMfAE}yl(;UBoT~%Ag))O#@wnwK!9p%Ylh(ju9))5)S z2kNv6ZOOJ0DS z&!K}6fCAz-8pC#C#rt590e2>(A&Va$U~}>l9R#0jh!6gdbpsHksH+B7iby$bE=5D6IjP70cQ{Kkd?z z5d2@$Z2@De;qQd;7DYc^rR|a_?o!6?coEN+Qqe;rfWH0lC{S9pnFo*hq|s3yZ8{8| z1xmwNAZ0-zW8Vp^9yfz!D7x(Z4uaF}hWhJraW5DGfYzQ(fWXnBG&)+;1PGinBHmEy z5c(C@`5FvuSU!CsCCwjzQp6Ov6Fy6C$9dfBW%@*$-kj>aG?B!u!;GjA+yR2#$cr=+ zr&kjJP3iHSkU2HfZeWtZDXb1s&ZCg=6Go{TR;@$ncccCkIw1}rAk{NfxHW}RS-rUU zrxmT@Vf=4k(X1i#kZKxE)^sOmJn>l4g0Uw+l+_i7od#mrgg#_-;-Vi6gmBfbz{Iin zIuJ|CJ7*luJHWF#611b*L{_(=XF^}U)6L<0yFEgP(Bo}Znxu}(?(Zfm4b@E#DUsHpDAPjJRHGz&ou8mp9w2*!$@&_~6HpEb53yOF0Z>lB)gdgjZP7&}lo;ptgaGrqaHfLXySuDym3D$6w<53Q$ z(-NR?UL5YW8BA-0;aFbNX}e&OkZ+qwL56@Cp5lb880J z%!`#LKkeBp@(^ULK!xcMrJF`us{^Sx7bKOBQ@qnb5cd&M+4Jy$i5BMKb{c{&4DzFB zeN2P083?sYGiU{(Q)@BU*&YoWi0crWX4|t=UZOvGWcDYiA+*vp%CD0%A0iDh~(gQ2|I0WPeE_-Z?#>(h% zCTCiz0pa*CCWG#S0+-`+8-s-cAUJ$D5U{-K(=HLYU>NlENO|JZ-rh3?O4AA@)m~?` z_E4)WstK<+dMuT-7BnT`MGdlEho;q}X)$+YJ&b;=0YGZn_ZT|mn8JHRHPN%2+gOyh z3HPZywyC&Mj`b*O8WJm&>^}<$Lx(9M>`g3+Hkw6uk)B42A|5qe|B^|sKHtHc_Oa1` zOGBEfcFN*v>I#qzEM8=&oB(?cV+y%M($TpW2)|qd0crHGk0gq*{1n4*Ij;1Tj(|g$f^L+qlm=;H(S!m599#K?NnQK;N`UEWiS& zQMG^!We6VAU=fzMN?7Quf8a`7Biz>R;9CvLge2zk@W@m^Yvj8ZUiN-V%dRiMJtsh} z^|Tn%#N?N-JA9Xab$1gOs~0EftGib8c!Y_*5F!SmLQD{ts-7Nz%~#xrrPO0jDYOOF z7OM%`sO>O;sPVui9TbevKvYG?+x>z5XV5jX1@ zH)dbP5-olBlUU3%F&V23`6T8!S4OtpDXn$Pf!IthX8QNx`Iu*8Gy9ZdTr7B;d@m6SjikLy;BIt1u<)FrP1nGaNv23-&j@K}>_tMTU({Bk>Rl8T! zo>qQ+zy7q+roCRHy~WdbfMHrc#%8>L&6LlvuYuq_D*&g_G$7{SBeV-rT@_k1e*&)r2s&7; zh4iME!2>!LXKIJn&dhlAWm4Rl}USLNZ$6n%d5RgLbt$*%tWtXm9Ul;aQ`qr>xCQ}Wg zmW*Ei2^_rlK6VTz4#8mWOT<>c#KRDx1)umQXz{0CfvieMo}+1`bEM-O0gmgAGxpZy z8RjC}8yI@Eat&eq9BJ}bSu1D|;K+|+mjHeVsOWY>yG9jhe_45S4kiIe6-ivsxAMyf z#y+NQ;K^_$0yh;&Uv%cmaEVZ$aw;bpZ;z?#7(DH;YqoC?F^b#C4;8~Q7CLx0WSCU-KnlH~l~QnQnM*d)Muf~RtHvZ<`| zsOyO_hC1cMTD;@)g1q=|r}+@_o9l%9>bD-rDuEhMZO4J=2bS^S7`RWpT!3#TbnhqD6Yga=EE8bdIjEvpe7a>egTUK1{PmD%-B<_TuVY+-a_z ziy7yHEcm@g?kS=!9TL_K)ISBNZQ}ru4b4SE!Umx{5amGMKfeY~{tjt;`rd{_cwAom zJu$bOsKKWQ)N2CNwxxg?s0Mr%p!|NI2EnptFk??1*7!`m1^OIYeIrlIrPD>k#|P?E zlr^cg{eVVN4fyOvxjawwscl$wrx*%Q8QoOd%{M7AG$NW-^WjsY}KceeFJFrJ{-b2g@Y*ORg3 zhjj*yeqeCSho>NM=KC?p{#|F_Jza<$S|kU!+S(IIE^wI-A=CPoqrs;sYT%t}1y4!T zQS%w=4Ju_aeyOoZO%veqWT^25IWX}!1gh;~@h}T|Se8RVEi6M4EWNd-RN68nc*sv} zH4&>LOkpYx`Kd=z^z(WuPUZaTy0Q*= zbu$=Ln20&WbqHCIYWg5kwIzV;qi)g<1#30W+{m;;!CK9YxSQD!XcLWjY2I#{sdd}7{nSF7@ z#=nNvF+Y#Tqa2+?+fNXz+XNiXX@a@u514bns8~*E*AY*+|JPA`1Fd{;{ov+&9XESaDZvVKTdBG836BgFs__CN*d@ zX;u)_$g>0eH8=wMcN+FO*fC%kO|6_u6b{v}?Sl$vKI;(0TSr8jln8sAE8@vM=*?A) z-SBs!WBqTLyfj>#Tk9+7#;fW-?Z$hH`t+V$nCd(C;nS7+C;IWuy#CAn{57-Bc?`C^ z(aXoyj~~we9_Kq-%$>fTck|f#=2Q7J)%R90kMk{kinppaPv?tseB0LXHuc}H=e=U< zf7ruqCZF>mewFX7`*}ot#a=$oUJr&dLwucf@MvGdIqs-`_jUeDNPU<0_%c)dflv51 zy#BS%_&IC6{abE#H9xbMNBYM|`Rqyf5v1bEaag|zZTt)l^YsoF9fr`m<%QFy&nhac zDrWS929t{`il;Dou3uPGGmbzjc-hZ$P!gH z-yIR6ZTut%sniL-g??2Pg{4)M>_^r2LWJnp^CjpwuDFN|2*H~sr8CN>x9qrp(GL>M zB`xyNko^v+(~D>3&#s!1%W_m4qAQ%6KL>4!eb+{c>OL-Tom){@UJhOID`!t)HE2F* z*1Y1$jD7(FKLgccc5W}W1=uMSvt}@Qsefkiys92;z`Ym4YyI^Pe~%%`D|~~ZM5cn@ zF7hq^oVWC?ixQy;^gy_zaOULc#rc{o^NTAgW>v70Hs66Lk>#FBCMhh>pE7IuWOe{u zEuURk!d^flO~Vjhq(d~br2(EYql(?;>*NqwPP)tW^TaPy6<5qDX1}P+xBGM6-nYde z?7mkW!l~f51bk6KL@PsK*YGcRTMPZf2mIkX9VJ`|od5*)%_wK|D=qo?6;tzP!HAJM`#Y5e!F;_x<;m^pVBg8bl|>b$diWISim|Vpl24t z-R;>0KLgegdQ~EN@gpg|)1UEHQFMB;sHC`PTJhxk8MCLeuD*>4Vo7uQ2^K@2*w1LE zbIw`7!b5z8iNb9^1!U<=3eU>YqDofaTbU@Ldn^wIU{Oh71-rLVr>hpQJ_yyy^5UX= z#8C0f$!suK;AiH`(YZ?Yoz3@tqR5O}03;2aslldKbzQ)g`3AW}TEY{sq~A0P*hiq$ zq~OOW>Yw<5r|<#vgk{RCiW!AfjJ7GYxMO`_3j8!#DX{2g!1IiL(`D9_DV4=lnnUS# zdw+X)GLq%6{kM`ZtcVVb-igOd{O~dfB8~+c`v>Yq| diff --git a/wasm_for_tests/tx_write_storage_key.wasm b/wasm_for_tests/tx_write_storage_key.wasm index e9cfb959e91e1d2716a3364a8e916362367ce083..5b3178be754253bc3a93d39e4359beed87b47196 100755 GIT binary patch delta 48353 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 delta 44131 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 MY1jYQla1m30@!#y+yDRo diff --git a/wasm_for_tests/vp_always_false.wasm b/wasm_for_tests/vp_always_false.wasm index 67b7d774744ecbc513889990cd0d17e6f3275269..d1da1d4c628c690c9b39ed712ef4476e82cd320b 100755 GIT binary patch delta 1894 zcmZ`(4Qy0J5Z>AM-rc*tUV8%VwY19X71{<`uBCb{Err{**B<5iW9bhi1PUcouRwvI z{HPp%MNu)5AyBJPkl#olEsr2dB506+0*W;T0|4S=bQs@;LHv{`!<{I1o{{Ug)8Jmdm1=YMNy(uHCB~23e~`7-`ux2Gv8Y=MGI9{ z4U36&Wyg6&j!H|<@aN=?SG1B+{!BsPgy7_wXKU-~8=^KG#zl|}J-7+#iC=>Y$_=N1 zqBb7S@ffFICd=S1k0PKv34iA0JW_#P

GuhLUBIGCrzAGkOl5U}7~f*HoMvyWk=H zN^FU{bHLf%6DfBxZqOEld1_Lywh{{EI>&+02tjSfAP=0Y6PWuK?o4^vp53 zJPz~NBy$LLaX|H(q$G$-9rXW~iJ5wm;6gEV{3*Q}bj;2eHQZsCnt%f4Ch>Gw93?j& zXQpO4usx$n_wj5p=wa(*Mzy9{x(X4DA6we$(Nk=uU99HeOvc3VNXW?yQx|J^*kdzc zvXh%YN}H*dJ5>U+`S`GCpj0N%N=nM&60))>Ho>Hc{w^%z|G@(phR4VDy6sd{X!9s3 z+_X!pKPZza*x@aaCHTrag@X;_$7RA*EE#t@kw*wj%VNv}B4MdWc)_CzR^?>Fu_hO( zgx7Nmxn3%p5HUG=^W(6s$_IL6tJF(Ya&u{HST^y69FU4n4bQGt_jvXkz1BY>UNg45* zCDWx}u(T}oy$`87d33~!EG*9MAf;y~jhZ|n7ydRATzS}AI!mGt2}pG7`9LB_-1TLJ z68H16zxb6M_(gd@3P-R>BF_z0Cq9IH>j7k|r?3K09bL+|@527XIBoL4M0SnOe)L3M zviirzJWvjI;k^5icz43r*16zQN^&wmm`tc)IetK+Cx?-S09;W9o(OFosNWly6RH@j zWP*dPpOCX=M6=?qt0(WCId-O#>fx*ZwYB zzJ2PxfeQHR5^zraO)7>}(_kCes%ePFg^e@=A>7z_57uMHv`XTA(`L%DY;5-uf3>{ zvR^s5w663xpbTGM=S?c3WBA)#0W&pVdW6v?cr`tE9--1(_uaZaIZk`m4-BEdF3w8m zXtm4tZq1vP%^3;FzD-%O#L1X}y<3-C3${$)Sb~AUr%&EmdI}L&Jfyp~G&odhxuH^D zF=N|7dGL$d>ZR~@?v}6X51sWgOV&Fn@~^R`OB$`cU2_!3wxZtecWO^E#-74(ck2IE z=exJTT%31cj5zUxiw!5gB%){{m1x9&bo2h+b@EN2wTCE`NUld>L3eug<0A5hJR*Xk yaLv2Lc=^DFtj7gr4}CFH%iu{0RG|?EM%yr>IABPV?f6w1$bM}k? delta 1757 zcmb6ZZA_JA^f}M_-Y+f}uK58d#>)kS5WIZM3&_`ffqO5=1@S5eKp|%bar#P1>4Uv_@>vc`mxi=AYi}ob#N|=RD`U z=ZsvmT)JpEQ0!Dg^g*cEAZds-LkM;@GuEu^42z9nu{1D+PY0igt2J{2aD`bIfC8XK z@JO49a}L0PGZO&GjYdlp(8oBL5U+wELQ%l1C<<5AXjSS+YA}KE;MIw~X#4ECM7rOHDE31~*H`E1|Mcfj&8RDT6_dz}3Tky5=i_MD7 zfmt}rqpTWB<93IlgRcW`b0255n5}$FMEs^?8YPX5C^uL;Pn=_V4??{(+)Y0bof?F1EO40E#^^Lx4-; zGDRrvGY*}iApUoSNV1kEJ#bA@t8_e{^pygK@L+1je2Yw*IgApSb+yBzx;0{A^=rE+ z&+CLV%N(8Sie|yvsY}3u`DrO&#-_Ab!rRkoO%Ah*XK7|JoVLWE44M^8Tu`wt(Wsg9 zP9Dr)O#dR}w=t_`)L-I3he?Af8`prCHqGd>shUAEx%iAou+jnUTz_$1_Ca0&8LDMhC&>(d0eJQhRs zBb1le*kBBcwyki={En>{hm+X1vWD>J$|jjzU867cer8tvQwZuqQ{HXW_d-y!(lFVJ z@oZx@=s4Oq4uxW56^)M@U8`FO_pT0sTg+aQCPn2<0m5BP$DvG=u6-Fy%&(bs=EtGt zJa8Xsp*iy7x|SUB=xMnwJ+`(kBW9>ILItz<_UT3gct!Tc6riWAXm`?;d)h~#6nAWL z5ff$!zZn4hv8bcRAxc^a^*I z-K!;eG>5MG2wB^7zN7>1?Yb&kdifRkBZzssUo^>;)Bn0K+E^vc8&q%EVq?sTv$}kf zOGnNZEujrv&rjh;x*EjySj5dL)4-}S9iPG-=2nxkkpwEiT!MIlkL-5#IMD5BO=2|PKo37RQfi*4tw)6U(*Y`W;{LcBa z_B&_oTMFD-NPetYl>|b~5Q0_Bj5S-NJal3m=A=?twRK^9rJZZQ6=q`q3V<5MBf_nm za{vyUSpn$WVzD`ZKE^@e3R%&a26k0ZlrU9|RHcqwHLzH>_Ak!Jb(c@q0u_}bBO;wy z(XKILQ`6GD6SF5PT2V27F)x3LzpVPDn%cVhunouX6-a_!+yr%mZ^AdqZHJDcCI-La z5e~r&md>3nML=mH{>)2xs2ttOaU$kzCDS5lOjwbo_Z~jY#A-s$88|<3(G&7OWKnF_ zkjkYvn86J$Y(aw{Go4331|DC;k6*LWV6-aG@AB{3)#pY?zfkHo>kNnt(hOOXP*1I7wz6&J4}4 zV@Gc9&}j@D06TF zNN6)`r4E&XSv-7XI8Z7Bh?A7EIEAcivPCedqQ47D|9|3vbi-2KB2sLAQzLey>!c^I<6NUHT7=Bh%Lik?MENR#7 zPr!x687UurLfy%sD_&(macLI`y}N1DJ>3AVfiur-^p&I(|VqnWK z7iXs@DCE59;jFOx#+e7_`!01^^?SIlLYS6n4>$K#jONN8FOQ*DTY{Z6so=q$n%`yH zcg{F4R0VHs9L}q|Lj^Ib>n%e^)%7vh-avB@z>N(LVLf)vtRUP!bG9r^c4KMUqtfW& zBm~8GvIGAXT^Dad(9mh-ogUoPI1vQ)H$H>{^U5qL(=?24Y9ah)QwXM+e>SDdqtw|o zgjdWy0EMP&&RVd7pWXua@nCZfDQ-5ma1#7;Cy~n=bFWL6hL#y59Bc_wHG8tnu$#wQ z8#VBm&P8K^?&xWEQ{Dybzrj>&=x`H$tD_#Knm=~jRw>EZrNc=gDT!J2dKzt0zqx$b zubfO;SGw#_g72(zCzjAPylqaOk>WF4LT?kiik6;BsI>3CUDq#1YXAD7Vf5BUn{l13 zR=M-mylYw_NQxpqAQ(q5o*-jg@aT!)Q5fv~8*;?@9{>OV delta 1755 zcmb6ZeN5F=^qzZv?~BKiXMO;R`QrgX2=X-_APDcr4?+zlMMJCsLa?)eu?Dd-EH;P5%D@sn9egIX%F0c^1+y^#0iZ?j zNQZ@U4#0sk3joQ@X4@p7k8v^~UIUYe5CW`1a7~NSl#Zk(3z!dHZA)8_v8d7%SX?!A zQj{}unkzABPI5}VFcmK3rCB-i-MM+?70YXCtAooTZb{q>anO$YpoZ`*_)7fZuwz4T zIu7zlcAcegr%M>%Z^PT%&sh~_iI0fL-$c4u(U=IY$=-hA95cEIIhW%4sErf&8&Tfq zlVO!hIGE0LZdjXjIykE_&zWf+*U47rY{6N%e0|FMLR0`?QFIPAx~BrlJ<$VzOJmbz zDE3nh?J_^^cR?gk&s83{CZS0=o=^Bvz#%-CoHo~{(q=BRLRLd-^JyNPm>A>QZp!mI zA>B69;D*p`csqFs*f1|839MM35<_@t^F6-t4mIP~qS@5YwWY{SHkx=@vJi8VPnf0i6_OZtgTxrQ*#7Q!1vL zo0b2Ac%a%=F$vSrG<26!p0n=GTqB5;M73C`R=(?(8ExGBMu)7%EDX|mDQh$t$A;g@Ib(a zj?_}#)q_9zrin8081-d_TkwrE$|gOqosDn!{r9c#X5}myEUf?>@qaPh#&l-TE15l# zm2}^{@Xw_)SGp`%oBtla-GgKKcNMv};20F+n+08bs2AOZHT=RmxUcYxQY~6|R*{IJ zIjXJMMSsIErWThGZYy4;>;{X=68er(3v;PH-?3(6Zx1o&dg;FlSIF?R=-@0yw`Z-2 z-sw@%nQ7Di@t7v!(bgynA+`T#khMch`MTxxiLBbvN$H6O$*S-uE7SOE*3*bOQE*OUz zX^H~4t}&ZDx*D%5kF8D1h#6>#(7-CceY(yBWio4H6429Dv}Vwids>IVgF7~55I(-C z7Cdrn)2|wMv3PBq6x-H^?}(y19PVZ~d>|o_IZBV&oi-R#cPVexKuN*a9 zw1PHtJU@XS>Zq09VYzCe>;lqFXU&YKm?TLpod60GHlt)CDr%p(L RfzzqG_MiTE|7o~!@;_2!=1%|s diff --git a/wasm_for_tests/vp_eval.wasm b/wasm_for_tests/vp_eval.wasm index 04ad77434b855de7d88a0545a7ed1e9727b50f48..1af66f64026a67e878630916ced9c1a353766dc4 100755 GIT binary patch delta 1886 zcmZ`(3s96*6u#&F|Ni?g%fhNW7au>1$|fL)#v+L954${81RsC`j*_TMOAm0eL8sUn zqns&cpp!ys7GmT$iRfQsSe(oSPwyHt7oiU5qfCFJSGXF)D~Ki+h{;rXKPYTq_m>W z$~iEwNG6atQyBmUki(>iaI+*y01`;d4739jMYRD9CIOeoNvv@b2aC*km@M06VI*G@ zDCW-5mXx^}Web(^ibc_p_HnaflBP{hc0E*>IVVR}d__`L?z}wzf+b5UtCuYg`x`r8 z5)5HKR1uZ=Iq4Uh6@ReA;1wQW)0xIx-0qZgC{94LRLp~ZTqF$=GgLWE5!@YCXtECV z4>7%ih`kEiBOkvvp3z_5^Sq#HRQ(uo~U*A95HnmQ1`P5zEuu0tuf^ zY7l_ulg>(jxIZ~HMHSa!PDK!lF1Pq(k48$IKHN^{KTkwc6LhXinu_4602LRzra3HD z&7?yPizPNEpdX=aS)6Gm&4L}Say5%*kUvc^+^o8kE|&2Bb7;YOzfwFeQseg#(4sfF!{wid`3(O;mIyOmw<{@V|ox z!VSNlc_`LQNrfh-l*l!+uzE^FFbVt83q=ZkOJBf2L3d^H9X zgDkDtZ<1wGMJZm-@8c)-;vUbaVBLBjWMh6oHy`Q6;eskY+KU;5M}_Hf;W5Duc&CeM zedYZN-orD#BBD`6wPIg$QBl&aJyaPtUGEzf&`vERF|(`)*0Dz`a;AaaS_3bJvae;l=WfP+EMLkar4i=4}@z+cy74!8YaRhyxDh zdyPN*lck9hDTc;$IUw2-s5M2iQj>t)YQ29;}aYeT*U1^$%k5qcGr_u#({H*et=zwEY`$9?SY>&m@vdbcTXL~emUVinC_;QUy zoaUXH8}I`5)-EJ^wRWXQ*5bP2hTOgo?(F?G<@p#=Vn2%TM zZa}_qc?D&y0NpDai0)h&gaRXBl}jvn>z9zyRiDDmP(p74O7P@rHyNOzfs?YXA(QN{ zH=GxnH>_Ps%E`6kWs@aQ)hx!yqd|pkwxT(mLcP-bJLvdqOFGd5E!ChK^7?=xU?U0E z*wC6xziNrGd*c;Owyn>sv497!wWnZRv)xF0F&%8yCY@K%Pv_KS`s5m4`dajoXG^F9 zDM!YkEsbXJ(K$K{#TG0R1zE6uy^S#|`ggP%O7}dDBinsrf1Y@*^arMPZM;Vx=&H8J zR7V=Iqg9n r+2il$k$&jZ13fY(+|T3SfX{gBaGQjDa6F!VKW=l>;0IBI@a@1q$H@s? delta 1889 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 S0%>;vWATLMYwp6|MfpE3=>Z`C From 866338d547a46ccef359f00d8f3e72136d4ac57c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 11 Jan 2023 16:45:34 +0100 Subject: [PATCH 11/18] core: remove storage_api StorageRead and StorageWrite from Storage To use storage_api, use WlStorage instead --- core/src/ledger/storage/mod.rs | 112 --------------------------------- 1 file changed, 112 deletions(-) diff --git a/core/src/ledger/storage/mod.rs b/core/src/ledger/storage/mod.rs index 49274d5d2b2..66049b40148 100644 --- a/core/src/ledger/storage/mod.rs +++ b/core/src/ledger/storage/mod.rs @@ -39,8 +39,6 @@ use crate::ledger::parameters::{self, EpochDuration, Parameters}; use crate::ledger::storage::merkle_tree::{ Error as MerkleTreeError, MerkleRoot, }; -use crate::ledger::storage_api; -use crate::ledger::storage_api::{ResultExt, StorageRead, StorageWrite}; #[cfg(any(feature = "tendermint", feature = "tendermint-abcipp"))] use crate::tendermint::merkle::proof::Proof; use crate::types::address::{ @@ -1005,116 +1003,6 @@ where } } -impl StorageRead for Storage -where - D: DB + for<'iter_> DBIter<'iter_>, - H: StorageHasher, -{ - type PrefixIter<'iter> = >::PrefixIter -where - Self: 'iter - ; - - fn read_bytes( - &self, - key: &crate::types::storage::Key, - ) -> std::result::Result>, storage_api::Error> { - self.db.read_subspace_val(key).into_storage_result() - } - - fn has_key( - &self, - key: &crate::types::storage::Key, - ) -> std::result::Result { - self.block.tree.has_key(key).into_storage_result() - } - - fn iter_prefix<'iter>( - &'iter self, - prefix: &crate::types::storage::Key, - ) -> std::result::Result, storage_api::Error> { - Ok(self.db.iter_prefix(prefix)) - } - - fn iter_next<'iter>( - &'iter self, - iter: &mut Self::PrefixIter<'iter>, - ) -> std::result::Result)>, storage_api::Error> - { - Ok(iter.next().map(|(key, val, _gas)| (key, val))) - } - - fn get_chain_id(&self) -> std::result::Result { - Ok(self.chain_id.to_string()) - } - - fn get_block_height( - &self, - ) -> std::result::Result { - Ok(self.block.height) - } - - fn get_block_hash( - &self, - ) -> std::result::Result { - Ok(self.block.hash.clone()) - } - - fn get_block_epoch( - &self, - ) -> std::result::Result { - Ok(self.block.epoch) - } - - fn get_tx_index(&self) -> std::result::Result { - Ok(self.tx_index) - } - - fn get_native_token( - &self, - ) -> std::result::Result { - Ok(self.native_token.clone()) - } -} - -impl StorageWrite for Storage -where - D: DB + for<'iter> DBIter<'iter>, - H: StorageHasher, -{ - fn write_bytes( - &mut self, - key: &crate::types::storage::Key, - val: impl AsRef<[u8]>, - ) -> storage_api::Result<()> { - // Note that this method is the same as `Storage::write`, but without - // gas and storage bytes len diff accounting, because it can only be - // used by the protocol that has a direct mutable access to storage - let val = val.as_ref(); - self.block.tree.update(key, val).into_storage_result()?; - let _ = self - .db - .write_subspace_val(self.block.height, key, val) - .into_storage_result()?; - Ok(()) - } - - fn delete( - &mut self, - key: &crate::types::storage::Key, - ) -> storage_api::Result<()> { - // Note that this method is the same as `Storage::delete`, but without - // gas and storage bytes len diff accounting, because it can only be - // used by the protocol that has a direct mutable access to storage - self.block.tree.delete(key).into_storage_result()?; - let _ = self - .db - .delete_subspace_val(self.block.height, key) - .into_storage_result()?; - Ok(()) - } -} - impl From for Error { fn from(error: MerkleTreeError) -> Self { Self::MerkleTreeError(error) From bb7d17330d47158061adcaca1fd7ce11ff61ea2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 11 Jan 2023 16:59:17 +0100 Subject: [PATCH 12/18] use WlStorage in Shell and in queries RequestCtx --- apps/src/lib/node/ledger/mod.rs | 7 +- .../lib/node/ledger/shell/finalize_block.rs | 100 +++++++--------- apps/src/lib/node/ledger/shell/governance.rs | 21 ++-- apps/src/lib/node/ledger/shell/init_chain.rs | 99 ++++++++-------- apps/src/lib/node/ledger/shell/mod.rs | 86 +++++++------- .../lib/node/ledger/shell/prepare_proposal.rs | 6 +- .../lib/node/ledger/shell/process_proposal.rs | 23 ++-- apps/src/lib/node/ledger/shell/queries.rs | 32 ++---- apps/src/lib/node/ledger/storage/mod.rs | 5 +- proof_of_stake/src/storage.rs | 92 +++++++++------ shared/src/ledger/native_vp/governance/mod.rs | 2 +- .../src/ledger/native_vp/governance/utils.rs | 107 ++++++++---------- shared/src/ledger/native_vp/parameters.rs | 2 +- shared/src/ledger/native_vp/slash_fund.rs | 2 +- shared/src/ledger/pos/mod.rs | 8 +- shared/src/ledger/pos/vp.rs | 2 +- shared/src/ledger/queries/mod.rs | 12 +- shared/src/ledger/queries/router.rs | 2 +- shared/src/ledger/queries/shell.rs | 41 ++++--- shared/src/ledger/queries/types.rs | 12 +- shared/src/ledger/queries/vp/pos.rs | 20 ++-- 21 files changed, 345 insertions(+), 336 deletions(-) diff --git a/apps/src/lib/node/ledger/mod.rs b/apps/src/lib/node/ledger/mod.rs index ca31310b740..06a137bd3a6 100644 --- a/apps/src/lib/node/ledger/mod.rs +++ b/apps/src/lib/node/ledger/mod.rs @@ -58,15 +58,16 @@ const ENV_VAR_RAYON_THREADS: &str = "NAMADA_RAYON_THREADS"; impl Shell { fn load_proposals(&mut self) { let proposals_key = gov_storage::get_commiting_proposals_prefix( - self.storage.last_epoch.0, + self.wl_storage.storage.last_epoch.0, ); - let (proposal_iter, _) = self.storage.iter_prefix(&proposals_key); + let (proposal_iter, _) = + self.wl_storage.storage.iter_prefix(&proposals_key); for (key, _, _) in proposal_iter { let key = Key::from_str(key.as_str()).expect("Key should be parsable"); if gov_storage::get_commit_proposal_epoch(&key).unwrap() - != self.storage.last_epoch.0 + != self.wl_storage.storage.last_epoch.0 { // NOTE: `iter_prefix` iterate over the matching prefix. In this // case a proposal with grace_epoch 110 will be diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 8455522303e..2f2ed4d1e91 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -2,7 +2,6 @@ use namada::ledger::pos::types::into_tm_voting_power; use namada::ledger::protocol; -use namada::ledger::storage::write_log::StorageModification; use namada::ledger::storage_api::StorageRead; use namada::types::storage::{BlockHash, BlockResults, Header}; use namada::types::token::Amount; @@ -59,7 +58,7 @@ where let mut stats = InternalStats::default(); // Tracks the accepted transactions - self.storage.block.results = BlockResults::default(); + self.wl_storage.storage.block.results = BlockResults::default(); for (tx_index, processed_tx) in req.txs.iter().enumerate() { let tx = if let Ok(tx) = Tx::try_from(processed_tx.tx.as_ref()) { tx @@ -129,7 +128,7 @@ where // if the rejected tx was decrypted, remove it // from the queue of txs to be processed if let TxType::Decrypted(_) = &tx_type { - self.storage.tx_queue.pop(); + self.wl_storage.storage.tx_queue.pop(); } continue; } @@ -152,39 +151,16 @@ where let balance_key = token::balance_key(&wrapper.fee.token, &fee_payer); - let balance: Amount = - match self.write_log.read(&balance_key).0 { - Some(wal_mod) => { - // Read from WAL - if let StorageModification::Write { value } = - wal_mod - { - Amount::try_from_slice(value).unwrap() - } else { - Amount::default() - } - } - None => { - // Read from storage - let balance = StorageRead::read( - &self.storage, - &balance_key, - ); - // Storage read must not fail, but there might - // be no value, in which - // case default (0) is returned - balance - .expect( - "Storage read in the protocol must \ - not fail", - ) - .unwrap_or_default() - } - }; + let balance: token::Amount = self + .wl_storage + .read(&balance_key) + .expect("must be able to read") + .unwrap_or_default(); match balance.checked_sub(wrapper_fees) { Some(amount) => { - self.write_log + self.wl_storage + .storage .write( &balance_key, amount.try_to_vec().unwrap(), @@ -198,7 +174,8 @@ where let reject = true; if reject { // Burn remaining funds - self.write_log + self.wl_storage + .storage .write( &balance_key, Amount::from(0).try_to_vec().unwrap(), @@ -215,7 +192,7 @@ where } } - self.storage.tx_queue.push(WrapperTxInQueue { + self.wl_storage.storage.tx_queue.push(WrapperTxInQueue { tx: wrapper.clone(), #[cfg(not(feature = "mainnet"))] has_valid_pow, @@ -224,7 +201,7 @@ where } TxType::Decrypted(inner) => { // We remove the corresponding wrapper tx from the queue - self.storage.tx_queue.pop(); + self.wl_storage.storage.tx_queue.pop(); let mut event = Event::new_tx_event(&tx_type, height.0); match inner { @@ -271,8 +248,8 @@ where .expect("transaction index out of bounds"), ), &mut self.gas_meter, - &mut self.write_log, - &self.storage, + &mut self.wl_storage.write_log, + &self.wl_storage.storage, &mut self.vp_wasm_cache, &mut self.tx_wasm_cache, ) @@ -287,10 +264,14 @@ where result ); stats.increment_successful_txs(); - self.write_log.commit_tx(); + self.wl_storage.commit_tx(); if !tx_event.contains_key("code") { tx_event["code"] = ErrorCodes::Ok.into(); - self.storage.block.results.accept(tx_index); + self.wl_storage + .storage + .block + .results + .accept(tx_index); } if let Some(ibc_event) = &result.ibc_event { // Add the IBC event besides the tx_event @@ -320,7 +301,7 @@ where result.vps_result.rejected_vps ); stats.increment_rejected_txs(); - self.write_log.drop_tx(); + self.wl_storage.drop_tx(); tx_event["code"] = ErrorCodes::InvalidTx.into(); } tx_event["gas_used"] = result.gas_used.to_string(); @@ -333,7 +314,7 @@ where msg ); stats.increment_errored_txs(); - self.write_log.drop_tx(); + self.wl_storage.drop_tx(); tx_event["gas_used"] = self .gas_meter .get_current_transaction_gas() @@ -382,22 +363,25 @@ where hash: BlockHash, byzantine_validators: Vec, ) -> (BlockHeight, bool) { - let height = self.storage.last_height + 1; + let height = self.wl_storage.storage.last_height + 1; self.gas_meter.reset(); - self.storage + self.wl_storage + .storage .begin_block(hash, height) .expect("Beginning a block shouldn't fail"); let header_time = header.time; - self.storage + self.wl_storage + .storage .set_header(header) .expect("Setting a header shouldn't fail"); self.byzantine_validators = byzantine_validators; let new_epoch = self + .wl_storage .storage .update_epoch(height, header_time) .expect("Must be able to update epoch"); @@ -410,11 +394,11 @@ where /// changes to the validator sets and consensus parameters fn update_epoch(&self, response: &mut shim::response::FinalizeBlock) { // Apply validator set update - let (current_epoch, _gas) = self.storage.get_current_epoch(); - let pos_params = self.storage.read_pos_params(); + let (current_epoch, _gas) = self.wl_storage.storage.get_current_epoch(); + let pos_params = self.wl_storage.read_pos_params(); // TODO ABCI validator updates on block H affects the validator set // on block H+2, do we need to update a block earlier? - self.storage.validator_set_update( + self.wl_storage.validator_set_update( current_epoch, &pos_params, |update| { @@ -473,10 +457,11 @@ mod test_finalize_block { // Add unshielded balance for fee paymenty let balance_key = token::balance_key( - &shell.storage.native_token, + &shell.wl_storage.storage.native_token, &Address::from(&keypair.ref_to()), ); shell + .wl_storage .storage .write(&balance_key, Amount::whole(1000).try_to_vec().unwrap()) .unwrap(); @@ -490,7 +475,7 @@ mod test_finalize_block { let wrapper = WrapperTx::new( Fee { amount: MIN_FEE.into(), - token: shell.storage.native_token.clone(), + token: shell.wl_storage.storage.native_token.clone(), }, &keypair, Epoch(0), @@ -563,7 +548,7 @@ mod test_finalize_block { let wrapper = WrapperTx::new( Fee { amount: 0.into(), - token: shell.storage.native_token.clone(), + token: shell.wl_storage.storage.native_token.clone(), }, &keypair, Epoch(0), @@ -601,7 +586,7 @@ mod test_finalize_block { assert_eq!(code, &String::from(ErrorCodes::InvalidTx)); } // check that the corresponding wrapper tx was removed from the queue - assert!(shell.storage.tx_queue.is_empty()); + assert!(shell.wl_storage.storage.tx_queue.is_empty()); } /// Test that if a tx is undecryptable, it is applied @@ -621,7 +606,7 @@ mod test_finalize_block { let wrapper = WrapperTx { fee: Fee { amount: 0.into(), - token: shell.storage.native_token.clone(), + token: shell.wl_storage.storage.native_token.clone(), }, pk: keypair.ref_to(), epoch: Epoch(0), @@ -659,7 +644,7 @@ mod test_finalize_block { assert!(log.contains("Transaction could not be decrypted.")) } // check that the corresponding wrapper tx was removed from the queue - assert!(shell.storage.tx_queue.is_empty()); + assert!(shell.wl_storage.storage.tx_queue.is_empty()); } /// Test that the wrapper txs are queued in the order they @@ -674,10 +659,11 @@ mod test_finalize_block { // Add unshielded balance for fee paymenty let balance_key = token::balance_key( - &shell.storage.native_token, + &shell.wl_storage.storage.native_token, &Address::from(&keypair.ref_to()), ); shell + .wl_storage .storage .write(&balance_key, Amount::whole(1000).try_to_vec().unwrap()) .unwrap(); @@ -699,7 +685,7 @@ mod test_finalize_block { let wrapper_tx = WrapperTx::new( Fee { amount: MIN_FEE.into(), - token: shell.storage.native_token.clone(), + token: shell.wl_storage.storage.native_token.clone(), }, &keypair, Epoch(0), @@ -736,7 +722,7 @@ mod test_finalize_block { let wrapper_tx = WrapperTx::new( Fee { amount: MIN_FEE.into(), - token: shell.storage.native_token.clone(), + token: shell.wl_storage.storage.native_token.clone(), }, &keypair, Epoch(0), diff --git a/apps/src/lib/node/ledger/shell/governance.rs b/apps/src/lib/node/ledger/shell/governance.rs index 67c49c714d0..ce16a638db3 100644 --- a/apps/src/lib/node/ledger/shell/governance.rs +++ b/apps/src/lib/node/ledger/shell/governance.rs @@ -50,9 +50,10 @@ where ) })?; - let votes = get_proposal_votes(&shell.storage, proposal_end_epoch, id); + let votes = + get_proposal_votes(&shell.wl_storage, proposal_end_epoch, id); let is_accepted = votes.and_then(|votes| { - compute_tally(&shell.storage, proposal_end_epoch, votes) + compute_tally(&shell.wl_storage, proposal_end_epoch, votes) }); let transfer_address = match is_accepted { @@ -82,6 +83,7 @@ where let pending_execution_key = gov_storage::get_proposal_execution_key(id); shell + .wl_storage .storage .write(&pending_execution_key, "") .expect("Should be able to write to storage."); @@ -92,19 +94,20 @@ where * need it here. */ TxIndex::default(), &mut BlockGasMeter::default(), - &mut shell.write_log, - &shell.storage, + &mut shell.wl_storage.write_log, + &shell.wl_storage.storage, &mut shell.vp_wasm_cache, &mut shell.tx_wasm_cache, ); shell + .wl_storage .storage .delete(&pending_execution_key) .expect("Should be able to delete the storage."); match tx_result { Ok(tx_result) => { if tx_result.is_accepted() { - shell.write_log.commit_tx(); + shell.wl_storage.write_log.commit_tx(); let proposal_event: Event = ProposalEvent::new( EventType::Proposal.to_string(), @@ -119,7 +122,7 @@ where proposal_author } else { - shell.write_log.drop_tx(); + shell.wl_storage.write_log.drop_tx(); let proposal_event: Event = ProposalEvent::new( EventType::Proposal.to_string(), @@ -136,7 +139,7 @@ where } } Err(_e) => { - shell.write_log.drop_tx(); + shell.wl_storage.write_log.drop_tx(); let proposal_event: Event = ProposalEvent::new( EventType::Proposal.to_string(), TallyResult::Passed, @@ -201,9 +204,9 @@ where } }; - let native_token = shell.storage.native_token.clone(); + let native_token = shell.wl_storage.storage.native_token.clone(); // transfer proposal locked funds - shell.storage.transfer( + shell.wl_storage.transfer( &native_token, funds, &gov_address, diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index c58a8bd4ac0..a5f74f00af2 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -6,6 +6,7 @@ use std::hash::Hash; use namada::core::ledger::testnet_pow; use namada::ledger::parameters::Parameters; use namada::ledger::pos::into_tm_voting_power; +use namada::ledger::storage_api::StorageWrite; use namada::types::key::*; #[cfg(not(feature = "dev"))] use sha2::{Digest, Sha256}; @@ -29,7 +30,7 @@ where init: request::InitChain, ) -> Result { let mut response = response::InitChain::default(); - let (current_chain_id, _) = self.storage.get_chain_id(); + let (current_chain_id, _) = self.wl_storage.storage.get_chain_id(); if current_chain_id != init.chain_id { return Err(Error::ChainId(format!( "Current chain ID: {}, Tendermint chain ID: {}", @@ -37,11 +38,13 @@ where ))); } #[cfg(not(feature = "dev"))] - let genesis = genesis::genesis(&self.base_dir, &self.storage.chain_id); + let genesis = + genesis::genesis(&self.base_dir, &self.wl_storage.storage.chain_id); #[cfg(not(feature = "dev"))] { let genesis_bytes = genesis.try_to_vec().unwrap(); - let errors = self.storage.chain_id.validate(genesis_bytes); + let errors = + self.wl_storage.storage.chain_id.validate(genesis_bytes); use itertools::Itertools; assert!( errors.is_empty(), @@ -135,13 +138,16 @@ where #[cfg(not(feature = "mainnet"))] wrapper_tx_fees, }; - parameters.init_storage(&mut self.storage); + parameters.init_storage(&mut self.wl_storage.storage); // Initialize governance parameters - genesis.gov_params.init_storage(&mut self.storage); + genesis + .gov_params + .init_storage(&mut self.wl_storage.storage); // Depends on parameters being initialized - self.storage + self.wl_storage + .storage .init_genesis_epoch(initial_height, genesis_time, ¶meters) .expect("Initializing genesis epoch must not fail"); @@ -184,19 +190,19 @@ where ); } - self.storage - .write(&Key::validity_predicate(&address), vp_code) + self.wl_storage + .write_bytes(&Key::validity_predicate(&address), vp_code) .unwrap(); if let Some(pk) = public_key { let pk_storage_key = pk_key(&address); - self.storage - .write(&pk_storage_key, pk.try_to_vec().unwrap()) + self.wl_storage + .write_bytes(&pk_storage_key, pk.try_to_vec().unwrap()) .unwrap(); } for (key, value) in storage { - self.storage.write(&key, value).unwrap(); + self.wl_storage.write_bytes(&key, value).unwrap(); } // When using a faucet WASM, initialize its PoW challenge storage @@ -209,7 +215,7 @@ where .faucet_withdrawal_limit .unwrap_or_else(|| token::Amount::whole(1_000)); testnet_pow::init_faucet_storage( - &mut self.storage, + &mut self.wl_storage, &address, difficulty, withdrawal_limit, @@ -223,9 +229,7 @@ where { let address: address::Address = (&public_key).into(); let pk_storage_key = pk_key(&address); - self.storage - .write(&pk_storage_key, public_key.try_to_vec().unwrap()) - .unwrap(); + self.wl_storage.write(&pk_storage_key, public_key).unwrap(); } // Initialize genesis token accounts @@ -258,16 +262,13 @@ where ); } - self.storage - .write(&Key::validity_predicate(&address), vp_code) + self.wl_storage + .write_bytes(&Key::validity_predicate(&address), vp_code) .unwrap(); for (owner, amount) in balances { - self.storage - .write( - &token::balance_key(&address, &owner), - amount.try_to_vec().unwrap(), - ) + self.wl_storage + .write(&token::balance_key(&address, &owner), amount) .unwrap(); } } @@ -299,55 +300,40 @@ where } let addr = &validator.pos_data.address; - self.storage - .write(&Key::validity_predicate(addr), vp_code) + self.wl_storage + .write_bytes(&Key::validity_predicate(addr), vp_code) .expect("Unable to write user VP"); // Validator account key let pk_key = pk_key(addr); - self.storage - .write( - &pk_key, - validator - .account_key - .try_to_vec() - .expect("encode public key"), - ) + self.wl_storage + .write(&pk_key, &validator.account_key) .expect("Unable to set genesis user public key"); // Account balance (tokens no staked in PoS) - self.storage + self.wl_storage .write( - &token::balance_key(&self.storage.native_token, addr), - validator - .non_staked_balance - .try_to_vec() - .expect("encode token amount"), + &token::balance_key( + &self.wl_storage.storage.native_token, + addr, + ), + validator.non_staked_balance, ) .expect("Unable to set genesis balance"); - self.storage - .write( - &protocol_pk_key(addr), - validator - .protocol_key - .try_to_vec() - .expect("encode protocol public key"), - ) + self.wl_storage + .write(&protocol_pk_key(addr), &validator.protocol_key) .expect("Unable to set genesis user protocol public key"); - self.storage + self.wl_storage .write( &dkg_session_keys::dkg_pk_key(addr), - validator - .dkg_public_key - .try_to_vec() - .expect("encode public DKG session key"), + &validator.dkg_public_key, ) .expect("Unable to set genesis user public DKG session key"); } // PoS system depends on epoch being initialized - let (current_epoch, _gas) = self.storage.get_current_epoch(); + let (current_epoch, _gas) = self.wl_storage.storage.get_current_epoch(); pos::init_genesis_storage( - &mut self.storage, + &mut self.wl_storage, &genesis.pos_params, genesis .validators @@ -355,7 +341,7 @@ where .map(|validator| &validator.pos_data), current_epoch, ); - ibc::init_genesis_storage(&mut self.storage); + ibc::init_genesis_storage(&mut self.wl_storage.storage); // Set the initial validator set for validator in genesis.validators { @@ -372,6 +358,11 @@ where ); response.validators.push(abci_validator); } + + self.wl_storage + .commit_genesis() + .expect("Must be able to commit genesis state"); + Ok(response) } } diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index 07e6c2fbc66..377277919f0 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -30,8 +30,9 @@ use namada::ledger::pos::namada_proof_of_stake::types::{ use namada::ledger::pos::namada_proof_of_stake::PosBase; use namada::ledger::storage::write_log::WriteLog; use namada::ledger::storage::{ - DBIter, Sha256Hasher, Storage, StorageHasher, DB, + DBIter, Sha256Hasher, Storage, StorageHasher, WlStorage, DB, }; +use namada::ledger::storage_api::StorageRead; use namada::ledger::{ibc, pos, protocol}; use namada::proto::{self, Tx}; use namada::types::address; @@ -195,12 +196,10 @@ where /// The id of the current chain #[allow(dead_code)] chain_id: ChainId, - /// The persistent storage - pub(super) storage: Storage, + /// The persistent storage with write log + pub(super) wl_storage: WlStorage, /// Gas meter for the current block gas_meter: BlockGasMeter, - /// Write log for the current block - write_log: WriteLog, /// Byzantine validators given from ABCI++ `prepare_proposal` are stored in /// this field. They will be slashed when we finalize the block. byzantine_validators: Vec, @@ -315,11 +314,14 @@ where TendermintMode::Seed => ShellMode::Seed, }; + let wl_storage = WlStorage { + storage, + write_log: WriteLog::default(), + }; Self { chain_id, - storage, + wl_storage, gas_meter: BlockGasMeter::default(), - write_log: WriteLog::default(), byzantine_validators: vec![], base_dir, wasm_dir, @@ -354,14 +356,14 @@ where /// Iterate over the wrapper txs in order #[allow(dead_code)] fn iter_tx_queue(&mut self) -> impl Iterator { - self.storage.tx_queue.iter() + self.wl_storage.storage.tx_queue.iter() } /// Load the Merkle root hash and the height of the last committed block, if /// any. This is returned when ABCI sends an `info` request. pub fn last_state(&mut self) -> response::Info { let mut response = response::Info::default(); - let result = self.storage.get_state(); + let result = self.wl_storage.storage.get_state(); match result { Some((root, height)) => { @@ -389,7 +391,7 @@ where where T: Clone + BorshDeserialize, { - let result = self.storage.read(key); + let result = self.wl_storage.storage.read(key); match result { Ok((bytes, _gas)) => match bytes { @@ -405,7 +407,7 @@ where /// Read the bytes for a storage key dropping any error pub fn read_storage_key_bytes(&self, key: &Key) -> Option> { - let result = self.storage.read(key); + let result = self.wl_storage.storage.read(key); match result { Ok((bytes, _gas)) => bytes, @@ -418,8 +420,8 @@ where if !self.byzantine_validators.is_empty() { let byzantine_validators = mem::take(&mut self.byzantine_validators); - let pos_params = self.storage.read_pos_params(); - let current_epoch = self.storage.block.epoch; + let pos_params = self.wl_storage.read_pos_params(); + let current_epoch = self.wl_storage.storage.block.epoch; for evidence in byzantine_validators { tracing::info!("Processing evidence {evidence:?}."); let evidence_height = match u64::try_from(evidence.height) { @@ -433,6 +435,7 @@ where } }; let evidence_epoch = match self + .wl_storage .storage .block .pred_epochs @@ -489,7 +492,7 @@ where } }; let validator = match self - .storage + .wl_storage .read_validator_address_raw_hash(&validator_raw_hash) { Some(validator) => validator, @@ -508,7 +511,7 @@ where evidence_epoch, evidence_height ); - if let Err(err) = self.storage.slash( + if let Err(err) = self.wl_storage.slash( &pos_params, current_epoch, evidence_epoch, @@ -547,22 +550,23 @@ where pub fn commit(&mut self) -> response::Commit { let mut response = response::Commit::default(); // commit changes from the write-log to storage - self.write_log - .commit_block(&mut self.storage) + self.wl_storage + .write_log + .commit_block(&mut self.wl_storage.storage) .expect("Expected committing block write log success"); // store the block's data in DB - self.storage.commit_block().unwrap_or_else(|e| { + self.wl_storage.commit_block().unwrap_or_else(|e| { tracing::error!( "Encountered a storage error while committing a block {:?}", e ) }); - let root = self.storage.merkle_root(); + let root = self.wl_storage.storage.merkle_root(); tracing::info!( "Committed block hash: {}, height: {}", root, - self.storage.last_height, + self.wl_storage.storage.last_height, ); response.data = root.0; response @@ -640,7 +644,7 @@ where TxIndex::default(), &mut gas_meter, &mut write_log, - &self.storage, + &self.wl_storage.storage, &mut vp_wasm_cache, &mut tx_wasm_cache, ) @@ -675,21 +679,18 @@ where genesis::genesis_config::open_genesis_config(genesis_path).unwrap(), ); self.mode.get_validator_address().map(|addr| { - let pk_bytes = self - .storage + let sk: common::SecretKey = self + .wl_storage .read(&pk_key(addr)) .expect( "A validator should have a public key associated with \ it's established account", ) - .0 .expect( "A validator should have a public key associated with \ it's established account", ); - let pk = common::SecretKey::deserialize(&mut pk_bytes.as_slice()) - .expect("Validator's public key should be deserializable") - .ref_to(); + let pk = sk.ref_to(); wallet.find_key_by_pk(&pk).expect( "A validator's established keypair should be stored in its \ wallet", @@ -707,13 +708,13 @@ where if let Some(solution) = &tx.pow_solution { if let (Some(faucet_address), _gas) = namada::ledger::parameters::read_faucet_account_parameter( - &self.storage, + &self.wl_storage.storage, ) .expect("Must be able to read faucet account parameter") { let source = Address::from(&tx.pk); return solution - .validate(&self.storage, &faucet_address, source) + .validate(&self.wl_storage, &faucet_address, source) .expect("Must be able to validate PoW solutions"); } } @@ -725,7 +726,7 @@ where fn get_wrapper_tx_fees(&self) -> token::Amount { let (fees, _gas) = namada::ledger::parameters::read_wrapper_tx_fees_parameter( - &self.storage, + &self.wl_storage.storage, ) .expect("Must be able to read wrapper tx fees parameter"); fees.unwrap_or(token::Amount::whole(MIN_FEE)) @@ -741,14 +742,14 @@ where if let Some(solution) = &tx.pow_solution { if let (Some(faucet_address), _gas) = namada::ledger::parameters::read_faucet_account_parameter( - &self.storage, + &self.wl_storage.storage, ) .expect("Must be able to read faucet account parameter") { let source = Address::from(&tx.pk); return solution .invalidate_if_valid( - &mut self.storage, + &mut self.wl_storage, &faucet_address, &source, ) @@ -922,11 +923,15 @@ mod test_utils { /// in the current block proposal #[cfg(test)] pub fn enqueue_tx(&mut self, wrapper: WrapperTx) { - self.shell.storage.tx_queue.push(WrapperTxInQueue { - tx: wrapper, - #[cfg(not(feature = "mainnet"))] - has_valid_pow: false, - }); + self.shell + .wl_storage + .storage + .tx_queue + .push(WrapperTxInQueue { + tx: wrapper, + #[cfg(not(feature = "mainnet"))] + has_valid_pow: false, + }); } } @@ -1005,7 +1010,7 @@ mod test_utils { #[cfg(not(feature = "mainnet"))] None, ); - shell.storage.tx_queue.push(WrapperTxInQueue { + shell.wl_storage.storage.tx_queue.push(WrapperTxInQueue { tx: wrapper, #[cfg(not(feature = "mainnet"))] has_valid_pow: false, @@ -1018,6 +1023,7 @@ mod test_utils { let pred_epochs = Default::default(); let address_gen = EstablishedAddressGen::new("test"); shell + .wl_storage .storage .db .write_block(BlockStateWrite { @@ -1031,7 +1037,7 @@ mod test_utils { next_epoch_min_start_time: DateTimeUtc::now(), address_gen: &address_gen, results: &BlockResults::default(), - tx_queue: &shell.storage.tx_queue, + tx_queue: &shell.wl_storage.storage.tx_queue, }) .expect("Test failed"); @@ -1052,6 +1058,6 @@ mod test_utils { tx_wasm_compilation_cache, address::nam(), ); - assert!(!shell.storage.tx_queue.is_empty()); + assert!(!shell.wl_storage.storage.tx_queue.is_empty()); } } diff --git a/apps/src/lib/node/ledger/shell/prepare_proposal.rs b/apps/src/lib/node/ledger/shell/prepare_proposal.rs index 04953fa0299..1783a127fd7 100644 --- a/apps/src/lib/node/ledger/shell/prepare_proposal.rs +++ b/apps/src/lib/node/ledger/shell/prepare_proposal.rs @@ -98,7 +98,7 @@ where .collect(); // decrypt the wrapper txs included in the previous block - let decrypted_txs = self.storage.tx_queue.iter().map( + let decrypted_txs = self.wl_storage.storage.tx_queue.iter().map( |WrapperTxInQueue { tx, #[cfg(not(feature = "mainnet"))] @@ -225,7 +225,7 @@ mod test_prepare_proposal { WrapperTx::new( Fee { amount: 0.into(), - token: shell.storage.native_token.clone(), + token: shell.wl_storage.storage.native_token.clone(), }, &keypair, Epoch(0), @@ -285,7 +285,7 @@ mod test_prepare_proposal { let wrapper_tx = WrapperTx::new( Fee { amount: 0.into(), - token: shell.storage.native_token.clone(), + token: shell.wl_storage.storage.native_token.clone(), }, &keypair, Epoch(0), diff --git a/apps/src/lib/node/ledger/shell/process_proposal.rs b/apps/src/lib/node/ledger/shell/process_proposal.rs index e75254499a6..11deec9e13f 100644 --- a/apps/src/lib/node/ledger/shell/process_proposal.rs +++ b/apps/src/lib/node/ledger/shell/process_proposal.rs @@ -43,7 +43,7 @@ where /// Check all the given txs. pub fn process_txs(&self, txs: &[Vec]) -> Vec { - let mut tx_queue_iter = self.storage.tx_queue.iter(); + let mut tx_queue_iter = self.wl_storage.storage.tx_queue.iter(); txs.iter() .map(|tx_bytes| { self.process_single_tx(tx_bytes, &mut tx_queue_iter) @@ -239,7 +239,7 @@ mod test_process_proposal { let wrapper = WrapperTx::new( Fee { amount: 0.into(), - token: shell.storage.native_token.clone(), + token: shell.wl_storage.storage.native_token.clone(), }, &keypair, Epoch(0), @@ -288,7 +288,7 @@ mod test_process_proposal { let mut wrapper = WrapperTx::new( Fee { amount: 100.into(), - token: shell.storage.native_token.clone(), + token: shell.wl_storage.storage.native_token.clone(), }, &keypair, Epoch(0), @@ -372,7 +372,7 @@ mod test_process_proposal { let wrapper = WrapperTx::new( Fee { amount: 1.into(), - token: shell.storage.native_token.clone(), + token: shell.wl_storage.storage.native_token.clone(), }, &keypair, Epoch(0), @@ -413,10 +413,11 @@ mod test_process_proposal { let keypair = crate::wallet::defaults::daewon_keypair(); // reduce address balance to match the 100 token fee let balance_key = token::balance_key( - &shell.storage.native_token, + &shell.wl_storage.storage.native_token, &Address::from(&keypair.ref_to()), ); shell + .wl_storage .storage .write(&balance_key, Amount::whole(99).try_to_vec().unwrap()) .unwrap(); @@ -427,8 +428,8 @@ mod test_process_proposal { ); let wrapper = WrapperTx::new( Fee { - amount: Amount::whole(100), - token: shell.storage.native_token.clone(), + amount: Amount::whole(1_000_100), + token: shell.wl_storage.storage.native_token.clone(), }, &keypair, Epoch(0), @@ -478,7 +479,7 @@ mod test_process_proposal { let wrapper = WrapperTx::new( Fee { amount: i.into(), - token: shell.storage.native_token.clone(), + token: shell.wl_storage.storage.native_token.clone(), }, &keypair, Epoch(0), @@ -548,7 +549,7 @@ mod test_process_proposal { let wrapper = WrapperTx::new( Fee { amount: 0.into(), - token: shell.storage.native_token.clone(), + token: shell.wl_storage.storage.native_token.clone(), }, &keypair, Epoch(0), @@ -609,7 +610,7 @@ mod test_process_proposal { let mut wrapper = WrapperTx::new( Fee { amount: 0.into(), - token: shell.storage.native_token.clone(), + token: shell.wl_storage.storage.native_token.clone(), }, &keypair, Epoch(0), @@ -664,7 +665,7 @@ mod test_process_proposal { let wrapper = WrapperTx { fee: Fee { amount: 0.into(), - token: shell.storage.native_token.clone(), + token: shell.wl_storage.storage.native_token.clone(), }, pk: keypair.ref_to(), epoch: Epoch(0), diff --git a/apps/src/lib/node/ledger/shell/queries.rs b/apps/src/lib/node/ledger/shell/queries.rs index 77715246ab8..4f63924b0f4 100644 --- a/apps/src/lib/node/ledger/shell/queries.rs +++ b/apps/src/lib/node/ledger/shell/queries.rs @@ -1,6 +1,6 @@ //! Shell methods for querying state -use borsh::{BorshDeserialize, BorshSerialize}; +use borsh::BorshSerialize; use ferveo_common::TendermintValidator; use namada::ledger::pos::into_tm_voting_power; use namada::ledger::queries::{RequestCtx, ResponseQuery}; @@ -23,7 +23,7 @@ where /// INVARIANT: This method must be stateless. pub fn query(&self, query: request::Query) -> response::Query { let ctx = RequestCtx { - storage: &self.storage, + wl_storage: &self.wl_storage, event_log: self.event_log(), vp_wasm_cache: self.vp_wasm_cache.read_only(), tx_wasm_cache: self.tx_wasm_cache.read_only(), @@ -32,7 +32,7 @@ where // Convert request to domain-type let request = match namada::ledger::queries::RequestQuery::try_from_tm( - &self.storage, + &self.wl_storage, query, ) { Ok(request) => request, @@ -70,7 +70,7 @@ where owner: &Address, ) -> token::Amount { let balance = storage_api::StorageRead::read( - &self.storage, + &self.wl_storage, &token::balance_key(token, owner), ); // Storage read must not fail, but there might be no value, in which @@ -90,11 +90,11 @@ where .try_to_vec() .expect("Serializing public key should not fail"); // get the current epoch - let (current_epoch, _) = self.storage.get_current_epoch(); + let (current_epoch, _) = self.wl_storage.storage.get_current_epoch(); // get the PoS params - let pos_params = self.storage.read_pos_params(); + let pos_params = self.wl_storage.read_pos_params(); // get the active validator set - self.storage + self.wl_storage .read_validator_set() .get(current_epoch) .expect("Validators for the next epoch should be known") @@ -102,34 +102,26 @@ where .iter() .find(|validator| { let pk_key = key::protocol_pk_key(&validator.address); - match self.storage.read(&pk_key) { - Ok((Some(bytes), _)) => bytes == pk_bytes, + match self.wl_storage.read_bytes(&pk_key) { + Ok(Some(bytes)) => bytes == pk_bytes, _ => false, } }) .map(|validator| { let dkg_key = key::dkg_session_keys::dkg_pk_key(&validator.address); - let bytes = self - .storage + let dkg_publickey: DkgPublicKey = self + .wl_storage .read(&dkg_key) .expect("Validator should have public dkg key") - .0 .expect("Validator should have public dkg key"); - let dkg_publickey = - &::deserialize( - &mut bytes.as_ref(), - ) - .expect( - "DKG public key in storage should be deserializable", - ); TendermintValidator { power: into_tm_voting_power( pos_params.tm_votes_per_token, validator.bonded_stake, ) as u64, address: validator.address.to_string(), - public_key: dkg_publickey.into(), + public_key: (&dkg_publickey).into(), } }) } diff --git a/apps/src/lib/node/ledger/storage/mod.rs b/apps/src/lib/node/ledger/storage/mod.rs index d7db889ada3..a5d0fed81d7 100644 --- a/apps/src/lib/node/ledger/storage/mod.rs +++ b/apps/src/lib/node/ledger/storage/mod.rs @@ -51,8 +51,7 @@ fn new_blake2b() -> Blake2b { #[cfg(test)] mod tests { use itertools::Itertools; - use namada::ledger::storage::testing::TestWlStorage; - use namada::ledger::storage::types; + use namada::ledger::storage::{types, WlStorage}; use namada::ledger::storage_api::{self, StorageWrite}; use namada::types::chain::ChainId; use namada::types::storage::{BlockHash, BlockHeight, Key}; @@ -359,7 +358,7 @@ mod tests { address::nam(), None, ); - let mut storage = TestWlStorage { + let mut storage = WlStorage { storage, write_log: Default::default(), }; diff --git a/proof_of_stake/src/storage.rs b/proof_of_stake/src/storage.rs index 5e11165c55a..e61fe381d4a 100644 --- a/proof_of_stake/src/storage.rs +++ b/proof_of_stake/src/storage.rs @@ -1,7 +1,7 @@ //! Proof-of-Stake storage keys and storage integration via [`PosBase`] trait. use namada_core::ledger::storage::types::{decode, encode}; -use namada_core::ledger::storage::{self, Storage, StorageHasher}; +use namada_core::ledger::storage::{self, StorageHasher, WlStorage}; use namada_core::types::address::Address; use namada_core::types::storage::{DbKeySeg, Key, KeySeg}; use namada_core::types::{key, token}; @@ -345,7 +345,7 @@ pub fn get_validator_address_from_bond(key: &Key) -> Option

{ } } -impl PosBase for Storage +impl PosBase for WlStorage where D: storage::DB + for<'iter> storage::DBIter<'iter>, H: StorageHasher, @@ -355,11 +355,11 @@ where super::SLASH_POOL_ADDRESS; fn staking_token_address(&self) -> namada_core::types::address::Address { - self.native_token.clone() + self.storage.native_token.clone() } fn read_pos_params(&self) -> PosParams { - let (value, _gas) = self.read(¶ms_key()).unwrap(); + let (value, _gas) = self.storage.read(¶ms_key()).unwrap(); decode(value.unwrap()).unwrap() } @@ -368,6 +368,7 @@ where raw_hash: impl AsRef, ) -> Option { let (value, _gas) = self + .storage .read(&validator_address_raw_hash_key(raw_hash)) .unwrap(); value.map(|value| decode(value).unwrap()) @@ -377,8 +378,10 @@ where &self, key: &namada_core::types::address::Address, ) -> Option { - let (value, _gas) = - self.read(&validator_consensus_key_key(key)).unwrap(); + let (value, _gas) = self + .storage + .read(&validator_consensus_key_key(key)) + .unwrap(); value.map(|value| decode(value).unwrap()) } @@ -386,7 +389,8 @@ where &self, key: &namada_core::types::address::Address, ) -> Option { - let (value, _gas) = self.read(&validator_state_key(key)).unwrap(); + let (value, _gas) = + self.storage.read(&validator_state_key(key)).unwrap(); value.map(|value| decode(value).unwrap()) } @@ -394,7 +398,8 @@ where &self, key: &namada_core::types::address::Address, ) -> Option { - let (value, _gas) = self.read(&validator_deltas_key(key)).unwrap(); + let (value, _gas) = + self.storage.read(&validator_deltas_key(key)).unwrap(); value.map(|value| decode(value).unwrap()) } @@ -402,7 +407,8 @@ where &self, key: &namada_core::types::address::Address, ) -> types::Slashes { - let (value, _gas) = self.read(&validator_slashes_key(key)).unwrap(); + let (value, _gas) = + self.storage.read(&validator_slashes_key(key)).unwrap(); value .map(|value| decode(value).unwrap()) .unwrap_or_default() @@ -412,8 +418,10 @@ where &self, key: &namada_core::types::address::Address, ) -> CommissionRates { - let (value, _gas) = - self.read(&validator_commission_rate_key(key)).unwrap(); + let (value, _gas) = self + .storage + .read(&validator_commission_rate_key(key)) + .unwrap(); decode(value.unwrap()).unwrap() } @@ -422,23 +430,24 @@ where key: &namada_core::types::address::Address, ) -> Decimal { let (value, _gas) = self + .storage .read(&validator_max_commission_rate_change_key(key)) .unwrap(); decode(value.unwrap()).unwrap() } fn read_validator_set(&self) -> ValidatorSets { - let (value, _gas) = self.read(&validator_set_key()).unwrap(); + let (value, _gas) = self.storage.read(&validator_set_key()).unwrap(); decode(value.unwrap()).unwrap() } fn read_total_deltas(&self) -> TotalDeltas { - let (value, _gas) = self.read(&total_deltas_key()).unwrap(); + let (value, _gas) = self.storage.read(&total_deltas_key()).unwrap(); decode(value.unwrap()).unwrap() } fn write_pos_params(&mut self, params: &PosParams) { - self.write(¶ms_key(), encode(params)).unwrap(); + self.storage.write(¶ms_key(), encode(params)).unwrap(); } fn write_validator_address_raw_hash( @@ -447,7 +456,8 @@ where consensus_key: &namada_core::types::key::common::PublicKey, ) { let raw_hash = key::tm_consensus_key_raw_hash(consensus_key); - self.write(&validator_address_raw_hash_key(raw_hash), encode(address)) + self.storage + .write(&validator_address_raw_hash_key(raw_hash), encode(address)) .unwrap(); } @@ -456,7 +466,8 @@ where key: &namada_core::types::address::Address, value: &CommissionRates, ) { - self.write(&validator_commission_rate_key(key), encode(value)) + self.storage + .write(&validator_commission_rate_key(key), encode(value)) .unwrap(); } @@ -465,11 +476,12 @@ where key: &namada_core::types::address::Address, value: &rust_decimal::Decimal, ) { - self.write( - &validator_max_commission_rate_change_key(key), - encode(value), - ) - .unwrap(); + self.storage + .write( + &validator_max_commission_rate_change_key(key), + encode(value), + ) + .unwrap(); } fn write_validator_consensus_key( @@ -477,7 +489,8 @@ where key: &namada_core::types::address::Address, value: &ValidatorConsensusKeys, ) { - self.write(&validator_consensus_key_key(key), encode(value)) + self.storage + .write(&validator_consensus_key_key(key), encode(value)) .unwrap(); } @@ -486,7 +499,8 @@ where key: &namada_core::types::address::Address, value: &ValidatorStates, ) { - self.write(&validator_state_key(key), encode(value)) + self.storage + .write(&validator_state_key(key), encode(value)) .unwrap(); } @@ -495,7 +509,8 @@ where key: &namada_core::types::address::Address, value: &ValidatorDeltas, ) { - self.write(&validator_deltas_key(key), encode(value)) + self.storage + .write(&validator_deltas_key(key), encode(value)) .unwrap(); } @@ -506,20 +521,25 @@ where ) { let mut slashes = PosBase::read_validator_slashes(self, validator); slashes.push(value); - self.write(&validator_slashes_key(validator), encode(&slashes)) + self.storage + .write(&validator_slashes_key(validator), encode(&slashes)) .unwrap(); } fn write_bond(&mut self, key: &BondId, value: &Bonds) { - self.write(&bond_key(key), encode(value)).unwrap(); + self.storage.write(&bond_key(key), encode(value)).unwrap(); } fn write_validator_set(&mut self, value: &ValidatorSets) { - self.write(&validator_set_key(), encode(value)).unwrap(); + self.storage + .write(&validator_set_key(), encode(value)) + .unwrap(); } fn write_total_deltas(&mut self, value: &TotalDeltas) { - self.write(&total_deltas_key(), encode(value)).unwrap(); + self.storage + .write(&total_deltas_key(), encode(value)) + .unwrap(); } fn credit_tokens( @@ -530,6 +550,7 @@ where ) { let key = token::balance_key(token, target); let new_balance = match self + .storage .read(&key) .expect("Unable to read token balance for PoS system") { @@ -540,7 +561,8 @@ where } _ => amount, }; - self.write(&key, encode(&new_balance)) + self.storage + .write(&key, encode(&new_balance)) .expect("Unable to write token balance for PoS system"); } @@ -554,6 +576,7 @@ where let src_key = token::balance_key(token, src); let dest_key = token::balance_key(token, dest); if let (Some(src_balance), _gas) = self + .storage .read(&src_key) .expect("Unable to read token balance for PoS system") { @@ -569,15 +592,18 @@ where return; } src_balance.spend(&amount); - let (dest_balance, _gas) = self.read(&dest_key).unwrap_or_default(); + let (dest_balance, _gas) = + self.storage.read(&dest_key).unwrap_or_default(); let mut dest_balance: namada_core::types::token::Amount = dest_balance .and_then(|b| decode(b).ok()) .unwrap_or_default(); dest_balance.receive(&amount); - self.write(&src_key, encode(&src_balance)) + self.storage + .write(&src_key, encode(&src_balance)) .expect("Unable to write token balance for PoS system"); - self.write(&dest_key, encode(&dest_balance)) + self.storage + .write(&dest_key, encode(&dest_balance)) .expect("Unable to write token balance for PoS system"); } else { tracing::error!( @@ -710,7 +736,7 @@ macro_rules! impl_pos_read_only { } impl_pos_read_only! { - impl PosReadOnly for Storage + impl PosReadOnly for WlStorage where DB: storage::DB + for<'iter> storage::DBIter<'iter> +'static, H: StorageHasher +'static, diff --git a/shared/src/ledger/native_vp/governance/mod.rs b/shared/src/ledger/native_vp/governance/mod.rs index f6b3187073a..b383d67484b 100644 --- a/shared/src/ledger/native_vp/governance/mod.rs +++ b/shared/src/ledger/native_vp/governance/mod.rs @@ -531,7 +531,7 @@ where /// Validate a governance parameter pub fn is_valid_parameter(&self, tx_data: &[u8]) -> Result { - utils::is_proposal_accepted(self.ctx.storage, tx_data) + utils::is_proposal_accepted(&self.ctx.pre(), tx_data) .map_err(Error::NativeVpError) } diff --git a/shared/src/ledger/native_vp/governance/utils.rs b/shared/src/ledger/native_vp/governance/utils.rs index 4fd0a682174..611edd82e7c 100644 --- a/shared/src/ledger/native_vp/governance/utils.rs +++ b/shared/src/ledger/native_vp/governance/utils.rs @@ -1,7 +1,6 @@ //! Governance utility functions use std::collections::HashMap; -use std::str::FromStr; use borsh::BorshDeserialize; use namada_proof_of_stake::PosReadOnly; @@ -9,11 +8,10 @@ use thiserror::Error; use crate::ledger::governance::storage as gov_storage; use crate::ledger::pos::BondId; -use crate::ledger::storage::{DBIter, Storage, StorageHasher, DB}; use crate::ledger::storage_api; use crate::types::address::Address; use crate::types::governance::{ProposalVote, TallyResult, VotePower}; -use crate::types::storage::{Epoch, Key}; +use crate::types::storage::Epoch; use crate::types::token; /// Proposal structure holding votes information necessary to compute the @@ -75,14 +73,13 @@ impl ProposalEvent { } /// Return a proposal result - accepted only when the result is `Ok(true)`. -pub fn compute_tally( - storage: &Storage, +pub fn compute_tally( + storage: &S, epoch: Epoch, votes: Votes, ) -> storage_api::Result where - D: DB + for<'iter> DBIter<'iter> + Sync + 'static, - H: StorageHasher + Sync + 'static, + S: storage_api::StorageRead + PosReadOnly, { let total_stake: VotePower = storage.total_stake(epoch)?.into(); @@ -119,20 +116,20 @@ where } /// Prepare Votes structure to compute proposal tally -pub fn get_proposal_votes( - storage: &Storage, +pub fn get_proposal_votes( + storage: &S, epoch: Epoch, proposal_id: u64, ) -> storage_api::Result where - D: DB + for<'iter> DBIter<'iter> + Sync + 'static, - H: StorageHasher + Sync + 'static, + S: storage_api::StorageRead + PosReadOnly, { let validators = storage.validator_addresses(epoch)?; let vote_prefix_key = gov_storage::get_proposal_vote_prefix_key(proposal_id); - let (vote_iter, _) = storage.iter_prefix(&vote_prefix_key); + let vote_iter = + storage_api::iter_prefix::(storage, &vote_prefix_key)?; let mut yay_validators = HashMap::new(); let mut yay_delegators: HashMap> = @@ -140,59 +137,51 @@ where let mut nay_delegators: HashMap> = HashMap::new(); - for (key, vote_bytes, _) in vote_iter { - let vote_key = Key::from_str(key.as_str()).ok(); - let vote = ProposalVote::try_from_slice(&vote_bytes[..]).ok(); - match (vote_key, vote) { - (Some(key), Some(vote)) => { - let voter_address = gov_storage::get_voter_address(&key); - match voter_address { - Some(voter_address) => { - if vote.is_yay() && validators.contains(voter_address) { - let amount: VotePower = storage - .validator_stake(voter_address, epoch)? - .into(); - yay_validators - .insert(voter_address.clone(), amount); - } else if !validators.contains(voter_address) { - let validator_address = - gov_storage::get_vote_delegation_address(&key); - match validator_address { - Some(validator) => { - let bond_id = BondId { - source: voter_address.clone(), - validator: validator.clone(), - }; - let amount = - storage.bond_amount(&bond_id, epoch)?; - if amount != token::Amount::default() { - if vote.is_yay() { - let entry = yay_delegators - .entry(voter_address.to_owned()) - .or_default(); - entry.insert( - validator.to_owned(), - VotePower::from(amount), - ); - } else { - let entry = nay_delegators - .entry(voter_address.to_owned()) - .or_default(); - entry.insert( - validator.to_owned(), - VotePower::from(amount), - ); - } - } + for next_vote in vote_iter { + let (vote_key, vote) = next_vote?; + let voter_address = gov_storage::get_voter_address(&vote_key); + match voter_address { + Some(voter_address) => { + if vote.is_yay() && validators.contains(voter_address) { + let amount: VotePower = + storage.validator_stake(voter_address, epoch)?.into(); + yay_validators.insert(voter_address.clone(), amount); + } else if !validators.contains(voter_address) { + let validator_address = + gov_storage::get_vote_delegation_address(&vote_key); + match validator_address { + Some(validator) => { + let bond_id = BondId { + source: voter_address.clone(), + validator: validator.clone(), + }; + let amount = + storage.bond_amount(&bond_id, epoch)?; + if amount != token::Amount::default() { + if vote.is_yay() { + let entry = yay_delegators + .entry(voter_address.to_owned()) + .or_default(); + entry.insert( + validator.to_owned(), + VotePower::from(amount), + ); + } else { + let entry = nay_delegators + .entry(voter_address.to_owned()) + .or_default(); + entry.insert( + validator.to_owned(), + VotePower::from(amount), + ); } - None => continue, } } + None => continue, } - None => continue, } } - _ => continue, + None => continue, } } diff --git a/shared/src/ledger/native_vp/parameters.rs b/shared/src/ledger/native_vp/parameters.rs index 0a762ca4f67..6c41ba8dc3d 100644 --- a/shared/src/ledger/native_vp/parameters.rs +++ b/shared/src/ledger/native_vp/parameters.rs @@ -52,7 +52,7 @@ where let key_type: KeyType = key.into(); match key_type { KeyType::PARAMETER => governance::utils::is_proposal_accepted( - self.ctx.storage, + &self.ctx.pre(), tx_data, ) .unwrap_or(false), diff --git a/shared/src/ledger/native_vp/slash_fund.rs b/shared/src/ledger/native_vp/slash_fund.rs index 49507056470..4caf0f3401f 100644 --- a/shared/src/ledger/native_vp/slash_fund.rs +++ b/shared/src/ledger/native_vp/slash_fund.rs @@ -61,7 +61,7 @@ where return true; } governance::utils::is_proposal_accepted( - self.ctx.storage, + &self.ctx.pre(), tx_data, ) .unwrap_or(false) diff --git a/shared/src/ledger/pos/mod.rs b/shared/src/ledger/pos/mod.rs index 2878f89fb2f..bcf8517aef5 100644 --- a/shared/src/ledger/pos/mod.rs +++ b/shared/src/ledger/pos/mod.rs @@ -10,7 +10,6 @@ use namada_proof_of_stake::PosBase; use rust_decimal::Decimal; pub use vp::PosVP; -use crate::ledger::storage::{self as ledger_storage, Storage, StorageHasher}; use crate::types::address::{Address, InternalAddress}; use crate::types::storage::Epoch; @@ -32,14 +31,13 @@ pub fn into_tm_voting_power( } /// Initialize storage in the genesis block. -pub fn init_genesis_storage<'a, DB, H>( - storage: &mut Storage, +pub fn init_genesis_storage<'a, S>( + storage: &mut S, params: &'a PosParams, validators: impl Iterator + Clone + 'a, current_epoch: Epoch, ) where - DB: ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, - H: StorageHasher, + S: PosBase, { storage .init_genesis(params, validators, current_epoch) diff --git a/shared/src/ledger/pos/vp.rs b/shared/src/ledger/pos/vp.rs index 2a2e87bdba2..92e1837412e 100644 --- a/shared/src/ledger/pos/vp.rs +++ b/shared/src/ledger/pos/vp.rs @@ -123,7 +123,7 @@ where for key in keys_changed { if is_params_key(key) { return governance::utils::is_proposal_accepted( - self.ctx.storage, + &self.ctx.pre(), tx_data, ) .map_err(Error::NativeVpError); diff --git a/shared/src/ledger/queries/mod.rs b/shared/src/ledger/queries/mod.rs index 131e5ce572f..565100b81b4 100644 --- a/shared/src/ledger/queries/mod.rs +++ b/shared/src/ledger/queries/mod.rs @@ -58,7 +58,7 @@ where H: 'static + StorageHasher + Sync, { if request.height != BlockHeight(0) - && request.height != ctx.storage.last_height + && request.height != ctx.wl_storage.storage.last_height { return Err(storage_api::Error::new_const( "This query doesn't support arbitrary block heights, only the \ @@ -159,7 +159,7 @@ mod testing { use super::*; use crate::ledger::events::log::EventLog; - use crate::ledger::storage::testing::TestStorage; + use crate::ledger::storage::testing::TestWlStorage; use crate::types::storage::BlockHeight; use crate::vm::wasm::{self, TxCache, VpCache}; use crate::vm::WasmCacheRoAccess; @@ -172,7 +172,7 @@ mod testing { /// RPC router pub rpc: RPC, /// storage - pub storage: TestStorage, + pub wl_storage: TestWlStorage, /// event log pub event_log: EventLog, /// VP wasm compilation cache @@ -193,7 +193,7 @@ mod testing { /// Initialize a test client for the given root RPC router pub fn new(rpc: RPC) -> Self { // Initialize the `TestClient` - let storage = TestStorage::default(); + let wl_storage = TestWlStorage::default(); let event_log = EventLog::default(); let (vp_wasm_cache, vp_cache_dir) = wasm::compilation_cache::common::testing::cache(); @@ -201,7 +201,7 @@ mod testing { wasm::compilation_cache::common::testing::cache(); Self { rpc, - storage, + wl_storage, event_log, vp_wasm_cache: vp_wasm_cache.read_only(), tx_wasm_cache: tx_wasm_cache.read_only(), @@ -236,7 +236,7 @@ mod testing { prove, }; let ctx = RequestCtx { - storage: &self.storage, + wl_storage: &self.wl_storage, event_log: &self.event_log, vp_wasm_cache: self.vp_wasm_cache.clone(), tx_wasm_cache: self.tx_wasm_cache.clone(), diff --git a/shared/src/ledger/queries/router.rs b/shared/src/ledger/queries/router.rs index 9bf1116a5c6..b05be9bb559 100644 --- a/shared/src/ledger/queries/router.rs +++ b/shared/src/ledger/queries/router.rs @@ -1007,7 +1007,7 @@ mod test { }; let ctx = RequestCtx { event_log: &client.event_log, - storage: &client.storage, + wl_storage: &client.wl_storage, vp_wasm_cache: client.vp_wasm_cache.clone(), tx_wasm_cache: client.tx_wasm_cache.clone(), storage_read_past_height_limit: None, diff --git a/shared/src/ledger/queries/shell.rs b/shared/src/ledger/queries/shell.rs index 61f43a6289c..43a51a1b0f2 100644 --- a/shared/src/ledger/queries/shell.rs +++ b/shared/src/ledger/queries/shell.rs @@ -90,7 +90,7 @@ where TxIndex(0), &mut gas_meter, &mut write_log, - ctx.storage, + &ctx.wl_storage.storage, &mut ctx.vp_wasm_cache, &mut ctx.tx_wasm_cache, ) @@ -111,9 +111,11 @@ where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - let (iter, _gas) = ctx.storage.iter_results(); - let mut results = - vec![BlockResults::default(); ctx.storage.block.height.0 as usize + 1]; + let (iter, _gas) = ctx.wl_storage.storage.iter_results(); + let mut results = vec![ + BlockResults::default(); + ctx.wl_storage.storage.block.height.0 as usize + 1 + ]; iter.for_each(|(key, value, _gas)| { let key = key .parse::() @@ -135,8 +137,12 @@ where H: 'static + StorageHasher + Sync, { // Conversion values are constructed on request - if let Some((addr, epoch, conv, pos)) = - ctx.storage.conversion_state.assets.get(&asset_type) + if let Some((addr, epoch, conv, pos)) = ctx + .wl_storage + .storage + .conversion_state + .assets + .get(&asset_type) { Ok(( addr.clone(), @@ -144,7 +150,7 @@ where Into::::into( conv.clone(), ), - ctx.storage.conversion_state.tree.path(*pos), + ctx.wl_storage.storage.conversion_state.tree.path(*pos), )) } else { Err(storage_api::Error::new(std::io::Error::new( @@ -171,7 +177,7 @@ where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - let data = ctx.storage.last_epoch; + let data = ctx.wl_storage.storage.last_epoch; Ok(data) } @@ -189,7 +195,9 @@ where H: 'static + StorageHasher + Sync, { if let Some(past_height_limit) = ctx.storage_read_past_height_limit { - if request.height.0 + past_height_limit < ctx.storage.last_height.0 { + if request.height.0 + past_height_limit + < ctx.wl_storage.storage.last_height.0 + { return Err(storage_api::Error::new(std::io::Error::new( std::io::ErrorKind::InvalidInput, format!( @@ -202,6 +210,7 @@ where } match ctx + .wl_storage .storage .read_with_height(&storage_key, request.height) .into_storage_result()? @@ -209,6 +218,7 @@ where (Some(value), _gas) => { let proof = if request.prove { let proof = ctx + .wl_storage .storage .get_existence_proof(&storage_key, &value, request.height) .into_storage_result()?; @@ -225,6 +235,7 @@ where (None, _gas) => { let proof = if request.prove { let proof = ctx + .wl_storage .storage .get_non_existence_proof(&storage_key, request.height) .into_storage_result()?; @@ -252,7 +263,7 @@ where { require_latest_height(&ctx, request)?; - let iter = storage_api::iter_prefix_bytes(ctx.storage, &storage_key)?; + let iter = storage_api::iter_prefix_bytes(ctx.wl_storage, &storage_key)?; let data: storage_api::Result> = iter .map(|iter_result| { let (key, value) = iter_result?; @@ -264,6 +275,7 @@ where let mut ops = vec![]; for PrefixValue { key, value } in &data { let mut proof: crate::tendermint::merkle::proof::Proof = ctx + .wl_storage .storage .get_existence_proof(key, value, request.height) .into_storage_result()?; @@ -291,7 +303,7 @@ where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - let data = StorageRead::has_key(ctx.storage, &storage_key)?; + let data = StorageRead::has_key(ctx.wl_storage, &storage_key)?; Ok(data) } @@ -370,7 +382,7 @@ mod test { // Request last committed epoch let read_epoch = RPC.shell().epoch(&client).await.unwrap(); - let current_epoch = client.storage.last_epoch; + let current_epoch = client.wl_storage.storage.last_epoch; assert_eq!(current_epoch, read_epoch); // Request dry run tx @@ -415,7 +427,10 @@ mod test { // Then write some balance ... let balance = token::Amount::from(1000); - StorageWrite::write(&mut client.storage, &balance_key, balance)?; + StorageWrite::write(&mut client.wl_storage, &balance_key, balance)?; + // It has to be committed to be visible in a query + client.wl_storage.commit_tx(); + client.wl_storage.commit_block().unwrap(); // ... there should be the same value now let read_balance = RPC .shell() diff --git a/shared/src/ledger/queries/types.rs b/shared/src/ledger/queries/types.rs index 0a15319af1c..fd65edcfd92 100644 --- a/shared/src/ledger/queries/types.rs +++ b/shared/src/ledger/queries/types.rs @@ -1,5 +1,7 @@ +use namada_core::ledger::storage::WlStorage; + use crate::ledger::events::log::EventLog; -use crate::ledger::storage::{DBIter, Storage, StorageHasher, DB}; +use crate::ledger::storage::{DBIter, StorageHasher, DB}; use crate::ledger::storage_api; use crate::tendermint::merkle::proof::Proof; use crate::types::storage::BlockHeight; @@ -16,8 +18,8 @@ where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - /// Reference to the ledger's [`Storage`]. - pub storage: &'shell Storage, + /// Reference to the ledger's [`WlStorage`]. + pub wl_storage: &'shell WlStorage, /// Log of events emitted by `FinalizeBlock` ABCI calls. pub event_log: &'shell EventLog, /// Cache of VP wasm compiled artifacts. @@ -146,7 +148,7 @@ impl RequestQuery { /// to use the latest committed block height as per tendermint ABCI Query /// spec. A negative block height will cause an error. pub fn try_from_tm( - storage: &Storage, + storage: &WlStorage, crate::tendermint_proto::abci::RequestQuery { data, path, @@ -161,7 +163,7 @@ impl RequestQuery { let height = match height { 0 => { // `0` means last committed height - storage.last_height + storage.storage.last_height } _ => BlockHeight(height.try_into().map_err(|_| { format!("Query height cannot be negative, got: {}", height) diff --git a/shared/src/ledger/queries/vp/pos.rs b/shared/src/ledger/queries/vp/pos.rs index 5c3fcf80df4..b6b63874b50 100644 --- a/shared/src/ledger/queries/vp/pos.rs +++ b/shared/src/ledger/queries/vp/pos.rs @@ -43,7 +43,7 @@ where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - ctx.storage.is_validator(&addr) + ctx.wl_storage.is_validator(&addr) } /// Get all the validator known addresses. These validators may be in any state, @@ -56,8 +56,8 @@ where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - let epoch = epoch.unwrap_or(ctx.storage.last_epoch); - ctx.storage.validator_addresses(epoch) + let epoch = epoch.unwrap_or(ctx.wl_storage.storage.last_epoch); + ctx.wl_storage.validator_addresses(epoch) } /// Get the total stake of a validator at the given epoch or current when @@ -72,8 +72,8 @@ where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - let epoch = epoch.unwrap_or(ctx.storage.last_epoch); - ctx.storage.validator_stake(&validator, epoch) + let epoch = epoch.unwrap_or(ctx.wl_storage.storage.last_epoch); + ctx.wl_storage.validator_stake(&validator, epoch) } /// Get the total stake in PoS system at the given epoch or current when `None`. @@ -85,8 +85,8 @@ where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - let epoch = epoch.unwrap_or(ctx.storage.last_epoch); - ctx.storage.total_stake(epoch) + let epoch = epoch.unwrap_or(ctx.wl_storage.storage.last_epoch); + ctx.wl_storage.total_stake(epoch) } /// Get the total bond amount for the given bond ID (this may be delegation or @@ -102,13 +102,13 @@ where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - let epoch = epoch.unwrap_or(ctx.storage.last_epoch); + let epoch = epoch.unwrap_or(ctx.wl_storage.storage.last_epoch); let bond_id = BondId { source: owner, validator, }; - ctx.storage.bond_amount(&bond_id, epoch) + ctx.wl_storage.bond_amount(&bond_id, epoch) } /// Find all the validator addresses to whom the given `owner` address has @@ -125,7 +125,7 @@ where let mut delegations: HashSet
= HashSet::new(); for iter_result in - storage_api::iter_prefix_bytes(ctx.storage, &bonds_prefix)? + storage_api::iter_prefix_bytes(ctx.wl_storage, &bonds_prefix)? { let (key, _bonds_bytes) = iter_result?; let validator_address = pos::get_validator_address_from_bond(&key) From 0f7779e5600a64ca0ab3772f9323b64f8bdb4181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 11 Jan 2023 17:01:18 +0100 Subject: [PATCH 13/18] use storage PrefixIter instead of DB iterator that respects write log --- shared/src/ledger/native_vp/mod.rs | 51 ++++++++------- shared/src/ledger/vp_host_fns.rs | 66 +++++++------------ shared/src/vm/host_env.rs | 102 +++++++++++++++-------------- shared/src/vm/prefix_iter.rs | 16 +++-- shared/src/vm/wasm/host_env.rs | 6 +- shared/src/vm/wasm/run.rs | 2 +- vm_env/src/lib.rs | 37 ++++++----- vp_prelude/src/lib.rs | 57 +++++++++------- 8 files changed, 170 insertions(+), 167 deletions(-) diff --git a/shared/src/ledger/native_vp/mod.rs b/shared/src/ledger/native_vp/mod.rs index e14378c3d35..231405dde54 100644 --- a/shared/src/ledger/native_vp/mod.rs +++ b/shared/src/ledger/native_vp/mod.rs @@ -176,7 +176,7 @@ where H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { - type PrefixIter<'iter> = >::PrefixIter where Self: 'iter; + type PrefixIter<'iter> = storage::PrefixIter<'iter, DB> where Self: 'iter; fn read_bytes( &self, @@ -198,18 +198,21 @@ where vp_host_fns::has_key_pre( &mut self.ctx.gas_meter.borrow_mut(), self.ctx.storage, + self.ctx.write_log, key, ) .into_storage_result() } - fn iter_next<'iter>( + fn iter_prefix<'iter>( &'iter self, - iter: &mut Self::PrefixIter<'iter>, - ) -> Result)>, storage_api::Error> { - vp_host_fns::iter_pre_next::( + prefix: &crate::types::storage::Key, + ) -> Result, storage_api::Error> { + vp_host_fns::iter_prefix_pre( &mut self.ctx.gas_meter.borrow_mut(), - iter, + self.ctx.write_log, + self.ctx.storage, + prefix, ) .into_storage_result() } @@ -217,11 +220,12 @@ where // ---- Methods below are implemented in `self.ctx`, because they are // the same in `pre/post` ---- - fn iter_prefix<'iter>( + fn iter_next<'iter>( &'iter self, - prefix: &crate::types::storage::Key, - ) -> Result, storage_api::Error> { - self.ctx.iter_prefix(prefix) + iter: &mut Self::PrefixIter<'iter>, + ) -> Result)>, storage_api::Error> { + vp_host_fns::iter_next::(&mut self.ctx.gas_meter.borrow_mut(), iter) + .into_storage_result() } fn get_chain_id(&self) -> Result { @@ -256,7 +260,7 @@ where H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { - type PrefixIter<'iter> = >::PrefixIter where Self:'iter; + type PrefixIter<'iter> = storage::PrefixIter<'iter, DB> where Self: 'iter; fn read_bytes( &self, @@ -284,14 +288,15 @@ where .into_storage_result() } - fn iter_next<'iter>( + fn iter_prefix<'iter>( &'iter self, - iter: &mut Self::PrefixIter<'iter>, - ) -> Result)>, storage_api::Error> { - vp_host_fns::iter_post_next::( + prefix: &crate::types::storage::Key, + ) -> Result, storage_api::Error> { + vp_host_fns::iter_prefix_post( &mut self.ctx.gas_meter.borrow_mut(), self.ctx.write_log, - iter, + self.ctx.storage, + prefix, ) .into_storage_result() } @@ -299,11 +304,12 @@ where // ---- Methods below are implemented in `self.ctx`, because they are // the same in `pre/post` ---- - fn iter_prefix<'iter>( + fn iter_next<'iter>( &'iter self, - prefix: &crate::types::storage::Key, - ) -> Result, storage_api::Error> { - self.ctx.iter_prefix(prefix) + iter: &mut Self::PrefixIter<'iter>, + ) -> Result)>, storage_api::Error> { + vp_host_fns::iter_next::(&mut self.ctx.gas_meter.borrow_mut(), iter) + .into_storage_result() } fn get_chain_id(&self) -> Result { @@ -339,7 +345,7 @@ where { type Post = CtxPostStorageRead<'view, 'a, DB, H, CA>; type Pre = CtxPreStorageRead<'view, 'a, DB, H, CA>; - type PrefixIter<'iter> = >::PrefixIter where Self: 'iter; + type PrefixIter<'iter> = storage::PrefixIter<'iter, DB> where Self: 'iter; fn pre(&'view self) -> Self::Pre { CtxPreStorageRead { ctx: self } @@ -426,8 +432,9 @@ where &'iter self, prefix: &Key, ) -> Result, storage_api::Error> { - vp_host_fns::iter_prefix( + vp_host_fns::iter_prefix_pre( &mut self.gas_meter.borrow_mut(), + self.write_log, self.storage, prefix, ) diff --git a/shared/src/ledger/vp_host_fns.rs b/shared/src/ledger/vp_host_fns.rs index 336981b2afc..5bcbd69cf41 100644 --- a/shared/src/ledger/vp_host_fns.rs +++ b/shared/src/ledger/vp_host_fns.rs @@ -311,71 +311,51 @@ where Ok(storage.native_token.clone()) } -/// Storage prefix iterator, ordered by storage keys. It will try to get an -/// iterator from the storage. -pub fn iter_prefix<'a, DB, H>( +/// Storage prefix iterator for prior state (before tx execution), ordered by +/// storage keys. It will try to get an iterator from the storage. +pub fn iter_prefix_pre<'a, DB, H>( gas_meter: &mut VpGasMeter, + write_log: &'a WriteLog, storage: &'a Storage, prefix: &Key, -) -> EnvResult<>::PrefixIter> +) -> EnvResult> where DB: storage::DB + for<'iter> storage::DBIter<'iter>, H: StorageHasher, { - let (iter, gas) = storage.iter_prefix(prefix); + let (iter, gas) = storage::iter_prefix_pre(write_log, storage, prefix); add_gas(gas_meter, gas)?; Ok(iter) } -/// Storage prefix iterator for prior state (before tx execution). It will try -/// to read from the storage. -pub fn iter_pre_next( +/// Storage prefix iterator for posterior state (after tx execution), ordered by +/// storage keys. It will try to get an iterator from the storage. +pub fn iter_prefix_post<'a, DB, H>( gas_meter: &mut VpGasMeter, - iter: &mut >::PrefixIter, -) -> EnvResult)>> + write_log: &'a WriteLog, + storage: &'a Storage, + prefix: &Key, +) -> EnvResult> where DB: storage::DB + for<'iter> storage::DBIter<'iter>, + H: StorageHasher, { - if let Some((key, val, gas)) = iter.next() { - add_gas(gas_meter, gas)?; - return Ok(Some((key, val))); - } - Ok(None) + let (iter, gas) = storage::iter_prefix_post(write_log, storage, prefix); + add_gas(gas_meter, gas)?; + Ok(iter) } -/// Storage prefix iterator next for posterior state (after tx execution). It -/// will try to read from the write log first and if no entry found then from -/// the storage. -pub fn iter_post_next( +/// Get the next item in a storage prefix iterator (pre or post). +pub fn iter_next( gas_meter: &mut VpGasMeter, - write_log: &WriteLog, - iter: &mut >::PrefixIter, + iter: &mut storage::PrefixIter, ) -> EnvResult)>> where DB: storage::DB + for<'iter> storage::DBIter<'iter>, { - for (key, val, iter_gas) in iter { - let (log_val, log_gas) = write_log.read( - &Key::parse(key.clone()).map_err(RuntimeError::StorageDataError)?, - ); - add_gas(gas_meter, iter_gas + log_gas)?; - match log_val { - Some(&write_log::StorageModification::Write { ref value }) => { - return Ok(Some((key, value.clone()))); - } - Some(&write_log::StorageModification::Delete) => { - // check the next because the key has already deleted - continue; - } - Some(&write_log::StorageModification::InitAccount { .. }) => { - // a VP of a new account doesn't need to be iterated - continue; - } - Some(&write_log::StorageModification::Temp { .. }) => { - return Err(RuntimeError::ReadTemporaryValueError); - } - None => return Ok(Some((key, val))), - } + if let Some((key, val, gas)) = iter.next() { + add_gas(gas_meter, gas)?; + return Ok(Some((key, val))); } Ok(None) } diff --git a/shared/src/vm/host_env.rs b/shared/src/vm/host_env.rs index ee8e8a36014..2ceaee746ff 100644 --- a/shared/src/vm/host_env.rs +++ b/shared/src/vm/host_env.rs @@ -696,7 +696,7 @@ pub fn tx_iter_prefix( ) -> TxResult where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -706,15 +706,17 @@ where .map_err(|e| TxRuntimeError::MemoryError(Box::new(e)))?; tx_add_gas(env, gas)?; - tracing::debug!("tx_iter_prefix {}, prefix {}", prefix, prefix_ptr); + tracing::debug!("tx_iter_prefix {}", prefix); let prefix = Key::parse(prefix).map_err(TxRuntimeError::StorageDataError)?; + let write_log = unsafe { env.ctx.write_log.get() }; let storage = unsafe { env.ctx.storage.get() }; - let iterators = unsafe { env.ctx.iterators.get() }; - let (iter, gas) = storage.iter_prefix(&prefix); + let (iter, gas) = storage::iter_prefix_post(write_log, storage, &prefix); tx_add_gas(env, gas)?; + + let iterators = unsafe { env.ctx.iterators.get() }; Ok(iterators.insert(iter).id()) } @@ -1183,7 +1185,9 @@ where let key = Key::parse(key).map_err(vp_host_fns::RuntimeError::StorageDataError)?; let storage = unsafe { env.ctx.storage.get() }; - let present = vp_host_fns::has_key_pre(gas_meter, storage, &key)?; + let write_log = unsafe { env.ctx.write_log.get() }; + let present = + vp_host_fns::has_key_pre(gas_meter, storage, write_log, &key)?; Ok(HostEnvResult::from(present).to_i64()) } @@ -1220,17 +1224,18 @@ where Ok(HostEnvResult::from(present).to_i64()) } -/// Storage prefix iterator function exposed to the wasm VM VP environment. -/// It will try to get an iterator from the storage and return the corresponding -/// ID of the iterator, ordered by storage keys. -pub fn vp_iter_prefix( +/// Storage prefix iterator function for prior state (before tx execution) +/// exposed to the wasm VM VP environment. It will try to get an iterator from +/// the storage and return the corresponding ID of the iterator, ordered by +/// storage keys. +pub fn vp_iter_prefix_pre( env: &VpVmEnv, prefix_ptr: u64, prefix_len: u64, ) -> vp_host_fns::EnvResult where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -1242,63 +1247,63 @@ where let gas_meter = unsafe { env.ctx.gas_meter.get() }; vp_host_fns::add_gas(gas_meter, gas)?; + tracing::debug!("vp_iter_prefix_pre {}", prefix); + let prefix = Key::parse(prefix) .map_err(vp_host_fns::RuntimeError::StorageDataError)?; - tracing::debug!("vp_iter_prefix {}", prefix); + let write_log = unsafe { env.ctx.write_log.get() }; let storage = unsafe { env.ctx.storage.get() }; - let iter = vp_host_fns::iter_prefix(gas_meter, storage, &prefix)?; + let iter = + vp_host_fns::iter_prefix_pre(gas_meter, write_log, storage, &prefix)?; + let iterators = unsafe { env.ctx.iterators.get() }; Ok(iterators.insert(iter).id()) } -/// Storage prefix iterator for prior state (before tx execution) function -/// exposed to the wasm VM VP environment. It will try to read from the storage. -/// -/// Returns `-1` when the key is not present, or the length of the data when -/// the key is present (the length may be `0`). -pub fn vp_iter_pre_next( +/// Storage prefix iterator function for posterior state (after tx execution) +/// exposed to the wasm VM VP environment. It will try to get an iterator from +/// the storage and return the corresponding ID of the iterator, ordered by +/// storage keys. +pub fn vp_iter_prefix_post( env: &VpVmEnv, - iter_id: u64, -) -> vp_host_fns::EnvResult + prefix_ptr: u64, + prefix_len: u64, +) -> vp_host_fns::EnvResult where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, { - tracing::debug!("vp_iter_pre_next iter_id {}", iter_id); + let (prefix, gas) = env + .memory + .read_string(prefix_ptr, prefix_len as _) + .map_err(|e| vp_host_fns::RuntimeError::MemoryError(Box::new(e)))?; + let gas_meter = unsafe { env.ctx.gas_meter.get() }; + vp_host_fns::add_gas(gas_meter, gas)?; + + tracing::debug!("vp_iter_prefix_post {}", prefix); + + let prefix = Key::parse(prefix) + .map_err(vp_host_fns::RuntimeError::StorageDataError)?; + + let write_log = unsafe { env.ctx.write_log.get() }; + let storage = unsafe { env.ctx.storage.get() }; + let iter = + vp_host_fns::iter_prefix_post(gas_meter, write_log, storage, &prefix)?; let iterators = unsafe { env.ctx.iterators.get() }; - let iter_id = PrefixIteratorId::new(iter_id); - if let Some(iter) = iterators.get_mut(iter_id) { - let gas_meter = unsafe { env.ctx.gas_meter.get() }; - if let Some((key, val)) = - vp_host_fns::iter_pre_next::(gas_meter, iter)? - { - let key_val = KeyVal { key, val } - .try_to_vec() - .map_err(vp_host_fns::RuntimeError::EncodingError)?; - let len: i64 = key_val - .len() - .try_into() - .map_err(vp_host_fns::RuntimeError::NumConversionError)?; - let result_buffer = unsafe { env.ctx.result_buffer.get() }; - result_buffer.replace(key_val); - return Ok(len); - } - } - Ok(HostEnvResult::Fail.to_i64()) + Ok(iterators.insert(iter).id()) } -/// Storage prefix iterator next for posterior state (after tx execution) -/// function exposed to the wasm VM VP environment. It will try to read from the -/// write log first and if no entry found then from the storage. +/// Storage prefix iterator for prior or posterior state function +/// exposed to the wasm VM VP environment. /// /// Returns `-1` when the key is not present, or the length of the data when /// the key is present (the length may be `0`). -pub fn vp_iter_post_next( +pub fn vp_iter_next( env: &VpVmEnv, iter_id: u64, ) -> vp_host_fns::EnvResult @@ -1309,16 +1314,13 @@ where EVAL: VpEvaluator, CA: WasmCacheAccess, { - tracing::debug!("vp_iter_post_next iter_id {}", iter_id); + tracing::debug!("vp_iter_next iter_id {}", iter_id); let iterators = unsafe { env.ctx.iterators.get() }; let iter_id = PrefixIteratorId::new(iter_id); if let Some(iter) = iterators.get_mut(iter_id) { let gas_meter = unsafe { env.ctx.gas_meter.get() }; - let write_log = unsafe { env.ctx.write_log.get() }; - if let Some((key, val)) = - vp_host_fns::iter_post_next::(gas_meter, write_log, iter)? - { + if let Some((key, val)) = vp_host_fns::iter_next(gas_meter, iter)? { let key_val = KeyVal { key, val } .try_to_vec() .map_err(vp_host_fns::RuntimeError::EncodingError)?; diff --git a/shared/src/vm/prefix_iter.rs b/shared/src/vm/prefix_iter.rs index e30cc728649..49e58b33ddf 100644 --- a/shared/src/vm/prefix_iter.rs +++ b/shared/src/vm/prefix_iter.rs @@ -3,6 +3,8 @@ use std::collections::HashMap; +use namada_core::ledger::storage::PrefixIter; + use crate::ledger::storage; /// A temporary iterators storage, used during a wasm run after which it's @@ -10,18 +12,18 @@ use crate::ledger::storage; #[derive(Debug)] pub struct PrefixIterators<'iter, DB> where - DB: storage::DBIter<'iter>, + DB: storage::DB + storage::DBIter<'iter>, { index: PrefixIteratorId, - iterators: HashMap, + iterators: HashMap>, } impl<'iter, DB> PrefixIterators<'iter, DB> where - DB: storage::DBIter<'iter>, + DB: storage::DB + storage::DBIter<'iter>, { /// Insert a new prefix iterator to the temporary storage. - pub fn insert(&mut self, iter: DB::PrefixIter) -> PrefixIteratorId { + pub fn insert(&mut self, iter: PrefixIter<'iter, DB>) -> PrefixIteratorId { let id = self.index; self.iterators.insert(id, iter); self.index = id.next_id(); @@ -32,7 +34,7 @@ where pub fn next( &mut self, id: PrefixIteratorId, - ) -> Option<::Item> { + ) -> Option< as Iterator>::Item> { self.iterators.get_mut(&id).and_then(|i| i.next()) } @@ -40,14 +42,14 @@ where pub fn get_mut( &mut self, id: PrefixIteratorId, - ) -> Option<&mut DB::PrefixIter> { + ) -> Option<&mut PrefixIter<'iter, DB>> { self.iterators.get_mut(&id) } } impl<'iter, DB> Default for PrefixIterators<'iter, DB> where - DB: storage::DBIter<'iter>, + DB: storage::DB + storage::DBIter<'iter>, { fn default() -> Self { Self { diff --git a/shared/src/vm/wasm/host_env.rs b/shared/src/vm/wasm/host_env.rs index 28fb2cc12a3..74c87ed69b9 100644 --- a/shared/src/vm/wasm/host_env.rs +++ b/shared/src/vm/wasm/host_env.rs @@ -108,9 +108,9 @@ where "namada_vp_result_buffer" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_result_buffer), "namada_vp_has_key_pre" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_has_key_pre), "namada_vp_has_key_post" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_has_key_post), - "namada_vp_iter_prefix" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_iter_prefix), - "namada_vp_iter_pre_next" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_iter_pre_next), - "namada_vp_iter_post_next" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_iter_post_next), + "namada_vp_iter_prefix_pre" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_iter_prefix_pre), + "namada_vp_iter_prefix_post" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_iter_prefix_pre), + "namada_vp_iter_next" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_iter_next), "namada_vp_get_chain_id" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_get_chain_id), "namada_vp_get_tx_index" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_get_tx_index), "namada_vp_get_block_height" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_get_block_height), diff --git a/shared/src/vm/wasm/run.rs b/shared/src/vm/wasm/run.rs index 233a5f72a0d..0efdac499cb 100644 --- a/shared/src/vm/wasm/run.rs +++ b/shared/src/vm/wasm/run.rs @@ -290,7 +290,7 @@ fn run_vp( } /// Validity predicate wasm evaluator for `eval` host function calls. -#[derive(Default)] +#[derive(Default, Debug)] pub struct VpEvalWasm where DB: storage::DB + for<'iter> storage::DBIter<'iter>, diff --git a/vm_env/src/lib.rs b/vm_env/src/lib.rs index b38feb83f26..e275c840d10 100644 --- a/vm_env/src/lib.rs +++ b/vm_env/src/lib.rs @@ -139,23 +139,26 @@ pub mod vp { // Returns 1 if the key is present in posterior state, -1 otherwise. pub fn namada_vp_has_key_post(key_ptr: u64, key_len: u64) -> i64; - // Get an ID of a data iterator with key prefix, ordered by storage - // keys. - pub fn namada_vp_iter_prefix(prefix_ptr: u64, prefix_len: u64) -> u64; - - // Read variable-length prior state when we don't know the size - // up-front, returns the size of the value (can be 0), or -1 if - // the key is not present. If a value is found, it will be placed in the - // result buffer, because we cannot allocate a buffer for it before - // we know its size. - pub fn namada_vp_iter_pre_next(iter_id: u64) -> i64; - - // Read variable-length posterior state when we don't know the size - // up-front, returns the size of the value (can be 0), or -1 if the - // key is not present. If a value is found, it will be placed in the - // result buffer, because we cannot allocate a buffer for it before - // we know its size. - pub fn namada_vp_iter_post_next(iter_id: u64) -> i64; + // Get an ID of a data iterator with key prefix in prior state, ordered + // by storage keys. + pub fn namada_vp_iter_prefix_pre( + prefix_ptr: u64, + prefix_len: u64, + ) -> u64; + + // Get an ID of a data iterator with key prefix in posterior state, + // ordered by storage keys. + pub fn namada_vp_iter_prefix_post( + prefix_ptr: u64, + prefix_len: u64, + ) -> u64; + + // Read variable-length iterator's next value when we don't know the + // size up-front, returns the size of the value (can be 0), or + // -1 if the key is not present. If a value is found, it will be + // placed in the result buffer, because we cannot allocate a + // buffer for it before we know its size. + pub fn namada_vp_iter_next(iter_id: u64) -> i64; // Get the chain ID pub fn namada_vp_get_chain_id(result_ptr: u64); diff --git a/vp_prelude/src/lib.rs b/vp_prelude/src/lib.rs index 33cb5f900c5..0d0680a2e69 100644 --- a/vp_prelude/src/lib.rs +++ b/vp_prelude/src/lib.rs @@ -262,8 +262,7 @@ impl<'view> VpEnv<'view> for Ctx { &'iter self, prefix: &storage::Key, ) -> Result, Error> { - // Both `CtxPreStorageRead` and `CtxPostStorageRead` have the same impl - iter_prefix_impl(prefix) + iter_prefix_pre_impl(prefix) } fn eval( @@ -334,26 +333,26 @@ impl StorageRead for CtxPreStorageRead<'_> { Ok(HostEnvResult::is_success(found)) } + fn iter_prefix<'iter>( + &'iter self, + prefix: &storage::Key, + ) -> Result, Error> { + iter_prefix_pre_impl(prefix) + } + + // ---- Methods below share the same implementation in `pre/post` ---- + fn iter_next<'iter>( &'iter self, iter: &mut Self::PrefixIter<'iter>, ) -> Result)>, Error> { - let read_result = unsafe { namada_vp_iter_pre_next(iter.0) }; + let read_result = unsafe { namada_vp_iter_next(iter.0) }; Ok(read_key_val_bytes_from_buffer( read_result, namada_vp_result_buffer, )) } - // ---- Methods below share the same implementation in `pre/post` ---- - - fn iter_prefix<'iter>( - &'iter self, - prefix: &storage::Key, - ) -> Result, Error> { - iter_prefix_impl(prefix) - } - fn get_chain_id(&self) -> Result { get_chain_id() } @@ -397,26 +396,26 @@ impl StorageRead for CtxPostStorageRead<'_> { Ok(HostEnvResult::is_success(found)) } + fn iter_prefix<'iter>( + &'iter self, + prefix: &storage::Key, + ) -> Result, Error> { + iter_prefix_post_impl(prefix) + } + + // ---- Methods below share the same implementation in `pre/post` ---- + fn iter_next<'iter>( &'iter self, iter: &mut Self::PrefixIter<'iter>, ) -> Result)>, Error> { - let read_result = unsafe { namada_vp_iter_post_next(iter.0) }; + let read_result = unsafe { namada_vp_iter_next(iter.0) }; Ok(read_key_val_bytes_from_buffer( read_result, namada_vp_result_buffer, )) } - // ---- Methods below share the same implementation in `pre/post` ---- - - fn iter_prefix<'iter>( - &'iter self, - prefix: &storage::Key, - ) -> Result, Error> { - iter_prefix_impl(prefix) - } - fn get_chain_id(&self) -> Result { get_chain_id() } @@ -442,12 +441,22 @@ impl StorageRead for CtxPostStorageRead<'_> { } } -fn iter_prefix_impl( +fn iter_prefix_pre_impl( + prefix: &storage::Key, +) -> Result)>, Error> { + let prefix = prefix.to_string(); + let iter_id = unsafe { + namada_vp_iter_prefix_pre(prefix.as_ptr() as _, prefix.len() as _) + }; + Ok(KeyValIterator(iter_id, PhantomData)) +} + +fn iter_prefix_post_impl( prefix: &storage::Key, ) -> Result)>, Error> { let prefix = prefix.to_string(); let iter_id = unsafe { - namada_vp_iter_prefix(prefix.as_ptr() as _, prefix.len() as _) + namada_vp_iter_prefix_post(prefix.as_ptr() as _, prefix.len() as _) }; Ok(KeyValIterator(iter_id, PhantomData)) } From 9657c7454486a54f5135f506a873ca4ec3e0ae17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 11 Jan 2023 17:01:49 +0100 Subject: [PATCH 14/18] tests: update tests to use WlStorage --- .../storage_api/collections/lazy_map.rs | 4 +- .../storage_api/collections/lazy_vec.rs | 4 +- core/src/ledger/testnet_pow.rs | 5 +- tests/src/native_vp/mod.rs | 4 +- tests/src/native_vp/pos.rs | 21 +- tests/src/storage_api/collections/lazy_map.rs | 2 +- tests/src/storage_api/collections/lazy_vec.rs | 2 +- .../collections/nested_lazy_map.rs | 2 +- tests/src/vm_host_env/ibc.rs | 20 +- tests/src/vm_host_env/mod.rs | 184 +++++++++++++----- tests/src/vm_host_env/tx.rs | 77 ++++---- tests/src/vm_host_env/vp.rs | 44 +++-- wasm/wasm_source/src/tx_bond.rs | 2 +- wasm/wasm_source/src/tx_unbond.rs | 2 +- wasm/wasm_source/src/tx_withdraw.rs | 7 +- wasm/wasm_source/src/vp_testnet_faucet.rs | 10 +- 16 files changed, 246 insertions(+), 144 deletions(-) diff --git a/core/src/ledger/storage_api/collections/lazy_map.rs b/core/src/ledger/storage_api/collections/lazy_map.rs index ec4a958002d..28802b79eb0 100644 --- a/core/src/ledger/storage_api/collections/lazy_map.rs +++ b/core/src/ledger/storage_api/collections/lazy_map.rs @@ -512,11 +512,11 @@ where #[cfg(test)] mod test { use super::*; - use crate::ledger::storage::testing::TestStorage; + use crate::ledger::storage::testing::TestWlStorage; #[test] fn test_lazy_map_basics() -> storage_api::Result<()> { - let mut storage = TestStorage::default(); + let mut storage = TestWlStorage::default(); let key = storage::Key::parse("test").unwrap(); let lazy_map = LazyMap::::open(key); diff --git a/core/src/ledger/storage_api/collections/lazy_vec.rs b/core/src/ledger/storage_api/collections/lazy_vec.rs index 0e0a5ab03bf..47b5c95c754 100644 --- a/core/src/ledger/storage_api/collections/lazy_vec.rs +++ b/core/src/ledger/storage_api/collections/lazy_vec.rs @@ -476,11 +476,11 @@ where #[cfg(test)] mod test { use super::*; - use crate::ledger::storage::testing::TestStorage; + use crate::ledger::storage::testing::TestWlStorage; #[test] fn test_lazy_vec_basics() -> storage_api::Result<()> { - let mut storage = TestStorage::default(); + let mut storage = TestWlStorage::default(); let key = storage::Key::parse("test").unwrap(); let lazy_vec = LazyVec::::open(key); diff --git a/core/src/ledger/testnet_pow.rs b/core/src/ledger/testnet_pow.rs index e40eb7a96e5..bed2273a706 100644 --- a/core/src/ledger/testnet_pow.rs +++ b/core/src/ledger/testnet_pow.rs @@ -523,14 +523,15 @@ mod test_with_tx_and_vp_env { tx_env.spawn_accounts([&faucet_address, &source]); init_faucet_storage( - &mut tx_env.storage, + &mut tx_env.wl_storage, &faucet_address, difficulty, withdrawal_limit, )?; + tx_env.commit_genesis(); let challenge = Challenge::new( - &mut tx_env.storage, + &mut tx_env.wl_storage, &faucet_address, source.clone(), )?; diff --git a/tests/src/native_vp/mod.rs b/tests/src/native_vp/mod.rs index 28dad14557b..6baf214a3be 100644 --- a/tests/src/native_vp/mod.rs +++ b/tests/src/native_vp/mod.rs @@ -49,8 +49,8 @@ impl TestNativeVpEnv { let ctx = Ctx { iterators: Default::default(), gas_meter: Default::default(), - storage: &self.tx_env.storage, - write_log: &self.tx_env.write_log, + storage: &self.tx_env.wl_storage.storage, + write_log: &self.tx_env.wl_storage.write_log, tx: &self.tx_env.tx, tx_index: &self.tx_env.tx_index, vp_wasm_cache: self.tx_env.vp_wasm_cache.clone(), diff --git a/tests/src/native_vp/pos.rs b/tests/src/native_vp/pos.rs index 69b7502a505..29460107bb9 100644 --- a/tests/src/native_vp/pos.rs +++ b/tests/src/native_vp/pos.rs @@ -117,17 +117,20 @@ pub fn init_pos( tx_host_env::with(|tx_env| { // Ensure that all the used // addresses exist - let native_token = tx_env.storage.native_token.clone(); + let native_token = tx_env.wl_storage.storage.native_token.clone(); tx_env.spawn_accounts([&native_token]); for validator in genesis_validators { tx_env.spawn_accounts([&validator.address]); } - tx_env.storage.block.epoch = start_epoch; + tx_env.wl_storage.storage.block.epoch = start_epoch; // Initialize PoS storage tx_env - .storage + .wl_storage .init_genesis(params, genesis_validators.iter(), start_epoch) .unwrap(); + + // Commit changes in WL to genesis state + tx_env.commit_genesis(); }); } @@ -248,7 +251,7 @@ mod tests { if !test_state.is_current_tx_valid { // Clear out the changes tx_host_env::with(|env| { - env.write_log.drop_tx(); + env.wl_storage.drop_tx(); }); } @@ -262,13 +265,13 @@ mod tests { tx_host_env::with(|env| { // Clear out the changes if !test_state.is_current_tx_valid { - env.write_log.drop_tx(); + env.wl_storage.drop_tx(); } // Also commit the last transaction(s) changes, if any env.commit_tx_and_block(); - env.storage.block.epoch = - env.storage.block.epoch.next(); + env.wl_storage.storage.block.epoch = + env.wl_storage.storage.block.epoch.next(); }); // Starting a new tx @@ -298,7 +301,7 @@ mod tests { // Clear out the invalid changes tx_host_env::with(|env| { - env.write_log.drop_tx(); + env.wl_storage.drop_tx(); }) } } @@ -851,7 +854,7 @@ pub mod testing { // Reset the gas meter on each change, so that we never run // out in this test env.gas_meter.reset(); - env.storage.block.epoch + env.wl_storage.storage.block.epoch }); println!("Current epoch {}", current_epoch); diff --git a/tests/src/storage_api/collections/lazy_map.rs b/tests/src/storage_api/collections/lazy_map.rs index afff09bbf1d..5268cf8b100 100644 --- a/tests/src/storage_api/collections/lazy_map.rs +++ b/tests/src/storage_api/collections/lazy_map.rs @@ -241,7 +241,7 @@ mod tests { match &transition { Transition::CommitTx => { // commit the tx without committing the block - tx_host_env::with(|env| env.write_log.commit_tx()); + tx_host_env::with(|env| env.wl_storage.commit_tx()); } Transition::CommitTxAndBlock => { // commit the tx and the block diff --git a/tests/src/storage_api/collections/lazy_vec.rs b/tests/src/storage_api/collections/lazy_vec.rs index 65e08b4ca74..b93c04885cb 100644 --- a/tests/src/storage_api/collections/lazy_vec.rs +++ b/tests/src/storage_api/collections/lazy_vec.rs @@ -235,7 +235,7 @@ mod tests { match &transition { Transition::CommitTx => { // commit the tx without committing the block - tx_host_env::with(|env| env.write_log.commit_tx()); + tx_host_env::with(|env| env.wl_storage.commit_tx()); } Transition::CommitTxAndBlock => { // commit the tx and the block diff --git a/tests/src/storage_api/collections/nested_lazy_map.rs b/tests/src/storage_api/collections/nested_lazy_map.rs index 9951eb318bd..b0ff35094c2 100644 --- a/tests/src/storage_api/collections/nested_lazy_map.rs +++ b/tests/src/storage_api/collections/nested_lazy_map.rs @@ -254,7 +254,7 @@ mod tests { match &transition { Transition::CommitTx => { // commit the tx without committing the block - tx_host_env::with(|env| env.write_log.commit_tx()); + tx_host_env::with(|env| env.wl_storage.commit_tx()); } Transition::CommitTxAndBlock => { // commit the tx and the block diff --git a/tests/src/vm_host_env/ibc.rs b/tests/src/vm_host_env/ibc.rs index 88f2d205a9d..1e604801860 100644 --- a/tests/src/vm_host_env/ibc.rs +++ b/tests/src/vm_host_env/ibc.rs @@ -115,6 +115,7 @@ pub fn validate_ibc_vp_from_tx<'a>( tx: &'a Tx, ) -> std::result::Result { let (verifiers, keys_changed) = tx_env + .wl_storage .write_log .verifiers_and_changed_keys(&tx_env.verifiers); let addr = Address::Internal(InternalAddress::Ibc); @@ -129,8 +130,8 @@ pub fn validate_ibc_vp_from_tx<'a>( let ctx = Ctx::new( &ADDRESS, - &tx_env.storage, - &tx_env.write_log, + &tx_env.wl_storage.storage, + &tx_env.wl_storage.write_log, tx, &TxIndex(0), VpGasMeter::new(0), @@ -150,6 +151,7 @@ pub fn validate_token_vp_from_tx<'a>( target: &Key, ) -> std::result::Result { let (verifiers, keys_changed) = tx_env + .wl_storage .write_log .verifiers_and_changed_keys(&tx_env.verifiers); if !keys_changed.contains(target) { @@ -164,8 +166,8 @@ pub fn validate_token_vp_from_tx<'a>( let ctx = Ctx::new( &ADDRESS, - &tx_env.storage, - &tx_env.write_log, + &tx_env.wl_storage.storage, + &tx_env.wl_storage.write_log, tx, &TxIndex(0), VpGasMeter::new(0), @@ -181,10 +183,14 @@ pub fn validate_token_vp_from_tx<'a>( /// Initialize the test storage. Requires initialized [`tx_host_env::ENV`]. pub fn init_storage() -> (Address, Address) { tx_host_env::with(|env| { - init_genesis_storage(&mut env.storage); + init_genesis_storage(&mut env.wl_storage.storage); // block header to check timeout timestamp - env.storage.set_header(tm_dummy_header()).unwrap(); - env.storage + env.wl_storage + .storage + .set_header(tm_dummy_header()) + .unwrap(); + env.wl_storage + .storage .begin_block(BlockHash::default(), BlockHeight(1)) .unwrap(); }); diff --git a/tests/src/vm_host_env/mod.rs b/tests/src/vm_host_env/mod.rs index 1ef6c9bc220..3f4adf6df5b 100644 --- a/tests/src/vm_host_env/mod.rs +++ b/tests/src/vm_host_env/mod.rs @@ -166,10 +166,8 @@ mod tests { tx_host_env::with(|env| { for i in sub_keys.iter() { let key = prefix.push(i).unwrap(); - let value = i.try_to_vec().unwrap(); - env.storage.write(&key, value).unwrap(); + env.wl_storage.write(&key, i).unwrap(); } - env.storage.commit_block().unwrap(); }); // Then try to iterate over their prefix @@ -234,23 +232,35 @@ mod tests { assert_eq!( tx::ctx().get_chain_id().unwrap(), - tx_host_env::with(|env| env.storage.get_chain_id().0) + tx_host_env::with(|env| env.wl_storage.storage.get_chain_id().0) ); assert_eq!( tx::ctx().get_block_height().unwrap(), - tx_host_env::with(|env| env.storage.get_block_height().0) + tx_host_env::with(|env| env + .wl_storage + .storage + .get_block_height() + .0) ); assert_eq!( tx::ctx().get_block_hash().unwrap(), - tx_host_env::with(|env| env.storage.get_block_hash().0) + tx_host_env::with(|env| env.wl_storage.storage.get_block_hash().0) ); assert_eq!( tx::ctx().get_block_epoch().unwrap(), - tx_host_env::with(|env| env.storage.get_current_epoch().0) + tx_host_env::with(|env| env + .wl_storage + .storage + .get_current_epoch() + .0) ); assert_eq!( tx::ctx().get_native_token().unwrap(), - tx_host_env::with(|env| env.storage.native_token.clone()) + tx_host_env::with(|env| env + .wl_storage + .storage + .native_token + .clone()) ); } @@ -264,10 +274,7 @@ mod tests { let key_raw = "key"; let key = storage::Key::parse(key_raw).unwrap(); let value = "test".to_string(); - let value_raw = value.try_to_vec().unwrap(); - vp_host_env::with(|env| { - env.write_log.write(&key, value_raw.clone()).unwrap() - }); + vp_host_env::with(|env| env.wl_storage.write(&key, &value).unwrap()); let read_pre_value: Option = vp::CTX.read_pre(&key).unwrap(); assert_eq!(None, read_pre_value); @@ -282,16 +289,16 @@ mod tests { let addr = address::testing::established_address_1(); let addr_key = storage::Key::from(addr.to_db_key()); - // Write some value to storage + // Write some value to storage ... let existing_key = addr_key.join(&Key::parse("existing_key_raw").unwrap()); let existing_value = vec![2_u8; 1000]; - // Values written to storage have to be encoded with Borsh - let existing_value_encoded = existing_value.try_to_vec().unwrap(); tx_env - .storage - .write(&existing_key, existing_value_encoded) + .wl_storage + .write(&existing_key, &existing_value) .unwrap(); + // ... and commit it + tx_env.wl_storage.commit_tx(); // In a transaction, write override the existing key's value and add // another key-value @@ -371,13 +378,13 @@ mod tests { // We'll write sub-key in some random order to check prefix iter's order let sub_keys = [2_i32, 1, i32::MAX, -1, 260, -2, i32::MIN, 5, 0]; - // Write some values to storage + // Write some values to storage ... for i in sub_keys.iter() { let key = prefix.push(i).unwrap(); - let value = i.try_to_vec().unwrap(); - tx_env.storage.write(&key, value).unwrap(); + tx_env.wl_storage.write(&key, i).unwrap(); } - tx_env.storage.commit_block().unwrap(); + // ... and commit them + tx_env.wl_storage.commit_tx(); // In a transaction, write override the existing key's value and add // another key-value @@ -431,7 +438,7 @@ mod tests { let pk_key = key::pk_key(&addr); let keypair = key::testing::keypair_1(); let pk = keypair.ref_to(); - env.storage + env.wl_storage .write(&pk_key, pk.try_to_vec().unwrap()) .unwrap(); // Initialize the environment @@ -478,23 +485,35 @@ mod tests { assert_eq!( vp::CTX.get_chain_id().unwrap(), - vp_host_env::with(|env| env.storage.get_chain_id().0) + vp_host_env::with(|env| env.wl_storage.storage.get_chain_id().0) ); assert_eq!( vp::CTX.get_block_height().unwrap(), - vp_host_env::with(|env| env.storage.get_block_height().0) + vp_host_env::with(|env| env + .wl_storage + .storage + .get_block_height() + .0) ); assert_eq!( vp::CTX.get_block_hash().unwrap(), - vp_host_env::with(|env| env.storage.get_block_hash().0) + vp_host_env::with(|env| env.wl_storage.storage.get_block_hash().0) ); assert_eq!( vp::CTX.get_block_epoch().unwrap(), - vp_host_env::with(|env| env.storage.get_current_epoch().0) + vp_host_env::with(|env| env + .wl_storage + .storage + .get_current_epoch() + .0) ); assert_eq!( vp::CTX.get_native_token().unwrap(), - vp_host_env::with(|env| env.storage.native_token.clone()) + vp_host_env::with(|env| env + .wl_storage + .storage + .native_token + .clone()) ); } @@ -569,7 +588,7 @@ mod tests { IbcError::ClientError(_), )); // drop the transaction - env.write_log.drop_tx(); + env.wl_storage.drop_tx(); // Start a transaction to create a new client tx_host_env::set(env); @@ -596,10 +615,14 @@ mod tests { // Commit env.commit_tx_and_block(); // update the block height for the following client update - env.storage + env.wl_storage + .storage .begin_block(BlockHash::default(), BlockHeight(2)) .unwrap(); - env.storage.set_header(tm_dummy_header()).unwrap(); + env.wl_storage + .storage + .set_header(tm_dummy_header()) + .unwrap(); // Start an invalid transaction tx_host_env::set(env); @@ -647,7 +670,7 @@ mod tests { IbcError::ClientError(_), )); // drop the transaction - env.write_log.drop_tx(); + env.wl_storage.drop_tx(); // Start a transaction to update the client tx_host_env::set(env); @@ -673,10 +696,14 @@ mod tests { // Commit env.commit_tx_and_block(); // update the block height for the following client update - env.storage + env.wl_storage + .storage .begin_block(BlockHash::default(), BlockHeight(3)) .unwrap(); - env.storage.set_header(tm_dummy_header()).unwrap(); + env.wl_storage + .storage + .set_header(tm_dummy_header()) + .unwrap(); // Start a transaction to upgrade the client tx_host_env::set(env); @@ -710,7 +737,10 @@ mod tests { let (client_id, client_state, writes) = ibc::prepare_client(); writes.into_iter().for_each(|(key, val)| { tx_host_env::with(|env| { - env.storage.write(&key, &val).expect("write error"); + env.wl_storage + .storage + .write(&key, &val) + .expect("write error"); }); }); @@ -751,7 +781,7 @@ mod tests { IbcError::ConnectionError(_), )); // drop the transaction - env.write_log.drop_tx(); + env.wl_storage.drop_tx(); // Start a transaction for ConnectionOpenInit tx_host_env::set(env); @@ -777,7 +807,10 @@ mod tests { // Commit env.commit_tx_and_block(); // set a block header again - env.storage.set_header(tm_dummy_header()).unwrap(); + env.wl_storage + .storage + .set_header(tm_dummy_header()) + .unwrap(); // Start the next transaction for ConnectionOpenAck tx_host_env::set(env); @@ -812,7 +845,10 @@ mod tests { let mut env = tx_host_env::take(); let (client_id, client_state, writes) = ibc::prepare_client(); writes.into_iter().for_each(|(key, val)| { - env.storage.write(&key, &val).expect("write error"); + env.wl_storage + .storage + .write(&key, &val) + .expect("write error"); }); // Start a transaction for ConnectionOpenTry @@ -839,7 +875,10 @@ mod tests { // Commit env.commit_tx_and_block(); // set a block header again - env.storage.set_header(tm_dummy_header()).unwrap(); + env.wl_storage + .storage + .set_header(tm_dummy_header()) + .unwrap(); // Start the next transaction for ConnectionOpenConfirm tx_host_env::set(env); @@ -876,7 +915,10 @@ mod tests { writes.extend(conn_writes); writes.into_iter().for_each(|(key, val)| { tx_host_env::with(|env| { - env.storage.write(&key, &val).expect("write error"); + env.wl_storage + .storage + .write(&key, &val) + .expect("write error"); }); }); @@ -918,7 +960,7 @@ mod tests { IbcError::ChannelError(_), )); // drop the transaction - env.write_log.drop_tx(); + env.wl_storage.drop_tx(); // Start an invalid transaction tx_host_env::set(env); @@ -965,7 +1007,7 @@ mod tests { IbcError::ChannelError(_), )); // drop the transaction - env.write_log.drop_tx(); + env.wl_storage.drop_tx(); // Start a transaction for ChannelOpenInit tx_host_env::set(env); @@ -1026,7 +1068,10 @@ mod tests { writes.extend(conn_writes); writes.into_iter().for_each(|(key, val)| { tx_host_env::with(|env| { - env.storage.write(&key, &val).expect("write error"); + env.wl_storage + .storage + .write(&key, &val) + .expect("write error"); }); }); @@ -1092,7 +1137,10 @@ mod tests { writes.extend(channel_writes); writes.into_iter().for_each(|(key, val)| { tx_host_env::with(|env| { - env.storage.write(&key, &val).expect("write error"); + env.wl_storage + .storage + .write(&key, &val) + .expect("write error"); }); }); @@ -1132,7 +1180,10 @@ mod tests { writes.extend(channel_writes); writes.into_iter().for_each(|(key, val)| { tx_host_env::with(|env| { - env.storage.write(&key, &val).expect("write error"); + env.wl_storage + .storage + .write(&key, &val) + .expect("write error"); }); }); @@ -1173,7 +1224,10 @@ mod tests { writes.extend(channel_writes); writes.into_iter().for_each(|(key, val)| { tx_host_env::with(|env| { - env.storage.write(&key, &val).expect("write error"); + env.wl_storage + .storage + .write(&key, &val) + .expect("write error"); }); }); @@ -1263,7 +1317,10 @@ mod tests { writes.insert(key, init_bal.try_to_vec().unwrap()); writes.into_iter().for_each(|(key, val)| { tx_host_env::with(|env| { - env.storage.write(&key, &val).expect("write error"); + env.wl_storage + .storage + .write(&key, &val) + .expect("write error"); }); }); @@ -1322,7 +1379,10 @@ mod tests { writes.into_iter().for_each(|(key, val)| { tx_host_env::with(|env| { - env.storage.write(&key, &val).expect("write error"); + env.wl_storage + .storage + .write(&key, &val) + .expect("write error"); }); }); @@ -1380,7 +1440,10 @@ mod tests { writes.extend(channel_writes); writes.into_iter().for_each(|(key, val)| { tx_host_env::with(|env| { - env.storage.write(&key, &val).expect("write error"); + env.wl_storage + .storage + .write(&key, &val) + .expect("write error"); }); }); // escrow in advance @@ -1392,7 +1455,10 @@ mod tests { ); let val = Amount::from(1_000_000_000u64).try_to_vec().unwrap(); tx_host_env::with(|env| { - env.storage.write(&escrow, &val).expect("write error"); + env.wl_storage + .storage + .write(&escrow, &val) + .expect("write error"); }); // Set this chain as the source zone @@ -1451,7 +1517,10 @@ mod tests { writes.extend(channel_writes); writes.into_iter().for_each(|(key, val)| { tx_host_env::with(|env| { - env.storage.write(&key, &val).expect("write error"); + env.wl_storage + .storage + .write(&key, &val) + .expect("write error"); }); }); @@ -1526,7 +1595,10 @@ mod tests { writes.extend(channel_writes); writes.into_iter().for_each(|(key, val)| { tx_host_env::with(|env| { - env.storage.write(&key, &val).expect("write error"); + env.wl_storage + .storage + .write(&key, &val) + .expect("write error"); }); }); @@ -1577,7 +1649,10 @@ mod tests { writes.extend(channel_writes); writes.into_iter().for_each(|(key, val)| { tx_host_env::with(|env| { - env.storage.write(&key, &val).expect("write error"); + env.wl_storage + .storage + .write(&key, &val) + .expect("write error"); }) }); @@ -1650,7 +1725,10 @@ mod tests { writes.extend(channel_writes); writes.into_iter().for_each(|(key, val)| { tx_host_env::with(|env| { - env.storage.write(&key, &val).expect("write error"); + env.wl_storage + .storage + .write(&key, &val) + .expect("write error"); }) }); diff --git a/tests/src/vm_host_env/tx.rs b/tests/src/vm_host_env/tx.rs index 0f7040941db..6c3ccd55ae7 100644 --- a/tests/src/vm_host_env/tx.rs +++ b/tests/src/vm_host_env/tx.rs @@ -1,12 +1,12 @@ use std::borrow::Borrow; use std::collections::BTreeSet; -use derivative::Derivative; use namada::ledger::gas::BlockGasMeter; use namada::ledger::parameters::{self, EpochDuration}; use namada::ledger::storage::mockdb::MockDB; use namada::ledger::storage::testing::TestStorage; use namada::ledger::storage::write_log::WriteLog; +use namada::ledger::storage::{Sha256Hasher, WlStorage}; use namada::proto::Tx; use namada::types::address::Address; use namada::types::storage::{Key, TxIndex}; @@ -41,12 +41,9 @@ pub mod tx_host_env { } /// Host environment structures required for transactions. -#[derive(Derivative)] -#[derivative(Debug)] +#[derive(Debug)] pub struct TestTxEnv { - #[derivative(Debug = "ignore")] - pub storage: TestStorage, - pub write_log: WriteLog, + pub wl_storage: WlStorage, pub iterators: PrefixIterators<'static, MockDB>, pub verifiers: BTreeSet
, pub gas_meter: BlockGasMeter, @@ -66,8 +63,10 @@ impl Default for TestTxEnv { wasm::compilation_cache::common::testing::cache(); Self { - storage: TestStorage::default(), - write_log: WriteLog::default(), + wl_storage: WlStorage { + storage: TestStorage::default(), + write_log: WriteLog::default(), + }, iterators: PrefixIterators::default(), gas_meter: BlockGasMeter::default(), tx_index: TxIndex::default(), @@ -84,11 +83,14 @@ impl Default for TestTxEnv { impl TestTxEnv { pub fn all_touched_storage_keys(&self) -> BTreeSet { - self.write_log.get_keys() + self.wl_storage.write_log.get_keys() } pub fn get_verifiers(&self) -> BTreeSet
{ - self.write_log.verifiers_and_changed_keys(&self.verifiers).0 + self.wl_storage + .write_log + .verifiers_and_changed_keys(&self.verifiers) + .0 } pub fn init_parameters( @@ -98,18 +100,18 @@ impl TestTxEnv { tx_whitelist: Option>, ) { let _ = parameters::update_epoch_parameter( - &mut self.storage, + &mut self.wl_storage.storage, &epoch_duration.unwrap_or(EpochDuration { min_num_of_blocks: 1, min_duration: DurationSecs(5), }), ); let _ = parameters::update_tx_whitelist_parameter( - &mut self.storage, + &mut self.wl_storage.storage, tx_whitelist.unwrap_or_default(), ); let _ = parameters::update_vp_whitelist_parameter( - &mut self.storage, + &mut self.wl_storage.storage, vp_whitelist.unwrap_or_default(), ); } @@ -133,16 +135,23 @@ impl TestTxEnv { } let key = Key::validity_predicate(address.borrow()); let vp_code = vec![]; - self.storage + self.wl_storage + .storage .write(&key, vp_code) .expect("Unable to write VP"); } } + /// Commit the genesis state. Typically, you'll want to call this after + /// setting up the initial state, before running a transaction. + pub fn commit_genesis(&mut self) { + self.wl_storage.commit_genesis().unwrap(); + } + pub fn commit_tx_and_block(&mut self) { - self.write_log.commit_tx(); - self.write_log - .commit_block(&mut self.storage) + self.wl_storage.commit_tx(); + self.wl_storage + .commit_block() .map_err(|err| println!("{:?}", err)) .ok(); self.iterators = PrefixIterators::default(); @@ -166,7 +175,8 @@ impl TestTxEnv { } None => token::balance_key(token, target), }; - self.storage + self.wl_storage + .storage .write(&storage_key, amount.try_to_vec().unwrap()) .unwrap(); } @@ -178,7 +188,8 @@ impl TestTxEnv { public_key: &key::common::PublicKey, ) { let storage_key = key::pk_key(address); - self.storage + self.wl_storage + .storage .write(&storage_key, public_key.try_to_vec().unwrap()) .unwrap(); } @@ -187,8 +198,8 @@ impl TestTxEnv { pub fn execute_tx(&mut self) -> Result<(), Error> { let empty_data = vec![]; wasm::run::tx( - &self.storage, - &mut self.write_log, + &self.wl_storage.storage, + &mut self.wl_storage.write_log, &mut self.gas_meter, &self.tx_index, &self.tx.code, @@ -278,16 +289,14 @@ mod native_tx_host_env { /// changes. pub fn set_from_vp_env(vp_env: TestVpEnv) { let TestVpEnv { - storage, - write_log, + wl_storage, tx, vp_wasm_cache, vp_cache_dir, .. } = vp_env; let tx_env = TestTxEnv { - storage, - write_log, + wl_storage, vp_wasm_cache, vp_cache_dir, tx, @@ -306,13 +315,12 @@ mod native_tx_host_env { #[no_mangle] extern "C" fn extern_fn_name( $($arg: $type),* ) { with(|TestTxEnv { - storage, - write_log, + wl_storage, iterators, verifiers, gas_meter, - result_buffer, - tx_index, + result_buffer, + tx_index, vp_wasm_cache, vp_cache_dir: _, tx_wasm_cache, @@ -321,8 +329,8 @@ mod native_tx_host_env { }: &mut TestTxEnv| { let tx_env = vm::host_env::testing::tx_env( - storage, - write_log, + &wl_storage.storage, + &mut wl_storage.write_log, iterators, verifiers, gas_meter, @@ -347,8 +355,7 @@ mod native_tx_host_env { extern "C" fn extern_fn_name( $($arg: $type),* ) -> $ret { with(|TestTxEnv { tx_index, - storage, - write_log, + wl_storage, iterators, verifiers, gas_meter, @@ -361,8 +368,8 @@ mod native_tx_host_env { }: &mut TestTxEnv| { let tx_env = vm::host_env::testing::tx_env( - storage, - write_log, + &wl_storage.storage, + &mut wl_storage.write_log, iterators, verifiers, gas_meter, diff --git a/tests/src/vm_host_env/vp.rs b/tests/src/vm_host_env/vp.rs index 88d9d6ca3cd..4d5bbf3ddec 100644 --- a/tests/src/vm_host_env/vp.rs +++ b/tests/src/vm_host_env/vp.rs @@ -4,6 +4,7 @@ use namada::ledger::gas::VpGasMeter; use namada::ledger::storage::mockdb::MockDB; use namada::ledger::storage::testing::TestStorage; use namada::ledger::storage::write_log::WriteLog; +use namada::ledger::storage::{Sha256Hasher, WlStorage}; use namada::proto::Tx; use namada::types::address::{self, Address}; use namada::types::storage::{self, Key, TxIndex}; @@ -35,10 +36,10 @@ pub mod vp_host_env { } /// Host environment structures required for transactions. +#[derive(Debug)] pub struct TestVpEnv { pub addr: Address, - pub storage: TestStorage, - pub write_log: WriteLog, + pub wl_storage: WlStorage, pub iterators: PrefixIterators<'static, MockDB>, pub gas_meter: VpGasMeter, pub tx: Tx, @@ -65,8 +66,10 @@ impl Default for TestVpEnv { Self { addr: address::testing::established_address_1(), - storage: TestStorage::default(), - write_log: WriteLog::default(), + wl_storage: WlStorage { + storage: TestStorage::default(), + write_log: WriteLog::default(), + }, iterators: PrefixIterators::default(), gas_meter: VpGasMeter::default(), tx: Tx::new(vec![], None), @@ -85,11 +88,14 @@ impl Default for TestVpEnv { impl TestVpEnv { pub fn all_touched_storage_keys(&self) -> BTreeSet { - self.write_log.get_keys() + self.wl_storage.write_log.get_keys() } pub fn get_verifiers(&self) -> BTreeSet
{ - self.write_log.verifiers_and_changed_keys(&self.verifiers).0 + self.wl_storage + .write_log + .verifiers_and_changed_keys(&self.verifiers) + .0 } } @@ -185,7 +191,7 @@ mod native_vp_host_env { // Write an empty validity predicate for the address, because it's used // to check if the address exists when we write into its storage let vp_key = Key::validity_predicate(&addr); - tx_env.storage.write(&vp_key, vec![]).unwrap(); + tx_env.wl_storage.storage.write(&vp_key, vec![]).unwrap(); tx_host_env::set(tx_env); apply_tx(&addr); @@ -193,6 +199,7 @@ mod native_vp_host_env { let tx_env = tx_host_env::take(); let verifiers_from_tx = &tx_env.verifiers; let (verifiers, keys_changed) = tx_env + .wl_storage .write_log .verifiers_and_changed_keys(verifiers_from_tx); if !verifiers.contains(&addr) { @@ -205,8 +212,7 @@ mod native_vp_host_env { let vp_env = TestVpEnv { addr, - storage: tx_env.storage, - write_log: tx_env.write_log, + wl_storage: tx_env.wl_storage, keys_changed, verifiers, ..Default::default() @@ -246,8 +252,7 @@ mod native_vp_host_env { extern "C" fn extern_fn_name( $($arg: $type),* ) { with(|TestVpEnv { addr, - storage, - write_log, + wl_storage, iterators, gas_meter, tx, @@ -264,8 +269,8 @@ mod native_vp_host_env { let env = vm::host_env::testing::vp_env( addr, - storage, - write_log, + &wl_storage.storage, + &wl_storage.write_log, iterators, gas_meter, tx, @@ -294,8 +299,7 @@ mod native_vp_host_env { extern "C" fn extern_fn_name( $($arg: $type),* ) -> $ret { with(|TestVpEnv { addr, - storage, - write_log, + wl_storage, iterators, gas_meter, tx, @@ -312,8 +316,8 @@ mod native_vp_host_env { let env = vm::host_env::testing::vp_env( addr, - storage, - write_log, + &wl_storage.storage, + &wl_storage.write_log, iterators, gas_meter, tx, @@ -343,9 +347,9 @@ mod native_vp_host_env { native_host_fn!(vp_result_buffer(result_ptr: u64)); native_host_fn!(vp_has_key_pre(key_ptr: u64, key_len: u64) -> i64); native_host_fn!(vp_has_key_post(key_ptr: u64, key_len: u64) -> i64); - native_host_fn!(vp_iter_prefix(prefix_ptr: u64, prefix_len: u64) -> u64); - native_host_fn!(vp_iter_pre_next(iter_id: u64) -> i64); - native_host_fn!(vp_iter_post_next(iter_id: u64) -> i64); + native_host_fn!(vp_iter_prefix_pre(prefix_ptr: u64, prefix_len: u64) -> u64); + native_host_fn!(vp_iter_prefix_post(prefix_ptr: u64, prefix_len: u64) -> u64); + native_host_fn!(vp_iter_next(iter_id: u64) -> i64); native_host_fn!(vp_get_chain_id(result_ptr: u64)); native_host_fn!(vp_get_block_height() -> u64); native_host_fn!(vp_get_tx_index() -> u32); diff --git a/wasm/wasm_source/src/tx_bond.rs b/wasm/wasm_source/src/tx_bond.rs index 219a6126304..d91868028c8 100644 --- a/wasm/wasm_source/src/tx_bond.rs +++ b/wasm/wasm_source/src/tx_bond.rs @@ -85,7 +85,7 @@ mod tests { // Ensure that the bond's source has enough tokens for the bond let target = bond.source.as_ref().unwrap_or(&bond.validator); - let native_token = tx_env.storage.native_token.clone(); + let native_token = tx_env.wl_storage.storage.native_token.clone(); tx_env.credit_tokens(target, &native_token, None, bond.amount); native_token }); diff --git a/wasm/wasm_source/src/tx_unbond.rs b/wasm/wasm_source/src/tx_unbond.rs index 70033286bf1..3392d6c174f 100644 --- a/wasm/wasm_source/src/tx_unbond.rs +++ b/wasm/wasm_source/src/tx_unbond.rs @@ -83,7 +83,7 @@ mod tests { init_pos(&genesis_validators[..], &pos_params, Epoch(0)); let native_token = tx_host_env::with(|tx_env| { - let native_token = tx_env.storage.native_token.clone(); + let native_token = tx_env.wl_storage.storage.native_token.clone(); if is_delegation { let source = unbond.source.as_ref().unwrap(); tx_env.spawn_accounts([source]); diff --git a/wasm/wasm_source/src/tx_withdraw.rs b/wasm/wasm_source/src/tx_withdraw.rs index e29415f800d..89ede199d40 100644 --- a/wasm/wasm_source/src/tx_withdraw.rs +++ b/wasm/wasm_source/src/tx_withdraw.rs @@ -92,7 +92,7 @@ mod tests { init_pos(&genesis_validators[..], &pos_params, Epoch(0)); let native_token = tx_host_env::with(|tx_env| { - let native_token = tx_env.storage.native_token.clone(); + let native_token = tx_env.wl_storage.storage.native_token.clone(); if is_delegation { let source = withdraw.source.as_ref().unwrap(); tx_env.spawn_accounts([source]); @@ -134,11 +134,12 @@ mod tests { // withdraw the unbonded tokens tx_host_env::with(|env| { for _ in 0..pos_params.unbonding_len { - env.storage.block.epoch = env.storage.block.epoch.next(); + env.wl_storage.storage.block.epoch = + env.wl_storage.storage.block.epoch.next(); } }); assert_eq!( - tx_host_env::with(|env| env.storage.block.epoch), + tx_host_env::with(|env| env.wl_storage.storage.block.epoch), Epoch(pos_params.unbonding_len) ); diff --git a/wasm/wasm_source/src/vp_testnet_faucet.rs b/wasm/wasm_source/src/vp_testnet_faucet.rs index 8e4e79719f6..1b8802df6e9 100644 --- a/wasm/wasm_source/src/vp_testnet_faucet.rs +++ b/wasm/wasm_source/src/vp_testnet_faucet.rs @@ -291,7 +291,7 @@ mod tests { let vp_owner = address::testing::established_address_1(); let difficulty = testnet_pow::Difficulty::try_new(0).unwrap(); let withdrawal_limit = token::Amount::from(MAX_FREE_DEBIT as u64); - testnet_pow::init_faucet_storage(&mut tx_env.storage, &vp_owner, difficulty, withdrawal_limit).unwrap(); + testnet_pow::init_faucet_storage(&mut tx_env.wl_storage, &vp_owner, difficulty, withdrawal_limit).unwrap(); let target = address::testing::established_address_2(); let token = address::nam(); @@ -303,6 +303,7 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.commit_genesis(); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { @@ -330,7 +331,7 @@ mod tests { let vp_owner = address::testing::established_address_1(); let difficulty = testnet_pow::Difficulty::try_new(0).unwrap(); let withdrawal_limit = token::Amount::from(MAX_FREE_DEBIT as u64); - testnet_pow::init_faucet_storage(&mut tx_env.storage, &vp_owner, difficulty, withdrawal_limit).unwrap(); + testnet_pow::init_faucet_storage(&mut tx_env.wl_storage, &vp_owner, difficulty, withdrawal_limit).unwrap(); let target = address::testing::established_address_2(); let target_key = key::testing::keypair_1(); @@ -343,9 +344,10 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.commit_genesis(); // Construct a PoW solution like a client would - let challenge = testnet_pow::Challenge::new(&mut tx_env.storage, &vp_owner, target.clone()).unwrap(); + let challenge = testnet_pow::Challenge::new(&mut tx_env.wl_storage, &vp_owner, target.clone()).unwrap(); let solution = challenge.solve(); let solution_bytes = solution.try_to_vec().unwrap(); // The signature itself doesn't matter and is not being checked in this @@ -391,7 +393,7 @@ mod tests { // Init the VP let difficulty = testnet_pow::Difficulty::try_new(0).unwrap(); let withdrawal_limit = token::Amount::from(MAX_FREE_DEBIT as u64); - testnet_pow::init_faucet_storage(&mut tx_env.storage, &vp_owner, difficulty, withdrawal_limit).unwrap(); + testnet_pow::init_faucet_storage(&mut tx_env.wl_storage, &vp_owner, difficulty, withdrawal_limit).unwrap(); let keypair = key::testing::keypair_1(); let public_key = &keypair.ref_to(); From cd24137ef3b4a04edfd586a7ea22152f00ed94f4 Mon Sep 17 00:00:00 2001 From: yito88 Date: Wed, 11 Jan 2023 21:10:06 +0100 Subject: [PATCH 15/18] fix IBC VP tests --- shared/src/ledger/ibc/vp/mod.rs | 44 +++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/shared/src/ledger/ibc/vp/mod.rs b/shared/src/ledger/ibc/vp/mod.rs index 8a807ce7549..108942b548f 100644 --- a/shared/src/ledger/ibc/vp/mod.rs +++ b/shared/src/ledger/ibc/vp/mod.rs @@ -548,20 +548,53 @@ mod tests { #[test] fn test_create_client() { - let (storage, mut write_log) = insert_init_states(); + let storage = TestStorage::default(); + let mut write_log = WriteLog::default(); let height = Height::new(0, 1); let header = MockHeader { height, timestamp: Timestamp::now(), }; + let client_id = get_client_id(); + // insert client type, state, and consensus state + let client_type_key = client_type_key(&client_id); + let client_type = ClientType::Mock.as_str().as_bytes().to_vec(); + write_log + .write(&client_type_key, client_type) + .expect("write failed"); let client_state = MockClientState::new(header).wrap_any(); let consensus_state = MockConsensusState::new(header).wrap_any(); let msg = MsgCreateAnyClient { - client_state, - consensus_state, + client_state: client_state.clone(), + consensus_state: consensus_state.clone(), signer: Signer::new("account0"), }; + let client_state_key = client_state_key(&get_client_id()); + let bytes = client_state.encode_vec().expect("encoding failed"); + write_log + .write(&client_state_key, bytes) + .expect("write failed"); + let consensus_key = consensus_state_key(&client_id, height); + let bytes = consensus_state.encode_vec().expect("encoding failed"); + write_log + .write(&consensus_key, bytes) + .expect("write failed"); + // insert update time and height + let client_update_time_key = client_update_timestamp_key(&client_id); + let bytes = TmTime::now().encode_vec().expect("encoding failed"); + write_log + .write(&client_update_time_key, bytes) + .expect("write failed"); + let client_update_height_key = client_update_height_key(&client_id); + let host_height = Height::new(10, 100); + write_log + .write( + &client_update_height_key, + host_height.encode_vec().expect("encoding failed"), + ) + .expect("write failed"); + let event = make_create_client_event(&get_client_id(), &msg); write_log.set_ibc_event(event.try_into().unwrap()); @@ -574,7 +607,6 @@ mod tests { let (vp_wasm_cache, _vp_cache_dir) = wasm::compilation_cache::common::testing::cache(); let mut keys_changed = BTreeSet::new(); - let client_state_key = client_state_key(&get_client_id()); keys_changed.insert(client_state_key); let verifiers = BTreeSet::new(); @@ -1400,7 +1432,6 @@ mod tests { let (storage, mut write_log) = insert_init_states(); // insert a port set_port(&mut write_log, 0); - write_log.commit_tx(); let tx_index = TxIndex::default(); let tx_code = vec![]; @@ -1442,7 +1473,6 @@ mod tests { // insert a port let index = 0; set_port(&mut write_log, index); - write_log.commit_tx(); let tx_index = TxIndex::default(); let tx_code = vec![]; @@ -1888,7 +1918,6 @@ mod tests { ); let ack = PacketAck::result_success().encode_to_vec(); write_log.write(&ack_key, ack).expect("write failed"); - write_log.commit_tx(); let tx_index = TxIndex::default(); let tx_code = vec![]; @@ -1939,7 +1968,6 @@ mod tests { ack_key(&get_port_id(), &get_channel_id(), Sequence::from(1)); let ack = PacketAck::result_success().encode_to_vec(); write_log.write(&ack_key, ack).expect("write failed"); - write_log.commit_tx(); let tx_index = TxIndex::default(); let tx_code = vec![]; From e18afde6c0bc39d9c158b57a984419c7535433e6 Mon Sep 17 00:00:00 2001 From: brentstone Date: Mon, 23 Jan 2023 02:28:24 -0500 Subject: [PATCH 16/18] fix documentation --- core/src/ledger/storage/write_log.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/ledger/storage/write_log.rs b/core/src/ledger/storage/write_log.rs index e7bf72b1d00..a754d876165 100644 --- a/core/src/ledger/storage/write_log.rs +++ b/core/src/ledger/storage/write_log.rs @@ -349,7 +349,7 @@ impl WriteLog { + for<'iter> ledger::storage::DBIter<'iter>, H: StorageHasher, { - // This hole function is almost the same as `commit_block`, except that + // This whole function is almost the same as `commit_block`, except that // we commit the state directly from `tx_write_log` let mut batch = Storage::::batch(); for (key, entry) in self.tx_write_log.iter() { From 1bcfb715030894c9c8c004fb7bc1e4c1cc67e5ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Thu, 12 Jan 2023 11:50:04 +0100 Subject: [PATCH 17/18] changelog: add #913 --- .changelog/unreleased/bug-fixes/913-fix-iter-prefix.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/bug-fixes/913-fix-iter-prefix.md diff --git a/.changelog/unreleased/bug-fixes/913-fix-iter-prefix.md b/.changelog/unreleased/bug-fixes/913-fix-iter-prefix.md new file mode 100644 index 00000000000..a242d36c69b --- /dev/null +++ b/.changelog/unreleased/bug-fixes/913-fix-iter-prefix.md @@ -0,0 +1,2 @@ +- Fixed the prefix iterator method to respect modifications in the write log. + ([#913](https://github.com/anoma/namada/pull/913)) \ No newline at end of file From c317e41ac8a3a44b4794b7928c19d71ad1358e96 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 1 Feb 2023 18:17:59 +0000 Subject: [PATCH 18/18] [ci] wasm checksums update --- wasm/checksums.json | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/wasm/checksums.json b/wasm/checksums.json index c36511452a6..425b24bc202 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,20 +1,20 @@ { - "tx_bond.wasm": "tx_bond.c69b0b6b85a8340473dace3e927bc01be12cb5ae82d3367938d0005522a29479.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.97b0d6f07c9db41320f1006e12e726098c93aad75eb843a575222c8f305462e7.wasm", - "tx_ibc.wasm": "tx_ibc.3637ae4b46f854b288c01e74eccc63b7e45c9c2d1ee3098b698f4039a6010705.wasm", - "tx_init_account.wasm": "tx_init_account.7aa4dbbf0ecad2d079766c47bf6c6d210523d1c4d74d118a02cde6427460a8c8.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.4a977c3d205b68114c6ec8f4ae8d933b768f146759a35de21796395626ca5d43.wasm", - "tx_init_validator.wasm": "tx_init_validator.34b54635942c83de4e4dc94445753ffe55942ebef8a95393ffb10d487e681822.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.054ff210ee793575d75a757bc5c28f88bae037bce99ceb27727e5e3b4c301116.wasm", - "tx_transfer.wasm": "tx_transfer.3fda6e26b50e7aa4b1d6e37fc631d5c55bb9370e6fac71f64f2be137b42df549.wasm", - "tx_unbond.wasm": "tx_unbond.5f4aae1a399f6d556cdf0c85df84b44e4725f0241f7d9ed276f44da08f0f0c84.wasm", - "tx_update_vp.wasm": "tx_update_vp.db4d8c11658c5f8e99fc39f0112be1ee480ab1f0045010d323ee3081a7afe802.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.50c5a13ff8218b1ba79fa7644542af5a9b0bb60316aaa7a630574f9f901f3962.wasm", - "tx_withdraw.wasm": "tx_withdraw.7ff9162d8c5cd40411fff38c2aed47fe0df952fc67f7a9a0c29f624b56d6d88a.wasm", - "vp_implicit.wasm": "vp_implicit.3c4257215de3845937d477f54bdf490bf1cf21acd688852e82277401902882f4.wasm", - "vp_masp.wasm": "vp_masp.08137fd20390a40f106c4ab8ae3a6e548002dff189042aee858c6e4bf10f94e5.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.ddd3135b67e2c116d6915862b93342db9708b9c5b44f5443545910fcea11f5fc.wasm", - "vp_token.wasm": "vp_token.b9622cb39e0141c3a8b9c6d5cba395ca2baf335f1b376079f66ba2bf6b8cd770.wasm", - "vp_user.wasm": "vp_user.f5a3a256d5a4fd12ea736c816e92402beceb1dfea54627209ce408862e290e7c.wasm", - "vp_validator.wasm": "vp_validator.c444b17e75c3a3f7fedf321476a3a7dd4950b131cf8f416a277f57d523613680.wasm" + "tx_bond.wasm": "tx_bond.dd3099e3d4408d5cfbc2d5a4f8b7e8784d30fafe25653852cd314e00a0ab3a90.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.f3cd1434d0e0651d7a916e0eedad7d006e893d735485ca71e43b56e305cb929a.wasm", + "tx_ibc.wasm": "tx_ibc.3f4db4d3270a00fb03107c82fac975101c213c7a26dce4222311f43e0d1f56a9.wasm", + "tx_init_account.wasm": "tx_init_account.a325187897761411205170dad5d6e62b3f23843358178045b11b5bc855d91021.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.e5ecc4d33efbfdfe4f09f716fa9c115cadfb540c546910e16c1edc3d8a201b9f.wasm", + "tx_init_validator.wasm": "tx_init_validator.5d7bc76bb3fbba0758e6ac7d0737782370c34e5e0de5f4e04a97a35dff6033ab.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.9a351c937882e1bf5dc6a45740f47d18b82d89d626785d0795bba53f1940c18a.wasm", + "tx_transfer.wasm": "tx_transfer.c5e6a611da195185fe1002146601f8194076b6a6df2fcfaa5dabd054c11ba6ef.wasm", + "tx_unbond.wasm": "tx_unbond.122252b1faf2c6cd0c4138c1c28e5f1a4c25a8a32d9cae113b9a34796e276ca1.wasm", + "tx_update_vp.wasm": "tx_update_vp.6f3acf7e2e77589187815367eca221e5496bb6f6da5453a58de953dd4eca00cf.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.b2c6a0b4a46013587b682c9089af6ff869ec1bd4a6855e011caf5775a07a44a1.wasm", + "tx_withdraw.wasm": "tx_withdraw.d515807101fa226f1688054e72c102b48d95a17fc59cd8f5a226d99b404ef288.wasm", + "vp_implicit.wasm": "vp_implicit.f1428855df5d67bb915a8081d45cd4b70a3fd1e6f1de88a5dc20d7c0c56b5cd1.wasm", + "vp_masp.wasm": "vp_masp.6f5676f0544badb01c7097e2f74c689473fcb684d732f2745f9c347465d06b3f.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.24f8abc18751fd40cbc40c92346011bb4db8d17bc9cf7b5a416f17cd707cbb13.wasm", + "vp_token.wasm": "vp_token.7522b17d69915cfdbb80fd28be7fc9c16551969b8dece33d95e8cc1c564b5f24.wasm", + "vp_user.wasm": "vp_user.2bf9f9ad541d5c3554812d71e703b8fc1a994b7f3a5fd7226ea1936002094ffe.wasm", + "vp_validator.wasm": "vp_validator.71369ffbb9d12e8696fb6ea69e16c2cc9b40cdd79d93f4289c5799397eb8f175.wasm" } \ No newline at end of file