From dad7b637e6a5de6b462156dfa379b47674078bfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 15 Dec 2023 10:34:10 +0000 Subject: [PATCH 001/118] create trans_token crate from storage_api/token mod --- Cargo.lock | 7 +++ Cargo.toml | 1 + Makefile | 1 + core/src/ledger/storage_api/mod.rs | 1 - trans_token/Cargo.toml | 16 ++++++ trans_token/src/lib.rs | 6 ++ .../token.rs => trans_token/src/storage.rs | 55 ++++++++++++++++--- 7 files changed, 78 insertions(+), 9 deletions(-) create mode 100644 trans_token/Cargo.toml create mode 100644 trans_token/src/lib.rs rename core/src/ledger/storage_api/token.rs => trans_token/src/storage.rs (75%) diff --git a/Cargo.lock b/Cargo.lock index 5504a26399..9e24e332e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4616,6 +4616,13 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "namada_trans_token" +version = "0.28.1" +dependencies = [ + "namada_core", +] + [[package]] name = "namada_tx_prelude" version = "0.29.0" diff --git a/Cargo.toml b/Cargo.toml index bd20510b10..e4950d9a0e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ members = [ "shared", "test_utils", "tests", + "trans_token", "tx_prelude", "vm_env", "vp_prelude", diff --git a/Makefile b/Makefile index 61377f20a7..50942c8ca4 100644 --- a/Makefile +++ b/Makefile @@ -44,6 +44,7 @@ crates += namada_proof_of_stake crates += namada_sdk crates += namada_test_utils crates += namada_tests +crates += namada_trans_token crates += namada_tx_prelude crates += namada_vm_env crates += namada_vp_prelude diff --git a/core/src/ledger/storage_api/mod.rs b/core/src/ledger/storage_api/mod.rs index ad239b2f4c..0111cc9ef5 100644 --- a/core/src/ledger/storage_api/mod.rs +++ b/core/src/ledger/storage_api/mod.rs @@ -7,7 +7,6 @@ mod error; pub mod governance; pub mod key; pub mod pgf; -pub mod token; pub mod tx; pub mod validation; diff --git a/trans_token/Cargo.toml b/trans_token/Cargo.toml new file mode 100644 index 0000000000..b89ec3142b --- /dev/null +++ b/trans_token/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "namada_trans_token" +description = "Namada transparent token" +resolver = "2" +authors.workspace = true +edition.workspace = true +documentation.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +namada_core = {path = "../core", default-features = false} \ No newline at end of file diff --git a/trans_token/src/lib.rs b/trans_token/src/lib.rs new file mode 100644 index 0000000000..50e2c10955 --- /dev/null +++ b/trans_token/src/lib.rs @@ -0,0 +1,6 @@ +//! Transparent token types, storage functions, and validation. + +mod storage; + +pub use namada_core::types::token::*; +pub use storage::*; diff --git a/core/src/ledger/storage_api/token.rs b/trans_token/src/storage.rs similarity index 75% rename from core/src/ledger/storage_api/token.rs rename to trans_token/src/storage.rs index 5d3e244a80..458dbe39a2 100644 --- a/core/src/ledger/storage_api/token.rs +++ b/trans_token/src/storage.rs @@ -1,13 +1,52 @@ -//! Token storage_api functions +use namada_core::ledger::storage_api; +use namada_core::types::address::{Address, InternalAddress}; +use namada_core::types::token; use super::{StorageRead, StorageWrite}; -use crate::ledger::storage_api; -use crate::types::address::{Address, InternalAddress}; -use crate::types::token; -pub use crate::types::token::{ - balance_key, is_any_minted_balance_key, is_balance_key, minted_balance_key, - minter_key, Amount, Change, -}; + +impl token::Parameters { + /// Initialize parameters for the token in storage during the genesis block. + pub fn init_storage( + &self, + address: &Address, + wl_storage: &mut ledger_storage::WlStorage, + ) where + DB: ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, + H: ledger_storage::StorageHasher, + { + let Self { + max_reward_rate: max_rate, + kd_gain_nom, + kp_gain_nom, + locked_ratio_target: locked_target, + } = self; + wl_storage + .write(&masp_last_inflation_key(address), Amount::zero()) + .expect( + "last inflation key for the given asset must be initialized", + ); + wl_storage + .write(&masp_last_locked_ratio_key(address), Dec::zero()) + .expect( + "last locked ratio key for the given asset must be initialized", + ); + wl_storage + .write(&masp_max_reward_rate_key(address), max_rate) + .expect("max reward rate for the given asset must be initialized"); + wl_storage + .write(&masp_locked_ratio_target_key(address), locked_target) + .expect("locked ratio must be initialized"); + wl_storage + .write(&masp_kp_gain_key(address), kp_gain_nom) + .expect("The nominal proportional gain must be initialized"); + wl_storage + .write(&masp_kd_gain_key(address), kd_gain_nom) + .expect("The nominal derivative gain must be initialized"); + wl_storage + .write(&minted_balance_key(address), Amount::zero()) + .expect("The total minted balance key must initialized"); + } +} /// Read the balance of a given token and owner. pub fn read_balance( From d2e240ec6cc98184725edd5fbbda761746d3413e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 15 Dec 2023 10:55:45 +0000 Subject: [PATCH 002/118] trans_token: add storage_key and move relevant fns from core --- core/src/types/token.rs | 322 +-------------------------------- trans_token/src/lib.rs | 1 + trans_token/src/storage_key.rs | 152 ++++++++++++++++ 3 files changed, 156 insertions(+), 319 deletions(-) create mode 100644 trans_token/src/storage_key.rs diff --git a/core/src/types/token.rs b/core/src/types/token.rs index d738086e7b..c2e4f128ad 100644 --- a/core/src/types/token.rs +++ b/core/src/types/token.rs @@ -14,16 +14,13 @@ use thiserror::Error; use super::dec::POS_DECIMAL_PRECISION; use crate::ibc::apps::transfer::types::Amount as IbcAmount; -use crate::ledger::storage as ledger_storage; use crate::ledger::storage_api::token::read_denom; -use crate::ledger::storage_api::{self, StorageRead, StorageWrite}; -use crate::types::address::{ - Address, DecodeError as AddressError, InternalAddress, MASP, -}; +use crate::ledger::storage_api::{self, StorageRead}; +use crate::types::address::{Address, DecodeError as AddressError}; use crate::types::dec::Dec; use crate::types::hash::Hash; use crate::types::storage; -use crate::types::storage::{DbKeySeg, Key, KeySeg}; +use crate::types::storage::{DbKeySeg, KeySeg}; use crate::types::uint::{self, Uint, I256}; /// Amount in micro units. For different granularity another representation @@ -968,110 +965,6 @@ impl From for IbcAmount { } } -/// Key segment for a balance key -pub const BALANCE_STORAGE_KEY: &str = "balance"; -/// Key segment for a denomination key -pub const DENOM_STORAGE_KEY: &str = "denomination"; -/// Key segment for multitoken minter -pub const MINTER_STORAGE_KEY: &str = "minter"; -/// Key segment for minted balance -pub const MINTED_STORAGE_KEY: &str = "minted"; -/// Key segment for head shielded transaction pointer keys -pub const HEAD_TX_KEY: &str = "head-tx"; -/// Key segment prefix for shielded transaction key -pub const TX_KEY_PREFIX: &str = "tx-"; -/// Key segment prefix for pinned shielded transactions -pub const PIN_KEY_PREFIX: &str = "pin-"; -/// Key segment prefix for the nullifiers -pub const MASP_NULLIFIERS_KEY: &str = "nullifiers"; -/// Key segment prefix for the note commitment merkle tree -pub const MASP_NOTE_COMMITMENT_TREE_KEY: &str = "commitment_tree"; -/// Key segment prefix for the note commitment anchor -pub const MASP_NOTE_COMMITMENT_ANCHOR_PREFIX: &str = "note_commitment_anchor"; -/// Key segment prefix for the convert anchor -pub const MASP_CONVERT_ANCHOR_KEY: &str = "convert_anchor"; -/// Last calculated inflation value handed out -pub const MASP_LAST_INFLATION_KEY: &str = "last_inflation"; -/// The last locked ratio -pub const MASP_LAST_LOCKED_RATIO_KEY: &str = "last_locked_ratio"; -/// The key for the nominal proportional gain of a shielded pool for a given -/// asset -pub const MASP_KP_GAIN_KEY: &str = "proportional_gain"; -/// The key for the nominal derivative gain of a shielded pool for a given asset -pub const MASP_KD_GAIN_KEY: &str = "derivative_gain"; -/// The key for the locked ratio target for a given asset -pub const MASP_LOCKED_RATIO_TARGET_KEY: &str = "locked_ratio_target"; -/// The key for the max reward rate for a given asset -pub const MASP_MAX_REWARD_RATE_KEY: &str = "max_reward_rate"; - -/// Gets the key for the given token address, error with the given -/// message to expect if the key is not in the address -pub fn key_of_token( - token_addr: &Address, - specific_key: &str, - expect_message: &str, -) -> Key { - Key::from(token_addr.to_db_key()) - .push(&specific_key.to_owned()) - .expect(expect_message) -} - -/// Obtain a storage key for user's balance. -pub fn balance_key(token_addr: &Address, owner: &Address) -> Key { - balance_prefix(token_addr) - .push(&owner.to_db_key()) - .expect("Cannot obtain a storage key") -} - -/// Obtain a storage key prefix for all users' balances. -pub fn balance_prefix(token_addr: &Address) -> Key { - Key::from(Address::Internal(InternalAddress::Multitoken).to_db_key()) - .push(&token_addr.to_db_key()) - .expect("Cannot obtain a storage key") - .push(&BALANCE_STORAGE_KEY.to_owned()) - .expect("Cannot obtain a storage key") -} - -/// Obtain a storage key for the multitoken minter. -pub fn minter_key(token_addr: &Address) -> Key { - Key::from(Address::Internal(InternalAddress::Multitoken).to_db_key()) - .push(&token_addr.to_db_key()) - .expect("Cannot obtain a storage key") - .push(&MINTER_STORAGE_KEY.to_owned()) - .expect("Cannot obtain a storage key") -} - -/// Obtain a storage key for the minted multitoken balance. -pub fn minted_balance_key(token_addr: &Address) -> Key { - balance_prefix(token_addr) - .push(&MINTED_STORAGE_KEY.to_owned()) - .expect("Cannot obtain a storage key") -} - -/// Obtain the nominal proportional key for the given token -pub fn masp_kp_gain_key(token_addr: &Address) -> Key { - key_of_token(token_addr, MASP_KP_GAIN_KEY, "nominal proproitonal gains") -} - -/// Obtain the nominal derivative key for the given token -pub fn masp_kd_gain_key(token_addr: &Address) -> Key { - key_of_token(token_addr, MASP_KD_GAIN_KEY, "nominal proproitonal gains") -} - -/// The max reward rate key for the given token -pub fn masp_max_reward_rate_key(token_addr: &Address) -> Key { - key_of_token(token_addr, MASP_MAX_REWARD_RATE_KEY, "max reward rate") -} - -/// Obtain the locked target ratio key for the given token -pub fn masp_locked_ratio_target_key(token_addr: &Address) -> Key { - key_of_token( - token_addr, - MASP_LOCKED_RATIO_TARGET_KEY, - "nominal proproitonal gains", - ) -} - /// Token parameters for each kind of asset held on chain #[derive( Clone, @@ -1098,50 +991,6 @@ pub struct Parameters { pub locked_ratio_target: Dec, } -impl Parameters { - /// Initialize parameters for the token in storage during the genesis block. - pub fn init_storage( - &self, - address: &Address, - wl_storage: &mut ledger_storage::WlStorage, - ) where - DB: ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, - H: ledger_storage::StorageHasher, - { - let Self { - max_reward_rate: max_rate, - kd_gain_nom, - kp_gain_nom, - locked_ratio_target: locked_target, - } = self; - wl_storage - .write(&masp_last_inflation_key(address), Amount::zero()) - .expect( - "last inflation key for the given asset must be initialized", - ); - wl_storage - .write(&masp_last_locked_ratio_key(address), Dec::zero()) - .expect( - "last locked ratio key for the given asset must be initialized", - ); - wl_storage - .write(&masp_max_reward_rate_key(address), max_rate) - .expect("max reward rate for the given asset must be initialized"); - wl_storage - .write(&masp_locked_ratio_target_key(address), locked_target) - .expect("locked ratio must be initialized"); - wl_storage - .write(&masp_kp_gain_key(address), kp_gain_nom) - .expect("The nominal proportional gain must be initialized"); - wl_storage - .write(&masp_kd_gain_key(address), kd_gain_nom) - .expect("The nominal derivative gain must be initialized"); - wl_storage - .write(&minted_balance_key(address), Amount::zero()) - .expect("The total minted balance key must initialized"); - } -} - impl Default for Parameters { fn default() -> Self { Self { @@ -1153,171 +1002,6 @@ impl Default for Parameters { } } -/// Check if the given storage key is balance key for the given token. If it is, -/// returns the owner. For minted balances, use [`is_any_minted_balance_key()`]. -pub fn is_balance_key<'a>( - token_addr: &Address, - key: &'a Key, -) -> Option<&'a Address> { - match &key.segments[..] { - [ - DbKeySeg::AddressSeg(addr), - DbKeySeg::AddressSeg(token), - DbKeySeg::StringSeg(balance), - DbKeySeg::AddressSeg(owner), - ] if *addr == Address::Internal(InternalAddress::Multitoken) - && token == token_addr - && balance == BALANCE_STORAGE_KEY => - { - Some(owner) - } - _ => None, - } -} - -/// Check if the given storage key is balance key for unspecified token. If it -/// is, returns the token and owner address. -pub fn is_any_token_balance_key(key: &Key) -> Option<[&Address; 2]> { - match &key.segments[..] { - [ - DbKeySeg::AddressSeg(addr), - DbKeySeg::AddressSeg(token), - DbKeySeg::StringSeg(balance), - DbKeySeg::AddressSeg(owner), - ] if *addr == Address::Internal(InternalAddress::Multitoken) - && balance == BALANCE_STORAGE_KEY => - { - Some([token, owner]) - } - _ => None, - } -} - -/// Obtain a storage key denomination of a token. -pub fn denom_key(token_addr: &Address) -> Key { - Key::from(token_addr.to_db_key()) - .push(&DENOM_STORAGE_KEY.to_owned()) - .expect("Cannot obtain a storage key") -} - -/// Check if the given storage key is a denomination key for the given token. -pub fn is_denom_key(token_addr: &Address, key: &Key) -> bool { - matches!(&key.segments[..], - [ - DbKeySeg::AddressSeg(addr), - .., - DbKeySeg::StringSeg(key), - ] if key == DENOM_STORAGE_KEY && addr == token_addr) -} - -/// Check if the given storage key is a masp key -pub fn is_masp_key(key: &Key) -> bool { - matches!(&key.segments[..], - [DbKeySeg::AddressSeg(addr), ..] if *addr == MASP - ) -} - -/// Check if the given storage key is allowed to be touched by a masp transfer -pub fn is_masp_allowed_key(key: &Key) -> bool { - match &key.segments[..] { - [DbKeySeg::AddressSeg(addr), DbKeySeg::StringSeg(key)] - if *addr == MASP - && (key == HEAD_TX_KEY - || key.starts_with(TX_KEY_PREFIX) - || key.starts_with(PIN_KEY_PREFIX) - || key == MASP_NOTE_COMMITMENT_TREE_KEY) => - { - true - } - - [ - DbKeySeg::AddressSeg(addr), - DbKeySeg::StringSeg(key), - DbKeySeg::StringSeg(_nullifier), - ] if *addr == MASP && key == MASP_NULLIFIERS_KEY => true, - _ => false, - } -} - -/// Check if the given storage key is a masp tx prefix key -pub fn is_masp_tx_prefix_key(key: &Key) -> bool { - matches!(&key.segments[..], - [DbKeySeg::AddressSeg(addr), - DbKeySeg::StringSeg(prefix), - ] if *addr == MASP && prefix.starts_with(TX_KEY_PREFIX)) -} - -/// Check if the given storage key is a masp tx pin key -pub fn is_masp_tx_pin_key(key: &Key) -> bool { - matches!(&key.segments[..], - [DbKeySeg::AddressSeg(addr), - DbKeySeg::StringSeg(prefix), - ] if *addr == MASP && prefix.starts_with(PIN_KEY_PREFIX)) -} - -/// Check if the given storage key is a masp nullifier key -pub fn is_masp_nullifier_key(key: &Key) -> bool { - matches!(&key.segments[..], - [DbKeySeg::AddressSeg(addr), - DbKeySeg::StringSeg(prefix), - DbKeySeg::StringSeg(_nullifier) - ] if *addr == MASP && prefix == MASP_NULLIFIERS_KEY) -} - -/// Obtain the storage key for the last locked ratio of a token -pub fn masp_last_locked_ratio_key(token_address: &Address) -> Key { - key_of_token( - token_address, - MASP_LAST_LOCKED_RATIO_KEY, - "cannot obtain storage key for the last locked ratio", - ) -} - -/// Obtain the storage key for the last inflation of a token -pub fn masp_last_inflation_key(token_address: &Address) -> Key { - key_of_token( - token_address, - MASP_LAST_INFLATION_KEY, - "cannot obtain storage key for the last inflation rate", - ) -} - -/// Check if the given storage key is for a minter of a unspecified token. -/// If it is, returns the token. -pub fn is_any_minter_key(key: &Key) -> Option<&Address> { - match &key.segments[..] { - [ - DbKeySeg::AddressSeg(addr), - DbKeySeg::AddressSeg(token), - DbKeySeg::StringSeg(minter), - ] if *addr == Address::Internal(InternalAddress::Multitoken) - && minter == MINTER_STORAGE_KEY => - { - Some(token) - } - _ => None, - } -} - -/// Check if the given storage key is for total supply of a unspecified token. -/// If it is, returns the token. -pub fn is_any_minted_balance_key(key: &Key) -> Option<&Address> { - match &key.segments[..] { - [ - DbKeySeg::AddressSeg(addr), - DbKeySeg::AddressSeg(token), - DbKeySeg::StringSeg(balance), - DbKeySeg::StringSeg(owner), - ] if *addr == Address::Internal(InternalAddress::Multitoken) - && balance == BALANCE_STORAGE_KEY - && owner == MINTED_STORAGE_KEY => - { - Some(token) - } - _ => None, - } -} - /// A simple bilateral token transfer #[derive( Debug, diff --git a/trans_token/src/lib.rs b/trans_token/src/lib.rs index 50e2c10955..2a5af85457 100644 --- a/trans_token/src/lib.rs +++ b/trans_token/src/lib.rs @@ -1,6 +1,7 @@ //! Transparent token types, storage functions, and validation. mod storage; +pub mod storage_key; pub use namada_core::types::token::*; pub use storage::*; diff --git a/trans_token/src/storage_key.rs b/trans_token/src/storage_key.rs new file mode 100644 index 0000000000..5ff7183353 --- /dev/null +++ b/trans_token/src/storage_key.rs @@ -0,0 +1,152 @@ +//! Transparent token storage keys + +/// Key segment for a balance key +const BALANCE_STORAGE_KEY: &str = "balance"; +/// Key segment for a denomination key +const DENOM_STORAGE_KEY: &str = "denomination"; +/// Key segment for multitoken minter +const MINTER_STORAGE_KEY: &str = "minter"; +/// Key segment for minted balance +const MINTED_STORAGE_KEY: &str = "minted"; + +// TODO: move to shielded +/// Key segment for head shielded transaction pointer keys +pub const HEAD_TX_KEY: &str = "head-tx"; +/// Key segment prefix for shielded transaction key +pub const TX_KEY_PREFIX: &str = "tx-"; +/// Key segment prefix for pinned shielded transactions +pub const PIN_KEY_PREFIX: &str = "pin-"; +/// Key segment prefix for the nullifiers +pub const MASP_NULLIFIERS_KEY: &str = "nullifiers"; +/// Key segment prefix for the note commitment merkle tree +pub const MASP_NOTE_COMMITMENT_TREE_KEY: &str = "commitment_tree"; +/// Key segment prefix for the note commitment anchor +pub const MASP_NOTE_COMMITMENT_ANCHOR_PREFIX: &str = "note_commitment_anchor"; +/// Key segment prefix for the convert anchor +pub const MASP_CONVERT_ANCHOR_KEY: &str = "convert_anchor"; +/// Last calculated inflation value handed out +pub const MASP_LAST_INFLATION_KEY: &str = "last_inflation"; +/// The last locked ratio +pub const MASP_LAST_LOCKED_RATIO_KEY: &str = "last_locked_ratio"; +/// The key for the nominal proportional gain of a shielded pool for a given +/// asset +pub const MASP_KP_GAIN_KEY: &str = "proportional_gain"; +/// The key for the nominal derivative gain of a shielded pool for a given asset +pub const MASP_KD_GAIN_KEY: &str = "derivative_gain"; +/// The key for the locked ratio target for a given asset +pub const MASP_LOCKED_RATIO_TARGET_KEY: &str = "locked_ratio_target"; +/// The key for the max reward rate for a given asset +pub const MASP_MAX_REWARD_RATE_KEY: &str = "max_reward_rate"; + +/// Gets the key for the given token address, error with the given +/// message to expect if the key is not in the address +pub fn key_of_token( + token_addr: &Address, + specific_key: &str, + expect_message: &str, +) -> Key { + Key::from(token_addr.to_db_key()) + .push(&specific_key.to_owned()) + .expect(expect_message) +} + +/// Obtain a storage key for user's balance. +pub fn balance_key(token_addr: &Address, owner: &Address) -> Key { + balance_prefix(token_addr) + .push(&owner.to_db_key()) + .expect("Cannot obtain a storage key") +} + +/// Obtain a storage key prefix for all users' balances. +pub fn balance_prefix(token_addr: &Address) -> Key { + Key::from(Address::Internal(InternalAddress::Multitoken).to_db_key()) + .push(&token_addr.to_db_key()) + .expect("Cannot obtain a storage key") + .push(&BALANCE_STORAGE_KEY.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Obtain a storage key for the multitoken minter. +pub fn minter_key(token_addr: &Address) -> Key { + Key::from(Address::Internal(InternalAddress::Multitoken).to_db_key()) + .push(&token_addr.to_db_key()) + .expect("Cannot obtain a storage key") + .push(&MINTER_STORAGE_KEY.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Obtain a storage key for the minted multitoken balance. +pub fn minted_balance_key(token_addr: &Address) -> Key { + balance_prefix(token_addr) + .push(&MINTED_STORAGE_KEY.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Obtain the nominal proportional key for the given token +pub fn masp_kp_gain_key(token_addr: &Address) -> Key { + key_of_token(token_addr, MASP_KP_GAIN_KEY, "nominal proproitonal gains") +} + +/// Obtain the nominal derivative key for the given token +pub fn masp_kd_gain_key(token_addr: &Address) -> Key { + key_of_token(token_addr, MASP_KD_GAIN_KEY, "nominal proproitonal gains") +} + +/// The max reward rate key for the given token +pub fn masp_max_reward_rate_key(token_addr: &Address) -> Key { + key_of_token(token_addr, MASP_MAX_REWARD_RATE_KEY, "max reward rate") +} + +/// Obtain the locked target ratio key for the given token +pub fn masp_locked_ratio_target_key(token_addr: &Address) -> Key { + key_of_token( + token_addr, + MASP_LOCKED_RATIO_TARGET_KEY, + "nominal proproitonal gains", + ) +} + +/// Obtain the storage key for the last inflation of a token +pub fn masp_last_inflation_key(token_address: &Address) -> Key { + key_of_token( + token_address, + MASP_LAST_INFLATION_KEY, + "cannot obtain storage key for the last inflation rate", + ) +} + +/// Check if the given storage key is for a minter of a unspecified token. +/// If it is, returns the token. +pub fn is_any_minter_key(key: &Key) -> Option<&Address> { + match &key.segments[..] { + [ + DbKeySeg::AddressSeg(addr), + DbKeySeg::AddressSeg(token), + DbKeySeg::StringSeg(minter), + ] if *addr == Address::Internal(InternalAddress::Multitoken) + && minter == MINTER_STORAGE_KEY => + { + Some(token) + } + _ => None, + } +} + +/// Check if the given storage key is for total supply of a unspecified token. +/// If it is, returns the token. +pub fn is_any_minted_balance_key(key: &Key) -> Option<&Address> { + match &key.segments[..] { + [ + DbKeySeg::AddressSeg(addr), + DbKeySeg::AddressSeg(token), + DbKeySeg::StringSeg(balance), + DbKeySeg::StringSeg(owner), + ] if *addr == Address::Internal(InternalAddress::Multitoken) + && balance == BALANCE_STORAGE_KEY + && owner == MINTED_STORAGE_KEY => + { + Some(token) + } + _ => None, + } +} From 0ee2f6de990c22aed30faa41f52a4c9565a84aaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 15 Dec 2023 11:00:52 +0000 Subject: [PATCH 003/118] move core token storage reads to trans_token::storage --- core/src/types/token.rs | 35 ----------------------------------- trans_token/src/storage.rs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 35 deletions(-) diff --git a/core/src/types/token.rs b/core/src/types/token.rs index c2e4f128ad..cfa2a2372c 100644 --- a/core/src/types/token.rs +++ b/core/src/types/token.rs @@ -14,8 +14,6 @@ use thiserror::Error; use super::dec::POS_DECIMAL_PRECISION; use crate::ibc::apps::transfer::types::Amount as IbcAmount; -use crate::ledger::storage_api::token::read_denom; -use crate::ledger::storage_api::{self, StorageRead}; use crate::types::address::{Address, DecodeError as AddressError}; use crate::types::dec::Dec; use crate::types::hash::Hash; @@ -218,23 +216,6 @@ impl Amount { .to_string_precise() } - /// Add denomination info if it exists in storage. - pub fn denominated( - &self, - token: &Address, - storage: &impl StorageRead, - ) -> storage_api::Result { - let denom = read_denom(storage, token)?.ok_or_else(|| { - storage_api::Error::SimpleMessage( - "No denomination found in storage for the given token", - ) - })?; - Ok(DenominatedAmount { - amount: *self, - denom, - }) - } - /// Return a denominated native token amount. #[inline] pub const fn native_denominated(self) -> DenominatedAmount { @@ -408,22 +389,6 @@ impl DenominatedAmount { .ok_or(AmountParseError::PrecisionOverflow) } - /// Convert this denominated amount into a plain amount by increasing its - /// precision to the given token's denomination and then taking the - /// significand. - pub fn to_amount( - self, - token: &Address, - storage: &impl StorageRead, - ) -> storage_api::Result { - let denom = read_denom(storage, token)?.ok_or_else(|| { - storage_api::Error::SimpleMessage( - "No denomination found in storage for the given token", - ) - })?; - self.scale(denom).map_err(storage_api::Error::new) - } - /// Multiply this number by 10^denom and return the computed integer if /// possible. Otherwise error out. pub fn scale( diff --git a/trans_token/src/storage.rs b/trans_token/src/storage.rs index 458dbe39a2..4d43aebf3c 100644 --- a/trans_token/src/storage.rs +++ b/trans_token/src/storage.rs @@ -225,3 +225,33 @@ where let total_supply_key = token::minted_balance_key(token); storage.write(&total_supply_key, new_total_supply) } + +/// Add denomination info if it exists in storage. +pub fn denominated( + amount: token::Amount, + token: &Address, + storage: &impl StorageRead, +) -> storage_api::Result { + let denom = read_denom(storage, token)?.ok_or_else(|| { + storage_api::Error::SimpleMessage( + "No denomination found in storage for the given token", + ) + })?; + Ok(DenominatedAmount { amount, denom }) +} + +/// Convert this denominated amount into a plain amount by increasing its +/// precision to the given token's denomination and then taking the +/// significand. +pub fn denom_to_amount( + denom_amount: DenominatedAmount, + token: &Address, + storage: &impl StorageRead, +) -> storage_api::Result { + let denom = read_denom(storage, token)?.ok_or_else(|| { + storage_api::Error::SimpleMessage( + "No denomination found in storage for the given token", + ) + })?; + denom_amount.scale(denom).map_err(storage_api::Error::new) +} From b103c27c2e47d2bcd4afc2bd662fbef25be11900 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 15 Dec 2023 12:18:58 +0000 Subject: [PATCH 004/118] core: replace token type import --- core/src/ledger/governance/cli/onchain.rs | 2 +- core/src/ledger/governance/storage/proposal.rs | 4 ++-- core/src/ledger/parameters/mod.rs | 5 ++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/core/src/ledger/governance/cli/onchain.rs b/core/src/ledger/governance/cli/onchain.rs index 60e70ddf7d..9f303be37a 100644 --- a/core/src/ledger/governance/cli/onchain.rs +++ b/core/src/ledger/governance/cli/onchain.rs @@ -10,9 +10,9 @@ use super::validation::{ ProposalValidation, }; use crate::ledger::governance::parameters::GovernanceParameters; -use crate::ledger::storage_api::token; use crate::types::address::Address; use crate::types::storage::Epoch; +use crate::types::token; #[derive( Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, diff --git a/core/src/ledger/governance/storage/proposal.rs b/core/src/ledger/governance/storage/proposal.rs index 13e1aecae1..be3212525f 100644 --- a/core/src/ledger/governance/storage/proposal.rs +++ b/core/src/ledger/governance/storage/proposal.rs @@ -9,10 +9,10 @@ use crate::ledger::governance::cli::onchain::{ PgfAction, PgfContinous, PgfRetro, PgfSteward, StewardsUpdate, }; use crate::ledger::governance::utils::{ProposalStatus, TallyType}; -use crate::ledger::storage_api::token::Amount; use crate::types::address::Address; use crate::types::hash::Hash; use crate::types::storage::Epoch; +use crate::types::token; #[allow(missing_docs)] #[derive(Debug, Error)] @@ -85,7 +85,7 @@ pub struct PGFTarget { /// The target address pub target: Address, /// The amount of token to fund the target address - pub amount: Amount, + pub amount: token::Amount, } /// The actions that a PGF Steward can propose to execute diff --git a/core/src/ledger/parameters/mod.rs b/core/src/ledger/parameters/mod.rs index d80f75aa4b..433cac3b25 100644 --- a/core/src/ledger/parameters/mod.rs +++ b/core/src/ledger/parameters/mod.rs @@ -7,7 +7,6 @@ use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use thiserror::Error; use super::storage::types; -use super::storage_api::token::Amount; use super::storage_api::{self, ResultExt, StorageRead, StorageWrite}; use crate::ledger::storage as ledger_storage; use crate::types::address::{Address, InternalAddress}; @@ -379,11 +378,11 @@ where pub fn read_gas_cost( storage: &S, token: &Address, -) -> storage_api::Result> +) -> storage_api::Result> where S: StorageRead, { - let gas_cost_table: BTreeMap = storage + let gas_cost_table: BTreeMap = storage .read(&storage::get_gas_cost_key())? .ok_or(ReadError::ParametersMissing) .into_storage_result()?; From 1520ad5233a07fd7506a8baa403920cc7efb7f01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 15 Dec 2023 12:26:41 +0000 Subject: [PATCH 005/118] core: separate gov code into `namada_governance` crate --- Cargo.lock | 9 ++++++++- Cargo.toml | 1 + Makefile | 1 + core/src/ledger/governance/storage/mod.rs | 6 ------ core/src/ledger/mod.rs | 1 - core/src/ledger/storage_api/mod.rs | 1 - governance/Cargo.toml | 16 ++++++++++++++++ .../governance => governance/src}/cli/mod.rs | 0 .../governance => governance/src}/cli/offline.rs | 0 .../governance => governance/src}/cli/onchain.rs | 0 .../src}/cli/validation.rs | 0 .../governance/mod.rs => governance/src/lib.rs | 0 .../governance => governance/src}/parameters.rs | 0 .../src}/storage/keys.rs | 0 .../src/storage/mod.rs | 9 ++++++++- .../src}/storage/proposal.rs | 0 .../src}/storage/vote.rs | 0 .../governance => governance/src}/utils.rs | 0 18 files changed, 34 insertions(+), 10 deletions(-) delete mode 100644 core/src/ledger/governance/storage/mod.rs create mode 100644 governance/Cargo.toml rename {core/src/ledger/governance => governance/src}/cli/mod.rs (100%) rename {core/src/ledger/governance => governance/src}/cli/offline.rs (100%) rename {core/src/ledger/governance => governance/src}/cli/onchain.rs (100%) rename {core/src/ledger/governance => governance/src}/cli/validation.rs (100%) rename core/src/ledger/governance/mod.rs => governance/src/lib.rs (100%) rename {core/src/ledger/governance => governance/src}/parameters.rs (100%) rename {core/src/ledger/governance => governance/src}/storage/keys.rs (100%) rename core/src/ledger/storage_api/governance.rs => governance/src/storage/mod.rs (98%) rename {core/src/ledger/governance => governance/src}/storage/proposal.rs (100%) rename {core/src/ledger/governance => governance/src}/storage/vote.rs (100%) rename {core/src/ledger/governance => governance/src}/utils.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 9e24e332e0..cb87b56708 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4459,6 +4459,13 @@ dependencies = [ "tokio", ] +[[package]] +name = "namada_governance" +version = "0.29.0" +dependencies = [ + "namada_core", +] + [[package]] name = "namada_light_sdk" version = "0.29.0" @@ -4618,7 +4625,7 @@ dependencies = [ [[package]] name = "namada_trans_token" -version = "0.28.1" +version = "0.29.0" dependencies = [ "namada_core", ] diff --git a/Cargo.toml b/Cargo.toml index e4950d9a0e..4d9210adbc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "ethereum_bridge", "light_sdk", "macros", + "governance", "proof_of_stake", "sdk", "shared", diff --git a/Makefile b/Makefile index 50942c8ca4..a7d5579f5f 100644 --- a/Makefile +++ b/Makefile @@ -39,6 +39,7 @@ crates += namada_apps crates += namada_benchmarks crates += namada_encoding_spec crates += namada_ethereum_bridge +crates += namada_governance crates += namada_macros crates += namada_proof_of_stake crates += namada_sdk diff --git a/core/src/ledger/governance/storage/mod.rs b/core/src/ledger/governance/storage/mod.rs deleted file mode 100644 index e2de8e6ab8..0000000000 --- a/core/src/ledger/governance/storage/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -/// Governance proposal keys -pub mod keys; -/// Proposal structures -pub mod proposal; -/// Vote structures -pub mod vote; diff --git a/core/src/ledger/mod.rs b/core/src/ledger/mod.rs index 96cf4d2331..ab4ec02ab5 100644 --- a/core/src/ledger/mod.rs +++ b/core/src/ledger/mod.rs @@ -2,7 +2,6 @@ pub mod eth_bridge; pub mod gas; -pub mod governance; pub mod ibc; pub mod inflation; pub mod masp_conversions; diff --git a/core/src/ledger/storage_api/mod.rs b/core/src/ledger/storage_api/mod.rs index 0111cc9ef5..2792b6c6df 100644 --- a/core/src/ledger/storage_api/mod.rs +++ b/core/src/ledger/storage_api/mod.rs @@ -4,7 +4,6 @@ pub mod account; pub mod collections; mod error; -pub mod governance; pub mod key; pub mod pgf; pub mod tx; diff --git a/governance/Cargo.toml b/governance/Cargo.toml new file mode 100644 index 0000000000..28866724c2 --- /dev/null +++ b/governance/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "namada_governance" +description = "Namada governance" +resolver = "2" +authors.workspace = true +edition.workspace = true +documentation.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +namada_core = {path = "../core", default-features = false} \ No newline at end of file diff --git a/core/src/ledger/governance/cli/mod.rs b/governance/src/cli/mod.rs similarity index 100% rename from core/src/ledger/governance/cli/mod.rs rename to governance/src/cli/mod.rs diff --git a/core/src/ledger/governance/cli/offline.rs b/governance/src/cli/offline.rs similarity index 100% rename from core/src/ledger/governance/cli/offline.rs rename to governance/src/cli/offline.rs diff --git a/core/src/ledger/governance/cli/onchain.rs b/governance/src/cli/onchain.rs similarity index 100% rename from core/src/ledger/governance/cli/onchain.rs rename to governance/src/cli/onchain.rs diff --git a/core/src/ledger/governance/cli/validation.rs b/governance/src/cli/validation.rs similarity index 100% rename from core/src/ledger/governance/cli/validation.rs rename to governance/src/cli/validation.rs diff --git a/core/src/ledger/governance/mod.rs b/governance/src/lib.rs similarity index 100% rename from core/src/ledger/governance/mod.rs rename to governance/src/lib.rs diff --git a/core/src/ledger/governance/parameters.rs b/governance/src/parameters.rs similarity index 100% rename from core/src/ledger/governance/parameters.rs rename to governance/src/parameters.rs diff --git a/core/src/ledger/governance/storage/keys.rs b/governance/src/storage/keys.rs similarity index 100% rename from core/src/ledger/governance/storage/keys.rs rename to governance/src/storage/keys.rs diff --git a/core/src/ledger/storage_api/governance.rs b/governance/src/storage/mod.rs similarity index 98% rename from core/src/ledger/storage_api/governance.rs rename to governance/src/storage/mod.rs index 3f3dfb2af6..f70d9a64b4 100644 --- a/core/src/ledger/storage_api/governance.rs +++ b/governance/src/storage/mod.rs @@ -1,4 +1,11 @@ -//! Governance +//! Governance storage + +/// Governance proposal keys +pub mod keys; +/// Proposal structures +pub mod proposal; +/// Vote structures +pub mod vote; use std::collections::BTreeMap; diff --git a/core/src/ledger/governance/storage/proposal.rs b/governance/src/storage/proposal.rs similarity index 100% rename from core/src/ledger/governance/storage/proposal.rs rename to governance/src/storage/proposal.rs diff --git a/core/src/ledger/governance/storage/vote.rs b/governance/src/storage/vote.rs similarity index 100% rename from core/src/ledger/governance/storage/vote.rs rename to governance/src/storage/vote.rs diff --git a/core/src/ledger/governance/utils.rs b/governance/src/utils.rs similarity index 100% rename from core/src/ledger/governance/utils.rs rename to governance/src/utils.rs From 74843e158d6cdc95aa0977b47e3f79daa3c21a77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 15 Dec 2023 12:32:23 +0000 Subject: [PATCH 006/118] governance: update import paths --- Cargo.lock | 1 + governance/Cargo.toml | 3 ++- governance/src/cli/offline.rs | 12 ++++++------ governance/src/cli/onchain.rs | 8 ++++---- governance/src/cli/validation.rs | 6 +++--- governance/src/lib.rs | 2 +- governance/src/parameters.rs | 4 ++-- governance/src/storage/keys.rs | 6 +++--- governance/src/storage/mod.rs | 26 ++++++++++++-------------- governance/src/storage/proposal.rs | 18 +++++++++--------- governance/src/utils.rs | 6 +++--- 11 files changed, 46 insertions(+), 46 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cb87b56708..693df58311 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4464,6 +4464,7 @@ name = "namada_governance" version = "0.29.0" dependencies = [ "namada_core", + "namada_trans_token", ] [[package]] diff --git a/governance/Cargo.toml b/governance/Cargo.toml index 28866724c2..39994f3a6f 100644 --- a/governance/Cargo.toml +++ b/governance/Cargo.toml @@ -13,4 +13,5 @@ repository.workspace = true version.workspace = true [dependencies] -namada_core = {path = "../core", default-features = false} \ No newline at end of file +namada_core = {path = "../core", default-features = false} +namada_trans_token = {path = "../trans_token"} \ No newline at end of file diff --git a/governance/src/cli/offline.rs b/governance/src/cli/offline.rs index ccf06a0b98..c800ef7264 100644 --- a/governance/src/cli/offline.rs +++ b/governance/src/cli/offline.rs @@ -4,16 +4,16 @@ use std::path::PathBuf; use borsh::{BorshDeserialize, BorshSerialize}; use borsh_ext::BorshSerializeExt; +use namada_core::proto::SignatureIndex; +use namada_core::types::account::AccountPublicKeysMap; +use namada_core::types::address::Address; +use namada_core::types::hash::Hash; +use namada_core::types::key::{common, RefTo, SigScheme}; +use namada_core::types::storage::Epoch; use serde::{Deserialize, Serialize}; use super::onchain::ProposalVote; use super::validation::{is_valid_tally_epoch, ProposalValidation}; -use crate::proto::SignatureIndex; -use crate::types::account::AccountPublicKeysMap; -use crate::types::address::Address; -use crate::types::hash::Hash; -use crate::types::key::{common, RefTo, SigScheme}; -use crate::types::storage::Epoch; #[derive( Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, diff --git a/governance/src/cli/onchain.rs b/governance/src/cli/onchain.rs index 9f303be37a..fbe6d1d901 100644 --- a/governance/src/cli/onchain.rs +++ b/governance/src/cli/onchain.rs @@ -1,6 +1,9 @@ use std::collections::BTreeMap; use borsh::{BorshDeserialize, BorshSerialize}; +use namada_core::types::address::Address; +use namada_core::types::storage::Epoch; +use namada_core::types::token; use serde::{Deserialize, Serialize}; use super::validation::{ @@ -9,10 +12,7 @@ use super::validation::{ is_valid_pgf_stewards_data, is_valid_proposal_period, is_valid_start_epoch, ProposalValidation, }; -use crate::ledger::governance::parameters::GovernanceParameters; -use crate::types::address::Address; -use crate::types::storage::Epoch; -use crate::types::token; +use crate::parameters::GovernanceParameters; #[derive( Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, diff --git a/governance/src/cli/validation.rs b/governance/src/cli/validation.rs index cb70d146ee..db3222614d 100644 --- a/governance/src/cli/validation.rs +++ b/governance/src/cli/validation.rs @@ -1,11 +1,11 @@ use std::collections::BTreeMap; +use namada_core::types::address::Address; +use namada_core::types::storage::Epoch; +use namada_core::types::token; use thiserror::Error; use super::onchain::{PgfFunding, StewardsUpdate}; -use crate::types::address::Address; -use crate::types::storage::Epoch; -use crate::types::token; /// This enum raprresent a proposal data #[derive(Clone, Debug, PartialEq, Error)] diff --git a/governance/src/lib.rs b/governance/src/lib.rs index 00fcb3a990..93508d5968 100644 --- a/governance/src/lib.rs +++ b/governance/src/lib.rs @@ -1,6 +1,6 @@ //! Governance library code -use crate::types::address::{self, Address}; +use namada_core::types::address::{self, Address}; /// governance CLI structures pub mod cli; diff --git a/governance/src/parameters.rs b/governance/src/parameters.rs index 5d91ce88b4..25a14073cf 100644 --- a/governance/src/parameters.rs +++ b/governance/src/parameters.rs @@ -1,8 +1,8 @@ use borsh::{BorshDeserialize, BorshSerialize}; +use namada_core::ledger::storage_api::{self, StorageRead, StorageWrite}; +use namada_core::types::token; use super::storage::keys as goverance_storage; -use crate::ledger::storage_api::{self, StorageRead, StorageWrite}; -use crate::types::token; #[derive( Clone, diff --git a/governance/src/storage/keys.rs b/governance/src/storage/keys.rs index 92beb9da36..ef6ada2ac8 100644 --- a/governance/src/storage/keys.rs +++ b/governance/src/storage/keys.rs @@ -1,8 +1,8 @@ +use namada_core::types::address::Address; +use namada_core::types::storage::{DbKeySeg, Key, KeySeg}; use namada_macros::StorageKeys; -use crate::ledger::governance::ADDRESS; -use crate::types::address::Address; -use crate::types::storage::{DbKeySeg, Key, KeySeg}; +use crate::ADDRESS; /// Storage keys for governance internal address. #[derive(StorageKeys)] diff --git a/governance/src/storage/mod.rs b/governance/src/storage/mod.rs index f70d9a64b4..0f2fc59332 100644 --- a/governance/src/storage/mod.rs +++ b/governance/src/storage/mod.rs @@ -10,23 +10,21 @@ pub mod vote; use std::collections::BTreeMap; use borsh::BorshDeserialize; - -use super::token; -use crate::ledger::governance::parameters::GovernanceParameters; -use crate::ledger::governance::storage::keys as governance_keys; -use crate::ledger::governance::storage::proposal::{ - ProposalType, StorageProposal, -}; -use crate::ledger::governance::storage::vote::StorageProposalVote; -use crate::ledger::governance::utils::Vote; -use crate::ledger::governance::ADDRESS as governance_address; -use crate::ledger::storage_api::{self, StorageRead, StorageWrite}; -use crate::types::address::Address; -use crate::types::storage::Epoch; -use crate::types::transaction::governance::{ +use namada_core::ledger::storage_api::{self, StorageRead, StorageWrite}; +use namada_core::types::address::Address; +use namada_core::types::storage::Epoch; +use namada_core::types::transaction::governance::{ InitProposalData, VoteProposalData, }; +use super::token; +use crate::parameters::GovernanceParameters; +use crate::storage::keys as governance_keys; +use crate::storage::proposal::{ProposalType, StorageProposal}; +use crate::storage::vote::StorageProposalVote; +use crate::utils::Vote; +use crate::ADDRESS as governance_address; + /// A proposal creation transaction. pub fn init_proposal( storage: &mut S, diff --git a/governance/src/storage/proposal.rs b/governance/src/storage/proposal.rs index be3212525f..940812eed5 100644 --- a/governance/src/storage/proposal.rs +++ b/governance/src/storage/proposal.rs @@ -2,17 +2,17 @@ use std::collections::{BTreeMap, HashSet}; use std::fmt::Display; use borsh::{BorshDeserialize, BorshSerialize}; +use namada_core::types::address::Address; +use namada_core::types::hash::Hash; +use namada_core::types::storage::Epoch; +use namada_core::types::token; use serde::{Deserialize, Serialize}; use thiserror::Error; -use crate::ledger::governance::cli::onchain::{ +use crate::cli::onchain::{ PgfAction, PgfContinous, PgfRetro, PgfSteward, StewardsUpdate, }; -use crate::ledger::governance::utils::{ProposalStatus, TallyType}; -use crate::types::address::Address; -use crate::types::hash::Hash; -use crate::types::storage::Epoch; -use crate::types::token; +use crate::utils::{ProposalStatus, TallyType}; #[allow(missing_docs)] #[derive(Debug, Error)] @@ -315,13 +315,13 @@ impl Display for StorageProposal { #[cfg(any(test, feature = "testing"))] /// Testing helpers and and strategies for governance proposals pub mod testing { + use namada_core::types::address::testing::arb_non_internal_address; + use namada_core::types::hash::testing::arb_hash; + use namada_core::types::token::testing::arb_amount; use proptest::prelude::Strategy; use proptest::{collection, option, prop_compose}; use super::*; - use crate::types::address::testing::arb_non_internal_address; - use crate::types::hash::testing::arb_hash; - use crate::types::token::testing::arb_amount; /// Generate an arbitrary add or removal of what's generated by the supplied /// strategy diff --git a/governance/src/utils.rs b/governance/src/utils.rs index 6b3129b588..fe6db8091d 100644 --- a/governance/src/utils.rs +++ b/governance/src/utils.rs @@ -2,13 +2,13 @@ use std::collections::HashMap; use std::fmt::Display; use borsh::{BorshDeserialize, BorshSerialize}; +use namada_core::types::address::Address; +use namada_core::types::storage::Epoch; +use namada_core::types::token; use super::cli::offline::OfflineVote; use super::storage::proposal::ProposalType; use super::storage::vote::StorageProposalVote; -use crate::types::address::Address; -use crate::types::storage::Epoch; -use crate::types::token; /// Proposal status pub enum ProposalStatus { From c39544a072c44aedc3edb5bdbd4537050af25bc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 15 Dec 2023 17:13:01 +0000 Subject: [PATCH 007/118] core: move pgf into namada_governance --- core/src/ledger/mod.rs | 1 - core/src/ledger/storage_api/mod.rs | 1 - governance/src/lib.rs | 1 + {core/src/ledger => governance/src}/pgf/cli/mod.rs | 0 {core/src/ledger => governance/src}/pgf/cli/steward.rs | 0 {core/src/ledger => governance/src}/pgf/inflation.rs | 0 {core/src/ledger => governance/src}/pgf/mod.rs | 0 {core/src/ledger => governance/src}/pgf/parameters.rs | 0 {core/src/ledger => governance/src}/pgf/storage/keys.rs | 0 {core/src/ledger => governance/src}/pgf/storage/mod.rs | 0 {core/src/ledger => governance/src}/pgf/storage/steward.rs | 0 .../storage_api/pgf.rs => governance/src/pgf/storage/storage.rs | 0 12 files changed, 1 insertion(+), 2 deletions(-) rename {core/src/ledger => governance/src}/pgf/cli/mod.rs (100%) rename {core/src/ledger => governance/src}/pgf/cli/steward.rs (100%) rename {core/src/ledger => governance/src}/pgf/inflation.rs (100%) rename {core/src/ledger => governance/src}/pgf/mod.rs (100%) rename {core/src/ledger => governance/src}/pgf/parameters.rs (100%) rename {core/src/ledger => governance/src}/pgf/storage/keys.rs (100%) rename {core/src/ledger => governance/src}/pgf/storage/mod.rs (100%) rename {core/src/ledger => governance/src}/pgf/storage/steward.rs (100%) rename core/src/ledger/storage_api/pgf.rs => governance/src/pgf/storage/storage.rs (100%) diff --git a/core/src/ledger/mod.rs b/core/src/ledger/mod.rs index ab4ec02ab5..2426e2931a 100644 --- a/core/src/ledger/mod.rs +++ b/core/src/ledger/mod.rs @@ -7,7 +7,6 @@ pub mod inflation; pub mod masp_conversions; pub mod masp_utils; pub mod parameters; -pub mod pgf; pub mod replay_protection; pub mod storage; pub mod storage_api; diff --git a/core/src/ledger/storage_api/mod.rs b/core/src/ledger/storage_api/mod.rs index 2792b6c6df..eb8213c823 100644 --- a/core/src/ledger/storage_api/mod.rs +++ b/core/src/ledger/storage_api/mod.rs @@ -5,7 +5,6 @@ pub mod account; pub mod collections; mod error; pub mod key; -pub mod pgf; pub mod tx; pub mod validation; diff --git a/governance/src/lib.rs b/governance/src/lib.rs index 93508d5968..4cd29205b3 100644 --- a/governance/src/lib.rs +++ b/governance/src/lib.rs @@ -6,6 +6,7 @@ use namada_core::types::address::{self, Address}; pub mod cli; /// governance parameters pub mod parameters; +pub mod pgf; /// governance storage pub mod storage; /// Governance utility functions/structs diff --git a/core/src/ledger/pgf/cli/mod.rs b/governance/src/pgf/cli/mod.rs similarity index 100% rename from core/src/ledger/pgf/cli/mod.rs rename to governance/src/pgf/cli/mod.rs diff --git a/core/src/ledger/pgf/cli/steward.rs b/governance/src/pgf/cli/steward.rs similarity index 100% rename from core/src/ledger/pgf/cli/steward.rs rename to governance/src/pgf/cli/steward.rs diff --git a/core/src/ledger/pgf/inflation.rs b/governance/src/pgf/inflation.rs similarity index 100% rename from core/src/ledger/pgf/inflation.rs rename to governance/src/pgf/inflation.rs diff --git a/core/src/ledger/pgf/mod.rs b/governance/src/pgf/mod.rs similarity index 100% rename from core/src/ledger/pgf/mod.rs rename to governance/src/pgf/mod.rs diff --git a/core/src/ledger/pgf/parameters.rs b/governance/src/pgf/parameters.rs similarity index 100% rename from core/src/ledger/pgf/parameters.rs rename to governance/src/pgf/parameters.rs diff --git a/core/src/ledger/pgf/storage/keys.rs b/governance/src/pgf/storage/keys.rs similarity index 100% rename from core/src/ledger/pgf/storage/keys.rs rename to governance/src/pgf/storage/keys.rs diff --git a/core/src/ledger/pgf/storage/mod.rs b/governance/src/pgf/storage/mod.rs similarity index 100% rename from core/src/ledger/pgf/storage/mod.rs rename to governance/src/pgf/storage/mod.rs diff --git a/core/src/ledger/pgf/storage/steward.rs b/governance/src/pgf/storage/steward.rs similarity index 100% rename from core/src/ledger/pgf/storage/steward.rs rename to governance/src/pgf/storage/steward.rs diff --git a/core/src/ledger/storage_api/pgf.rs b/governance/src/pgf/storage/storage.rs similarity index 100% rename from core/src/ledger/storage_api/pgf.rs rename to governance/src/pgf/storage/storage.rs From 5d0c3b182a4a6eace7ddf761d4fe9fdde5956078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 15 Dec 2023 17:45:41 +0000 Subject: [PATCH 008/118] gov: join pgf storage mods --- governance/src/pgf/storage/mod.rs | 125 ++++++++++++++++++++++++++ governance/src/pgf/storage/storage.rs | 124 ------------------------- 2 files changed, 125 insertions(+), 124 deletions(-) delete mode 100644 governance/src/pgf/storage/storage.rs diff --git a/governance/src/pgf/storage/mod.rs b/governance/src/pgf/storage/mod.rs index 59dc8fd622..15238dd9b1 100644 --- a/governance/src/pgf/storage/mod.rs +++ b/governance/src/pgf/storage/mod.rs @@ -1,4 +1,129 @@ +//! Pgf + /// Pgf storage keys pub mod keys; /// Pgf steward structures pub mod steward; + +use std::collections::HashMap; + +use crate::ledger::governance::storage::proposal::StoragePgfFunding; +use crate::ledger::pgf::parameters::PgfParameters; +use crate::ledger::pgf::storage::keys as pgf_keys; +use crate::ledger::pgf::storage::steward::StewardDetail; +use crate::ledger::storage_api::{self}; +use crate::types::address::Address; +use crate::types::dec::Dec; + +/// Query the current pgf steward set +pub fn get_stewards(storage: &S) -> storage_api::Result> +where + S: storage_api::StorageRead, +{ + let stewards = pgf_keys::stewards_handle() + .iter(storage)? + .filter_map(|data| match data { + Ok((_, steward)) => Some(steward), + Err(_) => None, + }) + .collect::>(); + + Ok(stewards) +} + +/// Query the a steward by address +pub fn get_steward( + storage: &S, + address: &Address, +) -> storage_api::Result> +where + S: storage_api::StorageRead, +{ + pgf_keys::stewards_handle().get(storage, address) +} + +/// Check if an address is a steward +pub fn is_steward( + storage: &S, + address: &Address, +) -> storage_api::Result +where + S: storage_api::StorageRead, +{ + pgf_keys::stewards_handle().contains(storage, address) +} + +/// Remove a steward +pub fn remove_steward( + storage: &mut S, + address: &Address, +) -> storage_api::Result<()> +where + S: storage_api::StorageRead + storage_api::StorageWrite, +{ + pgf_keys::stewards_handle().remove(storage, address)?; + + Ok(()) +} + +/// Query the current pgf continous payments +pub fn get_payments( + storage: &S, +) -> storage_api::Result> +where + S: storage_api::StorageRead, +{ + let fundings = pgf_keys::fundings_handle() + .iter(storage)? + .filter_map(|data| match data { + Ok((_, funding)) => Some(funding), + Err(_) => None, + }) + .collect::>(); + + Ok(fundings) +} + +/// Query the pgf parameters +pub fn get_parameters(storage: &S) -> storage_api::Result +where + S: storage_api::StorageRead, +{ + let pgf_inflation_rate_key = pgf_keys::get_pgf_inflation_rate_key(); + let stewards_inflation_rate_key = + pgf_keys::get_steward_inflation_rate_key(); + + let pgf_inflation_rate: Dec = storage + .read(&pgf_inflation_rate_key)? + .expect("Parameter should be defined."); + let stewards_inflation_rate: Dec = storage + .read(&stewards_inflation_rate_key)? + .expect("Parameter should be defined."); + + Ok(PgfParameters { + pgf_inflation_rate, + stewards_inflation_rate, + ..Default::default() + }) +} + +/// Update the commission for a steward +pub fn update_commission( + storage: &mut S, + address: Address, + reward_distribution: HashMap, +) -> storage_api::Result<()> +where + S: storage_api::StorageRead + storage_api::StorageWrite, +{ + pgf_keys::stewards_handle().insert( + storage, + address.clone(), + StewardDetail { + address, + reward_distribution, + }, + )?; + + Ok(()) +} diff --git a/governance/src/pgf/storage/storage.rs b/governance/src/pgf/storage/storage.rs deleted file mode 100644 index 29d7ac2486..0000000000 --- a/governance/src/pgf/storage/storage.rs +++ /dev/null @@ -1,124 +0,0 @@ -//! Pgf - -use std::collections::HashMap; - -use crate::ledger::governance::storage::proposal::StoragePgfFunding; -use crate::ledger::pgf::parameters::PgfParameters; -use crate::ledger::pgf::storage::keys as pgf_keys; -use crate::ledger::pgf::storage::steward::StewardDetail; -use crate::ledger::storage_api::{self}; -use crate::types::address::Address; -use crate::types::dec::Dec; - -/// Query the current pgf steward set -pub fn get_stewards(storage: &S) -> storage_api::Result> -where - S: storage_api::StorageRead, -{ - let stewards = pgf_keys::stewards_handle() - .iter(storage)? - .filter_map(|data| match data { - Ok((_, steward)) => Some(steward), - Err(_) => None, - }) - .collect::>(); - - Ok(stewards) -} - -/// Query the a steward by address -pub fn get_steward( - storage: &S, - address: &Address, -) -> storage_api::Result> -where - S: storage_api::StorageRead, -{ - pgf_keys::stewards_handle().get(storage, address) -} - -/// Check if an address is a steward -pub fn is_steward( - storage: &S, - address: &Address, -) -> storage_api::Result -where - S: storage_api::StorageRead, -{ - pgf_keys::stewards_handle().contains(storage, address) -} - -/// Remove a steward -pub fn remove_steward( - storage: &mut S, - address: &Address, -) -> storage_api::Result<()> -where - S: storage_api::StorageRead + storage_api::StorageWrite, -{ - pgf_keys::stewards_handle().remove(storage, address)?; - - Ok(()) -} - -/// Query the current pgf continuous payments -pub fn get_payments( - storage: &S, -) -> storage_api::Result> -where - S: storage_api::StorageRead, -{ - let fundings = pgf_keys::fundings_handle() - .iter(storage)? - .filter_map(|data| match data { - Ok((_, funding)) => Some(funding), - Err(_) => None, - }) - .collect::>(); - - Ok(fundings) -} - -/// Query the pgf parameters -pub fn get_parameters(storage: &S) -> storage_api::Result -where - S: storage_api::StorageRead, -{ - let pgf_inflation_rate_key = pgf_keys::get_pgf_inflation_rate_key(); - let stewards_inflation_rate_key = - pgf_keys::get_steward_inflation_rate_key(); - - let pgf_inflation_rate: Dec = storage - .read(&pgf_inflation_rate_key)? - .expect("Parameter should be defined."); - let stewards_inflation_rate: Dec = storage - .read(&stewards_inflation_rate_key)? - .expect("Parameter should be defined."); - - Ok(PgfParameters { - pgf_inflation_rate, - stewards_inflation_rate, - ..Default::default() - }) -} - -/// Update the commission for a steward -pub fn update_commission( - storage: &mut S, - address: Address, - reward_distribution: HashMap, -) -> storage_api::Result<()> -where - S: storage_api::StorageRead + storage_api::StorageWrite, -{ - pgf_keys::stewards_handle().insert( - storage, - address.clone(), - StewardDetail { - address, - reward_distribution, - }, - )?; - - Ok(()) -} From c800630de6ce620a331c2d7214e2512c69541e57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 15 Dec 2023 18:00:04 +0000 Subject: [PATCH 009/118] add namada_ibc crate --- Cargo.lock | 4 ++++ Cargo.toml | 4 +++- Makefile | 1 + core/src/ledger/mod.rs | 1 - core/src/types/ibc.rs | 1 - ibc/Cargo.toml | 8 ++++++++ {core/src/ledger/ibc => ibc/src}/context/client.rs | 0 {core/src/ledger/ibc => ibc/src}/context/common.rs | 0 {core/src/ledger/ibc => ibc/src}/context/execution.rs | 0 {core/src/ledger/ibc => ibc/src}/context/mod.rs | 0 {core/src/ledger/ibc => ibc/src}/context/router.rs | 0 {core/src/ledger/ibc => ibc/src}/context/storage.rs | 0 .../src/ledger/ibc => ibc/src}/context/token_transfer.rs | 0 {core/src/ledger/ibc => ibc/src}/context/transfer_mod.rs | 0 {core/src/ledger/ibc => ibc/src}/context/validation.rs | 0 core/src/ledger/ibc/mod.rs => ibc/src/lib.rs | 0 {core/src/ledger/ibc => ibc/src}/storage.rs | 0 17 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 ibc/Cargo.toml rename {core/src/ledger/ibc => ibc/src}/context/client.rs (100%) rename {core/src/ledger/ibc => ibc/src}/context/common.rs (100%) rename {core/src/ledger/ibc => ibc/src}/context/execution.rs (100%) rename {core/src/ledger/ibc => ibc/src}/context/mod.rs (100%) rename {core/src/ledger/ibc => ibc/src}/context/router.rs (100%) rename {core/src/ledger/ibc => ibc/src}/context/storage.rs (100%) rename {core/src/ledger/ibc => ibc/src}/context/token_transfer.rs (100%) rename {core/src/ledger/ibc => ibc/src}/context/transfer_mod.rs (100%) rename {core/src/ledger/ibc => ibc/src}/context/validation.rs (100%) rename core/src/ledger/ibc/mod.rs => ibc/src/lib.rs (100%) rename {core/src/ledger/ibc => ibc/src}/storage.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 693df58311..ac2736904b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4467,6 +4467,10 @@ dependencies = [ "namada_trans_token", ] +[[package]] +name = "namada_ibc" +version = "0.1.0" + [[package]] name = "namada_light_sdk" version = "0.29.0" diff --git a/Cargo.toml b/Cargo.toml index 4d9210adbc..98f67f3050 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,10 @@ members = [ "ethereum_bridge", "light_sdk", "macros", + "examples", "governance", + "ibc", + "macros", "proof_of_stake", "sdk", "shared", @@ -19,7 +22,6 @@ members = [ "tx_prelude", "vm_env", "vp_prelude", - "examples", ] # wasm packages have to be built separately diff --git a/Makefile b/Makefile index a7d5579f5f..2a3d691e97 100644 --- a/Makefile +++ b/Makefile @@ -40,6 +40,7 @@ crates += namada_benchmarks crates += namada_encoding_spec crates += namada_ethereum_bridge crates += namada_governance +crates += namada_ibc crates += namada_macros crates += namada_proof_of_stake crates += namada_sdk diff --git a/core/src/ledger/mod.rs b/core/src/ledger/mod.rs index 2426e2931a..aed6eeae92 100644 --- a/core/src/ledger/mod.rs +++ b/core/src/ledger/mod.rs @@ -2,7 +2,6 @@ pub mod eth_bridge; pub mod gas; -pub mod ibc; pub mod inflation; pub mod masp_conversions; pub mod masp_utils; diff --git a/core/src/types/ibc.rs b/core/src/types/ibc.rs index cfb2357fef..f517c84363 100644 --- a/core/src/types/ibc.rs +++ b/core/src/types/ibc.rs @@ -17,7 +17,6 @@ use crate::ibc::core::handler::types::events::{ Error as IbcEventError, IbcEvent as RawIbcEvent, }; use crate::ibc::primitives::proto::Protobuf; -pub use crate::ledger::ibc::storage::is_ibc_key; use crate::tendermint::abci::Event as AbciEvent; use crate::types::masp::PaymentAddress; use crate::types::token::Transfer; diff --git a/ibc/Cargo.toml b/ibc/Cargo.toml new file mode 100644 index 0000000000..d8e1feb686 --- /dev/null +++ b/ibc/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "namada_ibc" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/core/src/ledger/ibc/context/client.rs b/ibc/src/context/client.rs similarity index 100% rename from core/src/ledger/ibc/context/client.rs rename to ibc/src/context/client.rs diff --git a/core/src/ledger/ibc/context/common.rs b/ibc/src/context/common.rs similarity index 100% rename from core/src/ledger/ibc/context/common.rs rename to ibc/src/context/common.rs diff --git a/core/src/ledger/ibc/context/execution.rs b/ibc/src/context/execution.rs similarity index 100% rename from core/src/ledger/ibc/context/execution.rs rename to ibc/src/context/execution.rs diff --git a/core/src/ledger/ibc/context/mod.rs b/ibc/src/context/mod.rs similarity index 100% rename from core/src/ledger/ibc/context/mod.rs rename to ibc/src/context/mod.rs diff --git a/core/src/ledger/ibc/context/router.rs b/ibc/src/context/router.rs similarity index 100% rename from core/src/ledger/ibc/context/router.rs rename to ibc/src/context/router.rs diff --git a/core/src/ledger/ibc/context/storage.rs b/ibc/src/context/storage.rs similarity index 100% rename from core/src/ledger/ibc/context/storage.rs rename to ibc/src/context/storage.rs diff --git a/core/src/ledger/ibc/context/token_transfer.rs b/ibc/src/context/token_transfer.rs similarity index 100% rename from core/src/ledger/ibc/context/token_transfer.rs rename to ibc/src/context/token_transfer.rs diff --git a/core/src/ledger/ibc/context/transfer_mod.rs b/ibc/src/context/transfer_mod.rs similarity index 100% rename from core/src/ledger/ibc/context/transfer_mod.rs rename to ibc/src/context/transfer_mod.rs diff --git a/core/src/ledger/ibc/context/validation.rs b/ibc/src/context/validation.rs similarity index 100% rename from core/src/ledger/ibc/context/validation.rs rename to ibc/src/context/validation.rs diff --git a/core/src/ledger/ibc/mod.rs b/ibc/src/lib.rs similarity index 100% rename from core/src/ledger/ibc/mod.rs rename to ibc/src/lib.rs diff --git a/core/src/ledger/ibc/storage.rs b/ibc/src/storage.rs similarity index 100% rename from core/src/ledger/ibc/storage.rs rename to ibc/src/storage.rs From 4cd888aa0b9d39b20d0e90876c616e185d7c6e52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 15 Dec 2023 18:36:20 +0000 Subject: [PATCH 010/118] core: make namada_shielded_token crate out of masp mods --- Cargo.lock | 4 ++++ Cargo.toml | 1 + Makefile | 1 + core/src/ledger/mod.rs | 2 -- shielded_token/Cargo.toml | 8 ++++++++ .../src/conversion.rs | 0 shielded_token/src/lib.rs | 4 ++++ .../ledger/masp_utils.rs => shielded_token/src/utils.rs | 0 8 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 shielded_token/Cargo.toml rename core/src/ledger/masp_conversions.rs => shielded_token/src/conversion.rs (100%) create mode 100644 shielded_token/src/lib.rs rename core/src/ledger/masp_utils.rs => shielded_token/src/utils.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index ac2736904b..f2bc5a1f70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4568,6 +4568,10 @@ dependencies = [ "zeroize", ] +[[package]] +name = "namada_shielded_token" +version = "0.1.0" + [[package]] name = "namada_test_utils" version = "0.29.0" diff --git a/Cargo.toml b/Cargo.toml index 98f67f3050..0196f13feb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ members = [ "proof_of_stake", "sdk", "shared", + "shielded_token", "test_utils", "tests", "trans_token", diff --git a/Makefile b/Makefile index 2a3d691e97..194bee7d2a 100644 --- a/Makefile +++ b/Makefile @@ -44,6 +44,7 @@ crates += namada_ibc crates += namada_macros crates += namada_proof_of_stake crates += namada_sdk +crates += namada_shielded_token crates += namada_test_utils crates += namada_tests crates += namada_trans_token diff --git a/core/src/ledger/mod.rs b/core/src/ledger/mod.rs index aed6eeae92..cb83a3ed8a 100644 --- a/core/src/ledger/mod.rs +++ b/core/src/ledger/mod.rs @@ -3,8 +3,6 @@ pub mod eth_bridge; pub mod gas; pub mod inflation; -pub mod masp_conversions; -pub mod masp_utils; pub mod parameters; pub mod replay_protection; pub mod storage; diff --git a/shielded_token/Cargo.toml b/shielded_token/Cargo.toml new file mode 100644 index 0000000000..43fd32be90 --- /dev/null +++ b/shielded_token/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "namada_shielded_token" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/core/src/ledger/masp_conversions.rs b/shielded_token/src/conversion.rs similarity index 100% rename from core/src/ledger/masp_conversions.rs rename to shielded_token/src/conversion.rs diff --git a/shielded_token/src/lib.rs b/shielded_token/src/lib.rs new file mode 100644 index 0000000000..e8f97918f1 --- /dev/null +++ b/shielded_token/src/lib.rs @@ -0,0 +1,4 @@ +//! Namada shielded token. + +pub mod conversion; +pub mod utils; diff --git a/core/src/ledger/masp_utils.rs b/shielded_token/src/utils.rs similarity index 100% rename from core/src/ledger/masp_utils.rs rename to shielded_token/src/utils.rs From cc0b7ea7aad76eb1b97fcf40eb1c05c9d9dcc8ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 15 Dec 2023 18:40:43 +0000 Subject: [PATCH 011/118] shielded_token: move ConversionState into core::types::token --- core/src/ledger/storage/mockdb.rs | 2 +- core/src/types/token.rs | 27 ++++++++++++++++++++++++--- shielded_token/src/conversion.rs | 17 ----------------- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/core/src/ledger/storage/mockdb.rs b/core/src/ledger/storage/mockdb.rs index caf3ee88ac..b200583197 100644 --- a/core/src/ledger/storage/mockdb.rs +++ b/core/src/ledger/storage/mockdb.rs @@ -16,7 +16,6 @@ use super::merkle_tree::{ use super::{ BlockStateRead, BlockStateWrite, DBIter, DBWriteBatch, Error, Result, DB, }; -use crate::ledger::masp_conversions::ConversionState; use crate::ledger::replay_protection; use crate::ledger::storage::types::{self, KVBytes, PrefixIterator}; use crate::types::ethereum_events::Uint; @@ -28,6 +27,7 @@ use crate::types::storage::{ KEY_SEGMENT_SEPARATOR, }; use crate::types::time::DateTimeUtc; +use crate::types::token::ConversionState; /// An in-memory DB for testing. #[derive(Debug, Default)] diff --git a/core/src/types/token.rs b/core/src/types/token.rs index cfa2a2372c..80dabd37b6 100644 --- a/core/src/types/token.rs +++ b/core/src/types/token.rs @@ -1,6 +1,7 @@ //! A basic fungible token use std::cmp::Ordering; +use std::collections::BTreeMap; use std::fmt::Display; use std::iter::Sum; use std::ops::{Add, AddAssign, Div, Mul, Sub, SubAssign}; @@ -9,18 +10,38 @@ use std::str::FromStr; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use data_encoding::BASE32HEX_NOPAD; use ethabi::ethereum_types::U256; +use masp_primitives::asset_type::AssetType; +use masp_primitives::convert::AllowedConversion; +use masp_primitives::merkle_tree::FrozenCommitmentTree; +use masp_primitives::sapling; use serde::{Deserialize, Serialize}; use thiserror::Error; -use super::dec::POS_DECIMAL_PRECISION; use crate::ibc::apps::transfer::types::Amount as IbcAmount; use crate::types::address::{Address, DecodeError as AddressError}; -use crate::types::dec::Dec; +use crate::types::dec::{Dec, POS_DECIMAL_PRECISION}; use crate::types::hash::Hash; use crate::types::storage; -use crate::types::storage::{DbKeySeg, KeySeg}; +use crate::types::storage::{DbKeySeg, Epoch, KeySeg}; use crate::types::uint::{self, Uint, I256}; +/// A representation of the conversion state +#[derive(Debug, Default, BorshSerialize, BorshDeserialize)] +pub struct ConversionState { + /// The last amount of the native token distributed + pub normed_inflation: Option, + /// The tree currently containing all the conversions + pub tree: FrozenCommitmentTree, + /// A map from token alias to actual address. + pub tokens: BTreeMap, + /// Map assets to their latest conversion and position in Merkle tree + #[allow(clippy::type_complexity)] + pub assets: BTreeMap< + AssetType, + ((Address, MaspDenom), Epoch, AllowedConversion, usize), + >, +} + /// Amount in micro units. For different granularity another representation /// might be more appropriate. #[derive( diff --git a/shielded_token/src/conversion.rs b/shielded_token/src/conversion.rs index fc98c6ff65..cb2a167a4c 100644 --- a/shielded_token/src/conversion.rs +++ b/shielded_token/src/conversion.rs @@ -20,23 +20,6 @@ use crate::types::storage::Epoch; use crate::types::token::{self, DenominatedAmount, MaspDenom}; use crate::types::uint::Uint; -/// A representation of the conversion state -#[derive(Debug, Default, BorshSerialize, BorshDeserialize)] -pub struct ConversionState { - /// The last amount of the native token distributed - pub normed_inflation: Option, - /// The tree currently containing all the conversions - pub tree: FrozenCommitmentTree, - /// A map from token alias to actual address. - pub tokens: BTreeMap, - /// Map assets to their latest conversion and position in Merkle tree - #[allow(clippy::type_complexity)] - pub assets: BTreeMap< - AssetType, - ((Address, MaspDenom), Epoch, AllowedConversion, usize), - >, -} - /// Compute the MASP rewards by applying the PD-controller to the genesis /// parameters and the last inflation and last locked rewards ratio values. pub fn calculate_masp_rewards( From 31e8b84731b561b441e0034e3a623f94d9f1859e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 15 Dec 2023 18:53:50 +0000 Subject: [PATCH 012/118] move some governance types back into core --- core/src/types/governance.rs | 343 +++++++++++++++++++++++ core/src/types/mod.rs | 1 + core/src/types/transaction/governance.rs | 16 +- governance/src/cli/onchain.rs | 131 --------- governance/src/storage/proposal.rs | 140 --------- governance/src/storage/vote.rs | 70 ----- 6 files changed, 351 insertions(+), 350 deletions(-) create mode 100644 core/src/types/governance.rs diff --git a/core/src/types/governance.rs b/core/src/types/governance.rs new file mode 100644 index 0000000000..7bb2bc16f8 --- /dev/null +++ b/core/src/types/governance.rs @@ -0,0 +1,343 @@ +//! Governance proposal and voting types + +use std::collections::{BTreeMap, HashSet}; + +use borsh::{BorshDeserialize, BorshSerialize}; +use serde::{Deserialize, Serialize}; + +use crate::types::address::Address; +use crate::types::hash::Hash; +use crate::types::storage::Epoch; +use crate::types::token; + +/// Pgf default proposal +#[derive( + Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, +)] +pub struct DefaultProposal { + /// The proposal data + pub proposal: OnChainProposal, + /// The default proposal extra data + pub data: Option>, +} + +#[derive( + Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, +)] +/// The proposal structure +pub struct OnChainProposal { + /// The proposal id + pub id: Option, + /// The proposal content + pub content: BTreeMap, + /// The proposal author address + pub author: Address, + /// The epoch from which voting is allowed + pub voting_start_epoch: Epoch, + /// The epoch from which voting is stopped + pub voting_end_epoch: Epoch, + /// The epoch from which this changes are executed + pub grace_epoch: Epoch, +} + +/// Pgf funding proposal +#[derive( + Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, +)] +pub struct PgfFundingProposal { + /// The proposal data + pub proposal: OnChainProposal, + /// The Pgf funding proposal extra data + pub data: PgfFunding, +} + +/// Pgf fundings +#[derive( + Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, +)] +pub struct PgfFunding { + /// Pgf continous funding + pub continous: Vec, + /// pgf retro fundings + pub retro: Vec, +} + +/// Pgf continous funding +#[derive( + Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, +)] +pub struct PgfContinous { + /// Pgf target + pub target: PgfFundingTarget, + /// Pgf action + pub action: PgfAction, +} + +/// Pgf retro funding +#[derive( + Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, +)] +pub struct PgfRetro { + /// Pgf retro target + pub target: PgfFundingTarget, +} + +/// Pgf Target +#[derive( + Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, +)] +pub struct PgfFundingTarget { + /// Target amount + pub amount: token::Amount, + /// Target address + pub address: Address, +} + +/// Represent an proposal vote +#[derive( + Debug, + Clone, + BorshSerialize, + BorshDeserialize, + Serialize, + Deserialize, + PartialEq, +)] +pub enum ProposalVote { + /// Represent an yay proposal vote + Yay, + /// Represent an nay proposal vote + Nay, + /// Represent an abstain proposal vote + Abstain, +} + +/// Pgf action +#[derive( + Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, +)] +pub enum PgfAction { + /// Add action + Add, + /// Remove action + Remove, +} + +/// Pgf stewards proposal +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PgfStewardProposal { + /// The proposal data + pub proposal: OnChainProposal, + /// The Pgf steward proposal extra data + pub data: StewardsUpdate, +} + +/// Pgf steward proposal extra data +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct StewardsUpdate { + /// The optional steward to add + pub add: Option
, + /// The stewards to remove + pub remove: Vec
, +} + +/// An add or remove action for PGF +#[derive( + Debug, + Clone, + Hash, + PartialEq, + Eq, + PartialOrd, + Ord, + BorshSerialize, + BorshDeserialize, + Serialize, + Deserialize, +)] +pub enum AddRemove { + /// Add + Add(T), + /// Remove + Remove(T), +} + +/// The target of a PGF payment +#[derive( + Debug, + Clone, + PartialEq, + BorshSerialize, + BorshDeserialize, + Serialize, + Deserialize, + Ord, + Eq, + PartialOrd, +)] +pub struct PGFTarget { + /// The target address + pub target: Address, + /// The amount of token to fund the target address + pub amount: token::Amount, +} + +/// The actions that a PGF Steward can propose to execute +#[derive( + Debug, + Clone, + PartialEq, + BorshSerialize, + BorshDeserialize, + Serialize, + Deserialize, +)] +pub enum PGFAction { + /// A continuous payment + Continuous(AddRemove), + /// A retro payment + Retro(PGFTarget), +} + +/// The type of a Proposal +#[derive( + Debug, + Clone, + PartialEq, + BorshSerialize, + BorshDeserialize, + Serialize, + Deserialize, +)] +pub enum ProposalType { + /// Default governance proposal with the optional wasm code + Default(Option), + /// PGF stewards proposal + PGFSteward(HashSet>), + /// PGF funding proposal + PGFPayment(Vec), +} + +#[derive( + Debug, + Clone, + PartialEq, + BorshSerialize, + BorshDeserialize, + Eq, + Serialize, + Deserialize, +)] +/// The vote for a proposal +pub enum StorageProposalVote { + /// Yes + Yay(VoteType), + /// No + Nay, + /// Abstain + Abstain, +} + +/// The type of a governance vote with the optional associated Memo +#[derive( + Debug, + Clone, + PartialEq, + BorshSerialize, + BorshDeserialize, + Eq, + Serialize, + Deserialize, +)] +pub enum VoteType { + /// A default vote without Memo + Default, + /// A vote for the PGF stewards + PGFSteward, + /// A vote for a PGF payment proposal + PGFPayment, +} + +#[cfg(any(test, feature = "testing"))] +/// Testing helpers and and strategies for governance proposals +pub mod testing { + use proptest::prelude::*; + use proptest::{collection, option}; + + use super::*; + use crate::types::address::testing::arb_non_internal_address; + use crate::types::hash::testing::arb_hash; + use crate::types::token::testing::arb_amount; + + /// Generate an arbitrary add or removal of what's generated by the supplied + /// strategy + pub fn arb_add_remove( + strategy: X, + ) -> impl Strategy::Value>> { + (0..2, strategy).prop_map(|(discriminant, val)| match discriminant { + 0 => AddRemove::Add(val), + 1 => AddRemove::Remove(val), + _ => unreachable!(), + }) + } + + prop_compose! { + /// Generate an arbitrary PGF target + pub fn arb_pgf_target()( + target in arb_non_internal_address(), + amount in arb_amount(), + ) -> PGFTarget { + PGFTarget { + target, + amount, + } + } + } + + /// Generate an arbitrary PGF action + pub fn arb_pgf_action() -> impl Strategy { + arb_add_remove(arb_pgf_target()) + .prop_map(PGFAction::Continuous) + .boxed() + .prop_union(arb_pgf_target().prop_map(PGFAction::Retro).boxed()) + } + + /// Generate an arbitrary proposal type + pub fn arb_proposal_type() -> impl Strategy { + option::of(arb_hash()) + .prop_map(ProposalType::Default) + .boxed() + .prop_union( + collection::hash_set( + arb_add_remove(arb_non_internal_address()), + 0..10, + ) + .prop_map(ProposalType::PGFSteward) + .boxed(), + ) + .or(collection::vec(arb_pgf_action(), 0..10) + .prop_map(ProposalType::PGFPayment) + .boxed()) + } + + prop_compose! { + /// Geerate an arbitrary vote type + pub fn arb_vote_type()(discriminant in 0..3) -> VoteType { + match discriminant { + 0 => VoteType::Default, + 1 => VoteType::PGFSteward, + 2 => VoteType::PGFPayment, + _ => unreachable!(), + } + } + } + + /// Generate an arbitrary proposal vote + pub fn arb_proposal_vote() -> impl Strategy { + arb_vote_type() + .prop_map(StorageProposalVote::Yay) + .boxed() + .prop_union(Just(StorageProposalVote::Nay).boxed()) + .or(Just(StorageProposalVote::Abstain).boxed()) + } +} diff --git a/core/src/types/mod.rs b/core/src/types/mod.rs index 904e005f34..1027bdd26e 100644 --- a/core/src/types/mod.rs +++ b/core/src/types/mod.rs @@ -8,6 +8,7 @@ pub mod eth_abi; pub mod eth_bridge_pool; pub mod ethereum_events; pub mod ethereum_structs; +pub mod governance; pub mod hash; pub mod ibc; pub mod internal; diff --git a/core/src/types/transaction/governance.rs b/core/src/types/transaction/governance.rs index 8e43b488e3..f723ab6d1b 100644 --- a/core/src/types/transaction/governance.rs +++ b/core/src/types/transaction/governance.rs @@ -4,14 +4,11 @@ use borsh::{BorshDeserialize, BorshSerialize}; use serde::{Deserialize, Serialize}; use thiserror::Error; -use crate::ledger::governance::cli::onchain::{ - DefaultProposal, PgfFundingProposal, PgfStewardProposal, -}; -use crate::ledger::governance::storage::proposal::{ - AddRemove, PGFAction, PGFTarget, ProposalType, -}; -use crate::ledger::governance::storage::vote::StorageProposalVote; use crate::types::address::Address; +use crate::types::governance::{ + AddRemove, DefaultProposal, PGFAction, PGFTarget, PgfFundingProposal, + PgfStewardProposal, ProposalType, StorageProposalVote, +}; use crate::types::hash::Hash; use crate::types::storage::Epoch; @@ -171,9 +168,10 @@ pub mod tests { use proptest::{collection, prop_compose}; use super::*; - use crate::ledger::governance::storage::proposal::testing::arb_proposal_type; - use crate::ledger::governance::storage::vote::testing::arb_proposal_vote; use crate::types::address::testing::arb_non_internal_address; + use crate::types::governance::testing::{ + arb_proposal_type, arb_proposal_vote, + }; use crate::types::hash::testing::arb_hash; use crate::types::storage::testing::arb_epoch; diff --git a/governance/src/cli/onchain.rs b/governance/src/cli/onchain.rs index fbe6d1d901..af9e3e0f65 100644 --- a/governance/src/cli/onchain.rs +++ b/governance/src/cli/onchain.rs @@ -14,36 +14,6 @@ use super::validation::{ }; use crate::parameters::GovernanceParameters; -#[derive( - Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, -)] -/// The proposal structure -pub struct OnChainProposal { - /// The proposal id - pub id: Option, - /// The proposal content - pub content: BTreeMap, - /// The proposal author address - pub author: Address, - /// The epoch from which voting is allowed - pub voting_start_epoch: Epoch, - /// The epoch from which voting is stopped - pub voting_end_epoch: Epoch, - /// The epoch from which this changes are executed - pub grace_epoch: Epoch, -} - -/// Pgf default proposal -#[derive( - Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, -)] -pub struct DefaultProposal { - /// The proposal data - pub proposal: OnChainProposal, - /// The default proposal extra data - pub data: Option>, -} - impl DefaultProposal { /// Validate a default funding proposal pub fn validate( @@ -104,24 +74,6 @@ impl TryFrom<&[u8]> for DefaultProposal { } } -/// Pgf stewards proposal -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct PgfStewardProposal { - /// The proposal data - pub proposal: OnChainProposal, - /// The Pgf steward proposal extra data - pub data: StewardsUpdate, -} - -/// Pgf steward proposal extra data -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct StewardsUpdate { - /// The optional steward to add - pub add: Option
, - /// The stewards to remove - pub remove: Vec
, -} - impl PgfStewardProposal { /// Validate a Pgf stewards proposal pub fn validate( @@ -179,17 +131,6 @@ impl TryFrom<&[u8]> for PgfStewardProposal { } } -/// Pgf funding proposal -#[derive( - Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, -)] -pub struct PgfFundingProposal { - /// The proposal data - pub proposal: OnChainProposal, - /// The Pgf funding proposal extra data - pub data: PgfFunding, -} - impl PgfFundingProposal { /// Validate a Pgf funding proposal pub fn validate( @@ -253,17 +194,6 @@ pub struct PgfSteward { pub address: Address, } -/// Pgf action -#[derive( - Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, -)] -pub enum PgfAction { - /// Add action - Add, - /// Remove action - Remove, -} - impl PgfAction { /// Check if a pgf action is adding a steward pub fn is_add(&self) -> bool { @@ -271,67 +201,6 @@ impl PgfAction { } } -/// Pgf fundings -#[derive( - Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, -)] -pub struct PgfFunding { - /// Pgf continuous funding - pub continuous: Vec, - /// pgf retro fundings - pub retro: Vec, -} - -/// Pgf continous funding -#[derive( - Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, -)] -pub struct PgfContinous { - /// Pgf target - pub target: PgfFundingTarget, - /// Pgf action - pub action: PgfAction, -} - -/// Pgf retro funding -#[derive( - Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, -)] -pub struct PgfRetro { - /// Pgf retro target - pub target: PgfFundingTarget, -} - -/// Pgf Target -#[derive( - Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, -)] -pub struct PgfFundingTarget { - /// Target amount - pub amount: token::Amount, - /// Target address - pub address: Address, -} - -/// Represent an proposal vote -#[derive( - Debug, - Clone, - BorshSerialize, - BorshDeserialize, - Serialize, - Deserialize, - PartialEq, -)] -pub enum ProposalVote { - /// Represent an yay proposal vote - Yay, - /// Represent an nay proposal vote - Nay, - /// Represent an abstain proposal vote - Abstain, -} - impl TryFrom for ProposalVote { type Error = String; diff --git a/governance/src/storage/proposal.rs b/governance/src/storage/proposal.rs index 940812eed5..2c894e52b3 100644 --- a/governance/src/storage/proposal.rs +++ b/governance/src/storage/proposal.rs @@ -47,83 +47,6 @@ impl StoragePgfFunding { } } -/// An add or remove action for PGF -#[derive( - Debug, - Clone, - Hash, - PartialEq, - Eq, - PartialOrd, - Ord, - BorshSerialize, - BorshDeserialize, - Serialize, - Deserialize, -)] -pub enum AddRemove { - /// Add - Add(T), - /// Remove - Remove(T), -} - -/// The target of a PGF payment -#[derive( - Debug, - Clone, - PartialEq, - BorshSerialize, - BorshDeserialize, - Serialize, - Deserialize, - Ord, - Eq, - PartialOrd, -)] -pub struct PGFTarget { - /// The target address - pub target: Address, - /// The amount of token to fund the target address - pub amount: token::Amount, -} - -/// The actions that a PGF Steward can propose to execute -#[derive( - Debug, - Clone, - PartialEq, - BorshSerialize, - BorshDeserialize, - Serialize, - Deserialize, -)] -pub enum PGFAction { - /// A continuous payment - Continuous(AddRemove), - /// A retro payment - Retro(PGFTarget), -} - -/// The type of a Proposal -#[derive( - Debug, - Clone, - PartialEq, - BorshSerialize, - BorshDeserialize, - Serialize, - Deserialize, -)] -pub enum ProposalType { - /// Default governance proposal with the optional wasm code - Default(Option), - /// PGF stewards proposal - PGFSteward(HashSet>), - /// PGF funding proposal - PGFPayment(Vec), -} - impl ProposalType { /// Check if the proposal type is default pub fn is_default(&self) -> bool { @@ -311,66 +234,3 @@ impl Display for StorageProposal { ) } } - -#[cfg(any(test, feature = "testing"))] -/// Testing helpers and and strategies for governance proposals -pub mod testing { - use namada_core::types::address::testing::arb_non_internal_address; - use namada_core::types::hash::testing::arb_hash; - use namada_core::types::token::testing::arb_amount; - use proptest::prelude::Strategy; - use proptest::{collection, option, prop_compose}; - - use super::*; - - /// Generate an arbitrary add or removal of what's generated by the supplied - /// strategy - pub fn arb_add_remove( - strategy: X, - ) -> impl Strategy::Value>> { - (0..2, strategy).prop_map(|(discriminant, val)| match discriminant { - 0 => AddRemove::Add(val), - 1 => AddRemove::Remove(val), - _ => unreachable!(), - }) - } - - prop_compose! { - /// Generate an arbitrary PGF target - pub fn arb_pgf_target()( - target in arb_non_internal_address(), - amount in arb_amount(), - ) -> PGFTarget { - PGFTarget { - target, - amount, - } - } - } - - /// Generate an arbitrary PGF action - pub fn arb_pgf_action() -> impl Strategy { - arb_add_remove(arb_pgf_target()) - .prop_map(PGFAction::Continuous) - .boxed() - .prop_union(arb_pgf_target().prop_map(PGFAction::Retro).boxed()) - } - - /// Generate an arbitrary proposal type - pub fn arb_proposal_type() -> impl Strategy { - option::of(arb_hash()) - .prop_map(ProposalType::Default) - .boxed() - .prop_union( - collection::hash_set( - arb_add_remove(arb_non_internal_address()), - 0..10, - ) - .prop_map(ProposalType::PGFSteward) - .boxed(), - ) - .or(collection::vec(arb_pgf_action(), 0..10) - .prop_map(ProposalType::PGFPayment) - .boxed()) - } -} diff --git a/governance/src/storage/vote.rs b/governance/src/storage/vote.rs index 9be49fa9b7..8e838b4200 100644 --- a/governance/src/storage/vote.rs +++ b/governance/src/storage/vote.rs @@ -6,46 +6,6 @@ use serde::{Deserialize, Serialize}; use super::super::cli::onchain::ProposalVote; use super::proposal::ProposalType; -/// The type of a governance vote with the optional associated Memo -#[derive( - Debug, - Clone, - PartialEq, - BorshSerialize, - BorshDeserialize, - Eq, - Serialize, - Deserialize, -)] -pub enum VoteType { - /// A default vote without Memo - Default, - /// A vote for the PGF stewards - PGFSteward, - /// A vote for a PGF payment proposal - PGFPayment, -} - -#[derive( - Debug, - Clone, - PartialEq, - BorshSerialize, - BorshDeserialize, - Eq, - Serialize, - Deserialize, -)] -/// The vote for a proposal -pub enum StorageProposalVote { - /// Yes - Yay(VoteType), - /// No - Nay, - /// Abstain - Abstain, -} - impl StorageProposalVote { /// Check if a vote is yay pub fn is_yay(&self) -> bool { @@ -144,33 +104,3 @@ impl PartialEq for ProposalType { } } } - -#[cfg(any(test, feature = "testing"))] -/// Testing helpers and strategies for governance votes -pub mod testing { - use proptest::prelude::{Just, Strategy}; - use proptest::prop_compose; - - use super::*; - - prop_compose! { - /// Geerate an arbitrary vote type - pub fn arb_vote_type()(discriminant in 0..3) -> VoteType { - match discriminant { - 0 => VoteType::Default, - 1 => VoteType::PGFSteward, - 2 => VoteType::PGFPayment, - _ => unreachable!(), - } - } - } - - /// Generate an arbitrary proposal vote - pub fn arb_proposal_vote() -> impl Strategy { - arb_vote_type() - .prop_map(StorageProposalVote::Yay) - .boxed() - .prop_union(Just(StorageProposalVote::Nay).boxed()) - .or(Just(StorageProposalVote::Abstain).boxed()) - } -} From 05628416d0668711da1df417e1c7b193c0321023 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 15 Dec 2023 19:06:07 +0000 Subject: [PATCH 013/118] core: move eth_bridge storage mods to ethereum_bridge crate --- Cargo.lock | 1 + core/src/ledger/eth_bridge/mod.rs | 4 +- core/src/ledger/eth_bridge/storage/mod.rs | 120 ----------------- core/src/ledger/storage/mod.rs | 4 +- ethereum_bridge/Cargo.toml | 1 + .../src}/storage/bridge_pool.rs | 0 ethereum_bridge/src/storage/mod.rs | 124 +++++++++++++++++- .../src}/storage/whitelist.rs | 19 +-- .../src/storage/wrapped_erc20.rs | 0 9 files changed, 135 insertions(+), 138 deletions(-) delete mode 100644 core/src/ledger/eth_bridge/storage/mod.rs rename {core/src/ledger/eth_bridge => ethereum_bridge/src}/storage/bridge_pool.rs (100%) rename {core/src/ledger/eth_bridge => ethereum_bridge/src}/storage/whitelist.rs (91%) rename core/src/ledger/eth_bridge/storage/wrapped_erc20s.rs => ethereum_bridge/src/storage/wrapped_erc20.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index f2bc5a1f70..78045ef39b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4438,6 +4438,7 @@ dependencies = [ "namada_core", "namada_macros", "namada_proof_of_stake", + "namada_trans_token", "rand 0.8.5", "serde 1.0.193", "serde_json", diff --git a/core/src/ledger/eth_bridge/mod.rs b/core/src/ledger/eth_bridge/mod.rs index 5932c2c8d7..fda8964ff6 100644 --- a/core/src/ledger/eth_bridge/mod.rs +++ b/core/src/ledger/eth_bridge/mod.rs @@ -1,6 +1,4 @@ -//! Storage keys for the Ethereum bridge account - -pub mod storage; +//! Ethereum bridge account use crate::types::address::{Address, InternalAddress}; diff --git a/core/src/ledger/eth_bridge/storage/mod.rs b/core/src/ledger/eth_bridge/storage/mod.rs deleted file mode 100644 index 5e11db64ef..0000000000 --- a/core/src/ledger/eth_bridge/storage/mod.rs +++ /dev/null @@ -1,120 +0,0 @@ -//! Functionality for accessing the storage subspace -pub mod bridge_pool; -pub mod whitelist; -pub mod wrapped_erc20s; - -use super::ADDRESS; -use crate::ledger::parameters::storage::*; -use crate::ledger::parameters::ADDRESS as PARAM_ADDRESS; -use crate::types::address::Address; -use crate::types::storage::{DbKeySeg, Key, KeySeg}; -use crate::types::token::balance_key; - -/// Key prefix for the storage subspace -pub fn prefix() -> Key { - Key::from(ADDRESS.to_db_key()) -} - -/// Key for storing the initial Ethereum block height when -/// events will first be extracted from. -pub fn eth_start_height_key() -> Key { - get_eth_start_height_key_at_addr(PARAM_ADDRESS) -} - -/// The key to the escrow of the VP. -pub fn escrow_key(nam_addr: &Address) -> Key { - balance_key(nam_addr, &ADDRESS) -} - -/// Check if the given `key` contains an Ethereum -/// bridge address segment. -#[inline] -pub fn has_eth_addr_segment(key: &Key) -> bool { - key.segments - .iter() - .any(|s| matches!(s, DbKeySeg::AddressSeg(ADDRESS))) -} - -/// Returns whether a key belongs to this account or not -pub fn is_eth_bridge_key(nam_addr: &Address, key: &Key) -> bool { - key == &escrow_key(nam_addr) - || matches!(key.segments.get(0), Some(first_segment) if first_segment == &ADDRESS.to_db_key()) - || wrapped_erc20s::has_erc20_segment(key) -} - -/// A key for storing the active / inactive status -/// of the Ethereum bridge. -pub fn active_key() -> Key { - get_active_status_key_at_addr(PARAM_ADDRESS) -} - -/// Storage key for the minimum confirmations parameter. -pub fn min_confirmations_key() -> Key { - get_min_confirmations_key_at_addr(PARAM_ADDRESS) -} - -/// Storage key for the Ethereum address of wNam. -pub fn native_erc20_key() -> Key { - get_native_erc20_key_at_addr(PARAM_ADDRESS) -} - -/// Storage key for the Ethereum address of the bridge contract. -pub fn bridge_contract_key() -> Key { - get_bridge_contract_address_key_at_addr(PARAM_ADDRESS) -} - -#[cfg(test)] -mod test { - use super::*; - use crate::types::address; - use crate::types::address::nam; - use crate::types::ethereum_events::testing::arbitrary_eth_address; - - #[test] - fn test_is_eth_bridge_key_returns_true_for_eth_bridge_address() { - let key = Key::from(super::ADDRESS.to_db_key()); - assert!(is_eth_bridge_key(&nam(), &key)); - } - - #[test] - fn test_is_eth_bridge_key_returns_true_for_eth_bridge_subkey() { - let key = Key::from(super::ADDRESS.to_db_key()) - .push(&"arbitrary key segment".to_owned()) - .expect("Could not set up test"); - assert!(is_eth_bridge_key(&nam(), &key)); - } - - #[test] - fn test_is_eth_bridge_key_returns_true_for_eth_bridge_balance_key() { - let eth_addr = arbitrary_eth_address(); - let token = address::Address::Internal( - address::InternalAddress::Erc20(eth_addr), - ); - let key = - balance_key(&token, &address::testing::established_address_1()); - assert!(is_eth_bridge_key(&nam(), &key)); - } - - #[test] - fn test_is_eth_bridge_key_returns_false_for_different_address() { - let key = - Key::from(address::testing::established_address_1().to_db_key()); - assert!(!is_eth_bridge_key(&nam(), &key)); - } - - #[test] - fn test_is_eth_bridge_key_returns_false_for_different_address_subkey() { - let key = - Key::from(address::testing::established_address_1().to_db_key()) - .push(&"arbitrary key segment".to_owned()) - .expect("Could not set up test"); - assert!(!is_eth_bridge_key(&nam(), &key)); - } - - #[test] - fn test_is_eth_bridge_key_returns_false_for_non_eth_bridge_balance_key() { - let key = - balance_key(&nam(), &address::testing::established_address_1()); - assert!(!is_eth_bridge_key(&nam(), &key)); - } -} diff --git a/core/src/ledger/storage/mod.rs b/core/src/ledger/storage/mod.rs index 3c5cf39c35..caad5d80ec 100644 --- a/core/src/ledger/storage/mod.rs +++ b/core/src/ledger/storage/mod.rs @@ -28,9 +28,6 @@ use crate::ledger::eth_bridge::storage::bridge_pool::is_pending_transfer_key; use crate::ledger::gas::{ STORAGE_ACCESS_GAS_PER_BYTE, STORAGE_WRITE_GAS_PER_BYTE, }; -pub use crate::ledger::masp_conversions::{ - calculate_masp_rewards, encode_asset_type, ConversionState, -}; use crate::ledger::parameters::{self, EpochDuration, Parameters}; use crate::ledger::storage::merkle_tree::{ Error as MerkleTreeError, MerkleRoot, @@ -48,6 +45,7 @@ use crate::types::storage::{ BLOCK_HEIGHT_LENGTH, EPOCH_TYPE_LENGTH, }; use crate::types::time::DateTimeUtc; +pub use crate::types::token::ConversionState; /// A result of a function that may fail pub type Result = std::result::Result; diff --git a/ethereum_bridge/Cargo.toml b/ethereum_bridge/Cargo.toml index 7114cec224..0a2e005d2d 100644 --- a/ethereum_bridge/Cargo.toml +++ b/ethereum_bridge/Cargo.toml @@ -20,6 +20,7 @@ testing = [ [dependencies] namada_core = {path = "../core", default-features = false, features = ["ethers-derive"]} +namada_trans_token = {path = "../trans_token"} namada_macros = {path = "../macros"} namada_proof_of_stake = {path = "../proof_of_stake", default-features = false} borsh.workspace = true diff --git a/core/src/ledger/eth_bridge/storage/bridge_pool.rs b/ethereum_bridge/src/storage/bridge_pool.rs similarity index 100% rename from core/src/ledger/eth_bridge/storage/bridge_pool.rs rename to ethereum_bridge/src/storage/bridge_pool.rs diff --git a/ethereum_bridge/src/storage/mod.rs b/ethereum_bridge/src/storage/mod.rs index b6e62979de..7554d7f93c 100644 --- a/ethereum_bridge/src/storage/mod.rs +++ b/ethereum_bridge/src/storage/mod.rs @@ -3,7 +3,125 @@ pub mod eth_bridge_queries; pub mod parameters; pub mod proof; pub mod vote_tallies; +pub mod bridge_pool; pub mod vp; -pub use namada_core::ledger::eth_bridge::storage::{ - bridge_pool, wrapped_erc20s, *, -}; +pub mod whitelist; +pub mod wrapped_erc20; + +//! Functionality for accessing the storage subspace + +use namada_core::ledger::eth_bridge::ADDRESS; +use namada_core::ledger::parameters::storage::*; +use namada_core::ledger::parameters::ADDRESS as PARAM_ADDRESS; +use namada_core::types::address::Address; +use namada_core::types::storage::{DbKeySeg, Key, KeySeg}; +use namada_trans_token::storage_key::balance_key; + +/// Key prefix for the storage subspace +pub fn prefix() -> Key { + Key::from(ADDRESS.to_db_key()) +} + +/// Key for storing the initial Ethereum block height when +/// events will first be extracted from. +pub fn eth_start_height_key() -> Key { + get_eth_start_height_key_at_addr(PARAM_ADDRESS) +} + +/// The key to the escrow of the VP. +pub fn escrow_key(nam_addr: &Address) -> Key { + balance_key(nam_addr, &ADDRESS) +} + +/// Check if the given `key` contains an Ethereum +/// bridge address segment. +#[inline] +pub fn has_eth_addr_segment(key: &Key) -> bool { + key.segments + .iter() + .any(|s| matches!(s, DbKeySeg::AddressSeg(ADDRESS))) +} + +/// Returns whether a key belongs to this account or not +pub fn is_eth_bridge_key(nam_addr: &Address, key: &Key) -> bool { + key == &escrow_key(nam_addr) + || matches!(key.segments.get(0), Some(first_segment) if first_segment == &ADDRESS.to_db_key()) + || wrapped_erc20s::has_erc20_segment(key) +} + +/// A key for storing the active / inactive status +/// of the Ethereum bridge. +pub fn active_key() -> Key { + get_active_status_key_at_addr(PARAM_ADDRESS) +} + +/// Storage key for the minimum confirmations parameter. +pub fn min_confirmations_key() -> Key { + get_min_confirmations_key_at_addr(PARAM_ADDRESS) +} + +/// Storage key for the Ethereum address of wNam. +pub fn native_erc20_key() -> Key { + get_native_erc20_key_at_addr(PARAM_ADDRESS) +} + +/// Storage key for the Ethereum address of the bridge contract. +pub fn bridge_contract_key() -> Key { + get_bridge_contract_address_key_at_addr(PARAM_ADDRESS) +} + +#[cfg(test)] +mod test { + use super::*; + use crate::types::address; + use crate::types::address::nam; + use crate::types::ethereum_events::testing::arbitrary_eth_address; + + #[test] + fn test_is_eth_bridge_key_returns_true_for_eth_bridge_address() { + let key = Key::from(super::ADDRESS.to_db_key()); + assert!(is_eth_bridge_key(&nam(), &key)); + } + + #[test] + fn test_is_eth_bridge_key_returns_true_for_eth_bridge_subkey() { + let key = Key::from(super::ADDRESS.to_db_key()) + .push(&"arbitrary key segment".to_owned()) + .expect("Could not set up test"); + assert!(is_eth_bridge_key(&nam(), &key)); + } + + #[test] + fn test_is_eth_bridge_key_returns_true_for_eth_bridge_balance_key() { + let eth_addr = arbitrary_eth_address(); + let token = address::Address::Internal( + address::InternalAddress::Erc20(eth_addr), + ); + let key = + balance_key(&token, &address::testing::established_address_1()); + assert!(is_eth_bridge_key(&nam(), &key)); + } + + #[test] + fn test_is_eth_bridge_key_returns_false_for_different_address() { + let key = + Key::from(address::testing::established_address_1().to_db_key()); + assert!(!is_eth_bridge_key(&nam(), &key)); + } + + #[test] + fn test_is_eth_bridge_key_returns_false_for_different_address_subkey() { + let key = + Key::from(address::testing::established_address_1().to_db_key()) + .push(&"arbitrary key segment".to_owned()) + .expect("Could not set up test"); + assert!(!is_eth_bridge_key(&nam(), &key)); + } + + #[test] + fn test_is_eth_bridge_key_returns_false_for_non_eth_bridge_balance_key() { + let key = + balance_key(&nam(), &address::testing::established_address_1()); + assert!(!is_eth_bridge_key(&nam(), &key)); + } +} diff --git a/core/src/ledger/eth_bridge/storage/whitelist.rs b/ethereum_bridge/src/storage/whitelist.rs similarity index 91% rename from core/src/ledger/eth_bridge/storage/whitelist.rs rename to ethereum_bridge/src/storage/whitelist.rs index ed652fe659..3113bfad8a 100644 --- a/core/src/ledger/eth_bridge/storage/whitelist.rs +++ b/ethereum_bridge/src/storage/whitelist.rs @@ -5,20 +5,20 @@ use std::str::FromStr; -use super::super::ADDRESS as BRIDGE_ADDRESS; +use namada_core::ledger::eth_bridge::ADDRESS as BRIDGE_ADDRESS; +use namada_core::types::ethereum_events::EthAddress; +use namada_core::types::storage; +use namada_core::types::storage::DbKeySeg; +use namada_trans_token::storage_key::{denom_key, minted_balance_key}; + use super::{prefix as ethbridge_key_prefix, wrapped_erc20s}; -use crate::types::ethereum_events::EthAddress; -use crate::types::storage; -use crate::types::storage::DbKeySeg; -use crate::types::token::{denom_key, minted_balance_key}; mod segments { //! Storage key segments under the token whitelist. + use namada_core::types::address::Address; + use namada_core::types::storage::{DbKeySeg, Key}; use namada_macros::StorageKeys; - use crate::types::address::Address; - use crate::types::storage::{DbKeySeg, Key}; - /// The name of the main storage segment. pub(super) const MAIN_SEGMENT: &str = "whitelist"; @@ -118,8 +118,9 @@ pub fn is_cap_or_whitelisted_key(key: &storage::Key) -> bool { #[cfg(test)] mod tests { + use namada_core::types::ethereum_events::testing::DAI_ERC20_ETH_ADDRESS; + use super::*; - use crate::types::ethereum_events::testing::DAI_ERC20_ETH_ADDRESS; /// Test that storage key serialization yields the expected value. #[test] diff --git a/core/src/ledger/eth_bridge/storage/wrapped_erc20s.rs b/ethereum_bridge/src/storage/wrapped_erc20.rs similarity index 100% rename from core/src/ledger/eth_bridge/storage/wrapped_erc20s.rs rename to ethereum_bridge/src/storage/wrapped_erc20.rs From ee76120f24f53471cef16ef218bd73d36a45fbe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 15 Dec 2023 19:13:19 +0000 Subject: [PATCH 014/118] core: move merkle_tree into namada_merkle_tree crate --- Cargo.lock | 9 ++++++++- Cargo.toml | 1 + Makefile | 1 + core/Cargo.toml | 2 +- core/src/ledger/storage/mod.rs | 2 +- merkle_tree/Cargo.toml | 18 ++++++++++++++++++ .../merkle_tree.rs => merkle_tree/src/lib.rs | 0 7 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 merkle_tree/Cargo.toml rename core/src/ledger/storage/merkle_tree.rs => merkle_tree/src/lib.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 78045ef39b..560426bc6a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4398,7 +4398,6 @@ dependencies = [ "serde 1.0.193", "serde_json", "sha2 0.9.9", - "sparse-merkle-tree", "tendermint", "tendermint-proto", "test-log", @@ -4496,6 +4495,14 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "namada_merkle_tree" +version = "0.28.1" +dependencies = [ + "namada_core", + "sparse-merkle-tree", +] + [[package]] name = "namada_proof_of_stake" version = "0.29.0" diff --git a/Cargo.toml b/Cargo.toml index 0196f13feb..7c63456d08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ members = [ "governance", "ibc", "macros", + "merkle_tree", "proof_of_stake", "sdk", "shared", diff --git a/Makefile b/Makefile index 194bee7d2a..f8bd7bf1bf 100644 --- a/Makefile +++ b/Makefile @@ -42,6 +42,7 @@ crates += namada_ethereum_bridge crates += namada_governance crates += namada_ibc crates += namada_macros +crates += namada_merkle_tree crates += namada_proof_of_stake crates += namada_sdk crates += namada_shielded_token diff --git a/core/Cargo.toml b/core/Cargo.toml index 5c6ae0f6a1..c3cc6f1250 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -29,9 +29,9 @@ testing = [ [dependencies] namada_macros = {path = "../macros"} + ark-bls12-381.workspace = true ark-serialize.workspace = true -arse-merkle-tree.workspace = true bech32.workspace = true borsh.workspace = true borsh-ext.workspace = true diff --git a/core/src/ledger/storage/mod.rs b/core/src/ledger/storage/mod.rs index caad5d80ec..2c981069b0 100644 --- a/core/src/ledger/storage/mod.rs +++ b/core/src/ledger/storage/mod.rs @@ -1,7 +1,7 @@ //! Ledger's state storage with key-value backed store and a merkle tree pub mod ics23_specs; -pub mod merkle_tree; +#[cfg(any(test, feature = "testing"))] pub mod mockdb; pub mod traits; pub mod types; diff --git a/merkle_tree/Cargo.toml b/merkle_tree/Cargo.toml new file mode 100644 index 0000000000..6edfa3e1d6 --- /dev/null +++ b/merkle_tree/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "namada_merkle_tree" +description = "Namada merkle tree" +resolver = "2" +authors.workspace = true +edition.workspace = true +documentation.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +namada_core = {path = "../core", default-features = false} + +arse-merkle-tree.workspace = true \ No newline at end of file diff --git a/core/src/ledger/storage/merkle_tree.rs b/merkle_tree/src/lib.rs similarity index 100% rename from core/src/ledger/storage/merkle_tree.rs rename to merkle_tree/src/lib.rs From a469ad6b284f5d9ea3050f043d28a93c6f0345c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 15 Dec 2023 19:19:13 +0000 Subject: [PATCH 015/118] core: add namada_storage crate out of ledger::storage --- Cargo.lock | 32 +++++++++++++++++++ Cargo.toml | 1 + Makefile | 1 + core/src/ledger/mod.rs | 1 - core/src/ledger/parameters/mod.rs | 6 ++-- parameters/Cargo.toml | 17 ++++++++++ state/Cargo.toml | 19 +++++++++++ .../storage => state/src}/ics23_specs.rs | 0 .../ledger/storage/mod.rs => state/src/lib.rs | 0 .../ledger/storage => state/src}/mockdb.rs | 0 .../ledger/storage => state/src}/traits.rs | 0 .../src/ledger/storage => state/src}/types.rs | 0 .../storage => state/src}/wl_storage.rs | 0 .../ledger/storage => state/src}/write_log.rs | 0 tx/Cargo.toml | 20 ++++++++++++ tx_env/Cargo.toml | 17 ++++++++++ 16 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 parameters/Cargo.toml create mode 100644 state/Cargo.toml rename {core/src/ledger/storage => state/src}/ics23_specs.rs (100%) rename core/src/ledger/storage/mod.rs => state/src/lib.rs (100%) rename {core/src/ledger/storage => state/src}/mockdb.rs (100%) rename {core/src/ledger/storage => state/src}/traits.rs (100%) rename {core/src/ledger/storage => state/src}/types.rs (100%) rename {core/src/ledger/storage => state/src}/wl_storage.rs (100%) rename {core/src/ledger/storage => state/src}/write_log.rs (100%) create mode 100644 tx/Cargo.toml create mode 100644 tx_env/Cargo.toml diff --git a/Cargo.lock b/Cargo.lock index 560426bc6a..bc0868fe7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4503,6 +4503,14 @@ dependencies = [ "sparse-merkle-tree", ] +[[package]] +name = "namada_parameters" +version = "0.28.1" +dependencies = [ + "namada_core", + "namada_storage", +] + [[package]] name = "namada_proof_of_stake" version = "0.29.0" @@ -4580,6 +4588,13 @@ dependencies = [ name = "namada_shielded_token" version = "0.1.0" +[[package]] +name = "namada_storage" +version = "0.28.1" +dependencies = [ + "namada_core", +] + [[package]] name = "namada_test_utils" version = "0.29.0" @@ -4647,6 +4662,23 @@ dependencies = [ "namada_core", ] +[[package]] +name = "namada_tx" +version = "0.28.1" +dependencies = [ + "namada_core", + "namada_gas", + "tonic-build", +] + +[[package]] +name = "namada_tx_env" +version = "0.28.1" +dependencies = [ + "namada_core", + "namada_state", +] + [[package]] name = "namada_tx_prelude" version = "0.29.0" diff --git a/Cargo.toml b/Cargo.toml index 7c63456d08..6ea3f52289 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ members = [ "sdk", "shared", "shielded_token", + "state", "test_utils", "tests", "trans_token", diff --git a/Makefile b/Makefile index f8bd7bf1bf..e835edad25 100644 --- a/Makefile +++ b/Makefile @@ -46,6 +46,7 @@ crates += namada_merkle_tree crates += namada_proof_of_stake crates += namada_sdk crates += namada_shielded_token +crates += namada_state crates += namada_test_utils crates += namada_tests crates += namada_trans_token diff --git a/core/src/ledger/mod.rs b/core/src/ledger/mod.rs index cb83a3ed8a..60784ef52b 100644 --- a/core/src/ledger/mod.rs +++ b/core/src/ledger/mod.rs @@ -5,7 +5,6 @@ pub mod gas; pub mod inflation; pub mod parameters; pub mod replay_protection; -pub mod storage; pub mod storage_api; pub mod tx_env; pub mod vp_env; diff --git a/core/src/ledger/parameters/mod.rs b/core/src/ledger/parameters/mod.rs index 433cac3b25..c0ad2b450d 100644 --- a/core/src/ledger/parameters/mod.rs +++ b/core/src/ledger/parameters/mod.rs @@ -91,9 +91,9 @@ pub struct EpochDuration { #[derive(Error, Debug)] pub enum ReadError { #[error("Storage error: {0}")] - StorageError(ledger_storage::Error), + StorageError(namada_state::Error), #[error("Storage type error: {0}")] - StorageTypeError(types::Error), + StorageTypeError(namada_state::types::Error), #[error("Protocol parameters are missing, they must be always set")] ParametersMissing, } @@ -102,7 +102,7 @@ pub enum ReadError { #[derive(Error, Debug)] pub enum WriteError { #[error("Storage error: {0}")] - StorageError(ledger_storage::Error), + StorageError(namada_state::Error), #[error("Serialize error: {0}")] SerializeError(String), } diff --git a/parameters/Cargo.toml b/parameters/Cargo.toml new file mode 100644 index 0000000000..34eeb037fe --- /dev/null +++ b/parameters/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "namada_parameters" +description = "Namada parameters" +resolver = "2" +authors.workspace = true +edition.workspace = true +documentation.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +namada_core = { path = "../core", default-features = false } +namada_storage = { path = "../storage" } diff --git a/state/Cargo.toml b/state/Cargo.toml new file mode 100644 index 0000000000..969af24649 --- /dev/null +++ b/state/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "namada_state" +description = "Namada ledger state" +resolver = "2" +authors.workspace = true +edition.workspace = true +documentation.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +namada_core = {path = "../core", default-features = false} + +arse-merkle-tree.workspace = true +ics23.workspace = true \ No newline at end of file diff --git a/core/src/ledger/storage/ics23_specs.rs b/state/src/ics23_specs.rs similarity index 100% rename from core/src/ledger/storage/ics23_specs.rs rename to state/src/ics23_specs.rs diff --git a/core/src/ledger/storage/mod.rs b/state/src/lib.rs similarity index 100% rename from core/src/ledger/storage/mod.rs rename to state/src/lib.rs diff --git a/core/src/ledger/storage/mockdb.rs b/state/src/mockdb.rs similarity index 100% rename from core/src/ledger/storage/mockdb.rs rename to state/src/mockdb.rs diff --git a/core/src/ledger/storage/traits.rs b/state/src/traits.rs similarity index 100% rename from core/src/ledger/storage/traits.rs rename to state/src/traits.rs diff --git a/core/src/ledger/storage/types.rs b/state/src/types.rs similarity index 100% rename from core/src/ledger/storage/types.rs rename to state/src/types.rs diff --git a/core/src/ledger/storage/wl_storage.rs b/state/src/wl_storage.rs similarity index 100% rename from core/src/ledger/storage/wl_storage.rs rename to state/src/wl_storage.rs diff --git a/core/src/ledger/storage/write_log.rs b/state/src/write_log.rs similarity index 100% rename from core/src/ledger/storage/write_log.rs rename to state/src/write_log.rs diff --git a/tx/Cargo.toml b/tx/Cargo.toml new file mode 100644 index 0000000000..3f7b382ecf --- /dev/null +++ b/tx/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "namada_tx" +description = "Namada transaction construction types" +resolver = "2" +authors.workspace = true +edition.workspace = true +documentation.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +namada_core = { path = "../core", default-features = false } +namada_gas = { path = "../gas" } + +[build-dependencies] +tonic-build.workspace = true \ No newline at end of file diff --git a/tx_env/Cargo.toml b/tx_env/Cargo.toml new file mode 100644 index 0000000000..5c67646ebc --- /dev/null +++ b/tx_env/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "namada_tx_env" +description = "Namada transaction execution environment" +resolver = "2" +authors.workspace = true +edition.workspace = true +documentation.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +namada_core = { path = "../core", default-features = false } +namada_storage = { path = "../storage" } From 566fa6d5a4400d77b058f0cfedd9feb2397fb19a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 15 Dec 2023 19:24:15 +0000 Subject: [PATCH 016/118] core: add namada_parameters crate out of ledger::parameters --- Cargo.lock | 10 +++++----- Cargo.toml | 1 + Makefile | 1 + core/src/ledger/mod.rs | 1 - parameters/Cargo.toml | 3 +-- .../ledger/parameters/mod.rs => parameters/src/lib.rs | 0 .../ledger/parameters => parameters/src}/storage.rs | 0 7 files changed, 8 insertions(+), 8 deletions(-) rename core/src/ledger/parameters/mod.rs => parameters/src/lib.rs (100%) rename {core/src/ledger/parameters => parameters/src}/storage.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index bc0868fe7b..266ea3dc41 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4505,11 +4505,7 @@ dependencies = [ [[package]] name = "namada_parameters" -version = "0.28.1" -dependencies = [ - "namada_core", - "namada_storage", -] +version = "0.1.0" [[package]] name = "namada_proof_of_stake" @@ -4592,7 +4588,11 @@ version = "0.1.0" name = "namada_storage" version = "0.28.1" dependencies = [ + "ics23", "namada_core", + "namada_gas", + "namada_tx", + "sparse-merkle-tree", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 6ea3f52289..e53444fbda 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ members = [ "ibc", "macros", "merkle_tree", + "parameters", "proof_of_stake", "sdk", "shared", diff --git a/Makefile b/Makefile index e835edad25..b302d3577d 100644 --- a/Makefile +++ b/Makefile @@ -43,6 +43,7 @@ crates += namada_governance crates += namada_ibc crates += namada_macros crates += namada_merkle_tree +crates += namada_parameters crates += namada_proof_of_stake crates += namada_sdk crates += namada_shielded_token diff --git a/core/src/ledger/mod.rs b/core/src/ledger/mod.rs index 60784ef52b..9f71176306 100644 --- a/core/src/ledger/mod.rs +++ b/core/src/ledger/mod.rs @@ -3,7 +3,6 @@ pub mod eth_bridge; pub mod gas; pub mod inflation; -pub mod parameters; pub mod replay_protection; pub mod storage_api; pub mod tx_env; diff --git a/parameters/Cargo.toml b/parameters/Cargo.toml index 34eeb037fe..ca076cbcea 100644 --- a/parameters/Cargo.toml +++ b/parameters/Cargo.toml @@ -13,5 +13,4 @@ repository.workspace = true version.workspace = true [dependencies] -namada_core = { path = "../core", default-features = false } -namada_storage = { path = "../storage" } +namada_core = {path = "../core", default-features = false} diff --git a/core/src/ledger/parameters/mod.rs b/parameters/src/lib.rs similarity index 100% rename from core/src/ledger/parameters/mod.rs rename to parameters/src/lib.rs diff --git a/core/src/ledger/parameters/storage.rs b/parameters/src/storage.rs similarity index 100% rename from core/src/ledger/parameters/storage.rs rename to parameters/src/storage.rs From 1e04b0f3534cfbc900bb484dd8d69f0bb2fd0f46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 15 Dec 2023 19:28:37 +0000 Subject: [PATCH 017/118] parameters: fix imports --- Cargo.lock | 6 +++++- parameters/Cargo.toml | 3 ++- parameters/src/lib.rs | 17 +++++++---------- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 266ea3dc41..983a281b1f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4505,7 +4505,11 @@ dependencies = [ [[package]] name = "namada_parameters" -version = "0.1.0" +version = "0.28.1" +dependencies = [ + "namada_core", + "namada_storage", +] [[package]] name = "namada_proof_of_stake" diff --git a/parameters/Cargo.toml b/parameters/Cargo.toml index ca076cbcea..34eeb037fe 100644 --- a/parameters/Cargo.toml +++ b/parameters/Cargo.toml @@ -13,4 +13,5 @@ repository.workspace = true version.workspace = true [dependencies] -namada_core = {path = "../core", default-features = false} +namada_core = { path = "../core", default-features = false } +namada_storage = { path = "../storage" } diff --git a/parameters/src/lib.rs b/parameters/src/lib.rs index c0ad2b450d..5e4e38e196 100644 --- a/parameters/src/lib.rs +++ b/parameters/src/lib.rs @@ -4,18 +4,15 @@ pub mod storage; use std::collections::BTreeMap; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; +use namada_core::storage_api::{self, ResultExt, StorageRead, StorageWrite}; +use namada_core::types::address::{Address, InternalAddress}; +use namada_core::types::chain::ProposalBytes; +use namada_core::types::dec::Dec; +use namada_core::types::hash::Hash; +use namada_core::types::time::DurationSecs; +use namada_core::types::token; use thiserror::Error; -use super::storage::types; -use super::storage_api::{self, ResultExt, StorageRead, StorageWrite}; -use crate::ledger::storage as ledger_storage; -use crate::types::address::{Address, InternalAddress}; -use crate::types::chain::ProposalBytes; -use crate::types::dec::Dec; -use crate::types::hash::Hash; -use crate::types::time::DurationSecs; -use crate::types::token; - /// The internal address for storage keys representing parameters than /// can be changed via governance. pub const ADDRESS: Address = Address::Internal(InternalAddress::Parameters); From 642a4e418767b01e2a6497168d211b236b330faf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 15 Dec 2023 19:33:42 +0000 Subject: [PATCH 018/118] core: move `validate_tx_bytes` into parameters crate --- core/src/ledger/storage_api/mod.rs | 1 - core/src/ledger/storage_api/tx.rs | 19 ------------------- parameters/src/lib.rs | 14 ++++++++++++++ 3 files changed, 14 insertions(+), 20 deletions(-) delete mode 100644 core/src/ledger/storage_api/tx.rs diff --git a/core/src/ledger/storage_api/mod.rs b/core/src/ledger/storage_api/mod.rs index eb8213c823..452bde4510 100644 --- a/core/src/ledger/storage_api/mod.rs +++ b/core/src/ledger/storage_api/mod.rs @@ -5,7 +5,6 @@ pub mod account; pub mod collections; mod error; pub mod key; -pub mod tx; pub mod validation; use borsh::{BorshDeserialize, BorshSerialize}; diff --git a/core/src/ledger/storage_api/tx.rs b/core/src/ledger/storage_api/tx.rs deleted file mode 100644 index 0fdd56b34e..0000000000 --- a/core/src/ledger/storage_api/tx.rs +++ /dev/null @@ -1,19 +0,0 @@ -//! Tx storage_api functions - -use super::StorageRead; -use crate::ledger::parameters::storage::get_max_tx_bytes_key; -use crate::ledger::storage_api; - -/// Validate the size of a tx. -pub fn validate_tx_bytes( - storage: &S, - tx_size: usize, -) -> storage_api::Result -where - S: StorageRead, -{ - let max_tx_bytes: u32 = storage - .read(&get_max_tx_bytes_key())? - .expect("The max tx bytes param should be present in storage"); - Ok(tx_size <= max_tx_bytes as usize) -} diff --git a/parameters/src/lib.rs b/parameters/src/lib.rs index 5e4e38e196..e5d35ac35b 100644 --- a/parameters/src/lib.rs +++ b/parameters/src/lib.rs @@ -521,3 +521,17 @@ where fee_unshielding_descriptions_limit, }) } + +/// Validate the size of a tx. +pub fn validate_tx_bytes( + storage: &S, + tx_size: usize, +) -> storage_api::Result +where + S: StorageRead, +{ + let max_tx_bytes: u32 = storage + .read(&storage::get_max_tx_bytes_key())? + .expect("The max tx bytes param should be present in storage"); + Ok(tx_size <= max_tx_bytes as usize) +} From 3686e56d4c985ffdadb27ead298cf0d19ad864bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 15 Dec 2023 19:38:00 +0000 Subject: [PATCH 019/118] core: move ledger::gas into new namada_gas crate --- Cargo.lock | 8 ++++++++ Cargo.toml | 1 + Makefile | 1 + core/src/ledger/mod.rs | 1 - gas/Cargo.toml | 17 +++++++++++++++++ core/src/ledger/gas.rs => gas/src/lib.rs | 0 6 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 gas/Cargo.toml rename core/src/ledger/gas.rs => gas/src/lib.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 983a281b1f..40a7bd9bf6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4459,6 +4459,14 @@ dependencies = [ "tokio", ] +[[package]] +name = "namada_gas" +version = "0.28.1" +dependencies = [ + "namada_core", + "namada_parameters", +] + [[package]] name = "namada_governance" version = "0.29.0" diff --git a/Cargo.toml b/Cargo.toml index e53444fbda..37c779d0e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ members = [ "light_sdk", "macros", "examples", + "gas", "governance", "ibc", "macros", diff --git a/Makefile b/Makefile index b302d3577d..8cc2cf42a2 100644 --- a/Makefile +++ b/Makefile @@ -39,6 +39,7 @@ crates += namada_apps crates += namada_benchmarks crates += namada_encoding_spec crates += namada_ethereum_bridge +crates += namada_gas crates += namada_governance crates += namada_ibc crates += namada_macros diff --git a/core/src/ledger/mod.rs b/core/src/ledger/mod.rs index 9f71176306..2abed67e7b 100644 --- a/core/src/ledger/mod.rs +++ b/core/src/ledger/mod.rs @@ -1,7 +1,6 @@ //! The ledger modules pub mod eth_bridge; -pub mod gas; pub mod inflation; pub mod replay_protection; pub mod storage_api; diff --git a/gas/Cargo.toml b/gas/Cargo.toml new file mode 100644 index 0000000000..c7fa406099 --- /dev/null +++ b/gas/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "namada_gas" +description = "Namada gas" +resolver = "2" +authors.workspace = true +edition.workspace = true +documentation.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +namada_core = { path = "../core", default-features = false } +namada_parameters = { path = "../parameters" } \ No newline at end of file diff --git a/core/src/ledger/gas.rs b/gas/src/lib.rs similarity index 100% rename from core/src/ledger/gas.rs rename to gas/src/lib.rs From dbef2de616ee13526f9c11a2a81724661818c1fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 15 Dec 2023 19:40:07 +0000 Subject: [PATCH 020/118] gas: fix imports --- gas/src/lib.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/gas/src/lib.rs b/gas/src/lib.rs index f2c1e72844..22c24ec215 100644 --- a/gas/src/lib.rs +++ b/gas/src/lib.rs @@ -5,13 +5,11 @@ use std::fmt::Display; use std::ops::Div; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; +use namada_core::storage_api::{self, StorageRead}; +use namada_core::types::transaction::wrapper::GasLimit; use serde::{Deserialize, Serialize}; use thiserror::Error; -use super::parameters; -use super::storage_api::{self, StorageRead}; -use crate::types::transaction::wrapper::GasLimit; - #[allow(missing_docs)] #[derive(Error, Debug, Clone, PartialEq, Eq)] pub enum Error { @@ -71,7 +69,7 @@ pub fn get_max_block_gas( storage: &impl StorageRead, ) -> std::result::Result { storage - .read(¶meters::storage::get_max_block_gas_key())? + .read(&namada_parameters::storage::get_max_block_gas_key())? .ok_or(storage_api::Error::SimpleMessage( "Missing max_block_gas parameter from storage", )) From 94d1ba951bcbd4e331c3f849c9f0f480450222c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 15 Dec 2023 19:49:57 +0000 Subject: [PATCH 021/118] core: move proto and into namada_tx crate --- Cargo.lock | 82 +-------------------- Cargo.toml | 1 + Makefile | 1 + core/Cargo.toml | 3 - core/src/lib.rs | 1 - tx/Cargo.toml | 3 +- {core => tx}/build.rs | 0 core/src/proto/mod.rs => tx/src/lib.rs | 2 +- {core => tx}/src/proto/generated.rs | 0 {core => tx}/src/proto/generated/.gitignore | 0 {core/src/proto => tx/src}/types.rs | 0 11 files changed, 8 insertions(+), 85 deletions(-) rename {core => tx}/build.rs (100%) rename core/src/proto/mod.rs => tx/src/lib.rs (97%) rename {core => tx}/src/proto/generated.rs (100%) rename {core => tx}/src/proto/generated/.gitignore (100%) rename {core/src/proto => tx/src}/types.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 40a7bd9bf6..0de25a48b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -487,7 +487,7 @@ dependencies = [ "lazy_static", "lazycell", "peeking_take_while", - "prettyplease 0.2.15", + "prettyplease", "proc-macro2", "quote", "regex", @@ -2028,7 +2028,7 @@ dependencies = [ "dunce", "ethers-core", "eyre", - "prettyplease 0.2.15", + "prettyplease", "proc-macro2", "quote", "regex", @@ -2274,12 +2274,6 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - [[package]] name = "flate2" version = "1.0.28" @@ -4170,12 +4164,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "multimap" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" - [[package]] name = "namada" version = "0.29.0" @@ -4404,7 +4392,6 @@ dependencies = [ "thiserror", "tiny-keccak", "toml 0.5.11", - "tonic-build", "tracing", "tracing-subscriber", "uint", @@ -4680,15 +4667,7 @@ version = "0.28.1" dependencies = [ "namada_core", "namada_gas", - "tonic-build", -] - -[[package]] -name = "namada_tx_env" -version = "0.28.1" -dependencies = [ - "namada_core", - "namada_state", + "namada_storage", ] [[package]] @@ -5341,16 +5320,6 @@ dependencies = [ "ucd-trie", ] -[[package]] -name = "petgraph" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" -dependencies = [ - "fixedbitset", - "indexmap 2.1.0", -] - [[package]] name = "pharos" version = "0.5.3" @@ -5503,16 +5472,6 @@ dependencies = [ "yansi", ] -[[package]] -name = "prettyplease" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" -dependencies = [ - "proc-macro2", - "syn 1.0.109", -] - [[package]] name = "prettyplease" version = "0.2.15" @@ -5639,28 +5598,6 @@ dependencies = [ "prost-derive 0.12.3", ] -[[package]] -name = "prost-build" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" -dependencies = [ - "bytes", - "heck", - "itertools 0.10.5", - "lazy_static", - "log", - "multimap", - "petgraph", - "prettyplease 0.1.25", - "prost 0.11.9", - "prost-types 0.11.9", - "regex", - "syn 1.0.109", - "tempfile", - "which", -] - [[package]] name = "prost-derive" version = "0.11.9" @@ -7520,19 +7457,6 @@ dependencies = [ "tracing-futures", ] -[[package]] -name = "tonic-build" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf5e9b9c0f7e0a7c027dcfaba7b2c60816c7049171f679d99ee2ff65d0de8c4" -dependencies = [ - "prettyplease 0.1.25", - "proc-macro2", - "prost-build", - "quote", - "syn 1.0.109", -] - [[package]] name = "tower" version = "0.4.13" diff --git a/Cargo.toml b/Cargo.toml index 37c779d0e7..9935fafe3d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ members = [ "test_utils", "tests", "trans_token", + "tx", "tx_prelude", "vm_env", "vp_prelude", diff --git a/Makefile b/Makefile index 8cc2cf42a2..b10bbe6d22 100644 --- a/Makefile +++ b/Makefile @@ -52,6 +52,7 @@ crates += namada_state crates += namada_test_utils crates += namada_tests crates += namada_trans_token +crates += namada_tx crates += namada_tx_prelude crates += namada_vm_env crates += namada_vp_prelude diff --git a/core/Cargo.toml b/core/Cargo.toml index c3cc6f1250..c26208069a 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -85,6 +85,3 @@ rayon = {version = "=1.5.3"} test-log.workspace = true toml.workspace = true tracing-subscriber.workspace = true - -[build-dependencies] -tonic-build.workspace = true diff --git a/core/src/lib.rs b/core/src/lib.rs index cbc9588201..98511ddf2b 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -9,7 +9,6 @@ pub mod bytes; pub mod hints; pub mod ledger; -pub mod proto; pub mod types; pub use {ibc, tendermint, tendermint_proto}; diff --git a/tx/Cargo.toml b/tx/Cargo.toml index 3f7b382ecf..ed33616abb 100644 --- a/tx/Cargo.toml +++ b/tx/Cargo.toml @@ -15,6 +15,7 @@ version.workspace = true [dependencies] namada_core = { path = "../core", default-features = false } namada_gas = { path = "../gas" } +namada_storage = { path = "../storage" } [build-dependencies] -tonic-build.workspace = true \ No newline at end of file +tonic-build.workspace = true diff --git a/core/build.rs b/tx/build.rs similarity index 100% rename from core/build.rs rename to tx/build.rs diff --git a/core/src/proto/mod.rs b/tx/src/lib.rs similarity index 97% rename from core/src/proto/mod.rs rename to tx/src/lib.rs index 356b797709..a262ca4428 100644 --- a/core/src/proto/mod.rs +++ b/tx/src/lib.rs @@ -1,6 +1,6 @@ #![allow(missing_docs)] -pub mod generated; +pub mod proto::generated; mod types; pub use types::{ diff --git a/core/src/proto/generated.rs b/tx/src/proto/generated.rs similarity index 100% rename from core/src/proto/generated.rs rename to tx/src/proto/generated.rs diff --git a/core/src/proto/generated/.gitignore b/tx/src/proto/generated/.gitignore similarity index 100% rename from core/src/proto/generated/.gitignore rename to tx/src/proto/generated/.gitignore diff --git a/core/src/proto/types.rs b/tx/src/types.rs similarity index 100% rename from core/src/proto/types.rs rename to tx/src/types.rs From 20de0b736d686c5075ffd1fdfe659015c8c469b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 15 Dec 2023 19:52:31 +0000 Subject: [PATCH 022/118] update lock --- Cargo.lock | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0de25a48b3..f7911925f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -487,7 +487,7 @@ dependencies = [ "lazy_static", "lazycell", "peeking_take_while", - "prettyplease", + "prettyplease 0.2.15", "proc-macro2", "quote", "regex", @@ -2028,7 +2028,7 @@ dependencies = [ "dunce", "ethers-core", "eyre", - "prettyplease", + "prettyplease 0.2.15", "proc-macro2", "quote", "regex", @@ -2274,6 +2274,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "flate2" version = "1.0.28" @@ -4164,6 +4170,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "multimap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + [[package]] name = "namada" version = "0.29.0" @@ -4668,6 +4680,7 @@ dependencies = [ "namada_core", "namada_gas", "namada_storage", + "tonic-build", ] [[package]] @@ -5320,6 +5333,16 @@ dependencies = [ "ucd-trie", ] +[[package]] +name = "petgraph" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +dependencies = [ + "fixedbitset", + "indexmap 2.1.0", +] + [[package]] name = "pharos" version = "0.5.3" @@ -5472,6 +5495,16 @@ dependencies = [ "yansi", ] +[[package]] +name = "prettyplease" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +dependencies = [ + "proc-macro2", + "syn 1.0.109", +] + [[package]] name = "prettyplease" version = "0.2.15" @@ -5598,6 +5631,28 @@ dependencies = [ "prost-derive 0.12.3", ] +[[package]] +name = "prost-build" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" +dependencies = [ + "bytes", + "heck", + "itertools 0.10.5", + "lazy_static", + "log", + "multimap", + "petgraph", + "prettyplease 0.1.25", + "prost 0.11.9", + "prost-types 0.11.9", + "regex", + "syn 1.0.109", + "tempfile", + "which", +] + [[package]] name = "prost-derive" version = "0.11.9" @@ -7457,6 +7512,19 @@ dependencies = [ "tracing-futures", ] +[[package]] +name = "tonic-build" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf5e9b9c0f7e0a7c027dcfaba7b2c60816c7049171f679d99ee2ff65d0de8c4" +dependencies = [ + "prettyplease 0.1.25", + "proc-macro2", + "prost-build", + "quote", + "syn 1.0.109", +] + [[package]] name = "tower" version = "0.4.13" From a4490d4562627d1bdb99219c4293c1d7d8343297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Mon, 18 Dec 2023 09:25:22 +0000 Subject: [PATCH 023/118] core: move types::transaction mods into namada_tx::data --- core/src/types/mod.rs | 1 - {core/src/types/transaction => tx/src/data}/account.rs | 0 {core/src/types/transaction => tx/src/data}/decrypted.rs | 0 {core/src/types/transaction => tx/src/data}/governance.rs | 0 {core/src/types/transaction => tx/src/data}/mod.rs | 2 +- {core/src/types/transaction => tx/src/data}/pgf.rs | 0 {core/src/types/transaction => tx/src/data}/pos.rs | 0 {core/src/types/transaction => tx/src/data}/protocol.rs | 0 {core/src/types/transaction => tx/src/data}/wrapper.rs | 0 tx/src/lib.rs | 1 + 10 files changed, 2 insertions(+), 2 deletions(-) rename {core/src/types/transaction => tx/src/data}/account.rs (100%) rename {core/src/types/transaction => tx/src/data}/decrypted.rs (100%) rename {core/src/types/transaction => tx/src/data}/governance.rs (100%) rename {core/src/types/transaction => tx/src/data}/mod.rs (99%) rename {core/src/types/transaction => tx/src/data}/pgf.rs (100%) rename {core/src/types/transaction => tx/src/data}/pos.rs (100%) rename {core/src/types/transaction => tx/src/data}/protocol.rs (100%) rename {core/src/types/transaction => tx/src/data}/wrapper.rs (100%) diff --git a/core/src/types/mod.rs b/core/src/types/mod.rs index 1027bdd26e..2cea327105 100644 --- a/core/src/types/mod.rs +++ b/core/src/types/mod.rs @@ -19,7 +19,6 @@ pub mod storage; pub mod string_encoding; pub mod time; pub mod token; -pub mod transaction; pub mod uint; pub mod validity_predicate; pub mod vote_extensions; diff --git a/core/src/types/transaction/account.rs b/tx/src/data/account.rs similarity index 100% rename from core/src/types/transaction/account.rs rename to tx/src/data/account.rs diff --git a/core/src/types/transaction/decrypted.rs b/tx/src/data/decrypted.rs similarity index 100% rename from core/src/types/transaction/decrypted.rs rename to tx/src/data/decrypted.rs diff --git a/core/src/types/transaction/governance.rs b/tx/src/data/governance.rs similarity index 100% rename from core/src/types/transaction/governance.rs rename to tx/src/data/governance.rs diff --git a/core/src/types/transaction/mod.rs b/tx/src/data/mod.rs similarity index 99% rename from core/src/types/transaction/mod.rs rename to tx/src/data/mod.rs index 95b32b473f..04728fc472 100644 --- a/core/src/types/transaction/mod.rs +++ b/tx/src/data/mod.rs @@ -1,4 +1,4 @@ -//! Types that are used in transactions. +//! Data-Types that are used in transactions. /// txs to manage accounts pub mod account; diff --git a/core/src/types/transaction/pgf.rs b/tx/src/data/pgf.rs similarity index 100% rename from core/src/types/transaction/pgf.rs rename to tx/src/data/pgf.rs diff --git a/core/src/types/transaction/pos.rs b/tx/src/data/pos.rs similarity index 100% rename from core/src/types/transaction/pos.rs rename to tx/src/data/pos.rs diff --git a/core/src/types/transaction/protocol.rs b/tx/src/data/protocol.rs similarity index 100% rename from core/src/types/transaction/protocol.rs rename to tx/src/data/protocol.rs diff --git a/core/src/types/transaction/wrapper.rs b/tx/src/data/wrapper.rs similarity index 100% rename from core/src/types/transaction/wrapper.rs rename to tx/src/data/wrapper.rs diff --git a/tx/src/lib.rs b/tx/src/lib.rs index a262ca4428..da49706485 100644 --- a/tx/src/lib.rs +++ b/tx/src/lib.rs @@ -1,6 +1,7 @@ #![allow(missing_docs)] pub mod proto::generated; +pub mod data; mod types; pub use types::{ From 5ef1d70c8c8391313237a1924f30369fdc80783c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Mon, 18 Dec 2023 09:49:21 +0000 Subject: [PATCH 024/118] core: make vp_env crate out of ledger::vp_env mod --- Cargo.lock | 8 ++++++++ Cargo.toml | 1 + Makefile | 1 + core/src/ledger/mod.rs | 1 - vp_env/Cargo.toml | 17 +++++++++++++++++ core/src/ledger/vp_env.rs => vp_env/src/lib.rs | 0 6 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 vp_env/Cargo.toml rename core/src/ledger/vp_env.rs => vp_env/src/lib.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index f7911925f8..ee21a37837 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4707,6 +4707,14 @@ dependencies = [ "namada_core", ] +[[package]] +name = "namada_vp_env" +version = "0.28.1" +dependencies = [ + "namada_core", + "namada_tx", +] + [[package]] name = "namada_vp_prelude" version = "0.29.0" diff --git a/Cargo.toml b/Cargo.toml index 9935fafe3d..1e508143e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ members = [ "tx", "tx_prelude", "vm_env", + "vp_env", "vp_prelude", ] diff --git a/Makefile b/Makefile index b10bbe6d22..7e8822f0e6 100644 --- a/Makefile +++ b/Makefile @@ -55,6 +55,7 @@ crates += namada_trans_token crates += namada_tx crates += namada_tx_prelude crates += namada_vm_env +crates += namada_vp_env crates += namada_vp_prelude build: diff --git a/core/src/ledger/mod.rs b/core/src/ledger/mod.rs index 2abed67e7b..d91b9efa86 100644 --- a/core/src/ledger/mod.rs +++ b/core/src/ledger/mod.rs @@ -5,4 +5,3 @@ pub mod inflation; pub mod replay_protection; pub mod storage_api; pub mod tx_env; -pub mod vp_env; diff --git a/vp_env/Cargo.toml b/vp_env/Cargo.toml new file mode 100644 index 0000000000..eec6142de6 --- /dev/null +++ b/vp_env/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "namada_vp_env" +description = "Namada validity predicate environment" +resolver = "2" +authors.workspace = true +edition.workspace = true +documentation.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +namada_core = { path = "../core", default-features = false } +namada_tx = { path = "../tx" } diff --git a/core/src/ledger/vp_env.rs b/vp_env/src/lib.rs similarity index 100% rename from core/src/ledger/vp_env.rs rename to vp_env/src/lib.rs From 63953207e08d9b2a189e43b8097bdd6ef876a377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Mon, 18 Dec 2023 10:23:10 +0000 Subject: [PATCH 025/118] add namada_storage crate out of core::ledger::storage_api --- Cargo.lock | 9 ++++++++- Cargo.toml | 1 + Makefile | 1 + core/src/ledger/mod.rs | 1 - storage/Cargo.toml | 16 ++++++++++++++++ .../storage_api => storage/src}/account.rs | 0 .../src}/collections/lazy_map.rs | 0 .../src}/collections/lazy_set.rs | 0 .../src}/collections/lazy_vec.rs | 0 .../src}/collections/mod.rs | 0 .../ledger/storage_api => storage/src}/error.rs | 0 .../ledger/storage_api => storage/src}/key.rs | 0 .../storage_api/mod.rs => storage/src/lib.rs | 0 .../src}/validation/mod.rs | 0 14 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 storage/Cargo.toml rename {core/src/ledger/storage_api => storage/src}/account.rs (100%) rename {core/src/ledger/storage_api => storage/src}/collections/lazy_map.rs (100%) rename {core/src/ledger/storage_api => storage/src}/collections/lazy_set.rs (100%) rename {core/src/ledger/storage_api => storage/src}/collections/lazy_vec.rs (100%) rename {core/src/ledger/storage_api => storage/src}/collections/mod.rs (100%) rename {core/src/ledger/storage_api => storage/src}/error.rs (100%) rename {core/src/ledger/storage_api => storage/src}/key.rs (100%) rename core/src/ledger/storage_api/mod.rs => storage/src/lib.rs (100%) rename {core/src/ledger/storage_api => storage/src}/validation/mod.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index ee21a37837..4718cdf9cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4596,7 +4596,7 @@ name = "namada_shielded_token" version = "0.1.0" [[package]] -name = "namada_storage" +name = "namada_state" version = "0.28.1" dependencies = [ "ics23", @@ -4606,6 +4606,13 @@ dependencies = [ "sparse-merkle-tree", ] +[[package]] +name = "namada_storage" +version = "0.28.1" +dependencies = [ + "namada_core", +] + [[package]] name = "namada_test_utils" version = "0.29.0" diff --git a/Cargo.toml b/Cargo.toml index 1e508143e9..c09fab4535 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ members = [ "shared", "shielded_token", "state", + "storage", "test_utils", "tests", "trans_token", diff --git a/Makefile b/Makefile index 7e8822f0e6..970145b473 100644 --- a/Makefile +++ b/Makefile @@ -49,6 +49,7 @@ crates += namada_proof_of_stake crates += namada_sdk crates += namada_shielded_token crates += namada_state +crates += namada_storage crates += namada_test_utils crates += namada_tests crates += namada_trans_token diff --git a/core/src/ledger/mod.rs b/core/src/ledger/mod.rs index d91b9efa86..e0c5d1a5cc 100644 --- a/core/src/ledger/mod.rs +++ b/core/src/ledger/mod.rs @@ -3,5 +3,4 @@ pub mod eth_bridge; pub mod inflation; pub mod replay_protection; -pub mod storage_api; pub mod tx_env; diff --git a/storage/Cargo.toml b/storage/Cargo.toml new file mode 100644 index 0000000000..416cbd4ddd --- /dev/null +++ b/storage/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "namada_storage" +description = "Namada ledger storage" +resolver = "2" +authors.workspace = true +edition.workspace = true +documentation.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +namada_core = { path = "../core", default-features = false } diff --git a/core/src/ledger/storage_api/account.rs b/storage/src/account.rs similarity index 100% rename from core/src/ledger/storage_api/account.rs rename to storage/src/account.rs diff --git a/core/src/ledger/storage_api/collections/lazy_map.rs b/storage/src/collections/lazy_map.rs similarity index 100% rename from core/src/ledger/storage_api/collections/lazy_map.rs rename to storage/src/collections/lazy_map.rs diff --git a/core/src/ledger/storage_api/collections/lazy_set.rs b/storage/src/collections/lazy_set.rs similarity index 100% rename from core/src/ledger/storage_api/collections/lazy_set.rs rename to storage/src/collections/lazy_set.rs diff --git a/core/src/ledger/storage_api/collections/lazy_vec.rs b/storage/src/collections/lazy_vec.rs similarity index 100% rename from core/src/ledger/storage_api/collections/lazy_vec.rs rename to storage/src/collections/lazy_vec.rs diff --git a/core/src/ledger/storage_api/collections/mod.rs b/storage/src/collections/mod.rs similarity index 100% rename from core/src/ledger/storage_api/collections/mod.rs rename to storage/src/collections/mod.rs diff --git a/core/src/ledger/storage_api/error.rs b/storage/src/error.rs similarity index 100% rename from core/src/ledger/storage_api/error.rs rename to storage/src/error.rs diff --git a/core/src/ledger/storage_api/key.rs b/storage/src/key.rs similarity index 100% rename from core/src/ledger/storage_api/key.rs rename to storage/src/key.rs diff --git a/core/src/ledger/storage_api/mod.rs b/storage/src/lib.rs similarity index 100% rename from core/src/ledger/storage_api/mod.rs rename to storage/src/lib.rs diff --git a/core/src/ledger/storage_api/validation/mod.rs b/storage/src/validation/mod.rs similarity index 100% rename from core/src/ledger/storage_api/validation/mod.rs rename to storage/src/validation/mod.rs From 29a1704e843df66a06fa362cdd30c9468b0f73de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Mon, 18 Dec 2023 10:28:10 +0000 Subject: [PATCH 026/118] add namada_tx_env out of core::ledger::tx_env mod --- Cargo.lock | 8 ++++++++ Cargo.toml | 1 + Makefile | 1 + core/src/ledger/mod.rs | 1 - core/src/ledger/tx_env.rs => tx_env/src/lib.rs | 0 vp_env/Cargo.toml | 2 +- 6 files changed, 11 insertions(+), 2 deletions(-) rename core/src/ledger/tx_env.rs => tx_env/src/lib.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 4718cdf9cf..40ef843a2d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4690,6 +4690,14 @@ dependencies = [ "tonic-build", ] +[[package]] +name = "namada_tx_env" +version = "0.28.1" +dependencies = [ + "namada_core", + "namada_storage", +] + [[package]] name = "namada_tx_prelude" version = "0.29.0" diff --git a/Cargo.toml b/Cargo.toml index c09fab4535..46579dcbbd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ members = [ "tests", "trans_token", "tx", + "tx_env", "tx_prelude", "vm_env", "vp_env", diff --git a/Makefile b/Makefile index 970145b473..ac01676abf 100644 --- a/Makefile +++ b/Makefile @@ -54,6 +54,7 @@ crates += namada_test_utils crates += namada_tests crates += namada_trans_token crates += namada_tx +crates += namada_tx_env crates += namada_tx_prelude crates += namada_vm_env crates += namada_vp_env diff --git a/core/src/ledger/mod.rs b/core/src/ledger/mod.rs index e0c5d1a5cc..70c1773bfb 100644 --- a/core/src/ledger/mod.rs +++ b/core/src/ledger/mod.rs @@ -3,4 +3,3 @@ pub mod eth_bridge; pub mod inflation; pub mod replay_protection; -pub mod tx_env; diff --git a/core/src/ledger/tx_env.rs b/tx_env/src/lib.rs similarity index 100% rename from core/src/ledger/tx_env.rs rename to tx_env/src/lib.rs diff --git a/vp_env/Cargo.toml b/vp_env/Cargo.toml index eec6142de6..bd5955fd4f 100644 --- a/vp_env/Cargo.toml +++ b/vp_env/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "namada_vp_env" -description = "Namada validity predicate environment" +description = "Namada validity predicate execution environment" resolver = "2" authors.workspace = true edition.workspace = true From 46fbd33986181697188cdfd3a82e0c2f6652ad12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Mon, 18 Dec 2023 10:29:17 +0000 Subject: [PATCH 027/118] tx_env: update imports --- tx_env/src/lib.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tx_env/src/lib.rs b/tx_env/src/lib.rs index 83f566bfaf..f986b5c79e 100644 --- a/tx_env/src/lib.rs +++ b/tx_env/src/lib.rs @@ -2,11 +2,10 @@ //! inside a tx. use borsh::BorshSerialize; - -use crate::ledger::storage_api::{self, StorageRead, StorageWrite}; -use crate::types::address::Address; -use crate::types::ibc::IbcEvent; -use crate::types::storage; +use namada_core::types::address::Address; +use namada_core::types::ibc::IbcEvent; +use namada_core::types::storage; +use namada_storage::{self, StorageRead, StorageWrite}; /// Transaction host functions pub trait TxEnv: StorageRead + StorageWrite { From ff40cb5ed3f5336a94c630ec82fde4e492fb8af6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Mon, 18 Dec 2023 10:39:02 +0000 Subject: [PATCH 028/118] move core::types::internal::tx into namada_state::tx_queue --- core/src/types/internal.rs | 81 -------------------------------------- state/Cargo.toml | 6 ++- state/src/lib.rs | 1 + state/src/tx_queue.rs | 78 ++++++++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 83 deletions(-) create mode 100644 state/src/tx_queue.rs diff --git a/core/src/types/internal.rs b/core/src/types/internal.rs index ef9da587ba..c527bdabf8 100644 --- a/core/src/types/internal.rs +++ b/core/src/types/internal.rs @@ -45,84 +45,3 @@ impl From for HostEnvResult { if success { Self::Success } else { Self::Fail } } } - -mod tx_queue { - use borsh::{BorshDeserialize, BorshSerialize}; - - use crate::ledger::gas::Gas; - use crate::proto::Tx; - - /// A wrapper for `crate::types::transaction::WrapperTx` to conditionally - /// add `has_valid_pow` flag for only used in testnets. - #[derive(Debug, Clone, BorshDeserialize, BorshSerialize)] - pub struct TxInQueue { - /// Wrapper tx - pub tx: Tx, - /// The available gas remaining for the inner tx (for gas accounting). - /// This allows for a more detailed logging about the gas used by the - /// wrapper and that used by the inner - pub gas: Gas, - } - - #[derive(Default, Debug, Clone, BorshDeserialize, BorshSerialize)] - /// Wrapper txs to be decrypted in the next block proposal - pub struct TxQueue(std::collections::VecDeque); - - impl TxQueue { - /// Add a new wrapper at the back of the queue - pub fn push(&mut self, wrapper: TxInQueue) { - self.0.push_back(wrapper); - } - - /// Remove the wrapper at the head of the queue - pub fn pop(&mut self) -> Option { - self.0.pop_front() - } - - /// Get an iterator over the queue - pub fn iter(&self) -> impl std::iter::Iterator { - self.0.iter() - } - - /// Check if there are any txs in the queue - #[allow(dead_code)] - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - - /// Get reference to the element at the given index. - /// Returns [`None`] if index exceeds the queue length. - pub fn get(&self, index: usize) -> Option<&TxInQueue> { - self.0.get(index) - } - } -} - -pub use tx_queue::{TxInQueue, TxQueue}; - -/// Expired transaction kinds. -#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)] -pub enum ExpiredTx { - /// Broadcast the given Ethereum event. - EthereumEvent(EthereumEvent), -} - -/// Queue of expired transactions that need to be retransmitted. -#[derive(Default, Clone, Debug, BorshSerialize, BorshDeserialize)] -pub struct ExpiredTxsQueue { - inner: Vec, -} - -impl ExpiredTxsQueue { - /// Push a new transaction to the back of the queue. - #[inline] - pub fn push(&mut self, tx: ExpiredTx) { - self.inner.push(tx); - } - - /// Consume all the transactions in the queue. - #[inline] - pub fn drain(&mut self) -> impl Iterator + '_ { - self.inner.drain(..) - } -} diff --git a/state/Cargo.toml b/state/Cargo.toml index 969af24649..eecfa7a528 100644 --- a/state/Cargo.toml +++ b/state/Cargo.toml @@ -13,7 +13,9 @@ repository.workspace = true version.workspace = true [dependencies] -namada_core = {path = "../core", default-features = false} +namada_core = { path = "../core", default-features = false } +namada_gas = { path = "../gas" } +namada_tx = { path = "../tx" } arse-merkle-tree.workspace = true -ics23.workspace = true \ No newline at end of file +ics23.workspace = true diff --git a/state/src/lib.rs b/state/src/lib.rs index 2c981069b0..b58b0b6664 100644 --- a/state/src/lib.rs +++ b/state/src/lib.rs @@ -4,6 +4,7 @@ pub mod ics23_specs; #[cfg(any(test, feature = "testing"))] pub mod mockdb; pub mod traits; +pub mod tx_queue; pub mod types; pub mod wl_storage; pub mod write_log; diff --git a/state/src/tx_queue.rs b/state/src/tx_queue.rs new file mode 100644 index 0000000000..6369e056f5 --- /dev/null +++ b/state/src/tx_queue.rs @@ -0,0 +1,78 @@ +use borsh::{BorshDeserialize, BorshSerialize}; + +use namada_gas::Gas; +use namada_tx::proto::Tx; + +/// A wrapper for `crate::types::transaction::WrapperTx` to conditionally +/// add `has_valid_pow` flag for only used in testnets. +#[derive(Debug, Clone, BorshDeserialize, BorshSerialize)] +pub struct TxInQueue { + /// Wrapper tx + pub tx: Tx, + /// The available gas remaining for the inner tx (for gas accounting). + /// This allows for a more detailed logging about the gas used by the + /// wrapper and that used by the inner + pub gas: Gas, +} + +#[derive(Default, Debug, Clone, BorshDeserialize, BorshSerialize)] +/// Wrapper txs to be decrypted in the next block proposal +pub struct TxQueue(std::collections::VecDeque); + +impl TxQueue { + /// Add a new wrapper at the back of the queue + pub fn push(&mut self, wrapper: TxInQueue) { + self.0.push_back(wrapper); + } + + /// Remove the wrapper at the head of the queue + pub fn pop(&mut self) -> Option { + self.0.pop_front() + } + + /// Get an iterator over the queue + pub fn iter(&self) -> impl std::iter::Iterator { + self.0.iter() + } + + /// Check if there are any txs in the queue + #[allow(dead_code)] + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Get reference to the element at the given index. + /// Returns [`None`] if index exceeds the queue lenght. + pub fn get(&self, index: usize) -> Option<&TxInQueue> { + self.0.get(index) + } +} +} + +pub use tx_queue::{TxInQueue, TxQueue}; + +/// Expired transaction kinds. +#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)] +pub enum ExpiredTx { +/// Broadcast the given Ethereum event. +EthereumEvent(EthereumEvent), +} + +/// Queue of expired transactions that need to be retransmitted. +#[derive(Default, Clone, Debug, BorshSerialize, BorshDeserialize)] +pub struct ExpiredTxsQueue { +inner: Vec, +} + +impl ExpiredTxsQueue { +/// Push a new transaction to the back of the queue. +#[inline] +pub fn push(&mut self, tx: ExpiredTx) { + self.inner.push(tx); +} + +/// Consume all the transactions in the queue. +#[inline] +pub fn drain(&mut self) -> impl Iterator + '_ { + self.inner.drain(..) +} From a77ccd02f8e18a5a30951aa3d78099b2898c424a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 19 Dec 2023 10:49:26 +0000 Subject: [PATCH 029/118] add namada_account crate from namada_core::types::account --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + Makefile | 1 + account/Cargo.toml | 16 ++++++++++++++++ core/src/types/account.rs => account/src/lib.rs | 0 core/src/types/mod.rs | 1 - 6 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 account/Cargo.toml rename core/src/types/account.rs => account/src/lib.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 40ef843a2d..24fa1c1c17 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4248,6 +4248,13 @@ dependencies = [ "zeroize", ] +[[package]] +name = "namada_account" +version = "0.28.1" +dependencies = [ + "namada_core", +] + [[package]] name = "namada_apps" version = "0.29.0" diff --git a/Cargo.toml b/Cargo.toml index 46579dcbbd..3e2a675791 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ resolver = "2" members = [ + "account", "apps", "benches", "core", diff --git a/Makefile b/Makefile index ac01676abf..dd9ac9499c 100644 --- a/Makefile +++ b/Makefile @@ -35,6 +35,7 @@ audit-ignores += RUSTSEC-2021-0076 # Workspace crates crates := namada_core crates += namada +crates += namada_account crates += namada_apps crates += namada_benchmarks crates += namada_encoding_spec diff --git a/account/Cargo.toml b/account/Cargo.toml new file mode 100644 index 0000000000..53d546f6d9 --- /dev/null +++ b/account/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "namada_account" +description = "Namada user account" +resolver = "2" +authors.workspace = true +edition.workspace = true +documentation.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +namada_core = {path = "../core", default-features = false} \ No newline at end of file diff --git a/core/src/types/account.rs b/account/src/lib.rs similarity index 100% rename from core/src/types/account.rs rename to account/src/lib.rs diff --git a/core/src/types/mod.rs b/core/src/types/mod.rs index 2cea327105..b1c4fc8e63 100644 --- a/core/src/types/mod.rs +++ b/core/src/types/mod.rs @@ -1,6 +1,5 @@ //! Types definitions. -pub mod account; pub mod address; pub mod chain; pub mod dec; From 988272701561381c7af2a97118ffc1009d721695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 19 Dec 2023 10:49:52 +0000 Subject: [PATCH 030/118] namada_account: fix imports, update docstring --- account/src/lib.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/account/src/lib.rs b/account/src/lib.rs index eb360ecb1c..b3ed5829d2 100644 --- a/account/src/lib.rs +++ b/account/src/lib.rs @@ -1,14 +1,15 @@ -//! Helper structures to manage accounts +//! Support for signature based authorization of actions on a user account +//! using public key(s) and signature threshold (minimum number of signatures +//! needed to authorize an action) stored on-chain. use std::collections::{BTreeMap, HashMap}; use borsh::{BorshDeserialize, BorshSerialize}; +use namada_core::address::Address; +use namada_core::hints; +use namada_core::key::{common, RefTo}; use serde::{Deserialize, Serialize}; -use super::address::Address; -use super::key::{common, RefTo}; -use crate::hints; - #[derive( Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, )] From 069fbe302295a11dfe072ac03a00845e0b83c759 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 19 Dec 2023 10:57:32 +0000 Subject: [PATCH 031/118] namada_account: take account keys storage from namada_core::types::key --- Cargo.lock | 2 + account/Cargo.toml | 4 +- account/src/lib.rs | 3 ++ account/src/storage_key.rs | 89 +++++++++++++++++++++++++++++++++++++ core/src/types/key/mod.rs | 90 -------------------------------------- 5 files changed, 97 insertions(+), 91 deletions(-) create mode 100644 account/src/storage_key.rs diff --git a/Cargo.lock b/Cargo.lock index 24fa1c1c17..04f8ae96c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4253,6 +4253,8 @@ name = "namada_account" version = "0.28.1" dependencies = [ "namada_core", + "namada_macros", + "namada_storage", ] [[package]] diff --git a/account/Cargo.toml b/account/Cargo.toml index 53d546f6d9..77a53de081 100644 --- a/account/Cargo.toml +++ b/account/Cargo.toml @@ -13,4 +13,6 @@ repository.workspace = true version.workspace = true [dependencies] -namada_core = {path = "../core", default-features = false} \ No newline at end of file +namada_core = { path = "../core", default-features = false } +namada_macros = { path = "../macros" } +namada_storage = { path = "../storage" } diff --git a/account/src/lib.rs b/account/src/lib.rs index b3ed5829d2..3c522aeabb 100644 --- a/account/src/lib.rs +++ b/account/src/lib.rs @@ -2,6 +2,8 @@ //! using public key(s) and signature threshold (minimum number of signatures //! needed to authorize an action) stored on-chain. +mod storage_key; + use std::collections::{BTreeMap, HashMap}; use borsh::{BorshDeserialize, BorshSerialize}; @@ -9,6 +11,7 @@ use namada_core::address::Address; use namada_core::hints; use namada_core::key::{common, RefTo}; use serde::{Deserialize, Serialize}; +pub use storage_key::*; #[derive( Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, diff --git a/account/src/storage_key.rs b/account/src/storage_key.rs new file mode 100644 index 0000000000..608586a30a --- /dev/null +++ b/account/src/storage_key.rs @@ -0,0 +1,89 @@ +use namada_core::types::storage::{self, DbKeySeg}; +use namada_macros::StorageKeys; +use namada_storage::collections::lazy_map::LazyMap; +use namada_storage::collections::{lazy_map, LazyCollection}; + +/// Storage keys for account. +#[derive(StorageKeys)] +struct Keys { + public_keys: &'static str, + threshold: &'static str, + protocol_public_keys: &'static str, +} + +/// Obtain a storage key for user's public key. +pub fn pks_key_prefix(owner: &Address) -> storage::Key { + Key { + segments: vec![ + DbKeySeg::AddressSeg(owner.to_owned()), + DbKeySeg::StringSeg(Keys::VALUES.public_keys.to_string()), + ], + } +} + +/// LazyMap handler for the user's public key subspace +pub fn pks_handle(owner: &Address) -> LazyMap { + LazyMap::open(pks_key_prefix(owner)) +} + +/// Check if the given storage key is a public key. If it is, returns the owner. +pub fn is_pks_key(key: &storage::Key) -> Option<&Address> { + match &key.segments[..] { + [ + DbKeySeg::AddressSeg(owner), + DbKeySeg::StringSeg(prefix), + DbKeySeg::StringSeg(data), + DbKeySeg::StringSeg(index), + ] if prefix.as_str() == Keys::VALUES.public_keys + && data.as_str() == lazy_map::DATA_SUBKEY + && index.parse::().is_ok() => + { + Some(owner) + } + _ => None, + } +} + +/// Check if the given storage key is a threshol key. +pub fn is_threshold_key(key: &storage::Key) -> Option<&Address> { + match &key.segments[..] { + [DbKeySeg::AddressSeg(owner), DbKeySeg::StringSeg(prefix)] + if prefix.as_str() == Keys::VALUES.threshold => + { + Some(owner) + } + _ => None, + } +} + +/// Obtain the storage key for a user threshold +pub fn threshold_key(owner: &Address) -> storage::Key { + Key { + segments: vec![ + DbKeySeg::AddressSeg(owner.to_owned()), + DbKeySeg::StringSeg(Keys::VALUES.threshold.to_string()), + ], + } +} + +/// Obtain a storage key for user's protocol public key. +pub fn protocol_pk_key(owner: &Address) -> storage::Key { + Key { + segments: vec![ + DbKeySeg::AddressSeg(owner.to_owned()), + DbKeySeg::StringSeg(Keys::VALUES.protocol_public_keys.to_string()), + ], + } +} + +/// Check if the given storage key is a public key. If it is, returns the owner. +pub fn is_protocol_pk_key(key: &storage::Key) -> Option<&Address> { + match &key.segments[..] { + [DbKeySeg::AddressSeg(owner), DbKeySeg::StringSeg(key)] + if key.as_str() == Keys::VALUES.protocol_public_keys => + { + Some(owner) + } + _ => None, + } +} diff --git a/core/src/types/key/mod.rs b/core/src/types/key/mod.rs index 0782d32a6d..f4e591d6fd 100644 --- a/core/src/types/key/mod.rs +++ b/core/src/types/key/mod.rs @@ -11,105 +11,15 @@ use std::str::FromStr; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use borsh_ext::BorshSerializeExt; use data_encoding::HEXUPPER; -use lazy_map::LazyMap; -use namada_macros::StorageKeys; #[cfg(feature = "rand")] use rand::{CryptoRng, RngCore}; use serde::Serialize; use sha2::{Digest, Sha256}; use thiserror::Error; -use super::address::Address; -use super::storage::{self, DbKeySeg, Key}; use crate::ledger::storage::{Sha256Hasher, StorageHasher}; -use crate::ledger::storage_api::collections::{lazy_map, LazyCollection}; use crate::types::address; -/// Storage keys for account. -#[derive(StorageKeys)] -struct Keys { - public_keys: &'static str, - threshold: &'static str, - protocol_public_keys: &'static str, -} - -/// Obtain a storage key for user's public key. -pub fn pks_key_prefix(owner: &Address) -> storage::Key { - Key { - segments: vec![ - DbKeySeg::AddressSeg(owner.to_owned()), - DbKeySeg::StringSeg(Keys::VALUES.public_keys.to_string()), - ], - } -} - -/// LazyMap handler for the user's public key subspace -pub fn pks_handle(owner: &Address) -> LazyMap { - LazyMap::open(pks_key_prefix(owner)) -} - -/// Check if the given storage key is a public key. If it is, returns the owner. -pub fn is_pks_key(key: &Key) -> Option<&Address> { - match &key.segments[..] { - [ - DbKeySeg::AddressSeg(owner), - DbKeySeg::StringSeg(prefix), - DbKeySeg::StringSeg(data), - DbKeySeg::StringSeg(index), - ] if prefix.as_str() == Keys::VALUES.public_keys - && data.as_str() == lazy_map::DATA_SUBKEY - && index.parse::().is_ok() => - { - Some(owner) - } - _ => None, - } -} - -/// Check if the given storage key is a threshold key. -pub fn is_threshold_key(key: &Key) -> Option<&Address> { - match &key.segments[..] { - [DbKeySeg::AddressSeg(owner), DbKeySeg::StringSeg(prefix)] - if prefix.as_str() == Keys::VALUES.threshold => - { - Some(owner) - } - _ => None, - } -} - -/// Obtain the storage key for a user threshold -pub fn threshold_key(owner: &Address) -> storage::Key { - Key { - segments: vec![ - DbKeySeg::AddressSeg(owner.to_owned()), - DbKeySeg::StringSeg(Keys::VALUES.threshold.to_string()), - ], - } -} - -/// Obtain a storage key for user's protocol public key. -pub fn protocol_pk_key(owner: &Address) -> storage::Key { - Key { - segments: vec![ - DbKeySeg::AddressSeg(owner.to_owned()), - DbKeySeg::StringSeg(Keys::VALUES.protocol_public_keys.to_string()), - ], - } -} - -/// Check if the given storage key is a public key. If it is, returns the owner. -pub fn is_protocol_pk_key(key: &Key) -> Option<&Address> { - match &key.segments[..] { - [DbKeySeg::AddressSeg(owner), DbKeySeg::StringSeg(key)] - if key.as_str() == Keys::VALUES.protocol_public_keys => - { - Some(owner) - } - _ => None, - } -} - /// Represents an error in signature verification #[allow(missing_docs)] #[derive(Error, Debug)] From be9739ba24483c81554330fafc592a5bc0d41bc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 19 Dec 2023 11:30:49 +0000 Subject: [PATCH 032/118] move state::traits hasher into core::types::hash --- Cargo.lock | 1 + core/Cargo.toml | 1 + core/src/types/eth_abi.rs | 2 +- core/src/types/hash.rs | 123 ++++++++++++++++++++++++++++++++++++++ core/src/types/key/mod.rs | 58 +++++++++++++++++- state/src/traits.rs | 116 ----------------------------------- tx/src/types.rs | 53 ---------------- 7 files changed, 182 insertions(+), 172 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 04f8ae96c4..0da7c82964 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4407,6 +4407,7 @@ dependencies = [ "serde 1.0.193", "serde_json", "sha2 0.9.9", + "sparse-merkle-tree", "tendermint", "tendermint-proto", "test-log", diff --git a/core/Cargo.toml b/core/Cargo.toml index c26208069a..ec181f34a8 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -32,6 +32,7 @@ namada_macros = {path = "../macros"} ark-bls12-381.workspace = true ark-serialize.workspace = true +arse-merkle-tree.workspace = true bech32.workspace = true borsh.workspace = true borsh-ext.workspace = true diff --git a/core/src/types/eth_abi.rs b/core/src/types/eth_abi.rs index 27d2b93db1..b7544cf1aa 100644 --- a/core/src/types/eth_abi.rs +++ b/core/src/types/eth_abi.rs @@ -7,8 +7,8 @@ use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; #[doc(inline)] pub use ethabi::token::Token; -use crate::proto::{Signable, SignableEthMessage}; use crate::types::keccak::{keccak_hash, KeccakHash}; +use crate::types::key::{Signable, SignableEthMessage}; /// A container for data types that are able to be Ethereum ABI-encoded. #[derive(Clone, Debug, BorshSerialize, BorshDeserialize, BorshSchema)] diff --git a/core/src/types/hash.rs b/core/src/types/hash.rs index bb6c6e3acc..932e4595af 100644 --- a/core/src/types/hash.rs +++ b/core/src/types/hash.rs @@ -3,6 +3,8 @@ use std::fmt::{self, Display}; use std::str::FromStr; +use arse_merkle_tree::traits::Hasher; +use arse_merkle_tree::H256; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use data_encoding::HEXUPPER; use serde::{Deserialize, Serialize}; @@ -146,6 +148,127 @@ impl From for crate::tendermint::Hash { } } +/// The storage hasher used for the merkle tree. +pub trait StorageHasher: Hasher + fmt::Debug + Default { + /// Hash the value to store + fn hash(value: impl AsRef<[u8]>) -> H256; +} + +/// The storage hasher used for the merkle tree. +#[derive(Default)] +pub struct Sha256Hasher(Sha256); + +impl Hasher for Sha256Hasher { + fn write_bytes(&mut self, h: &[u8]) { + self.0.update(h) + } + + fn finish(self) -> H256 { + let hash = self.0.finalize(); + let bytes: [u8; 32] = hash + .as_slice() + .try_into() + .expect("Sha256 output conversion to fixed array shouldn't fail"); + bytes.into() + } + + fn hash_op() -> ics23::HashOp { + ics23::HashOp::Sha256 + } +} + +impl StorageHasher for Sha256Hasher { + fn hash(value: impl AsRef<[u8]>) -> H256 { + let mut hasher = Sha256::new(); + hasher.update(value.as_ref()); + let hash = hasher.finalize(); + let bytes: [u8; 32] = hash + .as_slice() + .try_into() + .expect("Sha256 output conversion to fixed array shouldn't fail"); + bytes.into() + } +} + +impl fmt::Debug for Sha256Hasher { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Sha256Hasher") + } +} + +/// A Keccak hasher algorithm. +pub struct KeccakHasher(tiny_keccak::Keccak); + +impl fmt::Debug for KeccakHasher { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "KeccakHasher") + } +} + +impl Default for KeccakHasher { + fn default() -> Self { + Self(tiny_keccak::Keccak::v256()) + } +} + +impl StorageHasher for KeccakHasher { + fn hash(value: impl AsRef<[u8]>) -> H256 { + use tiny_keccak::{Hasher, Keccak}; + + let mut output = [0u8; 32]; + let mut hasher = Keccak::v256(); + hasher.update(value.as_ref()); + hasher.finalize(&mut output); + output.into() + } +} + +impl Hasher for KeccakHasher { + fn write_bytes(&mut self, h: &[u8]) { + use tiny_keccak::Hasher; + + self.0.update(h); + } + + fn finish(self) -> H256 { + use tiny_keccak::Hasher; + let mut output = [0; 32]; + + self.0.finalize(&mut output); + output.into() + } +} + +/// A [`StorageHasher`] which can never be called. +#[derive(Debug)] +pub enum DummyHasher {} + +const DUMMY_HASHER_PANIC_MSG: &str = "A storage hasher was called, which \ + should never have been reachable from \ + any code path"; + +impl Default for DummyHasher { + fn default() -> Self { + unreachable!("{DUMMY_HASHER_PANIC_MSG}") + } +} + +impl StorageHasher for DummyHasher { + fn hash(_: impl AsRef<[u8]>) -> H256 { + unreachable!("{DUMMY_HASHER_PANIC_MSG}") + } +} + +impl Hasher for DummyHasher { + fn write_bytes(&mut self, _: &[u8]) { + unreachable!("{DUMMY_HASHER_PANIC_MSG}") + } + + fn finish(self) -> H256 { + unreachable!("{DUMMY_HASHER_PANIC_MSG}") + } +} + #[cfg(any(test, feature = "testing"))] /// Tests and strategies for hashes pub mod testing { diff --git a/core/src/types/key/mod.rs b/core/src/types/key/mod.rs index f4e591d6fd..0e4658fb93 100644 --- a/core/src/types/key/mod.rs +++ b/core/src/types/key/mod.rs @@ -13,12 +13,13 @@ use borsh_ext::BorshSerializeExt; use data_encoding::HEXUPPER; #[cfg(feature = "rand")] use rand::{CryptoRng, RngCore}; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; use thiserror::Error; -use crate::ledger::storage::{Sha256Hasher, StorageHasher}; use crate::types::address; +use crate::types::hash::{KeccakHasher, Sha256Hasher, StorageHasher}; +use crate::types::keccak::{keccak_hash, KeccakHash}; /// Represents an error in signature verification #[allow(missing_docs)] @@ -392,6 +393,59 @@ pub fn tm_raw_hash_to_string(raw_hash: impl AsRef<[u8]>) -> String { HEXUPPER.encode(raw_hash.as_ref()) } +/// A serialization method to provide to [`Signed`], such +/// that we may sign serialized data. +/// +/// This is a higher level version of [`SignableBytes`]. +pub trait Signable { + /// A byte vector containing the serialized data. + type Output: SignableBytes; + + /// The hashing algorithm to use to sign serialized + /// data with. + type Hasher: 'static + StorageHasher; + + /// Encodes `data` as a byte vector, with some arbitrary serialization + /// method. + /// + /// The returned output *must* be deterministic based on + /// `data`, so that two callers signing the same `data` will be + /// signing the same `Self::Output`. + fn as_signable(data: &T) -> Self::Output; +} + +/// Tag type that indicates we should use [`BorshSerialize`] +/// to sign data in a [`Signed`] wrapper. +#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)] +pub struct SerializeWithBorsh; + +/// Tag type that indicates we should use ABI serialization +/// to sign data in a [`Signed`] wrapper. +#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)] +pub struct SignableEthMessage; + +impl Signable for SerializeWithBorsh { + type Hasher = Sha256Hasher; + type Output = Vec; + + fn as_signable(data: &T) -> Vec { + data.serialize_to_vec() + } +} + +impl Signable for SignableEthMessage { + type Hasher = KeccakHasher; + type Output = KeccakHash; + + fn as_signable(hash: &KeccakHash) -> KeccakHash { + keccak_hash({ + let mut eth_message = Vec::from("\x19Ethereum Signed Message:\n32"); + eth_message.extend_from_slice(hash.as_ref()); + eth_message + }) + } +} + /// Helper trait to compress arbitrary bytes to a hash value, /// which can be signed over. pub trait SignableBytes: Sized + AsRef<[u8]> { diff --git a/state/src/traits.rs b/state/src/traits.rs index 40b094e411..15504404e8 100644 --- a/state/src/traits.rs +++ b/state/src/traits.rs @@ -306,119 +306,3 @@ impl Value for TreeBytes { TreeBytes::zero() } } - -/// The storage hasher used for the merkle tree. -pub trait StorageHasher: Hasher + fmt::Debug + Default { - /// Hash the value to store - fn hash(value: impl AsRef<[u8]>) -> H256; -} - -/// The storage hasher used for the merkle tree. -#[derive(Default)] -pub struct Sha256Hasher(Sha256); - -impl Hasher for Sha256Hasher { - fn write_bytes(&mut self, h: &[u8]) { - self.0.update(h) - } - - fn finish(self) -> H256 { - let hash = self.0.finalize(); - let bytes: [u8; 32] = hash - .as_slice() - .try_into() - .expect("Sha256 output conversion to fixed array shouldn't fail"); - bytes.into() - } - - fn hash_op() -> ics23::HashOp { - ics23::HashOp::Sha256 - } -} - -impl StorageHasher for Sha256Hasher { - fn hash(value: impl AsRef<[u8]>) -> H256 { - let mut hasher = Sha256::new(); - hasher.update(value.as_ref()); - let hash = hasher.finalize(); - let bytes: [u8; 32] = hash - .as_slice() - .try_into() - .expect("Sha256 output conversion to fixed array shouldn't fail"); - bytes.into() - } -} - -impl fmt::Debug for Sha256Hasher { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Sha256Hasher") - } -} - -/// A Keccak hasher algorithm. -pub struct KeccakHasher(tiny_keccak::Keccak); - -impl fmt::Debug for KeccakHasher { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "KeccakHasher") - } -} - -impl Default for KeccakHasher { - fn default() -> Self { - Self(tiny_keccak::Keccak::v256()) - } -} - -impl StorageHasher for KeccakHasher { - fn hash(value: impl AsRef<[u8]>) -> H256 { - use tiny_keccak::Keccak; - let mut output = [0u8; 32]; - let mut hasher = Keccak::v256(); - hasher.update(value.as_ref()); - hasher.finalize(&mut output); - output.into() - } -} - -impl Hasher for KeccakHasher { - fn write_bytes(&mut self, h: &[u8]) { - self.0.update(h); - } - - fn finish(self) -> H256 { - let mut output = [0; 32]; - self.0.finalize(&mut output); - output.into() - } -} - -/// A [`StorageHasher`] which can never be called. -#[derive(Debug)] -pub enum DummyHasher {} - -const DUMMY_HASHER_PANIC_MSG: &str = "A storage hasher was called, which \ - should never have been reachable from \ - any code path"; - -impl Default for DummyHasher { - fn default() -> Self { - unreachable!("{DUMMY_HASHER_PANIC_MSG}") - } -} - -impl StorageHasher for DummyHasher { - fn hash(_: impl AsRef<[u8]>) -> H256 { - unreachable!("{DUMMY_HASHER_PANIC_MSG}") - } -} - -impl Hasher for DummyHasher { - fn write_bytes(&mut self, _: &[u8]) { - unreachable!("{DUMMY_HASHER_PANIC_MSG}") - } - - fn finish(self) -> H256 { - unreachable!("{DUMMY_HASHER_PANIC_MSG}") - } -} diff --git a/tx/src/types.rs b/tx/src/types.rs index a4e7b6df85..82b20dd098 100644 --- a/tx/src/types.rs +++ b/tx/src/types.rs @@ -75,59 +75,6 @@ pub struct SignedTxData { pub sig: common::Signature, } -/// A serialization method to provide to [`Signed`], such -/// that we may sign serialized data. -/// -/// This is a higher level version of [`key::SignableBytes`]. -pub trait Signable { - /// A byte vector containing the serialized data. - type Output: key::SignableBytes; - - /// The hashing algorithm to use to sign serialized - /// data with. - type Hasher: 'static + StorageHasher; - - /// Encodes `data` as a byte vector, with some arbitrary serialization - /// method. - /// - /// The returned output *must* be deterministic based on - /// `data`, so that two callers signing the same `data` will be - /// signing the same `Self::Output`. - fn as_signable(data: &T) -> Self::Output; -} - -/// Tag type that indicates we should use [`BorshSerialize`] -/// to sign data in a [`Signed`] wrapper. -#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)] -pub struct SerializeWithBorsh; - -/// Tag type that indicates we should use ABI serialization -/// to sign data in a [`Signed`] wrapper. -#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)] -pub struct SignableEthMessage; - -impl Signable for SerializeWithBorsh { - type Hasher = Sha256Hasher; - type Output = Vec; - - fn as_signable(data: &T) -> Vec { - data.serialize_to_vec() - } -} - -impl Signable for SignableEthMessage { - type Hasher = KeccakHasher; - type Output = KeccakHash; - - fn as_signable(hash: &KeccakHash) -> KeccakHash { - keccak_hash({ - let mut eth_message = Vec::from("\x19Ethereum Signed Message:\n32"); - eth_message.extend_from_slice(hash.as_ref()); - eth_message - }) - } -} - /// A generic signed data wrapper for serialize-able types. /// /// The default serialization method is [`BorshSerialize`]. From c3c053378d112a7536b9fd5ef5949e95ef5dc36a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 19 Dec 2023 11:38:19 +0000 Subject: [PATCH 033/118] core/types/key: remove unused error case --- core/src/types/key/mod.rs | 2 -- tx/src/types.rs | 12 ++++-------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/core/src/types/key/mod.rs b/core/src/types/key/mod.rs index 0e4658fb93..985bb84c40 100644 --- a/core/src/types/key/mod.rs +++ b/core/src/types/key/mod.rs @@ -33,8 +33,6 @@ pub enum VerifySigError { MissingData, #[error("Signature belongs to a different scheme from the public key.")] MismatchedScheme, - #[error("Signature verification went out of gas: {0}")] - OutOfGas(#[from] crate::ledger::gas::Error), #[error( "The number of valid signatures did not meet the required threshold, \ required {0} got {1}" diff --git a/tx/src/types.rs b/tx/src/types.rs index 82b20dd098..ef557b5d2e 100644 --- a/tx/src/types.rs +++ b/tx/src/types.rs @@ -1239,14 +1239,10 @@ impl Tx { signer, &mut consume_verify_sig_gas, ) - .map_err(|e| { - if let VerifySigError::OutOfGas(inner) = e { - Error::OutOfGas(inner) - } else { - Error::InvalidSectionSignature( - "found invalid signature.".to_string(), - ) - } + .map_err(|_e| { + Error::InvalidSectionSignature( + "found invalid signature.".to_string(), + ) }); // Record the section witnessing these signatures if amt_verifieds? > 0 { From bd5b6d9ce20f3565d2a88257e649d124f842bd30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 19 Dec 2023 11:42:31 +0000 Subject: [PATCH 034/118] move EvalVp from core into tx::data::eval_vp --- core/src/types/validity_predicate.rs | 18 ------------------ tx/src/data/eval_vp.rs | 17 +++++++++++++++++ tx/src/data/mod.rs | 1 + 3 files changed, 18 insertions(+), 18 deletions(-) create mode 100644 tx/src/data/eval_vp.rs diff --git a/core/src/types/validity_predicate.rs b/core/src/types/validity_predicate.rs index 60f73af912..1ea8b3b494 100644 --- a/core/src/types/validity_predicate.rs +++ b/core/src/types/validity_predicate.rs @@ -1,23 +1,5 @@ //! Types that are used in validity predicates. -use borsh::{BorshDeserialize, BorshSerialize}; -use serde::{Deserialize, Serialize}; - -use crate::proto::Tx; -use crate::types::hash::Hash; - -/// A validity predicate with an input that is intended to be invoked via `eval` -/// host function. -#[derive( - Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, -)] -pub struct EvalVp { - /// The VP code hash to `eval` - pub vp_code_hash: Hash, - /// The input for the `eval`ed VP - pub input: Tx, -} - /// Sentinel used in validity predicates to signal events that require special /// replay protection handling back to the protocol. #[derive(Debug, Default)] diff --git a/tx/src/data/eval_vp.rs b/tx/src/data/eval_vp.rs new file mode 100644 index 0000000000..387a868ab8 --- /dev/null +++ b/tx/src/data/eval_vp.rs @@ -0,0 +1,17 @@ +use borsh::{BorshDeserialize, BorshSerialize}; +use namada_core::types::hash::Hash; +use serde::{Deserialize, Serialize}; + +use crate::proto::Tx; + +/// A validity predicate with an input that is intended to be invoked via `eval` +/// host function. +#[derive( + Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, +)] +pub struct EvalVp { + /// The VP code hash to `eval` + pub vp_code_hash: Hash, + /// The input for the `eval`ed VP + pub input: Tx, +} diff --git a/tx/src/data/mod.rs b/tx/src/data/mod.rs index 04728fc472..4a95bc63c5 100644 --- a/tx/src/data/mod.rs +++ b/tx/src/data/mod.rs @@ -5,6 +5,7 @@ pub mod account; /// txs that contain decrypted payloads or assertions of /// non-decryptability pub mod decrypted; +pub mod eval_vp; /// txs to manage governance pub mod governance; /// txs to manage pgf From a146bf36c09b21ae7caabc574a18ce8a0fa50d6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 19 Dec 2023 13:03:45 +0000 Subject: [PATCH 035/118] move core MembershipProof into merkle_tree crate --- core/src/types/storage.rs | 22 ---------------------- merkle_tree/src/lib.rs | 25 +++++++++++++++++++++++-- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/core/src/types/storage.rs b/core/src/types/storage.rs index 87d1b80e50..1b633cf086 100644 --- a/core/src/types/storage.rs +++ b/core/src/types/storage.rs @@ -11,7 +11,6 @@ use arse_merkle_tree::InternalKey; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use borsh_ext::BorshSerializeExt; use data_encoding::{BASE32HEX_NOPAD, HEXUPPER}; -use ics23::CommitmentProof; use index_set::vec::VecIndexSet; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -19,7 +18,6 @@ use thiserror::Error; use super::key::common; use crate::bytes::ByteBuf; use crate::hints; -use crate::ledger::eth_bridge::storage::bridge_pool::BridgePoolProof; use crate::types::address::{self, Address}; use crate::types::ethereum_events::{GetEventNonce, TransfersToNamada, Uint}; use crate::types::hash::Hash; @@ -498,26 +496,6 @@ impl From for Vec { } } -/// Type of membership proof from a merkle tree -pub enum MembershipProof { - /// ICS23 compliant membership proof - ICS23(CommitmentProof), - /// Bespoke membership proof for the Ethereum bridge pool - BridgePool(BridgePoolProof), -} - -impl From for MembershipProof { - fn from(proof: CommitmentProof) -> Self { - Self::ICS23(proof) - } -} - -impl From for MembershipProof { - fn from(proof: BridgePoolProof) -> Self { - Self::BridgePool(proof) - } -} - impl Key { /// Parses string and returns a key pub fn parse(string: impl AsRef) -> Result { diff --git a/merkle_tree/src/lib.rs b/merkle_tree/src/lib.rs index f0bbd46208..b7e448b14e 100644 --- a/merkle_tree/src/lib.rs +++ b/merkle_tree/src/lib.rs @@ -11,6 +11,7 @@ use borsh::{BorshDeserialize, BorshSerialize}; use borsh_ext::BorshSerializeExt; use ics23::commitment_proof::Proof as Ics23Proof; use ics23::{CommitmentProof, ExistenceProof, NonExistenceProof}; +use namada_core::ledger::eth_bridge::storage::bridge_pool::BridgePoolProof; use thiserror::Error; use super::traits::{StorageHasher, SubTreeRead, SubTreeWrite}; @@ -24,10 +25,30 @@ use crate::types::address::{Address, InternalAddress}; use crate::types::hash::Hash; use crate::types::keccak::KeccakHash; use crate::types::storage::{ - self, DbKeySeg, Error as StorageError, Key, MembershipProof, StringKey, - TreeBytes, TreeKeyError, IBC_KEY_LIMIT, + self, DbKeySeg, Error as StorageError, Key, StringKey, TreeBytes, + TreeKeyError, IBC_KEY_LIMIT, }; +/// Type of membership proof from a merkle tree +pub enum MembershipProof { + /// ICS23 compliant membership proof + ICS23(CommitmentProof), + /// Bespoke membership proof for the Ethereum bridge pool + BridgePool(BridgePoolProof), +} + +impl From for MembershipProof { + fn from(proof: CommitmentProof) -> Self { + Self::ICS23(proof) + } +} + +impl From for MembershipProof { + fn from(proof: BridgePoolProof) -> Self { + Self::BridgePool(proof) + } +} + #[allow(missing_docs)] #[derive(Error, Debug)] pub enum Error { From ea0a55aa3ede72c490791b90f0a237b4dec7d3fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 19 Dec 2023 17:50:30 +0000 Subject: [PATCH 036/118] move core::types::vote_extensions into namada_vote_ext crate --- Cargo.lock | 8 ++++++++ Cargo.toml | 1 + Makefile | 1 + core/src/types/mod.rs | 1 - vote_ext/Cargo.toml | 18 ++++++++++++++++++ .../src}/bridge_pool_roots.rs | 0 .../src}/ethereum_events.rs | 0 .../vote_extensions.rs => vote_ext/src/lib.rs | 0 .../src}/validator_set_update.rs | 0 9 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 vote_ext/Cargo.toml rename {core/src/types/vote_extensions => vote_ext/src}/bridge_pool_roots.rs (100%) rename {core/src/types/vote_extensions => vote_ext/src}/ethereum_events.rs (100%) rename core/src/types/vote_extensions.rs => vote_ext/src/lib.rs (100%) rename {core/src/types/vote_extensions => vote_ext/src}/validator_set_update.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 0da7c82964..e3fc573385 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4732,6 +4732,14 @@ dependencies = [ "namada_core", ] +[[package]] +name = "namada_vote_ext" +version = "0.28.1" +dependencies = [ + "namada_core", + "namada_tx", +] + [[package]] name = "namada_vp_env" version = "0.28.1" diff --git a/Cargo.toml b/Cargo.toml index 3e2a675791..49f0bab02d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ members = [ "tx_env", "tx_prelude", "vm_env", + "vote_ext", "vp_env", "vp_prelude", ] diff --git a/Makefile b/Makefile index dd9ac9499c..ea4884a373 100644 --- a/Makefile +++ b/Makefile @@ -58,6 +58,7 @@ crates += namada_tx crates += namada_tx_env crates += namada_tx_prelude crates += namada_vm_env +crates += namada_vote_ext crates += namada_vp_env crates += namada_vp_prelude diff --git a/core/src/types/mod.rs b/core/src/types/mod.rs index b1c4fc8e63..b2531f3a81 100644 --- a/core/src/types/mod.rs +++ b/core/src/types/mod.rs @@ -20,5 +20,4 @@ pub mod time; pub mod token; pub mod uint; pub mod validity_predicate; -pub mod vote_extensions; pub mod voting_power; diff --git a/vote_ext/Cargo.toml b/vote_ext/Cargo.toml new file mode 100644 index 0000000000..d3e7b34900 --- /dev/null +++ b/vote_ext/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "namada_vote_ext" +description = "Namada vote extensions" +resolver = "2" +authors.workspace = true +edition.workspace = true +documentation.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +namada_core = { path = "../core", default-features = false } +namada_tx = { path = "../tx" } + diff --git a/core/src/types/vote_extensions/bridge_pool_roots.rs b/vote_ext/src/bridge_pool_roots.rs similarity index 100% rename from core/src/types/vote_extensions/bridge_pool_roots.rs rename to vote_ext/src/bridge_pool_roots.rs diff --git a/core/src/types/vote_extensions/ethereum_events.rs b/vote_ext/src/ethereum_events.rs similarity index 100% rename from core/src/types/vote_extensions/ethereum_events.rs rename to vote_ext/src/ethereum_events.rs diff --git a/core/src/types/vote_extensions.rs b/vote_ext/src/lib.rs similarity index 100% rename from core/src/types/vote_extensions.rs rename to vote_ext/src/lib.rs diff --git a/core/src/types/vote_extensions/validator_set_update.rs b/vote_ext/src/validator_set_update.rs similarity index 100% rename from core/src/types/vote_extensions/validator_set_update.rs rename to vote_ext/src/validator_set_update.rs From 4fbd0028ef8240ca23d7e7da541c7513451a010c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 19 Dec 2023 17:53:20 +0000 Subject: [PATCH 037/118] core/types/internal: remove unsued import --- core/src/types/internal.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/src/types/internal.rs b/core/src/types/internal.rs index c527bdabf8..31401f2cce 100644 --- a/core/src/types/internal.rs +++ b/core/src/types/internal.rs @@ -2,8 +2,6 @@ use borsh::{BorshDeserialize, BorshSerialize}; -use crate::types::ethereum_events::EthereumEvent; - /// A result of a wasm call to host functions that may fail. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum HostEnvResult { From c3d4e4f8817cb0f1da29d3d474ff9affc8adf65d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 19 Dec 2023 17:59:53 +0000 Subject: [PATCH 038/118] move ERC20 address constructors back to core --- core/src/types/eth_bridge_pool.rs | 16 +++++++++++++--- ethereum_bridge/src/storage/wrapped_erc20.rs | 10 ---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/src/types/eth_bridge_pool.rs b/core/src/types/eth_bridge_pool.rs index 340c0faa3d..a7f9d2e9f6 100644 --- a/core/src/types/eth_bridge_pool.rs +++ b/core/src/types/eth_bridge_pool.rs @@ -8,7 +8,7 @@ use borsh_ext::BorshSerializeExt; use ethabi::token::Token; use serde::{Deserialize, Serialize}; -use crate::ledger::eth_bridge::storage::wrapped_erc20s; +use super::address::InternalAddress; use crate::types::address::Address; use crate::types::eth_abi::Encode; use crate::types::ethereum_events::{ @@ -173,16 +173,26 @@ pub struct PendingTransfer { pub gas_fee: GasFee, } +/// Construct a token address from an ERC20 address. +pub fn erc20_token_address(address: &EthAddress) -> Address { + Address::Internal(InternalAddress::Erc20(*address)) +} + +/// Construct a NUT token address from an ERC20 address. +pub fn erc20_nut_address(address: &EthAddress) -> Address { + Address::Internal(InternalAddress::Nut(*address)) +} + impl PendingTransfer { /// Get a token [`Address`] from this [`PendingTransfer`]. #[inline] pub fn token_address(&self) -> Address { match &self.transfer.kind { TransferToEthereumKind::Erc20 => { - wrapped_erc20s::token(&self.transfer.asset) + erc20_token_address(&self.transfer.asset) } TransferToEthereumKind::Nut => { - wrapped_erc20s::nut(&self.transfer.asset) + erc20_nut_address(&self.transfer.asset) } } } diff --git a/ethereum_bridge/src/storage/wrapped_erc20.rs b/ethereum_bridge/src/storage/wrapped_erc20.rs index e4cf39779a..0a4a576038 100644 --- a/ethereum_bridge/src/storage/wrapped_erc20.rs +++ b/ethereum_bridge/src/storage/wrapped_erc20.rs @@ -9,16 +9,6 @@ use crate::types::token::{ balance_key, minted_balance_key, MINTED_STORAGE_KEY, }; -/// Construct a token address from an ERC20 address. -pub fn token(address: &EthAddress) -> Address { - Address::Internal(InternalAddress::Erc20(*address)) -} - -/// Construct a NUT token address from an ERC20 address. -pub fn nut(address: &EthAddress) -> Address { - Address::Internal(InternalAddress::Nut(*address)) -} - /// Represents the type of a key relating to a wrapped ERC20 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)] pub enum KeyType { From 1aeb9a3598e767edc89e1c0a0d92857957b9fac9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 19 Dec 2023 18:21:24 +0000 Subject: [PATCH 039/118] trans_token: fix build --- Cargo.lock | 1 + trans_token/Cargo.toml | 3 +- trans_token/src/storage.rs | 111 +++++++++++-------------- trans_token/src/storage_key.rs | 143 +++++++++++++++++++++++++++------ 4 files changed, 170 insertions(+), 88 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e3fc573385..a654cfe1f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4688,6 +4688,7 @@ name = "namada_trans_token" version = "0.29.0" dependencies = [ "namada_core", + "namada_storage", ] [[package]] diff --git a/trans_token/Cargo.toml b/trans_token/Cargo.toml index b89ec3142b..2f56b73bb4 100644 --- a/trans_token/Cargo.toml +++ b/trans_token/Cargo.toml @@ -13,4 +13,5 @@ repository.workspace = true version.workspace = true [dependencies] -namada_core = {path = "../core", default-features = false} \ No newline at end of file +namada_core = { path = "../core", default-features = false } +namada_storage = { path = "../storage" } diff --git a/trans_token/src/storage.rs b/trans_token/src/storage.rs index 4d43aebf3c..16a7a40a90 100644 --- a/trans_token/src/storage.rs +++ b/trans_token/src/storage.rs @@ -1,18 +1,20 @@ -use namada_core::ledger::storage_api; +use namada_core::hints; use namada_core::types::address::{Address, InternalAddress}; use namada_core::types::token; +use namada_storage as storage; +use namada_storage::{StorageRead, StorageWrite}; -use super::{StorageRead, StorageWrite}; +use crate::storage_key::*; impl token::Parameters { /// Initialize parameters for the token in storage during the genesis block. - pub fn init_storage( + pub fn init_storage( &self, + storage: &mut S, address: &Address, - wl_storage: &mut ledger_storage::WlStorage, - ) where - DB: ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, - H: ledger_storage::StorageHasher, + ) -> storage::Result<()> + where + S: StorageRead + StorageWrite, { let Self { max_reward_rate: max_rate, @@ -20,31 +22,14 @@ impl token::Parameters { kp_gain_nom, locked_ratio_target: locked_target, } = self; - wl_storage - .write(&masp_last_inflation_key(address), Amount::zero()) - .expect( - "last inflation key for the given asset must be initialized", - ); - wl_storage - .write(&masp_last_locked_ratio_key(address), Dec::zero()) - .expect( - "last locked ratio key for the given asset must be initialized", - ); - wl_storage - .write(&masp_max_reward_rate_key(address), max_rate) - .expect("max reward rate for the given asset must be initialized"); - wl_storage - .write(&masp_locked_ratio_target_key(address), locked_target) - .expect("locked ratio must be initialized"); - wl_storage - .write(&masp_kp_gain_key(address), kp_gain_nom) - .expect("The nominal proportional gain must be initialized"); - wl_storage - .write(&masp_kd_gain_key(address), kd_gain_nom) - .expect("The nominal derivative gain must be initialized"); - wl_storage - .write(&minted_balance_key(address), Amount::zero()) - .expect("The total minted balance key must initialized"); + storage.write(&masp_last_inflation_key(address), Amount::zero())?; + storage.write(&masp_last_locked_ratio_key(address), Dec::zero())?; + storage.write(&masp_max_reward_rate_key(address), max_rate)?; + storage.write(&masp_locked_ratio_target_key(address), locked_target)?; + storage.write(&masp_kp_gain_key(address), kp_gain_nom)?; + storage.write(&masp_kd_gain_key(address), kd_gain_nom)?; + storage.write(&minted_balance_key(address), Amount::zero())?; + Ok(()) } } @@ -53,11 +38,11 @@ pub fn read_balance( storage: &S, token: &Address, owner: &Address, -) -> storage_api::Result +) -> storage::Result where S: StorageRead, { - let key = token::balance_key(token, owner); + let key = balance_key(token, owner); let balance = storage.read::(&key)?.unwrap_or_default(); Ok(balance) } @@ -66,11 +51,11 @@ where pub fn read_total_supply( storage: &S, token: &Address, -) -> storage_api::Result +) -> storage::Result where S: StorageRead, { - let key = token::minted_balance_key(token); + let key = minted_balance_key(token); let balance = storage.read::(&key)?.unwrap_or_default(); Ok(balance) } @@ -81,7 +66,7 @@ where pub fn read_denom( storage: &S, token: &Address, -) -> storage_api::Result> +) -> storage::Result> where S: StorageRead, { @@ -91,12 +76,12 @@ where // NB: always use the equivalent ERC20's smallest // denomination to specify amounts, if we cannot // find a denom in storage - (token::denom_key(&token), true) + (denom_key(&token), true) } Address::Internal(InternalAddress::IbcToken(_)) => { return Ok(Some(0u8.into())); } - token => (token::denom_key(token), false), + token => (denom_key(token), false), }; storage.read(&key).map(|opt_denom| { Some(opt_denom.unwrap_or_else(|| { @@ -107,7 +92,7 @@ where // assume the same behavior as NUTs? maybe this branch // is unreachable, anyway. when would regular tokens // ever not be denominated? - crate::hints::cold(); + hints::cold(); token::NATIVE_MAX_DECIMAL_PLACES.into() } })) @@ -119,11 +104,11 @@ pub fn write_denom( storage: &mut S, token: &Address, denom: token::Denomination, -) -> storage_api::Result<()> +) -> storage::Result<()> where S: StorageRead + StorageWrite, { - let key = token::denom_key(token); + let key = denom_key(token); storage.write(&key, denom) } @@ -136,32 +121,30 @@ pub fn transfer( src: &Address, dest: &Address, amount: token::Amount, -) -> storage_api::Result<()> +) -> storage::Result<()> where S: StorageRead + StorageWrite, { if amount.is_zero() { return Ok(()); } - let src_key = token::balance_key(token, src); + let src_key = balance_key(token, src); let src_balance = read_balance(storage, token, src)?; match src_balance.checked_sub(amount) { Some(new_src_balance) => { - let dest_key = token::balance_key(token, dest); + let dest_key = balance_key(token, dest); let dest_balance = read_balance(storage, token, dest)?; match dest_balance.checked_add(amount) { Some(new_dest_balance) => { storage.write(&src_key, new_src_balance)?; storage.write(&dest_key, new_dest_balance) } - None => Err(storage_api::Error::new_const( + None => Err(storage::Error::new_const( "The transfer would overflow destination balance", )), } } - None => { - Err(storage_api::Error::new_const("Insufficient source balance")) - } + None => Err(storage::Error::new_const("Insufficient source balance")), } } @@ -172,22 +155,22 @@ pub fn credit_tokens( token: &Address, dest: &Address, amount: token::Amount, -) -> storage_api::Result<()> +) -> storage::Result<()> where S: StorageRead + StorageWrite, { - let balance_key = token::balance_key(token, dest); + let balance_key = balance_key(token, dest); let cur_balance = read_balance(storage, token, dest)?; - let new_balance = cur_balance.checked_add(amount).ok_or_else(|| { - storage_api::Error::new_const("Token balance overflow") - })?; + let new_balance = cur_balance + .checked_add(amount) + .ok_or_else(|| storage::Error::new_const("Token balance overflow"))?; - let total_supply_key = token::minted_balance_key(token); + let total_supply_key = minted_balance_key(token); let cur_supply = storage .read::(&total_supply_key)? .unwrap_or_default(); let new_supply = cur_supply.checked_add(amount).ok_or_else(|| { - storage_api::Error::new_const("Token total supply overflow") + storage::Error::new_const("Token total supply overflow") })?; storage.write(&balance_key, new_balance)?; @@ -200,11 +183,11 @@ pub fn burn( token: &Address, source: &Address, amount: token::Amount, -) -> storage_api::Result<()> +) -> storage::Result<()> where S: StorageRead + StorageWrite, { - let key = token::balance_key(token, source); + let key = balance_key(token, source); let balance = read_balance(storage, token, source)?; let amount_to_burn = match balance.checked_sub(amount) { @@ -222,7 +205,7 @@ where let new_total_supply = total_supply.checked_sub(amount_to_burn).unwrap_or_default(); - let total_supply_key = token::minted_balance_key(token); + let total_supply_key = minted_balance_key(token); storage.write(&total_supply_key, new_total_supply) } @@ -231,9 +214,9 @@ pub fn denominated( amount: token::Amount, token: &Address, storage: &impl StorageRead, -) -> storage_api::Result { +) -> storage::Result { let denom = read_denom(storage, token)?.ok_or_else(|| { - storage_api::Error::SimpleMessage( + storage::Error::SimpleMessage( "No denomination found in storage for the given token", ) })?; @@ -247,11 +230,11 @@ pub fn denom_to_amount( denom_amount: DenominatedAmount, token: &Address, storage: &impl StorageRead, -) -> storage_api::Result { +) -> storage::Result { let denom = read_denom(storage, token)?.ok_or_else(|| { - storage_api::Error::SimpleMessage( + storage::Error::SimpleMessage( "No denomination found in storage for the given token", ) })?; - denom_amount.scale(denom).map_err(storage_api::Error::new) + denom_amount.scale(denom).map_err(storage::Error::new) } diff --git a/trans_token/src/storage_key.rs b/trans_token/src/storage_key.rs index 5ff7183353..c38264bebc 100644 --- a/trans_token/src/storage_key.rs +++ b/trans_token/src/storage_key.rs @@ -1,5 +1,8 @@ //! Transparent token storage keys +use namada_core::types::address::{self, Address, InternalAddress}; +use namada_core::types::storage::{self, DbKeySeg, KeySeg}; + /// Key segment for a balance key const BALANCE_STORAGE_KEY: &str = "balance"; /// Key segment for a denomination key @@ -44,61 +47,65 @@ pub fn key_of_token( token_addr: &Address, specific_key: &str, expect_message: &str, -) -> Key { - Key::from(token_addr.to_db_key()) +) -> storage::Key { + storage::Key::from(token_addr.to_db_key()) .push(&specific_key.to_owned()) .expect(expect_message) } /// Obtain a storage key for user's balance. -pub fn balance_key(token_addr: &Address, owner: &Address) -> Key { +pub fn balance_key(token_addr: &Address, owner: &Address) -> storage::Key { balance_prefix(token_addr) .push(&owner.to_db_key()) .expect("Cannot obtain a storage key") } /// Obtain a storage key prefix for all users' balances. -pub fn balance_prefix(token_addr: &Address) -> Key { - Key::from(Address::Internal(InternalAddress::Multitoken).to_db_key()) - .push(&token_addr.to_db_key()) - .expect("Cannot obtain a storage key") - .push(&BALANCE_STORAGE_KEY.to_owned()) - .expect("Cannot obtain a storage key") +pub fn balance_prefix(token_addr: &Address) -> storage::Key { + storage::Key::from( + Address::Internal(InternalAddress::Multitoken).to_db_key(), + ) + .push(&token_addr.to_db_key()) + .expect("Cannot obtain a storage key") + .push(&BALANCE_STORAGE_KEY.to_owned()) + .expect("Cannot obtain a storage key") } /// Obtain a storage key for the multitoken minter. -pub fn minter_key(token_addr: &Address) -> Key { - Key::from(Address::Internal(InternalAddress::Multitoken).to_db_key()) - .push(&token_addr.to_db_key()) - .expect("Cannot obtain a storage key") - .push(&MINTER_STORAGE_KEY.to_owned()) - .expect("Cannot obtain a storage key") +pub fn minter_key(token_addr: &Address) -> storage::Key { + storage::Key::from( + Address::Internal(InternalAddress::Multitoken).to_db_key(), + ) + .push(&token_addr.to_db_key()) + .expect("Cannot obtain a storage key") + .push(&MINTER_STORAGE_KEY.to_owned()) + .expect("Cannot obtain a storage key") } /// Obtain a storage key for the minted multitoken balance. -pub fn minted_balance_key(token_addr: &Address) -> Key { +pub fn minted_balance_key(token_addr: &Address) -> storage::Key { balance_prefix(token_addr) .push(&MINTED_STORAGE_KEY.to_owned()) .expect("Cannot obtain a storage key") } /// Obtain the nominal proportional key for the given token -pub fn masp_kp_gain_key(token_addr: &Address) -> Key { +pub fn masp_kp_gain_key(token_addr: &Address) -> storage::Key { key_of_token(token_addr, MASP_KP_GAIN_KEY, "nominal proproitonal gains") } /// Obtain the nominal derivative key for the given token -pub fn masp_kd_gain_key(token_addr: &Address) -> Key { +pub fn masp_kd_gain_key(token_addr: &Address) -> storage::Key { key_of_token(token_addr, MASP_KD_GAIN_KEY, "nominal proproitonal gains") } /// The max reward rate key for the given token -pub fn masp_max_reward_rate_key(token_addr: &Address) -> Key { +pub fn masp_max_reward_rate_key(token_addr: &Address) -> storage::Key { key_of_token(token_addr, MASP_MAX_REWARD_RATE_KEY, "max reward rate") } /// Obtain the locked target ratio key for the given token -pub fn masp_locked_ratio_target_key(token_addr: &Address) -> Key { +pub fn masp_locked_ratio_target_key(token_addr: &Address) -> storage::Key { key_of_token( token_addr, MASP_LOCKED_RATIO_TARGET_KEY, @@ -106,8 +113,98 @@ pub fn masp_locked_ratio_target_key(token_addr: &Address) -> Key { ) } +/// Check if the given storage key is balance key for the given token. If it is, +/// returns the owner. For minted balances, use [`is_any_minted_balance_key()`]. +pub fn is_balance_key<'a>( + token_addr: &Address, + key: &'a storage::Key, +) -> Option<&'a Address> { + match &key.segments[..] { + [ + DbKeySeg::AddressSeg(addr), + DbKeySeg::AddressSeg(token), + DbKeySeg::StringSeg(balance), + DbKeySeg::AddressSeg(owner), + ] if *addr == Address::Internal(InternalAddress::Multitoken) + && token == token_addr + && balance == BALANCE_STORAGE_KEY => + { + Some(owner) + } + _ => None, + } +} + +/// Check if the given storage key is balance key for unspecified token. If it +/// is, returns the token and owner address. +pub fn is_any_token_balance_key(key: &storage::Key) -> Option<[&Address; 2]> { + match &key.segments[..] { + [ + DbKeySeg::AddressSeg(addr), + DbKeySeg::AddressSeg(token), + DbKeySeg::StringSeg(balance), + DbKeySeg::AddressSeg(owner), + ] if *addr == Address::Internal(InternalAddress::Multitoken) + && balance == BALANCE_STORAGE_KEY => + { + Some([token, owner]) + } + _ => None, + } +} + +/// Obtain a storage key denomination of a token. +pub fn denom_key(token_addr: &Address) -> storage::Key { + storage::Key::from(token_addr.to_db_key()) + .push(&DENOM_STORAGE_KEY.to_owned()) + .expect("Cannot obtain a storage key") +} + +/// Check if the given storage key is a denomination key for the given token. +pub fn is_denom_key(token_addr: &Address, key: &storage::Key) -> bool { + matches!(&key.segments[..], + [ + DbKeySeg::AddressSeg(addr), + .., + DbKeySeg::StringSeg(key), + ] if key == DENOM_STORAGE_KEY && addr == token_addr) +} + +/// Check if the given storage key is a masp key +pub fn is_masp_key(key: &storage::Key) -> bool { + if key.segments.len() >= 2 { + matches!(&key.segments[..2], + [DbKeySeg::AddressSeg(addr), DbKeySeg::StringSeg(key)] + if *addr == address::MASP + && (key == HEAD_TX_KEY + || key.starts_with(TX_KEY_PREFIX) + || key.starts_with(PIN_KEY_PREFIX) + || key.starts_with(MASP_NULLIFIERS_KEY_PREFIX))) + } else { + false + } +} + +/// Check if the given storage key is a masp nullifier key +pub fn is_masp_nullifier_key(key: &storage::Key) -> bool { + matches!(&key.segments[..], + [DbKeySeg::AddressSeg(addr), + DbKeySeg::StringSeg(prefix), + .. + ] if *addr == address::MASP && prefix == MASP_NULLIFIERS_KEY_PREFIX) +} + +/// Obtain the storage key for the last locked ratio of a token +pub fn masp_last_locked_ratio_key(token_address: &Address) -> storage::Key { + key_of_token( + token_address, + MASP_LAST_LOCKED_RATIO_KEY, + "cannot obtain storage key for the last locked ratio", + ) +} + /// Obtain the storage key for the last inflation of a token -pub fn masp_last_inflation_key(token_address: &Address) -> Key { +pub fn masp_last_inflation_key(token_address: &Address) -> storage::Key { key_of_token( token_address, MASP_LAST_INFLATION_KEY, @@ -117,7 +214,7 @@ pub fn masp_last_inflation_key(token_address: &Address) -> Key { /// Check if the given storage key is for a minter of a unspecified token. /// If it is, returns the token. -pub fn is_any_minter_key(key: &Key) -> Option<&Address> { +pub fn is_any_minter_key(key: &storage::Key) -> Option<&Address> { match &key.segments[..] { [ DbKeySeg::AddressSeg(addr), @@ -134,7 +231,7 @@ pub fn is_any_minter_key(key: &Key) -> Option<&Address> { /// Check if the given storage key is for total supply of a unspecified token. /// If it is, returns the token. -pub fn is_any_minted_balance_key(key: &Key) -> Option<&Address> { +pub fn is_any_minted_balance_key(key: &storage::Key) -> Option<&Address> { match &key.segments[..] { [ DbKeySeg::AddressSeg(addr), From dfae90a20036f0ee5fe7d8d7a6a8b19933924573 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 20 Dec 2023 09:03:27 +0000 Subject: [PATCH 040/118] move account related code from namada_storage to namada_account --- account/src/lib.rs | 2 ++ .../src/account.rs => account/src/storage.rs | 0 account/src/storage_key.rs | 24 ++++++++++++++++--- storage/src/key.rs | 20 ---------------- 4 files changed, 23 insertions(+), 23 deletions(-) rename storage/src/account.rs => account/src/storage.rs (100%) delete mode 100644 storage/src/key.rs diff --git a/account/src/lib.rs b/account/src/lib.rs index 3c522aeabb..a574b3d96b 100644 --- a/account/src/lib.rs +++ b/account/src/lib.rs @@ -2,6 +2,7 @@ //! using public key(s) and signature threshold (minimum number of signatures //! needed to authorize an action) stored on-chain. +mod storage; mod storage_key; use std::collections::{BTreeMap, HashMap}; @@ -11,6 +12,7 @@ use namada_core::address::Address; use namada_core::hints; use namada_core::key::{common, RefTo}; use serde::{Deserialize, Serialize}; +pub use storage::*; pub use storage_key::*; #[derive( diff --git a/storage/src/account.rs b/account/src/storage.rs similarity index 100% rename from storage/src/account.rs rename to account/src/storage.rs diff --git a/account/src/storage_key.rs b/account/src/storage_key.rs index 608586a30a..44a348aa43 100644 --- a/account/src/storage_key.rs +++ b/account/src/storage_key.rs @@ -1,7 +1,25 @@ +use namada_core::types::address::Address; +use namada_core::types::key::common; use namada_core::types::storage::{self, DbKeySeg}; use namada_macros::StorageKeys; use namada_storage::collections::lazy_map::LazyMap; use namada_storage::collections::{lazy_map, LazyCollection}; +use namada_storage::{StorageRead, StorageWrite}; + +/// Reveal a PK of an implicit account - the PK is written into the storage +/// of the address derived from the PK. +pub fn reveal_pk( + storage: &mut S, + public_key: &common::PublicKey, +) -> Result<()> +where + S: StorageWrite + StorageRead, +{ + let owner: Address = public_key.into(); + pks_handle(&owner).insert(storage, 0, public_key.clone())?; + + Ok(()) +} /// Storage keys for account. #[derive(StorageKeys)] @@ -13,7 +31,7 @@ struct Keys { /// Obtain a storage key for user's public key. pub fn pks_key_prefix(owner: &Address) -> storage::Key { - Key { + storage::Key { segments: vec![ DbKeySeg::AddressSeg(owner.to_owned()), DbKeySeg::StringSeg(Keys::VALUES.public_keys.to_string()), @@ -58,7 +76,7 @@ pub fn is_threshold_key(key: &storage::Key) -> Option<&Address> { /// Obtain the storage key for a user threshold pub fn threshold_key(owner: &Address) -> storage::Key { - Key { + storage::Key { segments: vec![ DbKeySeg::AddressSeg(owner.to_owned()), DbKeySeg::StringSeg(Keys::VALUES.threshold.to_string()), @@ -68,7 +86,7 @@ pub fn threshold_key(owner: &Address) -> storage::Key { /// Obtain a storage key for user's protocol public key. pub fn protocol_pk_key(owner: &Address) -> storage::Key { - Key { + storage::Key { segments: vec![ DbKeySeg::AddressSeg(owner.to_owned()), DbKeySeg::StringSeg(Keys::VALUES.protocol_public_keys.to_string()), diff --git a/storage/src/key.rs b/storage/src/key.rs deleted file mode 100644 index 69bdd50720..0000000000 --- a/storage/src/key.rs +++ /dev/null @@ -1,20 +0,0 @@ -//! Cryptographic signature keys storage API - -use super::*; -use crate::types::address::Address; -use crate::types::key::*; - -/// Reveal a PK of an implicit account - the PK is written into the storage -/// of the address derived from the PK. -pub fn reveal_pk( - storage: &mut S, - public_key: &common::PublicKey, -) -> Result<()> -where - S: StorageWrite + StorageRead, -{ - let owner: Address = public_key.into(); - pks_handle(&owner).insert(storage, 0, public_key.clone())?; - - Ok(()) -} From 84a91b6a3bf2dfc839e55d3110b3edc44905e866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 20 Dec 2023 10:05:48 +0000 Subject: [PATCH 041/118] core: re-export borsh --- core/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/src/lib.rs b/core/src/lib.rs index 98511ddf2b..7aaad7beca 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -12,6 +12,11 @@ pub mod ledger; pub mod types; pub use {ibc, tendermint, tendermint_proto}; +/// Borsh binary encoding (re-exported) from official crate with custom ext. +pub mod borsh { + pub use borsh::*; + pub use borsh_ext::*; +} // A handy macro for tests #[cfg(test)] From 58b70661b9c252f7c80143de688ba0b9f6d7d7ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 20 Dec 2023 10:07:29 +0000 Subject: [PATCH 042/118] storage: fix build --- Cargo.lock | 3 ++ storage/Cargo.toml | 4 ++ storage/src/collections/lazy_map.rs | 65 ++++++++++------------------- storage/src/collections/lazy_set.rs | 25 ++++++----- storage/src/collections/lazy_vec.rs | 23 +++++----- storage/src/collections/mod.rs | 17 ++++---- storage/src/lib.rs | 16 +++---- storage/src/validation/mod.rs | 9 ++-- 8 files changed, 69 insertions(+), 93 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a654cfe1f3..c8f2886d8b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4620,7 +4620,10 @@ dependencies = [ name = "namada_storage" version = "0.28.1" dependencies = [ + "derivative", + "itertools 0.10.5", "namada_core", + "thiserror", ] [[package]] diff --git a/storage/Cargo.toml b/storage/Cargo.toml index 416cbd4ddd..9e42c77d9a 100644 --- a/storage/Cargo.toml +++ b/storage/Cargo.toml @@ -14,3 +14,7 @@ version.workspace = true [dependencies] namada_core = { path = "../core", default-features = false } + +derivative.workspace = true +itertools.workspace = true +thiserror.workspace = true diff --git a/storage/src/collections/lazy_map.rs b/storage/src/collections/lazy_map.rs index e17de83bef..5dd278abf1 100644 --- a/storage/src/collections/lazy_map.rs +++ b/storage/src/collections/lazy_map.rs @@ -5,15 +5,14 @@ use std::fmt::Debug; use std::hash::Hash; use std::marker::PhantomData; -use borsh::{BorshDeserialize, BorshSerialize}; +use namada_core::borsh::{BorshDeserialize, BorshSerialize}; +use namada_core::types::storage::{self, DbKeySeg, KeySeg}; use thiserror::Error; use super::super::Result; use super::{LazyCollection, ReadError}; -use crate::ledger::storage_api::validation::{self, Data}; -use crate::ledger::storage_api::{self, ResultExt, StorageRead, StorageWrite}; -use crate::ledger::vp_env::VpEnv; -use crate::types::storage::{self, DbKeySeg, KeySeg}; +use crate::validation::{self, Data}; +use crate::{ResultExt, StorageRead, StorageWrite}; /// Subkey corresponding to the data elements of the LazyMap pub const DATA_SUBKEY: &str = "data"; @@ -135,7 +134,7 @@ pub trait Collectable { fn collect_map( &self, storage: &S, - ) -> storage_api::Result; + ) -> crate::Result; } impl Collectable for LazyMap @@ -145,10 +144,7 @@ where { type Collected = BTreeMap; - fn collect_map( - &self, - storage: &S, - ) -> storage_api::Result + fn collect_map(&self, storage: &S) -> crate::Result where S: StorageRead, { @@ -175,10 +171,7 @@ where { type Collected = BTreeMap; - fn collect_map( - &self, - storage: &S, - ) -> storage_api::Result + fn collect_map(&self, storage: &S) -> crate::Result where S: StorageRead, { @@ -191,17 +184,6 @@ where } } -// impl Collectable for V { -// type Collected = Self; - -// fn collect_map( -// &self, -// _storage: &S, -// ) -> storage_api::Result { -// Ok(self.clone()) -// } -// } - /// [`LazyMap`] validation result pub type ValidationResult = std::result::Result; @@ -228,7 +210,7 @@ where fn is_valid_sub_key( &self, key: &storage::Key, - ) -> storage_api::Result> { + ) -> crate::Result> { let suffix = match key.split_prefix(&self.key) { None => { // not matching prefix, irrelevant @@ -298,7 +280,7 @@ where env: &ENV, storage_key: &storage::Key, sub_key: Self::SubKey, - ) -> storage_api::Result> + ) -> crate::Result> where ENV: for<'a> VpEnv<'a>, { @@ -323,7 +305,7 @@ where fn validate_changed_sub_keys( keys: Vec, - ) -> storage_api::Result> { + ) -> crate::Result> { // We have to group the nested sub-keys by the key from this map let mut grouped_by_key: HashMap< K, @@ -378,7 +360,7 @@ where fn is_valid_sub_key( &self, key: &storage::Key, - ) -> storage_api::Result> { + ) -> crate::Result> { let suffix = match key.split_prefix(&self.key) { None => { // not matching prefix, irrelevant @@ -427,7 +409,7 @@ where env: &ENV, storage_key: &storage::Key, sub_key: Self::SubKey, - ) -> storage_api::Result> + ) -> crate::Result> where ENV: for<'a> VpEnv<'a>, { @@ -438,7 +420,7 @@ where fn validate_changed_sub_keys( keys: Vec, - ) -> storage_api::Result> { + ) -> crate::Result> { Ok(keys .into_iter() .map(|change| { @@ -491,7 +473,7 @@ where S: StorageRead, { let prefix = self.get_data_key(key); - let mut iter = storage_api::iter_prefix_bytes(storage, &prefix)?; + let mut iter = crate::iter_prefix_bytes(storage, &prefix)?; Ok(iter.next().is_some()) } @@ -527,7 +509,7 @@ where )>, > + 'iter, > { - let iter = storage_api::iter_prefix_with_filter( + let iter = crate::iter_prefix_with_filter( storage, &self.get_data_prefix(), |key| self.is_data_sub_key(key), @@ -547,7 +529,7 @@ where S: StorageRead, { let mut iter = - storage_api::iter_prefix_bytes(storage, &self.get_data_prefix())?; + crate::iter_prefix_bytes(storage, &self.get_data_prefix())?; Ok(iter.next().is_none()) } } @@ -638,7 +620,7 @@ where S: StorageRead, { let mut iter = - storage_api::iter_prefix_bytes(storage, &self.get_data_prefix())?; + crate::iter_prefix_bytes(storage, &self.get_data_prefix())?; Ok(iter.next().is_none()) } @@ -652,8 +634,7 @@ where where S: StorageRead, { - let iter = - storage_api::iter_prefix_bytes(storage, &self.get_data_prefix())?; + let iter = crate::iter_prefix_bytes(storage, &self.get_data_prefix())?; iter.count().try_into().into_storage_result() } @@ -668,7 +649,7 @@ where &self, storage: &'iter impl StorageRead, ) -> Result> + 'iter> { - let iter = storage_api::iter_prefix(storage, &self.get_data_prefix())?; + let iter = crate::iter_prefix(storage, &self.get_data_prefix())?; Ok(iter.map(|key_val_res| { let (key, val) = key_val_res?; let last_key_seg = key @@ -722,7 +703,7 @@ mod test { use crate::types::address::{self, Address}; #[test] - fn test_lazy_map_basics() -> storage_api::Result<()> { + fn test_lazy_map_basics() -> crate::Result<()> { let mut storage = TestWlStorage::default(); let key = storage::Key::parse("test").unwrap(); @@ -816,7 +797,7 @@ mod test { } #[test] - fn test_lazy_map_with_addr_key() -> storage_api::Result<()> { + fn test_lazy_map_with_addr_key() -> crate::Result<()> { let mut storage = TestWlStorage::default(); let key = storage::Key::parse("test").unwrap(); @@ -864,7 +845,7 @@ mod test { } #[test] - fn test_nested_lazy_map_with_addr_key() -> storage_api::Result<()> { + fn test_nested_lazy_map_with_addr_key() -> crate::Result<()> { let mut storage = TestWlStorage::default(); let key = storage::Key::parse("test").unwrap(); @@ -917,7 +898,7 @@ mod test { } #[test] - fn test_nested_map_basics() -> storage_api::Result<()> { + fn test_nested_map_basics() -> crate::Result<()> { let mut storage = TestWlStorage::default(); let key = storage::Key::parse("testing").unwrap(); diff --git a/storage/src/collections/lazy_set.rs b/storage/src/collections/lazy_set.rs index 9dc6747465..4bd95f7072 100644 --- a/storage/src/collections/lazy_set.rs +++ b/storage/src/collections/lazy_set.rs @@ -3,13 +3,12 @@ use std::fmt::Debug; use std::marker::PhantomData; +use namada_core::types::storage::{self, DbKeySeg, KeySeg}; use thiserror::Error; use super::super::Result; use super::{LazyCollection, ReadError}; -use crate::ledger::storage_api::{self, ResultExt, StorageRead, StorageWrite}; -use crate::ledger::vp_env::VpEnv; -use crate::types::storage::{self, DbKeySeg, KeySeg}; +use crate::{ResultExt, StorageRead, StorageWrite}; /// A lazy set. /// @@ -74,7 +73,7 @@ where fn is_valid_sub_key( &self, key: &storage::Key, - ) -> storage_api::Result> { + ) -> crate::Result> { let suffix = match key.split_prefix(&self.key) { None => { // not matching prefix, irrelevant @@ -115,7 +114,7 @@ where env: &ENV, storage_key: &storage::Key, sub_key: Self::SubKey, - ) -> storage_api::Result> + ) -> crate::Result> where ENV: for<'a> VpEnv<'a>, { @@ -125,7 +124,7 @@ where fn validate_changed_sub_keys( keys: Vec, - ) -> storage_api::Result> { + ) -> crate::Result> { Ok(keys) } } @@ -176,7 +175,7 @@ where { let present = self.contains(storage, &key)?; if present { - return Err(storage_api::Error::new_const("Occupied")); + return Err(crate::Error::new_const("Occupied")); } let key = self.get_key(&key); @@ -204,7 +203,7 @@ where where S: StorageRead, { - let mut iter = storage_api::iter_prefix_bytes(storage, &self.key)?; + let mut iter = crate::iter_prefix_bytes(storage, &self.key)?; Ok(iter.next().is_none()) } @@ -218,7 +217,7 @@ where where S: StorageRead, { - let iter = storage_api::iter_prefix_bytes(storage, &self.key)?; + let iter = crate::iter_prefix_bytes(storage, &self.key)?; iter.count().try_into().into_storage_result() } @@ -232,7 +231,7 @@ where &self, storage: &'iter impl StorageRead, ) -> Result> + 'iter> { - let iter = storage_api::iter_prefix(storage, &self.key)?; + let iter = crate::iter_prefix(storage, &self.key)?; Ok(iter.map(|key_val_res| { let (key, ()) = key_val_res?; let last_key_seg = key @@ -250,7 +249,7 @@ pub fn determine_action( env: &ENV, storage_key: &storage::Key, parsed_key: K, -) -> storage_api::Result>> +) -> crate::Result>> where ENV: for<'a> VpEnv<'a>, { @@ -278,7 +277,7 @@ mod test { use crate::types::address::{self, Address}; #[test] - fn test_lazy_set_basics() -> storage_api::Result<()> { + fn test_lazy_set_basics() -> crate::Result<()> { let mut storage = TestWlStorage::default(); let key = storage::Key::parse("test").unwrap(); @@ -358,7 +357,7 @@ mod test { } #[test] - fn test_lazy_set_with_addr_key() -> storage_api::Result<()> { + fn test_lazy_set_with_addr_key() -> crate::Result<()> { let mut storage = TestWlStorage::default(); let key = storage::Key::parse("test").unwrap(); diff --git a/storage/src/collections/lazy_vec.rs b/storage/src/collections/lazy_vec.rs index d21ca1c515..85d68d0153 100644 --- a/storage/src/collections/lazy_vec.rs +++ b/storage/src/collections/lazy_vec.rs @@ -4,15 +4,14 @@ use std::collections::BTreeSet; use std::fmt::Debug; use std::marker::PhantomData; -use borsh::{BorshDeserialize, BorshSerialize}; +use namada_core::borsh::{BorshDeserialize, BorshSerialize}; +use namada_core::types::storage::{self, DbKeySeg, KeySeg}; use thiserror::Error; use super::super::Result; use super::LazyCollection; -use crate::ledger::storage_api::validation::{self, Data}; -use crate::ledger::storage_api::{self, ResultExt, StorageRead, StorageWrite}; -use crate::ledger::vp_env::VpEnv; -use crate::types::storage::{self, DbKeySeg, KeySeg}; +use crate::validation::{self, Data}; +use crate::{ResultExt, StorageRead, StorageWrite}; /// Subkey pointing to the length of the LazyVec pub const LEN_SUBKEY: &str = "len"; @@ -130,7 +129,7 @@ where fn is_valid_sub_key( &self, key: &storage::Key, - ) -> storage_api::Result> { + ) -> crate::Result> { let suffix = match key.split_prefix(&self.key) { None => { // not matching prefix, irrelevant @@ -184,7 +183,7 @@ where env: &ENV, storage_key: &storage::Key, sub_key: Self::SubKey, - ) -> storage_api::Result> + ) -> crate::Result> where ENV: for<'a> VpEnv<'a>, { @@ -211,7 +210,7 @@ where /// monotonically increasing from zero) fn validate_changed_sub_keys( keys: Vec, - ) -> storage_api::Result> { + ) -> crate::Result> { let mut actions = vec![]; // We need to accumulate some values for what's changed @@ -498,7 +497,7 @@ where &self, storage: &'iter impl StorageRead, ) -> Result> + 'iter> { - let iter = storage_api::iter_prefix(storage, &self.get_data_prefix())?; + let iter = crate::iter_prefix(storage, &self.get_data_prefix())?; Ok(iter.map(|key_val_res| { let (_key, val) = key_val_res?; Ok(val) @@ -514,7 +513,7 @@ mod test { use crate::types::address::{self, Address}; #[test] - fn test_lazy_vec_basics() -> storage_api::Result<()> { + fn test_lazy_vec_basics() -> crate::Result<()> { let mut storage = TestWlStorage::default(); let key = storage::Key::parse("test").unwrap(); @@ -562,7 +561,7 @@ mod test { } #[test] - fn test_lazy_vec_with_addr() -> storage_api::Result<()> { + fn test_lazy_vec_with_addr() -> crate::Result<()> { let mut storage = TestWlStorage::default(); let key = storage::Key::parse("test").unwrap(); @@ -605,7 +604,7 @@ mod test { /// Test iterator on a `LazyVec` nested inside a `LazyMap` #[test] - fn test_nested_lazy_vec_iter() -> storage_api::Result<()> { + fn test_nested_lazy_vec_iter() -> crate::Result<()> { let mut storage = TestWlStorage::default(); let prefix = storage::Key::parse("test").unwrap(); diff --git a/storage/src/collections/mod.rs b/storage/src/collections/mod.rs index ff1136acd5..92d1632e7c 100644 --- a/storage/src/collections/mod.rs +++ b/storage/src/collections/mod.rs @@ -9,8 +9,8 @@ use std::fmt::Debug; -use borsh::BorshDeserialize; use derivative::Derivative; +use namada_core::borsh::BorshDeserialize; use thiserror::Error; pub mod lazy_map; @@ -20,10 +20,7 @@ pub mod lazy_vec; pub use lazy_map::LazyMap; pub use lazy_set::LazySet; pub use lazy_vec::LazyVec; - -use crate::ledger::storage_api; -use crate::ledger::vp_env::VpEnv; -use crate::types::storage; +use namada_core::types::storage; #[allow(missing_docs)] #[derive(Error, Debug)] @@ -73,7 +70,7 @@ pub trait LazyCollection { fn is_valid_sub_key( &self, key: &storage::Key, - ) -> storage_api::Result>; + ) -> crate::Result>; /// Check if the given storage key is a valid data key. /// @@ -91,7 +88,7 @@ pub trait LazyCollection { env: &ENV, storage_key: &storage::Key, sub_key: Self::SubKey, - ) -> storage_api::Result> + ) -> crate::Result> where ENV: for<'a> VpEnv<'a>; @@ -99,7 +96,7 @@ pub trait LazyCollection { /// a vector of `Self::Action`s, if the changes are valid fn validate_changed_sub_keys( keys: Vec, - ) -> storage_api::Result>; + ) -> crate::Result>; /// Accumulate storage changes inside a `ValidationBuilder`. This is /// typically done by the validity predicate while looping through the @@ -114,7 +111,7 @@ pub trait LazyCollection { env: &ENV, builder: &mut Option>, key_changed: &storage::Key, - ) -> storage_api::Result + ) -> crate::Result where ENV: for<'a> VpEnv<'a>, { @@ -135,7 +132,7 @@ pub trait LazyCollection { /// changes storage keys have been processed. fn validate( builder: ValidationBuilder, - ) -> storage_api::Result> { + ) -> crate::Result> { Self::validate_changed_sub_keys(builder.changes) } } diff --git a/storage/src/lib.rs b/storage/src/lib.rs index 452bde4510..ce8b8cfee7 100644 --- a/storage/src/lib.rs +++ b/storage/src/lib.rs @@ -1,18 +1,14 @@ //! The common storage read trait is implemented in the storage, client RPC, tx //! and VPs (both native and WASM). -pub mod account; pub mod collections; mod error; -pub mod key; pub mod validation; -use borsh::{BorshDeserialize, BorshSerialize}; -use borsh_ext::BorshSerializeExt; pub use error::{CustomError, Error, OptionExt, Result, ResultExt}; - -use crate::types::address::Address; -use crate::types::storage::{ +use namada_core::borsh::{BorshDeserialize, BorshSerialize, BorshSerializeExt}; +use namada_core::types::address::Address; +use namada_core::types::storage::{ self, BlockHash, BlockHeight, Epoch, Header, TxIndex, }; @@ -145,7 +141,7 @@ pub trait StorageWrite { /// Iterate items matching the given prefix, ordered by the storage keys. pub fn iter_prefix_bytes<'a>( storage: &'a impl StorageRead, - prefix: &crate::types::storage::Key, + prefix: &storage::Key, ) -> Result)>> + 'a> { let iter = storage.iter_prefix(prefix)?; let iter = itertools::unfold(iter, |iter| { @@ -174,7 +170,7 @@ pub fn iter_prefix_bytes<'a>( /// storage keys. pub fn iter_prefix<'a, T>( storage: &'a impl StorageRead, - prefix: &crate::types::storage::Key, + prefix: &storage::Key, ) -> Result> + 'a> where T: BorshDeserialize, @@ -220,7 +216,7 @@ where /// don't pass the filter. For `iter_prefix_bytes`, `filter` works fine. pub fn iter_prefix_with_filter<'a, T, F>( storage: &'a impl StorageRead, - prefix: &crate::types::storage::Key, + prefix: &storage::Key, filter: F, ) -> Result> + 'a> where diff --git a/storage/src/validation/mod.rs b/storage/src/validation/mod.rs index ca0e779a75..0e745defd1 100644 --- a/storage/src/validation/mod.rs +++ b/storage/src/validation/mod.rs @@ -2,11 +2,8 @@ use std::fmt::Debug; -use borsh::BorshDeserialize; - -use crate::ledger::storage_api; -use crate::ledger::vp_env::VpEnv; -use crate::types::storage; +use namada_core::borsh::BorshDeserialize; +use namada_core::types::storage; /// Data update with prior and posterior state. #[derive(Clone, Debug)] @@ -34,7 +31,7 @@ pub enum Data { pub fn read_data( env: &ENV, key: &storage::Key, -) -> Result>, storage_api::Error> +) -> Result>, crate::Error> where T: BorshDeserialize, ENV: for<'a> VpEnv<'a>, From 7ab93dd5ec87114cc90f8a0104dcae9046e73393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 20 Dec 2023 10:15:24 +0000 Subject: [PATCH 043/118] move collection validation helpers from storage to vp_env --- .../src/validation/mod.rs => vp_env/src/collection_validation.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename storage/src/validation/mod.rs => vp_env/src/collection_validation.rs (100%) diff --git a/storage/src/validation/mod.rs b/vp_env/src/collection_validation.rs similarity index 100% rename from storage/src/validation/mod.rs rename to vp_env/src/collection_validation.rs From a097b72418cc2bbda13cdf7f46a8673483456a09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 20 Dec 2023 10:35:59 +0000 Subject: [PATCH 044/118] mv proto symlink to namada_tx --- {core => tx}/proto | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {core => tx}/proto (100%) diff --git a/core/proto b/tx/proto similarity index 100% rename from core/proto rename to tx/proto From a81a3ad12ed5e152f9e5e9d07ad16385bc152f90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 20 Dec 2023 10:58:15 +0000 Subject: [PATCH 045/118] move collection validation helpers into vp_env crate --- Cargo.lock | 3 +- storage/Cargo.toml | 1 - storage/src/collections/lazy_map.rs | 161 +------------ storage/src/collections/lazy_set.rs | 59 ----- storage/src/collections/lazy_vec.rs | 223 ------------------ storage/src/collections/mod.rs | 76 ------ storage/src/lib.rs | 1 - vp_env/Cargo.toml | 3 + vp_env/src/collection_validation.rs | 51 ---- vp_env/src/collection_validation/lazy_map.rs | 153 ++++++++++++ vp_env/src/collection_validation/lazy_set.rs | 71 ++++++ vp_env/src/collection_validation/lazy_vec.rs | 233 +++++++++++++++++++ vp_env/src/collection_validation/mod.rs | 135 +++++++++++ vp_env/src/lib.rs | 67 +++--- 14 files changed, 633 insertions(+), 604 deletions(-) delete mode 100644 vp_env/src/collection_validation.rs create mode 100644 vp_env/src/collection_validation/lazy_map.rs create mode 100644 vp_env/src/collection_validation/lazy_set.rs create mode 100644 vp_env/src/collection_validation/lazy_vec.rs create mode 100644 vp_env/src/collection_validation/mod.rs diff --git a/Cargo.lock b/Cargo.lock index c8f2886d8b..4e35be4345 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4620,7 +4620,6 @@ dependencies = [ name = "namada_storage" version = "0.28.1" dependencies = [ - "derivative", "itertools 0.10.5", "namada_core", "thiserror", @@ -4749,7 +4748,9 @@ name = "namada_vp_env" version = "0.28.1" dependencies = [ "namada_core", + "namada_storage", "namada_tx", + "thiserror", ] [[package]] diff --git a/storage/Cargo.toml b/storage/Cargo.toml index 9e42c77d9a..0c252d32d8 100644 --- a/storage/Cargo.toml +++ b/storage/Cargo.toml @@ -15,6 +15,5 @@ version.workspace = true [dependencies] namada_core = { path = "../core", default-features = false } -derivative.workspace = true itertools.workspace = true thiserror.workspace = true diff --git a/storage/src/collections/lazy_map.rs b/storage/src/collections/lazy_map.rs index 5dd278abf1..9a0f6a78b0 100644 --- a/storage/src/collections/lazy_map.rs +++ b/storage/src/collections/lazy_map.rs @@ -1,6 +1,6 @@ //! Lazy map. -use std::collections::{BTreeMap, HashMap}; +use std::collections::BTreeMap; use std::fmt::Debug; use std::hash::Hash; use std::marker::PhantomData; @@ -11,7 +11,6 @@ use thiserror::Error; use super::super::Result; use super::{LazyCollection, ReadError}; -use crate::validation::{self, Data}; use crate::{ResultExt, StorageRead, StorageWrite}; /// Subkey corresponding to the data elements of the LazyMap @@ -45,40 +44,6 @@ pub enum SubKey { Data(K), } -/// Possible sub-keys of a [`LazyMap`], together with their [`validation::Data`] -/// that contains prior and posterior state. -#[derive(Clone, Debug)] -pub enum SubKeyWithData { - /// Data sub-key, further sub-keyed by its literal map key - Data(K, Data), -} - -/// Possible actions that can modify a simple (not nested) [`LazyMap`]. This -/// roughly corresponds to the methods that have `StorageWrite` access. -#[derive(Clone, Debug)] -pub enum Action { - /// Insert or update a value `V` at key `K` in a [`LazyMap`]. - Insert(K, V), - /// Remove a value `V` at key `K` from a [`LazyMap`]. - Remove(K, V), - /// Update a value `V` at key `K` in a [`LazyMap`]. - Update { - /// key at which the value is updated - key: K, - /// value before the update - pre: V, - /// value after the update - post: V, - }, -} - -/// Possible actions that can modify a nested [`LazyMap`]. -#[derive(Clone, Debug)] -pub enum NestedAction { - /// Nested collection action `A` at key `K` - At(K, A), -} - /// Possible sub-keys of a nested [`LazyMap`] #[derive(Clone, Debug, PartialEq)] pub enum NestedSubKey { @@ -100,31 +65,6 @@ pub enum ValidationError { InvalidNestedSubKey(storage::Key), } -// pub trait EagerMapFromIter { -// fn from_iter(iter: I) -> Self -// where -// I: IntoIterator; -// } - -// impl EagerMapFromIter for HashMap { -// fn from_iter(iter: I) -> Self -// where -// I: IntoIterator, -// { -// iter.into_iter().collect() -// } -// } - -// impl EagerMapFromIter for BTreeMap { -// fn from_iter(iter: I) -> Self -// where -// K: Eq + Hash + Ord, -// I: IntoIterator, -// { -// iter.into_iter().collect() -// } -// } - /// Trait used to facilitate collection of lazy maps into eager maps pub trait Collectable { /// The type of the value of the lazy map @@ -184,18 +124,12 @@ where } } -/// [`LazyMap`] validation result -pub type ValidationResult = std::result::Result; - impl LazyCollection for LazyMap where K: storage::KeySeg + Clone + Hash + Eq + Debug, V: LazyCollection + Debug, { - type Action = NestedAction::Action>; type SubKey = NestedSubKey::SubKey>; - type SubKeyWithData = - NestedSubKey::SubKeyWithData>; type Value = ::Value; fn open(key: storage::Key) -> Self { @@ -275,66 +209,6 @@ where _ => false, } } - - fn read_sub_key_data( - env: &ENV, - storage_key: &storage::Key, - sub_key: Self::SubKey, - ) -> crate::Result> - where - ENV: for<'a> VpEnv<'a>, - { - let NestedSubKey::Data { - key, - // In here, we just have a nested sub-key without data - nested_sub_key, - } = sub_key; - // Try to read data from the nested collection - let nested_data = ::read_sub_key_data( - env, - storage_key, - nested_sub_key, - )?; - // If found, transform it back into a `NestedSubKey`, but with - // `nested_sub_key` replaced with the one we read - Ok(nested_data.map(|nested_sub_key| NestedSubKey::Data { - key, - nested_sub_key, - })) - } - - fn validate_changed_sub_keys( - keys: Vec, - ) -> crate::Result> { - // We have to group the nested sub-keys by the key from this map - let mut grouped_by_key: HashMap< - K, - Vec<::SubKeyWithData>, - > = HashMap::new(); - for NestedSubKey::Data { - key, - nested_sub_key, - } in keys - { - grouped_by_key - .entry(key) - .or_insert_with(Vec::new) - .push(nested_sub_key); - } - - // Recurse for each sub-keys group - let mut actions = vec![]; - for (key, sub_keys) in grouped_by_key { - let nested_actions = - ::validate_changed_sub_keys(sub_keys)?; - actions.extend( - nested_actions - .into_iter() - .map(|action| NestedAction::At(key.clone(), action)), - ); - } - Ok(actions) - } } impl LazyCollection for LazyMap @@ -342,9 +216,7 @@ where K: storage::KeySeg + Debug, V: BorshDeserialize + BorshSerialize + 'static + Debug, { - type Action = Action; type SubKey = SubKey; - type SubKeyWithData = SubKeyWithData; type Value = V; /// Create or use an existing map with the given storage `key`. @@ -404,37 +276,6 @@ where fn is_data_sub_key(&self, key: &storage::Key) -> bool { matches!(self.is_valid_sub_key(key), Ok(Some(_))) } - - fn read_sub_key_data( - env: &ENV, - storage_key: &storage::Key, - sub_key: Self::SubKey, - ) -> crate::Result> - where - ENV: for<'a> VpEnv<'a>, - { - let SubKey::Data(key) = sub_key; - let data = validation::read_data(env, storage_key)?; - Ok(data.map(|data| SubKeyWithData::Data(key, data))) - } - - fn validate_changed_sub_keys( - keys: Vec, - ) -> crate::Result> { - Ok(keys - .into_iter() - .map(|change| { - let SubKeyWithData::Data(key, data) = change; - match data { - Data::Add { post } => Action::Insert(key, post), - Data::Update { pre, post } => { - Action::Update { key, pre, post } - } - Data::Delete { pre } => Action::Remove(key, pre), - } - }) - .collect()) - } } // Generic `LazyMap` methods that require no bounds on values `V` diff --git a/storage/src/collections/lazy_set.rs b/storage/src/collections/lazy_set.rs index 4bd95f7072..267a14fe5d 100644 --- a/storage/src/collections/lazy_set.rs +++ b/storage/src/collections/lazy_set.rs @@ -33,16 +33,6 @@ pub enum SubKey { Data(K), } -/// Possible actions that can modify a [`LazySet`]. This roughly corresponds to -/// the methods that have `StorageWrite` access. -#[derive(Clone, Debug)] -pub enum Action { - /// Insert a key `K` in a [`LazySet`]. - Insert(K), - /// Remove a key `K` from a [`LazySet`]. - Remove(K), -} - #[allow(missing_docs)] #[derive(Error, Debug)] pub enum ValidationError { @@ -50,16 +40,11 @@ pub enum ValidationError { InvalidSubKey(storage::Key), } -/// [`LazySet`] validation result -pub type ValidationResult = std::result::Result; - impl LazyCollection for LazySet where K: storage::KeySeg + Debug, { - type Action = Action; type SubKey = SubKey; - type SubKeyWithData = Action; type Value = (); /// Create or use an existing map with the given storage `key`. @@ -109,24 +94,6 @@ where fn is_data_sub_key(&self, key: &storage::Key) -> bool { matches!(self.is_valid_sub_key(key), Ok(Some(_))) } - - fn read_sub_key_data( - env: &ENV, - storage_key: &storage::Key, - sub_key: Self::SubKey, - ) -> crate::Result> - where - ENV: for<'a> VpEnv<'a>, - { - let SubKey::Data(key) = sub_key; - determine_action(env, storage_key, key) - } - - fn validate_changed_sub_keys( - keys: Vec, - ) -> crate::Result> { - Ok(keys) - } } // `LazySet` methods @@ -244,32 +211,6 @@ where } } -/// Determine what action was taken from the pre/post state -pub fn determine_action( - env: &ENV, - storage_key: &storage::Key, - parsed_key: K, -) -> crate::Result>> -where - ENV: for<'a> VpEnv<'a>, -{ - let pre = env.read_pre(storage_key)?; - let post = env.read_post(storage_key)?; - Ok(match (pre, post) { - (None, None) => { - // If the key was inserted and then deleted in the same tx, we don't - // need to validate it as it's not visible to any VPs - None - } - (None, Some(())) => Some(Action::Insert(parsed_key)), - (Some(()), None) => Some(Action::Remove(parsed_key)), - (Some(()), Some(())) => { - // Because the value for set is a unit, we can skip this too - None - } - }) -} - #[cfg(test)] mod test { use super::*; diff --git a/storage/src/collections/lazy_vec.rs b/storage/src/collections/lazy_vec.rs index 85d68d0153..6ac076d956 100644 --- a/storage/src/collections/lazy_vec.rs +++ b/storage/src/collections/lazy_vec.rs @@ -1,6 +1,5 @@ //! Lazy dynamically-sized vector. -use std::collections::BTreeSet; use std::fmt::Debug; use std::marker::PhantomData; @@ -10,7 +9,6 @@ use thiserror::Error; use super::super::Result; use super::LazyCollection; -use crate::validation::{self, Data}; use crate::{ResultExt, StorageRead, StorageWrite}; /// Subkey pointing to the length of the LazyVec @@ -42,35 +40,6 @@ pub enum SubKey { Data(Index), } -/// Possible sub-keys of a [`LazyVec`], together with their [`validation::Data`] -/// that contains prior and posterior state. -#[derive(Debug)] -pub enum SubKeyWithData { - /// Length sub-key - Len(Data), - /// Data sub-key, further sub-keyed by its index - Data(Index, Data), -} - -/// Possible actions that can modify a [`LazyVec`]. This roughly corresponds to -/// the methods that have `StorageWrite` access. -#[derive(Clone, Debug)] -pub enum Action { - /// Push a value `T` into a [`LazyVec`] - Push(T), - /// Pop a value `T` from a [`LazyVec`] - Pop(T), - /// Update a value `T` at index from pre to post state in a [`LazyVec`] - Update { - /// index at which the value is updated - index: Index, - /// value before the update - pre: T, - /// value after the update - post: T, - }, -} - #[allow(missing_docs)] #[derive(Error, Debug)] pub enum ValidationError { @@ -104,16 +73,11 @@ pub enum UpdateError { InvalidIndex { index: Index, len: u64 }, } -/// [`LazyVec`] validation result -pub type ValidationResult = std::result::Result; - impl LazyCollection for LazyVec where T: BorshSerialize + BorshDeserialize + 'static + Debug, { - type Action = Action; type SubKey = SubKey; - type SubKeyWithData = SubKeyWithData; type Value = T; /// Create or use an existing vector with the given storage `key`. @@ -178,193 +142,6 @@ where // The `SubKey::Len` is not data sub-key matches!(sub_key, Ok(Some(SubKey::Data(_)))) } - - fn read_sub_key_data( - env: &ENV, - storage_key: &storage::Key, - sub_key: Self::SubKey, - ) -> crate::Result> - where - ENV: for<'a> VpEnv<'a>, - { - let change = match sub_key { - SubKey::Len => { - let data = validation::read_data(env, storage_key)?; - data.map(SubKeyWithData::Len) - } - SubKey::Data(index) => { - let data = validation::read_data(env, storage_key)?; - data.map(|data| SubKeyWithData::Data(index, data)) - } - }; - Ok(change) - } - - /// The validation rules for a [`LazyVec`] are: - /// - A difference in the vector's length must correspond to the - /// difference in how many elements were pushed versus how many elements - /// were popped. - /// - An empty vector must be deleted from storage - /// - In addition, we check that indices of any changes are within an - /// expected range (i.e. the vectors indices should always be - /// monotonically increasing from zero) - fn validate_changed_sub_keys( - keys: Vec, - ) -> crate::Result> { - let mut actions = vec![]; - - // We need to accumulate some values for what's changed - let mut post_gt_pre = false; - let mut len_diff: u64 = 0; - let mut len_pre: u64 = 0; - let mut added = BTreeSet::::default(); - let mut updated = BTreeSet::::default(); - let mut deleted = BTreeSet::::default(); - - for key in keys { - match key { - SubKeyWithData::Len(data) => match data { - Data::Add { post } => { - if post == 0 { - return Err( - ValidationError::EmptyVecShouldBeDeleted, - ) - .into_storage_result(); - } - post_gt_pre = true; - len_diff = post; - } - Data::Update { pre, post } => { - if post == 0 { - return Err( - ValidationError::EmptyVecShouldBeDeleted, - ) - .into_storage_result(); - } - if post > pre { - post_gt_pre = true; - len_diff = post - pre; - } else { - len_diff = pre - post; - } - len_pre = pre; - } - Data::Delete { pre } => { - len_diff = pre; - len_pre = pre; - } - }, - SubKeyWithData::Data(index, data) => match data { - Data::Add { post } => { - actions.push(Action::Push(post)); - added.insert(index); - } - Data::Update { pre, post } => { - actions.push(Action::Update { index, pre, post }); - updated.insert(index); - } - Data::Delete { pre } => { - actions.push(Action::Pop(pre)); - deleted.insert(index); - } - }, - } - } - let added_len: u64 = added - .len() - .try_into() - .map_err(ValidationError::IndexOverflow) - .into_storage_result()?; - let deleted_len: u64 = deleted - .len() - .try_into() - .map_err(ValidationError::IndexOverflow) - .into_storage_result()?; - - if len_diff != 0 - && !(if post_gt_pre { - deleted_len + len_diff == added_len - } else { - added_len + len_diff == deleted_len - }) - { - return Err(ValidationError::InvalidLenDiff).into_storage_result(); - } - - let mut last_added = Option::None; - // Iterate additions in increasing order of indices - for index in added { - if let Some(last_added) = last_added { - // Following additions should be at monotonically increasing - // indices - let expected = last_added + 1; - if expected != index { - return Err(ValidationError::UnexpectedPushIndex { - got: index, - expected, - }) - .into_storage_result(); - } - } else if index != len_pre { - // The first addition must be at the pre length value. - // If something is deleted and a new value is added - // in its place, it will go through `Data::Update` - // instead. - return Err(ValidationError::UnexpectedPushIndex { - got: index, - expected: len_pre, - }) - .into_storage_result(); - } - last_added = Some(index); - } - - let mut last_deleted = Option::None; - // Also iterate deletions in increasing order of indices - for index in deleted { - if let Some(last_added) = last_deleted { - // Following deletions should be at monotonically increasing - // indices - let expected = last_added + 1; - if expected != index { - return Err(ValidationError::UnexpectedPopIndex { - got: index, - expected, - }) - .into_storage_result(); - } - } - last_deleted = Some(index); - } - if let Some(index) = last_deleted { - if len_pre > 0 { - let expected = len_pre - 1; - if index != expected { - // The last deletion must be at the pre length value minus 1 - return Err(ValidationError::UnexpectedPopIndex { - got: index, - expected: len_pre, - }) - .into_storage_result(); - } - } - } - - // And finally iterate updates - for index in updated { - // Update index has to be within the length bounds - let max = len_pre + len_diff; - if index >= max { - return Err(ValidationError::UnexpectedUpdateIndex { - got: index, - max, - }) - .into_storage_result(); - } - } - - Ok(actions) - } } // Generic `LazyVec` methods that require no bounds on values `T` diff --git a/storage/src/collections/mod.rs b/storage/src/collections/mod.rs index 92d1632e7c..8de1f05092 100644 --- a/storage/src/collections/mod.rs +++ b/storage/src/collections/mod.rs @@ -9,7 +9,6 @@ use std::fmt::Debug; -use derivative::Derivative; use namada_core::borsh::BorshDeserialize; use thiserror::Error; @@ -46,16 +45,9 @@ pub struct Nested; /// /// An empty collection must be deleted from storage. pub trait LazyCollection { - /// Actions on the collection determined from changed storage keys by - /// `Self::validate` - type Action; - /// Possible sub-keys in the collection type SubKey: Debug; - /// Possible sub-keys together with the data read from storage - type SubKeyWithData: Debug; - /// A type of a value in the inner-most collection type Value: BorshDeserialize + Debug; @@ -78,72 +70,4 @@ pub trait LazyCollection { /// example for `LazyVec`, which has an additional sub-key for length of the /// vec, only the element data sub-keys would return `true`. fn is_data_sub_key(&self, key: &storage::Key) -> bool; - - /// Try to read and decode the data for each change storage key in prior and - /// posterior state. If there is no value in neither prior or posterior - /// state (which is a possible state when transaction e.g. writes and then - /// deletes one storage key, but it is treated as a no-op as it doesn't - /// affect result of validation), returns `Ok(None)`. - fn read_sub_key_data( - env: &ENV, - storage_key: &storage::Key, - sub_key: Self::SubKey, - ) -> crate::Result> - where - ENV: for<'a> VpEnv<'a>; - - /// Validate changed sub-keys associated with their data and return back - /// a vector of `Self::Action`s, if the changes are valid - fn validate_changed_sub_keys( - keys: Vec, - ) -> crate::Result>; - - /// Accumulate storage changes inside a `ValidationBuilder`. This is - /// typically done by the validity predicate while looping through the - /// changed keys. If the resulting `builder` is not `None`, one must - /// call `fn build()` on it to get the validation result. - /// This function will return `Ok(true)` if the storage key is a valid - /// sub-key of this collection, `Ok(false)` if the storage key doesn't match - /// the prefix of this collection, or error if the prefix matches this - /// collection, but the key itself is not recognized. - fn accumulate( - &self, - env: &ENV, - builder: &mut Option>, - key_changed: &storage::Key, - ) -> crate::Result - where - ENV: for<'a> VpEnv<'a>, - { - if let Some(sub) = self.is_valid_sub_key(key_changed)? { - let change = Self::read_sub_key_data(env, key_changed, sub)?; - if let Some(change) = change { - let builder = - builder.get_or_insert(ValidationBuilder::default()); - builder.changes.push(change); - } - return Ok(true); - } - Ok(false) - } - - /// Execute validation on the validation builder, to be called when - /// `accumulate` instantiates the builder to `Some(_)`, after all the - /// changes storage keys have been processed. - fn validate( - builder: ValidationBuilder, - ) -> crate::Result> { - Self::validate_changed_sub_keys(builder.changes) - } -} - -/// Validation builder from storage changes. The changes can -/// be accumulated with `LazyCollection::accumulate()` and then turned into a -/// list of valid actions on the collection with `LazyCollection::validate()`. -#[derive(Debug, Derivative)] -// https://mcarton.github.io/rust-derivative/latest/Default.html#custom-bound -#[derivative(Default(bound = ""))] -pub struct ValidationBuilder { - /// The accumulator of found changes under the vector - pub changes: Vec, } diff --git a/storage/src/lib.rs b/storage/src/lib.rs index ce8b8cfee7..f4355a32e0 100644 --- a/storage/src/lib.rs +++ b/storage/src/lib.rs @@ -3,7 +3,6 @@ pub mod collections; mod error; -pub mod validation; pub use error::{CustomError, Error, OptionExt, Result, ResultExt}; use namada_core::borsh::{BorshDeserialize, BorshSerialize, BorshSerializeExt}; diff --git a/vp_env/Cargo.toml b/vp_env/Cargo.toml index bd5955fd4f..fcb59d6362 100644 --- a/vp_env/Cargo.toml +++ b/vp_env/Cargo.toml @@ -14,4 +14,7 @@ version.workspace = true [dependencies] namada_core = { path = "../core", default-features = false } +namada_storage = { path = "../storage" } namada_tx = { path = "../tx" } + +thiserror.workspace = true diff --git a/vp_env/src/collection_validation.rs b/vp_env/src/collection_validation.rs deleted file mode 100644 index 0e745defd1..0000000000 --- a/vp_env/src/collection_validation.rs +++ /dev/null @@ -1,51 +0,0 @@ -//! Storage change validation helpers - -use std::fmt::Debug; - -use namada_core::borsh::BorshDeserialize; -use namada_core::types::storage; - -/// Data update with prior and posterior state. -#[derive(Clone, Debug)] -pub enum Data { - /// Newly added value - Add { - /// Posterior state - post: T, - }, - /// Updated value prior and posterior state - Update { - /// Prior state - pre: T, - /// Posterior state - post: T, - }, - /// Deleted value - Delete { - /// Prior state - pre: T, - }, -} - -/// Read the prior and posterior state for the given key. -pub fn read_data( - env: &ENV, - key: &storage::Key, -) -> Result>, crate::Error> -where - T: BorshDeserialize, - ENV: for<'a> VpEnv<'a>, -{ - let pre = env.read_pre(key)?; - let post = env.read_post(key)?; - Ok(match (pre, post) { - (None, None) => { - // If the key was inserted and then deleted in the same tx, we don't - // need to validate it as it's not visible to any VPs - None - } - (None, Some(post)) => Some(Data::Add { post }), - (Some(pre), None) => Some(Data::Delete { pre }), - (Some(pre), Some(post)) => Some(Data::Update { pre, post }), - }) -} diff --git a/vp_env/src/collection_validation/lazy_map.rs b/vp_env/src/collection_validation/lazy_map.rs new file mode 100644 index 0000000000..57020bcaa2 --- /dev/null +++ b/vp_env/src/collection_validation/lazy_map.rs @@ -0,0 +1,153 @@ +//! LazyMap validation helpers + +use std::ops::Index; + +use namada_core::borsh::{BorshDeserialize, BorshSerialize}; +use namada_core::types::storage; +use namada_storage::collection::lazy_map::{LazyMap, NestedSubKey, SubKey}; + +use super::{read_data, Data, LazyCollectionExt, ValidationBuilder}; + +/// Possible sub-keys of a [`LazyMap`], together with their [`validation::Data`] +/// that contains prior and posterior state. +#[derive(Clone, Debug)] +pub enum SubKeyWithData { + /// Data sub-key, further sub-keyed by its literal map key + Data(K, Data), +} + +/// Possible actions that can modify a simple (not nested) [`LazyMap`]. This +/// roughly corresponds to the methods that have `StorageWrite` access. +#[derive(Clone, Debug)] +pub enum Action { + /// Insert or update a value `V` at key `K` in a [`LazyMap`]. + Insert(K, V), + /// Remove a value `V` at key `K` from a [`LazyMap`]. + Remove(K, V), + /// Update a value `V` at key `K` in a [`LazyMap`]. + Update { + /// key at which the value is updated + key: K, + /// value before the update + pre: V, + /// value after the update + post: V, + }, +} + +/// Possible actions that can modify a nested [`LazyMap`]. +#[derive(Clone, Debug)] +pub enum NestedAction { + /// Nested collection action `A` at key `K` + At(K, A), +} + +impl LazyCollectionExt for LazyMap +where + K: storage::KeySeg + Clone + Hash + Eq + Debug, + V: LazyCollectionExt + Debug, +{ + type Action = NestedAction::Action>; + type SubKeyWithData = + NestedSubKey::SubKeyWithData>; + + fn read_sub_key_data( + env: &ENV, + storage_key: &storage::Key, + sub_key: Self::SubKey, + ) -> namada_storage::Result> + where + ENV: for<'a> VpEnv<'a>, + { + let NestedSubKey::Data { + key, + // In here, we just have a nested sub-key without data + nested_sub_key, + } = sub_key; + // Try to read data from the nested collection + let nested_data = ::read_sub_key_data( + env, + storage_key, + nested_sub_key, + )?; + // If found, transform it back into a `NestedSubKey`, but with + // `nested_sub_key` replaced with the one we read + Ok(nested_data.map(|nested_sub_key| NestedSubKey::Data { + key, + nested_sub_key, + })) + } + + fn validate_changed_sub_keys( + keys: Vec, + ) -> namada_storage::Result> { + // We have to group the nested sub-keys by the key from this map + let mut grouped_by_key: HashMap< + K, + Vec<::SubKeyWithData>, + > = HashMap::new(); + for NestedSubKey::Data { + key, + nested_sub_key, + } in keys + { + grouped_by_key + .entry(key) + .or_insert_with(Vec::new) + .push(nested_sub_key); + } + + // Recurse for each sub-keys group + let mut actions = vec![]; + for (key, sub_keys) in grouped_by_key { + let nested_actions = + ::validate_changed_sub_keys(sub_keys)?; + actions.extend( + nested_actions + .into_iter() + .map(|action| NestedAction::At(key.clone(), action)), + ); + } + Ok(actions) + } +} + +impl LazyCollectionExt for LazyMap +where + K: storage::KeySeg + Debug, + V: BorshDeserialize + BorshSerialize + 'static + Debug, +{ + type Action = Action; + type SubKeyWithData = SubKeyWithData; + + fn read_sub_key_data( + env: &ENV, + storage_key: &storage::Key, + sub_key: Self::SubKey, + ) -> namada_storage::Result> + where + ENV: for<'a> VpEnv<'a>, + { + let SubKey::Data(key) = sub_key; + let data = read_data(env, storage_key)?; + Ok(data.map(|data| SubKeyWithData::Data(key, data))) + } + + fn validate_changed_sub_keys( + keys: Vec, + ) -> namada_storage::Result> { + Ok(keys + .into_iter() + .map(|change| { + let SubKeyWithData::Data(key, data) = change; + match data { + Data::Add { post } => Action::Insert(key, post), + Data::Update { pre, post } => { + Action::Update { key, pre, post } + } + Data::Delete { pre } => Action::Remove(key, pre), + } + }) + .collect()) + } +} diff --git a/vp_env/src/collection_validation/lazy_set.rs b/vp_env/src/collection_validation/lazy_set.rs new file mode 100644 index 0000000000..8cfb4e6e2d --- /dev/null +++ b/vp_env/src/collection_validation/lazy_set.rs @@ -0,0 +1,71 @@ +//! LazySet validation helpers + +use std::ops::Index; + +use namada_core::borsh::{BorshDeserialize, BorshSerialize}; +use namada_core::types::storage; +use namada_storage::collection::lazy_set::{LazySet, SubKey}; + +use super::{Data, LazyCollectionExt, ValidationBuilder}; + +/// Possible actions that can modify a [`LazySet`]. This roughly corresponds to +/// the methods that have `StorageWrite` access. +#[derive(Clone, Debug)] +pub enum Action { + /// Insert a key `K` in a [`LazySet`]. + Insert(K), + /// Remove a key `K` from a [`LazySet`]. + Remove(K), +} + +impl LazyCollectionExt for LazySet +where + K: storage::KeySeg + Debug, +{ + type Action = Action; + type SubKeyWithData = Action; + + fn read_sub_key_data( + env: &ENV, + storage_key: &storage::Key, + sub_key: Self::SubKey, + ) -> namada_storage::Result> + where + ENV: for<'a> VpEnv<'a>, + { + let SubKey::Data(key) = sub_key; + determine_action(env, storage_key, key) + } + + fn validate_changed_sub_keys( + keys: Vec, + ) -> namada_storage::Result> { + Ok(keys) + } +} + +/// Determine what action was taken from the pre/post state +pub fn determine_action( + env: &ENV, + storage_key: &storage::Key, + parsed_key: K, +) -> crate::Result>> +where + ENV: for<'a> VpEnv<'a>, +{ + let pre = env.read_pre(storage_key)?; + let post = env.read_post(storage_key)?; + Ok(match (pre, post) { + (None, None) => { + // If the key was inserted and then deleted in the same tx, we don't + // need to validate it as it's not visible to any VPs + None + } + (None, Some(())) => Some(Action::Insert(parsed_key)), + (Some(()), None) => Some(Action::Remove(parsed_key)), + (Some(()), Some(())) => { + // Because the value for set is a unit, we can skip this too + None + } + }) +} diff --git a/vp_env/src/collection_validation/lazy_vec.rs b/vp_env/src/collection_validation/lazy_vec.rs new file mode 100644 index 0000000000..cabad4794d --- /dev/null +++ b/vp_env/src/collection_validation/lazy_vec.rs @@ -0,0 +1,233 @@ +//! LazyVec validation helpers + +use std::ops::Index; + +use namada_core::borsh::{BorshDeserialize, BorshSerialize}; +use namada_core::types::storage; +use namada_storage::collection::lazy_vec::{LazyVec, SubKey}; + +use super::{read_data, Data, LazyCollectionExt, ValidationBuilder}; + +/// Possible sub-keys of a [`LazyVec`], together with their [`validation::Data`] +/// that contains prior and posterior state. +#[derive(Debug)] +pub enum SubKeyWithData { + /// Length sub-key + Len(Data), + /// Data sub-key, further sub-keyed by its index + Data(Index, Data), +} + +/// Possible actions that can modify a [`LazyVec`]. This roughly corresponds to +/// the methods that have `StorageWrite` access. +#[derive(Clone, Debug)] +pub enum Action { + /// Push a value `T` into a [`LazyVec`] + Push(T), + /// Pop a value `T` from a [`LazyVec`] + Pop(T), + /// Update a value `T` at index from pre to post state in a [`LazyVec`] + Update { + /// index at which the value is updated + index: Index, + /// value before the update + pre: T, + /// value after the update + post: T, + }, +} + +impl LazyCollectionExt for LazyVec +where + T: BorshSerialize + BorshDeserialize + 'static + Debug, +{ + type Action = Action; + type SubKeyWithData = SubKeyWithData; + + fn read_sub_key_data( + env: &ENV, + storage_key: &storage::Key, + sub_key: Self::SubKey, + ) -> namada_storage::Result> + where + ENV: for<'a> VpEnv<'a>, + { + let change = match sub_key { + SubKey::Len => { + let data = read_data(env, storage_key)?; + data.map(SubKeyWithData::Len) + } + SubKey::Data(index) => { + let data = read_data(env, storage_key)?; + data.map(|data| SubKeyWithData::Data(index, data)) + } + }; + Ok(change) + } + + /// The validation rules for a [`LazyVec`] are: + /// - A difference in the vector's length must correspond to the + /// difference in how many elements were pushed versus how many elements + /// were popped. + /// - An empty vector must be deleted from storage + /// - In addition, we check that indices of any changes are within an + /// expected range (i.e. the vectors indices should always be + /// monotonically increasing from zero) + fn validate_changed_sub_keys( + keys: Vec, + ) -> namada_storage::Result> { + let mut actions = vec![]; + + // We need to accumulate some values for what's changed + let mut post_gt_pre = false; + let mut len_diff: u64 = 0; + let mut len_pre: u64 = 0; + let mut added = BTreeSet::::default(); + let mut updated = BTreeSet::::default(); + let mut deleted = BTreeSet::::default(); + + for key in keys { + match key { + SubKeyWithData::Len(data) => match data { + Data::Add { post } => { + if post == 0 { + return Err( + ValidationError::EmptyVecShouldBeDeleted, + ) + .into_storage_result(); + } + post_gt_pre = true; + len_diff = post; + } + Data::Update { pre, post } => { + if post == 0 { + return Err( + ValidationError::EmptyVecShouldBeDeleted, + ) + .into_storage_result(); + } + if post > pre { + post_gt_pre = true; + len_diff = post - pre; + } else { + len_diff = pre - post; + } + len_pre = pre; + } + Data::Delete { pre } => { + len_diff = pre; + len_pre = pre; + } + }, + SubKeyWithData::Data(index, data) => match data { + Data::Add { post } => { + actions.push(Action::Push(post)); + added.insert(index); + } + Data::Update { pre, post } => { + actions.push(Action::Update { index, pre, post }); + updated.insert(index); + } + Data::Delete { pre } => { + actions.push(Action::Pop(pre)); + deleted.insert(index); + } + }, + } + } + let added_len: u64 = added + .len() + .try_into() + .map_err(ValidationError::IndexOverflow) + .into_storage_result()?; + let deleted_len: u64 = deleted + .len() + .try_into() + .map_err(ValidationError::IndexOverflow) + .into_storage_result()?; + + if len_diff != 0 + && !(if post_gt_pre { + deleted_len + len_diff == added_len + } else { + added_len + len_diff == deleted_len + }) + { + return Err(ValidationError::InvalidLenDiff).into_storage_result(); + } + + let mut last_added = Option::None; + // Iterate additions in increasing order of indices + for index in added { + if let Some(last_added) = last_added { + // Following additions should be at monotonically increasing + // indices + let expected = last_added + 1; + if expected != index { + return Err(ValidationError::UnexpectedPushIndex { + got: index, + expected, + }) + .into_storage_result(); + } + } else if index != len_pre { + // The first addition must be at the pre length value. + // If something is deleted and a new value is added + // in its place, it will go through `Data::Update` + // instead. + return Err(ValidationError::UnexpectedPushIndex { + got: index, + expected: len_pre, + }) + .into_storage_result(); + } + last_added = Some(index); + } + + let mut last_deleted = Option::None; + // Also iterate deletions in increasing order of indices + for index in deleted { + if let Some(last_added) = last_deleted { + // Following deletions should be at monotonically increasing + // indices + let expected = last_added + 1; + if expected != index { + return Err(ValidationError::UnexpectedPopIndex { + got: index, + expected, + }) + .into_storage_result(); + } + } + last_deleted = Some(index); + } + if let Some(index) = last_deleted { + if len_pre > 0 { + let expected = len_pre - 1; + if index != expected { + // The last deletion must be at the pre length value minus 1 + return Err(ValidationError::UnexpectedPopIndex { + got: index, + expected: len_pre, + }) + .into_storage_result(); + } + } + } + + // And finally iterate updates + for index in updated { + // Update index has to be within the length bounds + let max = len_pre + len_diff; + if index >= max { + return Err(ValidationError::UnexpectedUpdateIndex { + got: index, + max, + }) + .into_storage_result(); + } + } + + Ok(actions) + } +} diff --git a/vp_env/src/collection_validation/mod.rs b/vp_env/src/collection_validation/mod.rs new file mode 100644 index 0000000000..2eef49e8ae --- /dev/null +++ b/vp_env/src/collection_validation/mod.rs @@ -0,0 +1,135 @@ +//! Storage change validation helpers + +pub mod lazy_map; +pub mod lazy_set; +pub mod lazy_vec; + +use std::fmt::Debug; + +use namada_core::borsh::BorshDeserialize; +use namada_core::types::storage; +use namada_storage::collections::LazyCollection; + +use crate::VpEnv; + +/// Validation builder from storage changes. The changes can +/// be accumulated with `LazyCollection::accumulate()` and then turned into a +/// list of valid actions on the collection with `LazyCollection::validate()`. +#[derive(Debug, Derivative)] +// https://mcarton.github.io/rust-derivative/latest/Default.html#custom-bound +#[derivative(Default(bound = ""))] +pub struct ValidationBuilder { + /// The accumulator of found changes under the vector + pub changes: Vec, +} + +/// Data update with prior and posterior state. +#[derive(Clone, Debug)] +pub enum Data { + /// Newly added value + Add { + /// Posterior state + post: T, + }, + /// Updated value prior and posterior state + Update { + /// Prior state + pre: T, + /// Posterior state + post: T, + }, + /// Deleted value + Delete { + /// Prior state + pre: T, + }, +} + +/// Read the prior and posterior state for the given key. +pub fn read_data( + env: &ENV, + key: &storage::Key, +) -> Result>, namada_storage::Error> +where + T: BorshDeserialize, + ENV: for<'a> VpEnv<'a>, +{ + let pre = env.read_pre(key)?; + let post = env.read_post(key)?; + Ok(match (pre, post) { + (None, None) => { + // If the key was inserted and then deleted in the same tx, we don't + // need to validate it as it's not visible to any VPs + None + } + (None, Some(post)) => Some(Data::Add { post }), + (Some(pre), None) => Some(Data::Delete { pre }), + (Some(pre), Some(post)) => Some(Data::Update { pre, post }), + }) +} + +pub trait LazyCollectionExt: LazyCollection { + /// Actions on the collection determined from changed storage keys by + /// `Self::validate` + type Action; + + /// Possible sub-keys together with the data read from storage + type SubKeyWithData: Debug; + + /// Try to read and decode the data for each change storage key in prior and + /// posterior state. If there is no value in neither prior or posterior + /// state (which is a possible state when transaction e.g. writes and then + /// deletes one storage key, but it is treated as a no-op as it doesn't + /// affect result of validation), returns `Ok(None)`. + fn read_sub_key_data( + env: &ENV, + storage_key: &storage::Key, + sub_key: Self::SubKey, + ) -> namada_storage::Result> + where + ENV: for<'a> VpEnv<'a>; + + /// Validate changed sub-keys associated with their data and return back + /// a vector of `Self::Action`s, if the changes are valid + fn validate_changed_sub_keys( + keys: Vec, + ) -> namada_storage::Result>; + + /// Accumulate storage changes inside a `ValidationBuilder`. This is + /// typically done by the validity predicate while looping through the + /// changed keys. If the resulting `builder` is not `None`, one must + /// call `fn build()` on it to get the validation result. + /// This function will return `Ok(true)` if the storage key is a valid + /// sub-key of this collection, `Ok(false)` if the storage key doesn't match + /// the prefix of this collection, or error if the prefix matches this + /// collection, but the key itself is not recognized. + fn accumulate( + &self, + env: &ENV, + builder: &mut Option>, + key_changed: &storage::Key, + ) -> namada_storage::Result + where + ENV: for<'a> VpEnv<'a>, + { + if let Some(sub) = self.is_valid_sub_key(key_changed)? { + let change = Self::read_sub_key_data(env, key_changed, sub)?; + if let Some(change) = change { + let builder = + builder.get_or_insert(ValidationBuilder::default()); + builder.changes.push(change); + } + return Ok(true); + } + Ok(false) + } + + /// Execute validation on the validation builder, to be called when + /// `accumulate` instantiates the builder to `Some(_)`, after all the + /// changes storage keys have been processed. + fn validate( + builder: ValidationBuilder, + ) -> namada_storage::Result> { + Self::validate_changed_sub_keys(builder.changes) + } +} diff --git a/vp_env/src/lib.rs b/vp_env/src/lib.rs index 728e66d9a6..adca8a0ba9 100644 --- a/vp_env/src/lib.rs +++ b/vp_env/src/lib.rs @@ -1,20 +1,20 @@ //! Validity predicate environment contains functions that can be called from //! inside validity predicates. -use borsh::BorshDeserialize; -use masp_primitives::transaction::Transaction; +pub mod collection_validation; -use super::storage_api::{self, OptionExt, ResultExt, StorageRead}; -use crate::proto::Tx; -use crate::types::address::Address; -use crate::types::hash::Hash; -use crate::types::ibc::{ +use masp_primitives::transaction::Transaction; +use namada_core::borsh::BorshDeserialize; +use namada_core::types::address::Address; +use namada_core::types::hash::Hash; +use namada_core::types::ibc::{ get_shielded_transfer, IbcEvent, MsgShieldedTransfer, EVENT_TYPE_PACKET, }; -use crate::types::storage::{ +use namada_core::types::storage::{ BlockHash, BlockHeight, Epoch, Header, Key, TxIndex, }; -use crate::types::token::Transfer; +use namada_core::types::token::Transfer; +use namada_storage::{OptionExt, ResultExt, StorageRead}; /// Validity predicate's environment is available for native VPs and WASM VPs pub trait VpEnv<'view> @@ -44,54 +44,54 @@ where fn read_temp( &self, key: &Key, - ) -> Result, storage_api::Error>; + ) -> Result, namada_storage::Error>; /// Storage read temporary state raw bytes (after tx execution). It will try /// to read from only the write log. fn read_bytes_temp( &self, key: &Key, - ) -> Result>, storage_api::Error>; + ) -> Result>, namada_storage::Error>; /// Getting the chain ID. - fn get_chain_id(&self) -> Result; + fn get_chain_id(&self) -> Result; /// Getting the block height. The height is that of the block to which the /// current transaction is being applied. - fn get_block_height(&self) -> Result; + fn get_block_height(&self) -> Result; /// Getting the block header. fn get_block_header( &self, height: BlockHeight, - ) -> Result, storage_api::Error>; + ) -> Result, namada_storage::Error>; /// Getting the block hash. The height is that of the block to which the /// current transaction is being applied. - fn get_block_hash(&self) -> Result; + fn get_block_hash(&self) -> Result; /// Getting the block epoch. The epoch is that of the block to which the /// current transaction is being applied. - fn get_block_epoch(&self) -> Result; + fn get_block_epoch(&self) -> Result; /// Get the shielded transaction index. - fn get_tx_index(&self) -> Result; + fn get_tx_index(&self) -> Result; /// Get the address of the native token. - fn get_native_token(&self) -> Result; + fn get_native_token(&self) -> Result; /// Get the IBC events. fn get_ibc_events( &self, event_type: String, - ) -> Result, storage_api::Error>; + ) -> Result, namada_storage::Error>; /// Storage prefix iterator, ordered by storage keys. It will try to get an /// iterator from the storage. fn iter_prefix<'iter>( &'iter self, prefix: &Key, - ) -> Result, storage_api::Error>; + ) -> Result, namada_storage::Error>; /// Evaluate a validity predicate with given data. The address, changed /// storage keys and verifiers will have the same values as the input to @@ -103,16 +103,16 @@ where &self, vp_code: Hash, input_data: Tx, - ) -> Result; + ) -> Result; /// Get a tx hash - fn get_tx_code_hash(&self) -> Result, storage_api::Error>; + fn get_tx_code_hash(&self) -> Result, namada_storage::Error>; /// Get the shielded action including the transfer and the masp tx fn get_shielded_action( &self, tx_data: &Tx, - ) -> Result<(Transfer, Transaction), storage_api::Error> { + ) -> Result<(Transfer, Transaction), namada_storage::Error> { let signed = tx_data; let data = signed.data().ok_or_err_msg("No transaction data")?; if let Ok(transfer) = Transfer::try_from_slice(&data) { @@ -137,7 +137,7 @@ where let events = self.get_ibc_events(EVENT_TYPE_PACKET.to_string())?; // The receiving event should be only one in the single IBC transaction let event = events.first().ok_or_else(|| { - storage_api::Error::new_const( + namada_storage::Error::new_const( "No IBC event for the shielded action", ) })?; @@ -145,14 +145,14 @@ where .into_storage_result()? .map(|shielded| (shielded.transfer, shielded.masp_tx)) .ok_or_else(|| { - storage_api::Error::new_const( + namada_storage::Error::new_const( "No shielded transfer in the IBC event", ) }) } /// Charge the provided gas for the current vp - fn charge_gas(&self, used_gas: u64) -> Result<(), storage_api::Error>; + fn charge_gas(&self, used_gas: u64) -> Result<(), namada_storage::Error>; // ---- Methods below have default implementation via `pre/post` ---- @@ -161,7 +161,7 @@ where fn read_pre( &'view self, key: &Key, - ) -> Result, storage_api::Error> { + ) -> Result, namada_storage::Error> { self.pre().read(key) } @@ -170,7 +170,7 @@ where fn read_bytes_pre( &'view self, key: &Key, - ) -> Result>, storage_api::Error> { + ) -> Result>, namada_storage::Error> { self.pre().read_bytes(key) } @@ -180,7 +180,7 @@ where fn read_post( &'view self, key: &Key, - ) -> Result, storage_api::Error> { + ) -> Result, namada_storage::Error> { self.post().read(key) } @@ -190,13 +190,16 @@ where fn read_bytes_post( &'view self, key: &Key, - ) -> Result>, storage_api::Error> { + ) -> Result>, namada_storage::Error> { self.post().read_bytes(key) } /// Storage `has_key` in prior state (before tx execution). It will try to /// read from the storage. - fn has_key_pre(&'view self, key: &Key) -> Result { + fn has_key_pre( + &'view self, + key: &Key, + ) -> Result { self.pre().has_key(key) } @@ -205,7 +208,7 @@ where fn has_key_post( &'view self, key: &Key, - ) -> Result { + ) -> Result { self.post().has_key(key) } } From f6f4333b69bb3d5b6173caee3cbf97e2200f9ba7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 20 Dec 2023 11:16:11 +0000 Subject: [PATCH 046/118] parameters: fix build --- Cargo.lock | 3 +++ parameters/Cargo.toml | 4 ++++ parameters/src/lib.rs | 40 +++++++++++++++++++-------------------- parameters/src/storage.rs | 4 ++-- 4 files changed, 29 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4e35be4345..27018109e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4524,8 +4524,11 @@ dependencies = [ name = "namada_parameters" version = "0.28.1" dependencies = [ + "borsh", "namada_core", + "namada_macros", "namada_storage", + "thiserror", ] [[package]] diff --git a/parameters/Cargo.toml b/parameters/Cargo.toml index 34eeb037fe..1880fddcc7 100644 --- a/parameters/Cargo.toml +++ b/parameters/Cargo.toml @@ -14,4 +14,8 @@ version.workspace = true [dependencies] namada_core = { path = "../core", default-features = false } +namada_macros = { path = "../macros" } namada_storage = { path = "../storage" } + +borsh.workspace = true +thiserror.workspace = true diff --git a/parameters/src/lib.rs b/parameters/src/lib.rs index e5d35ac35b..090a732c4b 100644 --- a/parameters/src/lib.rs +++ b/parameters/src/lib.rs @@ -3,14 +3,14 @@ pub mod storage; use std::collections::BTreeMap; -use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; -use namada_core::storage_api::{self, ResultExt, StorageRead, StorageWrite}; +use namada_core::borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use namada_core::types::address::{Address, InternalAddress}; use namada_core::types::chain::ProposalBytes; use namada_core::types::dec::Dec; use namada_core::types::hash::Hash; use namada_core::types::time::DurationSecs; use namada_core::types::token; +use namada_storage::{self, ResultExt, StorageRead, StorageWrite}; use thiserror::Error; /// The internal address for storage keys representing parameters than @@ -88,9 +88,9 @@ pub struct EpochDuration { #[derive(Error, Debug)] pub enum ReadError { #[error("Storage error: {0}")] - StorageError(namada_state::Error), + StorageError(namada_storage::Error), #[error("Storage type error: {0}")] - StorageTypeError(namada_state::types::Error), + StorageTypeError(namada_core::types::storage::Error), #[error("Protocol parameters are missing, they must be always set")] ParametersMissing, } @@ -99,14 +99,14 @@ pub enum ReadError { #[derive(Error, Debug)] pub enum WriteError { #[error("Storage error: {0}")] - StorageError(namada_state::Error), + StorageError(namada_storage::Error), #[error("Serialize error: {0}")] SerializeError(String), } impl Parameters { /// Initialize parameters in storage in the genesis block. - pub fn init_storage(&self, storage: &mut S) -> storage_api::Result<()> + pub fn init_storage(&self, storage: &mut S) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -217,7 +217,7 @@ impl Parameters { /// Get the max signatures per transactio parameter pub fn max_signatures_per_transaction( storage: &S, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -230,7 +230,7 @@ where pub fn update_max_expected_time_per_block_parameter( storage: &mut S, value: &DurationSecs, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -243,7 +243,7 @@ where pub fn update_vp_whitelist_parameter( storage: &mut S, value: Vec, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -262,7 +262,7 @@ where pub fn update_tx_whitelist_parameter( storage: &mut S, value: Vec, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -281,7 +281,7 @@ where pub fn update_epoch_parameter( storage: &mut S, value: &EpochDuration, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -294,7 +294,7 @@ where pub fn update_epochs_per_year_parameter( storage: &mut S, value: &u64, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -307,7 +307,7 @@ where pub fn update_staked_ratio_parameter( storage: &mut S, value: &Dec, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -320,7 +320,7 @@ where pub fn update_pos_inflation_amount_parameter( storage: &mut S, value: &u64, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -332,7 +332,7 @@ where pub fn update_implicit_vp( storage: &mut S, implicit_vp: &[u8], -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -346,7 +346,7 @@ where pub fn update_max_signature_per_tx( storage: &mut S, value: u8, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -359,7 +359,7 @@ where /// Read the the epoch duration parameter from store pub fn read_epoch_duration_parameter( storage: &S, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead, { @@ -375,7 +375,7 @@ where pub fn read_gas_cost( storage: &S, token: &Address, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -388,7 +388,7 @@ where /// Read all the parameters from storage. Returns the parameters and gas /// cost. -pub fn read(storage: &S) -> storage_api::Result +pub fn read(storage: &S) -> namada_storage::Result where S: StorageRead, { @@ -526,7 +526,7 @@ where pub fn validate_tx_bytes( storage: &S, tx_size: usize, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead, { diff --git a/parameters/src/storage.rs b/parameters/src/storage.rs index 6f065fa854..4fe761d17f 100644 --- a/parameters/src/storage.rs +++ b/parameters/src/storage.rs @@ -1,10 +1,10 @@ //! Parameters storage +use namada_core::types::address::Address; +use namada_core::types::storage::{DbKeySeg, Key}; use namada_macros::StorageKeys; use super::ADDRESS; -use crate::types::address::Address; -use crate::types::storage::{DbKeySeg, Key}; #[derive(StorageKeys)] struct Keys { From 2c31e10cb6674ae2676c5d8621960bcdbc9217bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 20 Dec 2023 11:26:17 +0000 Subject: [PATCH 047/118] gas: fix build --- Cargo.lock | 3 +++ gas/Cargo.toml | 6 +++++- gas/src/lib.rs | 25 +++++++------------------ tx/src/data/wrapper.rs | 11 +++++++++++ 4 files changed, 26 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 27018109e0..c62483330b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4472,8 +4472,11 @@ dependencies = [ name = "namada_gas" version = "0.28.1" dependencies = [ + "borsh", "namada_core", "namada_parameters", + "namada_storage", + "thiserror", ] [[package]] diff --git a/gas/Cargo.toml b/gas/Cargo.toml index c7fa406099..f523e5e38e 100644 --- a/gas/Cargo.toml +++ b/gas/Cargo.toml @@ -14,4 +14,8 @@ version.workspace = true [dependencies] namada_core = { path = "../core", default-features = false } -namada_parameters = { path = "../parameters" } \ No newline at end of file +namada_parameters = { path = "../parameters" } +namada_storage = { path = "../storage" } + +borsh.workspace = true +thiserror.workspace = true diff --git a/gas/src/lib.rs b/gas/src/lib.rs index 22c24ec215..78fd1536af 100644 --- a/gas/src/lib.rs +++ b/gas/src/lib.rs @@ -4,9 +4,8 @@ use std::fmt::Display; use std::ops::Div; -use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; -use namada_core::storage_api::{self, StorageRead}; -use namada_core::types::transaction::wrapper::GasLimit; +use namada_core::borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; +use namada_storage::StorageRead; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -61,16 +60,16 @@ pub const MASP_VERIFY_SHIELDED_TX_GAS: u64 = 62_381_957; pub type Result = std::result::Result; /// Decimal scale of Gas units -const SCALE: u64 = 10_000; +pub const SCALE: u64 = 10_000; /// Helper function to retrieve the `max_block_gas` protocol parameter from /// storage pub fn get_max_block_gas( storage: &impl StorageRead, -) -> std::result::Result { +) -> std::result::Result { storage .read(&namada_parameters::storage::get_max_block_gas_key())? - .ok_or(storage_api::Error::SimpleMessage( + .ok_or(namada_storage::Error::SimpleMessage( "Missing max_block_gas parameter from storage", )) } @@ -152,16 +151,6 @@ impl Display for Gas { } } -impl From for Gas { - // Derive a Gas instance with a sub amount which is exactly a whole amount - // since the limit represents gas in whole units - fn from(value: GasLimit) -> Self { - Self { - sub: u64::from(value) * SCALE, - } - } -} - /// Trait to share gas operations for transactions and validity predicates pub trait GasMetering { /// Add gas cost. It will return error when the @@ -262,9 +251,9 @@ impl GasMetering for TxGasMeter { } impl TxGasMeter { - /// Initialize a new Tx gas meter. Requires the `GasLimit` for the specific + /// Initialize a new Tx gas meter. Requires a gas limit for the specific /// wrapper transaction - pub fn new(tx_gas_limit: GasLimit) -> Self { + pub fn new(tx_gas_limit: impl Into) -> Self { Self { tx_gas_limit: tx_gas_limit.into(), transaction_gas: Gas::default(), diff --git a/tx/src/data/wrapper.rs b/tx/src/data/wrapper.rs index 926185788a..509a34206f 100644 --- a/tx/src/data/wrapper.rs +++ b/tx/src/data/wrapper.rs @@ -10,6 +10,7 @@ pub mod wrapper_tx { use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use borsh_ext::BorshSerializeExt; use masp_primitives::transaction::Transaction; + use namada_gas::Gas; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; use thiserror::Error; @@ -163,6 +164,16 @@ pub mod wrapper_tx { } } + impl From for Gas { + // Derive a Gas instance with a sub amount which is exactly a whole + // amount since the limit represents gas in whole units + fn from(value: GasLimit) -> Self { + Self { + sub: u64::from(value) * gas::SCALE, + } + } + } + /// A transaction with an encrypted payload, an optional shielded pool /// unshielding tx for fee payment and some non-encrypted metadata for /// inclusion and / or verification purposes From ff9455a1bc4e1cc98e2404dccbfe2834854925cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 20 Dec 2023 12:04:11 +0000 Subject: [PATCH 048/118] move vote_ext tx data types from tx to vote_ext crate --- tx/src/data/protocol.rs | 178 ---------------------------------------- vote_ext/src/lib.rs | 178 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+), 178 deletions(-) diff --git a/tx/src/data/protocol.rs b/tx/src/data/protocol.rs index 11940f8617..ba1335b084 100644 --- a/tx/src/data/protocol.rs +++ b/tx/src/data/protocol.rs @@ -53,184 +53,6 @@ impl ProtocolTx { } } -macro_rules! ethereum_tx_data_deserialize_inner { - ($variant:ty) => { - impl TryFrom<&Tx> for $variant { - type Error = TxError; - - fn try_from(tx: &Tx) -> Result { - let tx_data = tx.data().ok_or_else(|| { - TxError::Deserialization( - "Expected protocol tx type associated data".into(), - ) - })?; - Self::try_from_slice(&tx_data) - .map_err(|err| TxError::Deserialization(err.to_string())) - } - } - }; -} - -macro_rules! ethereum_tx_data_declare { - ( - $( #[$outer_attrs:meta] )* - { - $( - $(#[$inner_attrs:meta])* - $variant:ident ($inner_ty:ty) - ),* $(,)? - } - ) => { - $( #[$outer_attrs] )* - pub enum EthereumTxData { - $( - $(#[$inner_attrs])* - $variant ( $inner_ty ) - ),* - } - - /// All the variants of [`EthereumTxData`], stored - /// in a trait. - #[allow(missing_docs)] - pub trait EthereumTxDataVariants { - $( type $variant; )* - } - - impl EthereumTxDataVariants for EthereumTxData { - $( type $variant = $inner_ty; )* - } - - #[allow(missing_docs)] - pub mod ethereum_tx_data_variants { - //! All the variants of [`EthereumTxData`], stored - //! in a module. - use super::*; - - $( pub type $variant = $inner_ty; )* - } - - $( ethereum_tx_data_deserialize_inner!($inner_ty); )* - }; - } - -ethereum_tx_data_declare! { - /// Data associated with Ethereum protocol transactions. - #[derive(Clone, Debug, BorshSerialize, BorshDeserialize, BorshSchema)] - { - /// Ethereum events contained in vote extensions that - /// are compressed before being included on chain - EthereumEvents(ethereum_events::VextDigest), - /// Collection of signatures over the Ethereum bridge - /// pool merkle root and nonce. - BridgePool(bridge_pool_roots::MultiSignedVext), - /// Validator set updates contained in vote extensions - ValidatorSetUpdate(validator_set_update::VextDigest), - /// Ethereum events seen by some validator - EthEventsVext(ethereum_events::SignedVext), - /// Signature over the Ethereum bridge pool merkle root and nonce. - BridgePoolVext(bridge_pool_roots::SignedVext), - /// Validator set update signed by some validator - ValSetUpdateVext(validator_set_update::SignedVext), - } -} - -impl TryFrom<&Tx> for EthereumTxData { - type Error = TxError; - - fn try_from(tx: &Tx) -> Result { - let TxType::Protocol(protocol_tx) = tx.header().tx_type else { - return Err(TxError::Deserialization( - "Expected protocol tx type".into(), - )); - }; - let Some(tx_data) = tx.data() else { - return Err(TxError::Deserialization( - "Expected protocol tx type associated data".into(), - )); - }; - Self::deserialize(&protocol_tx.tx, &tx_data) - } -} - -impl EthereumTxData { - /// Sign transaction Ethereum data and wrap it in a [`Tx`]. - pub fn sign( - &self, - signing_key: &common::SecretKey, - chain_id: ChainId, - ) -> Tx { - let (tx_data, tx_type) = self.serialize(); - let mut outer_tx = - Tx::from_type(TxType::Protocol(Box::new(ProtocolTx { - pk: signing_key.ref_to(), - tx: tx_type, - }))); - outer_tx.header.chain_id = chain_id; - outer_tx.set_data(Data::new(tx_data)); - outer_tx.add_section(Section::Signature(Signature::new( - outer_tx.sechashes(), - [(0, signing_key.clone())].into_iter().collect(), - None, - ))); - outer_tx - } - - /// Serialize Ethereum protocol transaction data. - pub fn serialize(&self) -> (Vec, ProtocolTxType) { - macro_rules! match_of_type { - ( $( $type:ident ),* $(,)?) => { - match self { - $( EthereumTxData::$type(x) => - (x.serialize_to_vec(), ProtocolTxType::$type)),* - } - } - } - match_of_type! { - EthereumEvents, - BridgePool, - ValidatorSetUpdate, - EthEventsVext, - BridgePoolVext, - ValSetUpdateVext, - } - } - - /// Deserialize Ethereum protocol transaction data. - pub fn deserialize( - tx_type: &ProtocolTxType, - data: &[u8], - ) -> Result { - let deserialize: fn(&[u8]) -> _ = match tx_type { - ProtocolTxType::EthereumEvents => |data| { - BorshDeserialize::try_from_slice(data) - .map(EthereumTxData::EthereumEvents) - }, - ProtocolTxType::BridgePool => |data| { - BorshDeserialize::try_from_slice(data) - .map(EthereumTxData::BridgePool) - }, - ProtocolTxType::ValidatorSetUpdate => |data| { - BorshDeserialize::try_from_slice(data) - .map(EthereumTxData::ValidatorSetUpdate) - }, - ProtocolTxType::EthEventsVext => |data| { - BorshDeserialize::try_from_slice(data) - .map(EthereumTxData::EthEventsVext) - }, - ProtocolTxType::BridgePoolVext => |data| { - BorshDeserialize::try_from_slice(data) - .map(EthereumTxData::BridgePoolVext) - }, - ProtocolTxType::ValSetUpdateVext => |data| { - BorshDeserialize::try_from_slice(data) - .map(EthereumTxData::ValSetUpdateVext) - }, - }; - deserialize(data) - .map_err(|err| TxError::Deserialization(err.to_string())) - } -} - #[derive( Clone, Debug, diff --git a/vote_ext/src/lib.rs b/vote_ext/src/lib.rs index f38b4b494e..cb72ba88af 100644 --- a/vote_ext/src/lib.rs +++ b/vote_ext/src/lib.rs @@ -21,3 +21,181 @@ pub struct VoteExtension { /// Vote extension data related with validator set updates. pub validator_set_update: Option, } + +macro_rules! ethereum_tx_data_deserialize_inner { + ($variant:ty) => { + impl TryFrom<&Tx> for $variant { + type Error = TxError; + + fn try_from(tx: &Tx) -> Result { + let tx_data = tx.data().ok_or_else(|| { + TxError::Deserialization( + "Expected protocol tx type associated data".into(), + ) + })?; + Self::try_from_slice(&tx_data) + .map_err(|err| TxError::Deserialization(err.to_string())) + } + } + }; +} + +macro_rules! ethereum_tx_data_declare { + ( + $( #[$outer_attrs:meta] )* + { + $( + $(#[$inner_attrs:meta])* + $variant:ident ($inner_ty:ty) + ),* $(,)? + } + ) => { + $( #[$outer_attrs] )* + pub enum EthereumTxData { + $( + $(#[$inner_attrs])* + $variant ( $inner_ty ) + ),* + } + + /// All the variants of [`EthereumTxData`], stored + /// in a trait. + #[allow(missing_docs)] + pub trait EthereumTxDataVariants { + $( type $variant; )* + } + + impl EthereumTxDataVariants for EthereumTxData { + $( type $variant = $inner_ty; )* + } + + #[allow(missing_docs)] + pub mod ethereum_tx_data_variants { + //! All the variants of [`EthereumTxData`], stored + //! in a module. + use super::*; + + $( pub type $variant = $inner_ty; )* + } + + $( ethereum_tx_data_deserialize_inner!($inner_ty); )* + }; + } + +ethereum_tx_data_declare! { + /// Data associated with Ethereum protocol transactions. + #[derive(Clone, Debug, BorshSerialize, BorshDeserialize, BorshSchema)] + { + /// Ethereum events contained in vote extensions that + /// are compressed before being included on chain + EthereumEvents(ethereum_events::VextDigest), + /// Collection of signatures over the Ethereum bridge + /// pool merkle root and nonce. + BridgePool(bridge_pool_roots::MultiSignedVext), + /// Validator set updates contained in vote extensions + ValidatorSetUpdate(validator_set_update::VextDigest), + /// Ethereum events seen by some validator + EthEventsVext(ethereum_events::SignedVext), + /// Signature over the Ethereum bridge pool merkle root and nonce. + BridgePoolVext(bridge_pool_roots::SignedVext), + /// Validator set update signed by some validator + ValSetUpdateVext(validator_set_update::SignedVext), + } +} + +impl TryFrom<&Tx> for EthereumTxData { + type Error = TxError; + + fn try_from(tx: &Tx) -> Result { + let TxType::Protocol(protocol_tx) = tx.header().tx_type else { + return Err(TxError::Deserialization( + "Expected protocol tx type".into(), + )); + }; + let Some(tx_data) = tx.data() else { + return Err(TxError::Deserialization( + "Expected protocol tx type associated data".into(), + )); + }; + Self::deserialize(&protocol_tx.tx, &tx_data) + } +} + +impl EthereumTxData { + /// Sign transaction Ethereum data and wrap it in a [`Tx`]. + pub fn sign( + &self, + signing_key: &common::SecretKey, + chain_id: ChainId, + ) -> Tx { + let (tx_data, tx_type) = self.serialize(); + let mut outer_tx = + Tx::from_type(TxType::Protocol(Box::new(ProtocolTx { + pk: signing_key.ref_to(), + tx: tx_type, + }))); + outer_tx.header.chain_id = chain_id; + outer_tx.set_data(Data::new(tx_data)); + outer_tx.add_section(Section::Signature(Signature::new( + outer_tx.sechashes(), + [(0, signing_key.clone())].into_iter().collect(), + None, + ))); + outer_tx + } + + /// Serialize Ethereum protocol transaction data. + pub fn serialize(&self) -> (Vec, ProtocolTxType) { + macro_rules! match_of_type { + ( $( $type:ident ),* $(,)?) => { + match self { + $( EthereumTxData::$type(x) => + (x.serialize_to_vec(), ProtocolTxType::$type)),* + } + } + } + match_of_type! { + EthereumEvents, + BridgePool, + ValidatorSetUpdate, + EthEventsVext, + BridgePoolVext, + ValSetUpdateVext, + } + } + + /// Deserialize Ethereum protocol transaction data. + pub fn deserialize( + tx_type: &ProtocolTxType, + data: &[u8], + ) -> Result { + let deserialize: fn(&[u8]) -> _ = match tx_type { + ProtocolTxType::EthereumEvents => |data| { + BorshDeserialize::try_from_slice(data) + .map(EthereumTxData::EthereumEvents) + }, + ProtocolTxType::BridgePool => |data| { + BorshDeserialize::try_from_slice(data) + .map(EthereumTxData::BridgePool) + }, + ProtocolTxType::ValidatorSetUpdate => |data| { + BorshDeserialize::try_from_slice(data) + .map(EthereumTxData::ValidatorSetUpdate) + }, + ProtocolTxType::EthEventsVext => |data| { + BorshDeserialize::try_from_slice(data) + .map(EthereumTxData::EthEventsVext) + }, + ProtocolTxType::BridgePoolVext => |data| { + BorshDeserialize::try_from_slice(data) + .map(EthereumTxData::BridgePoolVext) + }, + ProtocolTxType::ValSetUpdateVext => |data| { + BorshDeserialize::try_from_slice(data) + .map(EthereumTxData::ValSetUpdateVext) + }, + }; + deserialize(data) + .map_err(|err| TxError::Deserialization(err.to_string())) + } +} From 7a3edd16dc884175e80809651bee69f7e384a221 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 20 Dec 2023 12:19:10 +0000 Subject: [PATCH 049/118] tx: wip fix build --- Cargo.lock | 11 ++- account/src/lib.rs | 71 ----------------- core/src/types/governance.rs | 24 ++++++ core/src/types/key/mod.rs | 74 ++++++++++++++++- gas/src/lib.rs | 2 +- governance/src/storage/proposal.rs | 25 +----- tx/Cargo.toml | 12 ++- tx/build.rs | 2 +- tx/src/data/account.rs | 15 ++-- tx/src/data/decrypted.rs | 5 +- tx/src/data/eval_vp.rs | 4 +- tx/src/data/governance.rs | 27 +++---- tx/src/data/mod.rs | 33 ++++---- tx/src/data/pgf.rs | 14 ++-- tx/src/data/pos.rs | 19 +++-- tx/src/data/protocol.rs | 15 ++-- tx/src/data/wrapper.rs | 28 +++---- tx/src/lib.rs | 5 +- tx/src/proto/mod.rs | 3 + tx/src/types.rs | 122 ++++++++++++++++------------- 20 files changed, 269 insertions(+), 242 deletions(-) create mode 100644 tx/src/proto/mod.rs diff --git a/Cargo.lock b/Cargo.lock index c62483330b..41130a65e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4703,9 +4703,18 @@ dependencies = [ name = "namada_tx" version = "0.28.1" dependencies = [ + "ark-bls12-381", + "borsh", + "data-encoding", + "masp_primitives", "namada_core", "namada_gas", - "namada_storage", + "prost 0.12.3", + "prost-types 0.12.3", + "serde 1.0.193", + "serde_json", + "sha2 0.9.9", + "thiserror", "tonic-build", ] diff --git a/account/src/lib.rs b/account/src/lib.rs index a574b3d96b..5a29aa3616 100644 --- a/account/src/lib.rs +++ b/account/src/lib.rs @@ -45,74 +45,3 @@ impl Account { self.public_keys_map.get_index_from_public_key(public_key) } } - -#[derive( - Debug, - Clone, - BorshSerialize, - BorshDeserialize, - Serialize, - Deserialize, - Default, -)] -/// Holds the public key map data as a bimap for efficient querying -pub struct AccountPublicKeysMap { - /// Hashmap from public key to index - pub pk_to_idx: HashMap, - /// Hashmap from index key to public key - pub idx_to_pk: HashMap, -} - -impl FromIterator for AccountPublicKeysMap { - fn from_iter>(iter: T) -> Self { - let mut pk_to_idx = HashMap::new(); - let mut idx_to_pk = HashMap::new(); - - for (index, public_key) in iter.into_iter().enumerate() { - if hints::unlikely(index > u8::MAX as usize) { - panic!( - "Only up to 255 signers are allowed in a multisig account" - ); - } - pk_to_idx.insert(public_key.to_owned(), index as u8); - idx_to_pk.insert(index as u8, public_key.to_owned()); - } - - Self { - pk_to_idx, - idx_to_pk, - } - } -} - -impl AccountPublicKeysMap { - /// Retrieve a public key from the index - pub fn get_public_key_from_index( - &self, - index: u8, - ) -> Option { - self.idx_to_pk.get(&index).cloned() - } - - /// Retrieve the index of a public key - pub fn get_index_from_public_key( - &self, - public_key: &common::PublicKey, - ) -> Option { - self.pk_to_idx.get(public_key).cloned() - } - - /// Index the given set of secret keys - pub fn index_secret_keys( - &self, - secret_keys: Vec, - ) -> BTreeMap { - secret_keys - .into_iter() - .filter_map(|secret_key: common::SecretKey| { - self.get_index_from_public_key(&secret_key.ref_to()) - .map(|index| (index, secret_key)) - }) - .collect() - } -} diff --git a/core/src/types/governance.rs b/core/src/types/governance.rs index 7bb2bc16f8..d260476e91 100644 --- a/core/src/types/governance.rs +++ b/core/src/types/governance.rs @@ -4,6 +4,7 @@ use std::collections::{BTreeMap, HashSet}; use borsh::{BorshDeserialize, BorshSerialize}; use serde::{Deserialize, Serialize}; +use thiserror::Error; use crate::types::address::Address; use crate::types::hash::Hash; @@ -141,6 +142,29 @@ pub struct StewardsUpdate { pub remove: Vec
, } +#[allow(missing_docs)] +#[derive(Debug, Error)] +pub enum ProposalTypeError { + #[error("Invalid proposal type.")] + InvalidProposalType, +} + +impl TryFrom for HashSet> { + type Error = ProposalTypeError; + + fn try_from(value: StewardsUpdate) -> Result { + let mut data = HashSet::default(); + + if value.add.is_some() { + data.insert(AddRemove::Add(value.add.unwrap())); + } + for steward in value.remove { + data.insert(AddRemove::Remove(steward)); + } + Ok(data) + } +} + /// An add or remove action for PGF #[derive( Debug, diff --git a/core/src/types/key/mod.rs b/core/src/types/key/mod.rs index 985bb84c40..eb7baac984 100644 --- a/core/src/types/key/mod.rs +++ b/core/src/types/key/mod.rs @@ -4,6 +4,7 @@ pub mod common; pub mod ed25519; pub mod secp256k1; +use std::collections::{BTreeMap, HashMap}; use std::fmt::{Debug, Display}; use std::hash::Hash; use std::str::FromStr; @@ -17,6 +18,7 @@ use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; use thiserror::Error; +use crate::hints; use crate::types::address; use crate::types::hash::{KeccakHasher, Sha256Hasher, StorageHasher}; use crate::types::keccak::{keccak_hash, KeccakHash}; @@ -73,8 +75,78 @@ pub enum ParseSecretKeyError { MismatchedScheme, } -/// A value-to-value conversion that consumes the input value. +#[derive( + Debug, + Clone, + BorshSerialize, + BorshDeserialize, + Serialize, + Deserialize, + Default, +)] +/// Holds the public key map data as a bimap for efficient quering +pub struct AccountPublicKeysMap { + /// Hashmap from public key to index + pub pk_to_idx: HashMap, + /// Hashmap from index key to public key + pub idx_to_pk: HashMap, +} + +impl FromIterator for AccountPublicKeysMap { + fn from_iter>(iter: T) -> Self { + let mut pk_to_idx = HashMap::new(); + let mut idx_to_pk = HashMap::new(); + + for (index, public_key) in iter.into_iter().enumerate() { + if hints::unlikely(index > u8::MAX as usize) { + panic!( + "Only up to 255 signers are allowed in a multisig account" + ); + } + pk_to_idx.insert(public_key.to_owned(), index as u8); + idx_to_pk.insert(index as u8, public_key.to_owned()); + } + + Self { + pk_to_idx, + idx_to_pk, + } + } +} +impl AccountPublicKeysMap { + /// Retrive a public key from the index + pub fn get_public_key_from_index( + &self, + index: u8, + ) -> Option { + self.idx_to_pk.get(&index).cloned() + } + + /// Retrive the index of a public key + pub fn get_index_from_public_key( + &self, + public_key: &common::PublicKey, + ) -> Option { + self.pk_to_idx.get(public_key).cloned() + } + + /// Index the given set of secret keys + pub fn index_secret_keys( + &self, + secret_keys: Vec, + ) -> BTreeMap { + secret_keys + .into_iter() + .filter_map(|secret_key: common::SecretKey| { + self.get_index_from_public_key(&secret_key.ref_to()) + .map(|index| (index, secret_key)) + }) + .collect() + } +} + +/// A value-to-value conversion that consumes the input value. pub trait RefTo { /// Performs the conversion. fn ref_to(&self) -> T; diff --git a/gas/src/lib.rs b/gas/src/lib.rs index 78fd1536af..d3d757805d 100644 --- a/gas/src/lib.rs +++ b/gas/src/lib.rs @@ -60,7 +60,7 @@ pub const MASP_VERIFY_SHIELDED_TX_GAS: u64 = 62_381_957; pub type Result = std::result::Result; /// Decimal scale of Gas units -pub const SCALE: u64 = 10_000; +const SCALE: u64 = 10_000; /// Helper function to retrieve the `max_block_gas` protocol parameter from /// storage diff --git a/governance/src/storage/proposal.rs b/governance/src/storage/proposal.rs index 2c894e52b3..98aaedbfa3 100644 --- a/governance/src/storage/proposal.rs +++ b/governance/src/storage/proposal.rs @@ -14,14 +14,7 @@ use crate::cli::onchain::{ }; use crate::utils::{ProposalStatus, TallyType}; -#[allow(missing_docs)] -#[derive(Debug, Error)] -pub enum ProposalTypeError { - #[error("Invalid proposal type.")] - InvalidProposalType, -} - -/// Storage structure for pgf fundings +/// Storage struture for pgf fundings #[derive( Debug, Clone, @@ -64,22 +57,6 @@ impl Display for ProposalType { } } -impl TryFrom for HashSet> { - type Error = ProposalTypeError; - - fn try_from(value: StewardsUpdate) -> Result { - let mut data = HashSet::default(); - - if value.add.is_some() { - data.insert(AddRemove::Add(value.add.unwrap())); - } - for steward in value.remove { - data.insert(AddRemove::Remove(steward)); - } - Ok(data) - } -} - impl TryFrom for AddRemove
{ type Error = ProposalTypeError; diff --git a/tx/Cargo.toml b/tx/Cargo.toml index ed33616abb..8aa8cbdf0a 100644 --- a/tx/Cargo.toml +++ b/tx/Cargo.toml @@ -15,7 +15,17 @@ version.workspace = true [dependencies] namada_core = { path = "../core", default-features = false } namada_gas = { path = "../gas" } -namada_storage = { path = "../storage" } + +ark-bls12-381.workspace = true +borsh.workspace = true +data-encoding.workspace = true +masp_primitives.workspace = true +prost.workspace = true +prost-types.workspace = true +serde.workspace = true +serde_json.workspace = true +sha2.workspace = true +thiserror.workspace = true [build-dependencies] tonic-build.workspace = true diff --git a/tx/build.rs b/tx/build.rs index ae567b8bdc..ff6f1f37ca 100644 --- a/tx/build.rs +++ b/tx/build.rs @@ -1,6 +1,6 @@ use std::{env, str}; -/// Path to the .proto source files, relative to `core` directory +/// Path to the .proto source files, relative to `tx` crate directory const PROTO_SRC: &str = "./proto"; fn main() { diff --git a/tx/src/data/account.rs b/tx/src/data/account.rs index 3e4271f6f0..606cf4c63c 100644 --- a/tx/src/data/account.rs +++ b/tx/src/data/account.rs @@ -1,10 +1,9 @@ -use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; +use namada_core::borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; +use namada_core::types::address::Address; +use namada_core::types::hash::Hash; +use namada_core::types::key::common; use serde::{Deserialize, Serialize}; -use crate::types::address::Address; -use crate::types::hash::Hash; -use crate::types::key::common; - /// A tx data type to initialize a new established account #[derive( Debug, @@ -54,13 +53,13 @@ pub struct UpdateAccount { #[cfg(any(test, feature = "testing"))] /// Tests and strategies for accounts pub mod tests { + use namada_core::types::address::testing::arb_non_internal_address; + use namada_core::types::hash::testing::arb_hash; + use namada_core::types::key::testing::arb_common_pk; use proptest::prelude::Just; use proptest::{collection, option, prop_compose}; use super::*; - use crate::types::address::testing::arb_non_internal_address; - use crate::types::hash::testing::arb_hash; - use crate::types::key::testing::arb_common_pk; prop_compose! { /// Generate an account initialization diff --git a/tx/src/data/decrypted.rs b/tx/src/data/decrypted.rs index 55bf01d9dc..d764f03d00 100644 --- a/tx/src/data/decrypted.rs +++ b/tx/src/data/decrypted.rs @@ -3,8 +3,9 @@ pub use ark_bls12_381::Bls12_381 as EllipticCurve; /// Integration of Ferveo cryptographic primitives to enable decrypting txs. /// *Not wasm compatible* pub mod decrypted_tx { - use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; - use borsh_ext::BorshSerializeExt; + use namada_core::borsh::{ + BorshDeserialize, BorshSchema, BorshSerialize, BorshSerializeExt, + }; use sha2::{Digest, Sha256}; #[derive( diff --git a/tx/src/data/eval_vp.rs b/tx/src/data/eval_vp.rs index 387a868ab8..e248018ad5 100644 --- a/tx/src/data/eval_vp.rs +++ b/tx/src/data/eval_vp.rs @@ -1,8 +1,8 @@ -use borsh::{BorshDeserialize, BorshSerialize}; +use namada_core::borsh::{BorshDeserialize, BorshSerialize}; use namada_core::types::hash::Hash; use serde::{Deserialize, Serialize}; -use crate::proto::Tx; +use crate::Tx; /// A validity predicate with an input that is intended to be invoked via `eval` /// host function. diff --git a/tx/src/data/governance.rs b/tx/src/data/governance.rs index f723ab6d1b..f2c63a4345 100644 --- a/tx/src/data/governance.rs +++ b/tx/src/data/governance.rs @@ -1,16 +1,15 @@ use std::collections::HashSet; -use borsh::{BorshDeserialize, BorshSerialize}; -use serde::{Deserialize, Serialize}; -use thiserror::Error; - -use crate::types::address::Address; -use crate::types::governance::{ +use namada_core::borsh::{BorshDeserialize, BorshSerialize}; +use namada_core::types::address::Address; +use namada_core::types::governance::{ AddRemove, DefaultProposal, PGFAction, PGFTarget, PgfFundingProposal, PgfStewardProposal, ProposalType, StorageProposalVote, }; -use crate::types::hash::Hash; -use crate::types::storage::Epoch; +use namada_core::types::hash::Hash; +use namada_core::types::storage::Epoch; +use serde::{Deserialize, Serialize}; +use thiserror::Error; #[allow(missing_docs)] #[derive(Debug, Error)] @@ -165,15 +164,15 @@ impl TryFrom for InitProposalData { #[cfg(any(test, feature = "testing"))] /// Tests and strategies for governance pub mod tests { + use namada_core::types::address::testing::arb_non_internal_address; + use namada_core::types::governance::testing::{ + arb_proposal_type, arb_proposal_vote, + }; + use namada_core::types::hash::testing::arb_hash; + use namada_core::types::storage::testing::arb_epoch; use proptest::{collection, prop_compose}; use super::*; - use crate::types::address::testing::arb_non_internal_address; - use crate::types::governance::testing::{ - arb_proposal_type, arb_proposal_vote, - }; - use crate::types::hash::testing::arb_hash; - use crate::types::storage::testing::arb_epoch; prop_compose! { /// Generate a proposal initialization diff --git a/tx/src/data/mod.rs b/tx/src/data/mod.rs index 4a95bc63c5..4b408bb850 100644 --- a/tx/src/data/mod.rs +++ b/tx/src/data/mod.rs @@ -21,22 +21,23 @@ use std::collections::BTreeSet; use std::fmt::{self, Display}; use std::str::FromStr; -use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; -use borsh_ext::BorshSerializeExt; pub use decrypted::*; +use namada_core::borsh::{ + BorshDeserialize, BorshSchema, BorshSerialize, BorshSerializeExt, +}; +use namada_core::types::address::Address; +use namada_core::types::ethereum_structs::EthBridgeEvent; +use namada_core::types::hash::Hash; +use namada_core::types::ibc::IbcEvent; +use namada_core::types::storage; +use namada_gas::{Gas, VpsGas}; use num_derive::{FromPrimitive, ToPrimitive}; use num_traits::{FromPrimitive, ToPrimitive}; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; pub use wrapper::*; -use crate::ledger::gas::{Gas, VpsGas}; -use crate::types::address::Address; -use crate::types::ethereum_structs::EthBridgeEvent; -use crate::types::hash::Hash; -use crate::types::ibc::IbcEvent; -use crate::types::storage; -use crate::types::transaction::protocol::ProtocolTx; +use crate::data::protocol::ProtocolTx; /// The different result codes that the ledger may send back to a client /// indicating the status of their submitted tx. @@ -343,12 +344,13 @@ impl TxSentinel { #[cfg(test)] mod test_process_tx { + use namada_core::types::address::nam; + use namada_core::types::key::*; + use namada_core::types::storage::Epoch; + use namada_core::types::token::{Amount, DenominatedAmount}; + use super::*; use crate::proto::{Code, Data, Section, Signature, Tx, TxError}; - use crate::types::address::nam; - use crate::types::key::*; - use crate::types::storage::Epoch; - use crate::types::token::{Amount, DenominatedAmount}; fn gen_keypair() -> common::SecretKey { use rand::prelude::ThreadRng; @@ -506,8 +508,9 @@ fn test_process_tx_decrypted_unsigned() { /// signature #[test] fn test_process_tx_decrypted_signed() { + use namada_core::types::key::*; + use crate::proto::{Code, Data, Section, Signature, Tx}; - use crate::types::key::*; fn gen_keypair() -> common::SecretKey { use rand::prelude::ThreadRng; @@ -517,7 +520,7 @@ fn test_process_tx_decrypted_signed() { ed25519::SigScheme::generate(&mut rng).try_to_sk().unwrap() } - use crate::types::key::Signature as S; + use namada_core::types::key::Signature as S; let mut decrypted = Tx::from_type(TxType::Decrypted(DecryptedTx::Decrypted)); // Invalid signed data diff --git a/tx/src/data/pgf.rs b/tx/src/data/pgf.rs index 02ee320253..1151aa18a7 100644 --- a/tx/src/data/pgf.rs +++ b/tx/src/data/pgf.rs @@ -1,12 +1,11 @@ use std::collections::HashMap; -use borsh::{BorshDeserialize, BorshSerialize}; +use namada_core::borsh::{BorshDeserialize, BorshSerialize}; +use namada_core::types::address::Address; +use namada_core::types::dec::Dec; use serde::{Deserialize, Serialize}; use thiserror::Error; -use crate::types::address::Address; -use crate::types::dec::Dec; - #[allow(missing_docs)] #[derive(Debug, Error)] pub enum PgfError { @@ -34,12 +33,11 @@ pub struct UpdateStewardCommission { #[cfg(any(test, feature = "testing"))] /// Tests and strategies for PGF pub mod tests { + use namada_core::types::address::testing::arb_non_internal_address; + use namada_core::types::dec::testing::arb_dec; + use namada_core::types::transaction::pgf::UpdateStewardCommission; use proptest::{collection, prop_compose}; - use crate::types::address::testing::arb_non_internal_address; - use crate::types::dec::testing::arb_dec; - use crate::types::transaction::pgf::UpdateStewardCommission; - prop_compose! { /// Generate an arbitraary steward commission update pub fn arb_update_steward_commission()( diff --git a/tx/src/data/pos.rs b/tx/src/data/pos.rs index 1ac5fc8a38..f4699bf201 100644 --- a/tx/src/data/pos.rs +++ b/tx/src/data/pos.rs @@ -1,13 +1,12 @@ //! Types used for PoS system transactions -use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; +use namada_core::borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; +use namada_core::types::address::Address; +use namada_core::types::dec::Dec; +use namada_core::types::key::{common, secp256k1}; +use namada_core::types::token; use serde::{Deserialize, Serialize}; -use crate::types::address::Address; -use crate::types::dec::Dec; -use crate::types::key::{common, secp256k1}; -use crate::types::token; - /// A tx data type to become a validator account. #[derive( Debug, @@ -210,13 +209,13 @@ pub struct ConsensusKeyChange { #[cfg(any(test, feature = "testing"))] /// Tests and strategies for proof-of-stake pub mod tests { + use namada_core::types::address::testing::arb_non_internal_address; + use namada_core::types::dec::testing::arb_dec; + use namada_core::types::key::testing::{arb_common_pk, arb_pk}; + use namada_core::types::token::testing::arb_amount; use proptest::{option, prop_compose}; use super::*; - use crate::types::address::testing::arb_non_internal_address; - use crate::types::dec::testing::arb_dec; - use crate::types::key::testing::{arb_common_pk, arb_pk}; - use crate::types::token::testing::arb_amount; prop_compose! { /// Generate a bond diff --git a/tx/src/data/protocol.rs b/tx/src/data/protocol.rs index ba1335b084..b1ce5892fa 100644 --- a/tx/src/data/protocol.rs +++ b/tx/src/data/protocol.rs @@ -1,17 +1,14 @@ //! Types for sending and verifying txs //! used in Namada protocols -use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; -use borsh_ext::BorshSerializeExt; +use namada_core::borsh::{ + BorshDeserialize, BorshSchema, BorshSerialize, BorshSerializeExt, +}; +use namada_core::types::key::*; use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; -use crate::proto::{Data, Section, Signature, Tx, TxError}; -use crate::types::chain::ChainId; -use crate::types::key::*; -use crate::types::transaction::{Digest, Sha256, TxType}; -use crate::types::vote_extensions::{ - bridge_pool_roots, ethereum_events, validator_set_update, -}; +use crate::TxError; #[derive( Clone, diff --git a/tx/src/data/wrapper.rs b/tx/src/data/wrapper.rs index 509a34206f..a5867ad932 100644 --- a/tx/src/data/wrapper.rs +++ b/tx/src/data/wrapper.rs @@ -7,21 +7,23 @@ pub mod wrapper_tx { use std::str::FromStr; pub use ark_bls12_381::Bls12_381 as EllipticCurve; - use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; - use borsh_ext::BorshSerializeExt; use masp_primitives::transaction::Transaction; + use namada_core::borsh::{ + BorshDeserialize, BorshSchema, BorshSerialize, BorshSerializeExt, + }; + use namada_core::types::address::{Address, MASP}; + use namada_core::types::hash::Hash; + use namada_core::types::key::*; + use namada_core::types::storage::Epoch; + use namada_core::types::token::{Amount, DenominatedAmount, Transfer}; + use namada_core::types::uint::Uint; use namada_gas::Gas; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; use thiserror::Error; - use crate::proto::{Code, Data, Section, Tx}; - use crate::types::address::{Address, MASP}; - use crate::types::hash::Hash; - use crate::types::key::*; - use crate::types::storage::Epoch; - use crate::types::token::{Amount, DenominatedAmount, Transfer}; - use crate::types::uint::Uint; + use crate::data::{DecryptedTx, TxType}; + use crate::{Code, Data, Section, Tx}; /// TODO: Determine a sane number for this const GAS_LIMIT_RESOLUTION: u64 = 1; @@ -168,9 +170,7 @@ pub mod wrapper_tx { // Derive a Gas instance with a sub amount which is exactly a whole // amount since the limit represents gas in whole units fn from(value: GasLimit) -> Self { - Self { - sub: u64::from(value) * gas::SCALE, - } + Self::from_whole_units(u64::from(value)) } } @@ -297,9 +297,7 @@ pub mod wrapper_tx { unshield: Transaction, ) -> Result { let mut tx = - Tx::from_type(crate::types::transaction::TxType::Decrypted( - crate::types::transaction::DecryptedTx::Decrypted, - )); + Tx::from_type(TxType::Decrypted(DecryptedTx::Decrypted)); let masp_section = tx.add_section(Section::MaspTx(unshield)); let masp_hash = Hash( masp_section diff --git a/tx/src/lib.rs b/tx/src/lib.rs index da49706485..9e874aaede 100644 --- a/tx/src/lib.rs +++ b/tx/src/lib.rs @@ -1,13 +1,12 @@ #![allow(missing_docs)] -pub mod proto::generated; pub mod data; +pub mod proto; mod types; pub use types::{ standalone_signature, verify_standalone_sig, Code, Commitment, - CompressedSignature, Data, Error, Header, MaspBuilder, Section, - SerializeWithBorsh, Signable, SignableEthMessage, Signature, + CompressedSignature, Data, Error, Header, MaspBuilder, Section, Signature, SignatureIndex, Signed, Signer, Tx, TxError, }; diff --git a/tx/src/proto/mod.rs b/tx/src/proto/mod.rs new file mode 100644 index 0000000000..6aa71c7eec --- /dev/null +++ b/tx/src/proto/mod.rs @@ -0,0 +1,3 @@ +mod generated; + +pub use generated::types::Tx; diff --git a/tx/src/types.rs b/tx/src/types.rs index ef557b5d2e..a32e06ed10 100644 --- a/tx/src/types.rs +++ b/tx/src/types.rs @@ -5,35 +5,39 @@ use std::convert::TryFrom; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; -use borsh::schema::{add_definition, Declaration, Definition}; -use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; -use borsh_ext::BorshSerializeExt; use data_encoding::HEXUPPER; use masp_primitives::transaction::builder::Builder; use masp_primitives::transaction::components::sapling::builder::SaplingMetadata; use masp_primitives::transaction::Transaction; use masp_primitives::zip32::ExtendedFullViewingKey; -use prost::Message; +use namada_core::borsh::schema::{add_definition, Declaration, Definition}; +use namada_core::borsh::{ + BorshDeserialize, BorshSchema, BorshSerialize, BorshSerializeExt, +}; +use namada_core::types::address::Address; +use namada_core::types::chain::ChainId; +use namada_core::types::key::{AccountPublicKeysMap, *}; +use namada_core::types::storage::Epoch; +use namada_core::types::time::DateTimeUtc; +use namada_core::types::token::MaspDenom; use serde::de::Error as SerdeError; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; use thiserror::Error; -use super::generated::types; -use crate::ledger::gas; -use crate::ledger::storage::{KeccakHasher, Sha256Hasher, StorageHasher}; -use crate::types::account::AccountPublicKeysMap; -use crate::types::address::Address; -use crate::types::chain::ChainId; -use crate::types::keccak::{keccak_hash, KeccakHash}; -use crate::types::key::{self, *}; -use crate::types::storage::Epoch; -use crate::types::time::DateTimeUtc; -use crate::types::token::MaspDenom; -use crate::types::transaction::protocol::ProtocolTx; -use crate::types::transaction::{ - hash_tx, DecryptedTx, Fee, GasLimit, TxType, WrapperTx, -}; +use crate::data::protocol::ProtocolTx; +use crate::data::{hash_tx, DecryptedTx, Fee, GasLimit, TxType, WrapperTx}; +use crate::proto; + +/// Represents an error in signature verification +#[allow(missing_docs)] +#[derive(Error, Debug)] +pub enum VerifySigError { + #[error("{0}")] + VerifySig(#[from] namada_core::types::key::VerifySigError), + #[error("{0}")] + Gas(#[from] namada_gas::Error), +} #[derive(Error, Debug)] pub enum Error { @@ -54,7 +58,7 @@ pub enum Error { #[error("The wrapper signature is invalid.")] InvalidWrapperSignature, #[error("Signature verification went out of gas: {0}")] - OutOfGas(gas::Error), + OutOfGas(namada_gas::Error), } pub type Result = std::result::Result; @@ -169,6 +173,7 @@ impl> Signed { &signed_bytes, &self.sig, ) + .map_err(Into::into) } } @@ -194,6 +199,7 @@ pub fn verify_standalone_sig>( &signed_data, sig, ) + .map_err(Into::into) } /// A section representing transaction data @@ -242,7 +248,7 @@ pub struct CommitmentError; )] pub enum Commitment { /// Result of applying hash function to bytes - Hash(crate::types::hash::Hash), + Hash(namada_core::types::hash::Hash), /// Result of applying identity function to bytes Id(Vec), } @@ -272,7 +278,7 @@ impl Commitment { } /// Return the contained hash commitment - pub fn hash(&self) -> crate::types::hash::Hash { + pub fn hash(&self) -> namada_core::types::hash::Hash { match self { Self::Id(code) => hash_tx(code), Self::Hash(hash) => *hash, @@ -320,7 +326,7 @@ impl Code { /// Make a new code section with the given hash pub fn from_hash( - hash: crate::types::hash::Hash, + hash: namada_core::types::hash::Hash, tag: Option, ) -> Self { Self { @@ -431,7 +437,7 @@ pub enum Signer { )] pub struct Signature { /// The hash of the section being signed - pub targets: Vec, + pub targets: Vec, /// The public keys against which the signatures should be verified pub signer: Signer, /// The signature over the above hash @@ -441,7 +447,7 @@ pub struct Signature { impl Signature { /// Sign the given section hash with the given key and return a section pub fn new( - targets: Vec, + targets: Vec, secret_keys: BTreeMap, signer: Option
, ) -> Self { @@ -491,13 +497,13 @@ impl Signature { } /// Get the hash of this section - pub fn get_hash(&self) -> crate::types::hash::Hash { - crate::types::hash::Hash( + pub fn get_hash(&self) -> namada_core::types::hash::Hash { + namada_core::types::hash::Hash( self.hash(&mut Sha256::new()).finalize_reset().into(), ) } - pub fn get_raw_hash(&self) -> crate::types::hash::Hash { + pub fn get_raw_hash(&self) -> namada_core::types::hash::Hash { Self { signer: Signer::PubKeys(vec![]), signatures: BTreeMap::new(), @@ -515,7 +521,7 @@ impl Signature { consume_verify_sig_gas: &mut F, ) -> std::result::Result where - F: FnMut() -> std::result::Result<(), crate::ledger::gas::Error>, + F: FnMut() -> std::result::Result<(), namada_gas::Error>, { // Records whether there are any successful verifications let mut verifications = 0; @@ -711,7 +717,7 @@ impl From for Vec { )] pub struct MaspBuilder { /// The MASP transaction that this section witnesses - pub target: crate::types::hash::Hash, + pub target: namada_core::types::hash::Hash, /// The decoded set of asset types used by the transaction. Useful for /// offline wallets trying to display AssetTypes. pub asset_types: HashSet<(Address, MaspDenom, Epoch)>, @@ -811,8 +817,8 @@ impl Section { } /// Get the hash of this section - pub fn get_hash(&self) -> crate::types::hash::Hash { - crate::types::hash::Hash( + pub fn get_hash(&self) -> namada_core::types::hash::Hash { + namada_core::types::hash::Hash( self.hash(&mut Sha256::new()).finalize_reset().into(), ) } @@ -918,9 +924,9 @@ pub struct Header { /// A transaction timestamp pub timestamp: DateTimeUtc, /// The SHA-256 hash of the transaction's code section - pub code_hash: crate::types::hash::Hash, + pub code_hash: namada_core::types::hash::Hash, /// The SHA-256 hash of the transaction's data section - pub data_hash: crate::types::hash::Hash, + pub data_hash: namada_core::types::hash::Hash, /// The type of this transaction pub tx_type: TxType, } @@ -933,8 +939,8 @@ impl Header { chain_id: ChainId::default(), expiration: None, timestamp: DateTimeUtc::now(), - code_hash: crate::types::hash::Hash::default(), - data_hash: crate::types::hash::Hash::default(), + code_hash: namada_core::types::hash::Hash::default(), + data_hash: namada_core::types::hash::Hash::default(), } } @@ -1008,7 +1014,9 @@ impl TryFrom<&[u8]> for Tx { type Error = Error; fn try_from(tx_bytes: &[u8]) -> Result { - let tx = types::Tx::decode(tx_bytes).map_err(Error::TxDecodingError)?; + use prost::Message; + + let tx = proto::Tx::decode(tx_bytes).map_err(Error::TxDecodingError)?; BorshDeserialize::try_from_slice(&tx.data) .map_err(Error::TxDeserializingError) } @@ -1069,12 +1077,12 @@ impl Tx { } /// Get the transaction header hash - pub fn header_hash(&self) -> crate::types::hash::Hash { + pub fn header_hash(&self) -> namada_core::types::hash::Hash { Section::Header(self.header.clone()).get_hash() } /// Gets the hash of the decrypted transaction's header - pub fn raw_header_hash(&self) -> crate::types::hash::Hash { + pub fn raw_header_hash(&self) -> namada_core::types::hash::Hash { let mut raw_header = self.header(); raw_header.tx_type = TxType::Raw; @@ -1082,7 +1090,7 @@ impl Tx { } /// Get hashes of all the sections in this transaction - pub fn sechashes(&self) -> Vec { + pub fn sechashes(&self) -> Vec { let mut hashes = vec![self.header_hash()]; for sec in &self.sections { hashes.push(sec.get_hash()); @@ -1099,7 +1107,7 @@ impl Tx { /// Get the transaction section with the given hash pub fn get_section( &self, - hash: &crate::types::hash::Hash, + hash: &namada_core::types::hash::Hash, ) -> Option> { if self.header_hash() == *hash { return Some(Cow::Owned(Section::Header(self.header.clone()))); @@ -1123,12 +1131,12 @@ impl Tx { } /// Get the hash of this transaction's code from the heeader - pub fn code_sechash(&self) -> &crate::types::hash::Hash { + pub fn code_sechash(&self) -> &namada_core::types::hash::Hash { &self.header.code_hash } /// Set the transaction code hash stored in the header - pub fn set_code_sechash(&mut self, hash: crate::types::hash::Hash) { + pub fn set_code_sechash(&mut self, hash: namada_core::types::hash::Hash) { self.header.code_hash = hash } @@ -1153,12 +1161,12 @@ impl Tx { } /// Get the transaction data hash stored in the header - pub fn data_sechash(&self) -> &crate::types::hash::Hash { + pub fn data_sechash(&self) -> &namada_core::types::hash::Hash { &self.header.data_hash } /// Set the transaction data hash stored in the header - pub fn set_data_sechash(&mut self, hash: crate::types::hash::Hash) { + pub fn set_data_sechash(&mut self, hash: namada_core::types::hash::Hash) { self.header.data_hash = hash } @@ -1182,10 +1190,12 @@ impl Tx { } } - /// Convert this transaction into protobufs + /// Convert this transaction into protobufs bytes pub fn to_bytes(&self) -> Vec { + use prost::Message; + let mut bytes = vec![]; - let tx: types::Tx = types::Tx { + let tx: proto::Tx = proto::Tx { data: self.serialize_to_vec(), }; tx.encode(&mut bytes) @@ -1197,7 +1207,7 @@ impl Tx { /// public key pub fn verify_signatures( &self, - hashes: &[crate::types::hash::Hash], + hashes: &[namada_core::types::hash::Hash], public_keys_index_map: AccountPublicKeysMap, signer: &Option
, threshold: u8, @@ -1205,7 +1215,7 @@ impl Tx { mut consume_verify_sig_gas: F, ) -> std::result::Result, Error> where - F: FnMut() -> std::result::Result<(), crate::ledger::gas::Error>, + F: FnMut() -> std::result::Result<(), namada_gas::Error>, { let max_signatures = max_signatures.unwrap_or(u8::MAX); // Records the public key indices used in successful signatures @@ -1270,7 +1280,7 @@ impl Tx { pub fn verify_signature( &self, public_key: &common::PublicKey, - hashes: &[crate::types::hash::Hash], + hashes: &[namada_core::types::hash::Hash], ) -> Result<&Signature> { self.verify_signatures( hashes, @@ -1408,9 +1418,9 @@ impl Tx { /// Add an extra section to the tx builder by hash pub fn add_extra_section_from_hash( &mut self, - hash: crate::types::hash::Hash, + hash: namada_core::types::hash::Hash, tag: Option, - ) -> crate::types::hash::Hash { + ) -> namada_core::types::hash::Hash { let sechash = self .add_section(Section::ExtraData(Code::from_hash(hash, tag))) .get_hash(); @@ -1422,7 +1432,7 @@ impl Tx { &mut self, code: Vec, tag: Option, - ) -> (&mut Self, crate::types::hash::Hash) { + ) -> (&mut Self, namada_core::types::hash::Hash) { let sechash = self .add_section(Section::ExtraData(Code::new(code, tag))) .get_hash(); @@ -1433,7 +1443,7 @@ impl Tx { pub fn add_masp_tx_section( &mut self, tx: Transaction, - ) -> (&mut Self, crate::types::hash::Hash) { + ) -> (&mut Self, namada_core::types::hash::Hash) { let sechash = self.add_section(Section::MaspTx(tx)).get_hash(); (self, sechash) } @@ -1447,7 +1457,7 @@ impl Tx { /// Add wasm code to the tx builder from hash pub fn add_code_from_hash( &mut self, - code_hash: crate::types::hash::Hash, + code_hash: namada_core::types::hash::Hash, tag: Option, ) -> &mut Self { self.set_code(Code::from_hash(code_hash, tag)); @@ -1484,7 +1494,7 @@ impl Tx { fee_payer: common::PublicKey, epoch: Epoch, gas_limit: GasLimit, - fee_unshield_hash: Option, + fee_unshield_hash: Option, ) -> &mut Self { self.header.tx_type = TxType::Wrapper(Box::new(WrapperTx::new( fee, From cf340751ad92421e54fada2acd3027c2e9060406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 20 Dec 2023 16:32:11 +0000 Subject: [PATCH 050/118] trans_token: fix build --- trans_token/src/storage.rs | 53 +++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/trans_token/src/storage.rs b/trans_token/src/storage.rs index 16a7a40a90..9f5c0072c5 100644 --- a/trans_token/src/storage.rs +++ b/trans_token/src/storage.rs @@ -1,36 +1,35 @@ use namada_core::hints; use namada_core::types::address::{Address, InternalAddress}; -use namada_core::types::token; +use namada_core::types::dec::Dec; +use namada_core::types::token::{self, Amount, DenominatedAmount}; use namada_storage as storage; use namada_storage::{StorageRead, StorageWrite}; use crate::storage_key::*; -impl token::Parameters { - /// Initialize parameters for the token in storage during the genesis block. - pub fn init_storage( - &self, - storage: &mut S, - address: &Address, - ) -> storage::Result<()> - where - S: StorageRead + StorageWrite, - { - let Self { - max_reward_rate: max_rate, - kd_gain_nom, - kp_gain_nom, - locked_ratio_target: locked_target, - } = self; - storage.write(&masp_last_inflation_key(address), Amount::zero())?; - storage.write(&masp_last_locked_ratio_key(address), Dec::zero())?; - storage.write(&masp_max_reward_rate_key(address), max_rate)?; - storage.write(&masp_locked_ratio_target_key(address), locked_target)?; - storage.write(&masp_kp_gain_key(address), kp_gain_nom)?; - storage.write(&masp_kd_gain_key(address), kd_gain_nom)?; - storage.write(&minted_balance_key(address), Amount::zero())?; - Ok(()) - } +/// Initialize parameters for the token in storage during the genesis block. +pub fn write_params( + params: &token::Parameters, + storage: &mut S, + address: &Address, +) -> storage::Result<()> +where + S: StorageRead + StorageWrite, +{ + let token::Parameters { + max_reward_rate: max_rate, + kd_gain_nom, + kp_gain_nom, + locked_ratio_target: locked_target, + } = params; + storage.write(&masp_last_inflation_key(address), Amount::zero())?; + storage.write(&masp_last_locked_ratio_key(address), Dec::zero())?; + storage.write(&masp_max_reward_rate_key(address), max_rate)?; + storage.write(&masp_locked_ratio_target_key(address), locked_target)?; + storage.write(&masp_kp_gain_key(address), kp_gain_nom)?; + storage.write(&masp_kd_gain_key(address), kd_gain_nom)?; + storage.write(&minted_balance_key(address), Amount::zero())?; + Ok(()) } /// Read the balance of a given token and owner. @@ -220,7 +219,7 @@ pub fn denominated( "No denomination found in storage for the given token", ) })?; - Ok(DenominatedAmount { amount, denom }) + Ok(DenominatedAmount::new(amount, denom)) } /// Convert this denominated amount into a plain amount by increasing its From 795be984a8380275a3ea5a53d05e7158c94ff053 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 20 Dec 2023 16:45:29 +0000 Subject: [PATCH 051/118] macros: use full paths types used in `derive(StorageKeys)` --- macros/src/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 3bdc56b160..594a9ae991 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -269,10 +269,10 @@ fn derive_storage_keys_inner(struct_def: TokenStream2) -> TokenStream2 { let id = syn::Ident::new(&id, ident.span()); quote! { #[allow(missing_docs)] - pub(crate) fn #id(key: &Key, address: &Address) -> bool { + pub(crate) fn #id(key: &namada_core::types::storage::Key, address: &Address) -> bool { matches!(&key.segments[..], [ - DbKeySeg::AddressSeg(a), - DbKeySeg::StringSeg(#ident), + namada_core::types::storage::DbKeySeg::AddressSeg(a), + namada_core::types::storage::DbKeySeg::StringSeg(#ident), ] if a == address && #ident == #struct_def_ident::VALUES.#ident) } } @@ -282,11 +282,11 @@ fn derive_storage_keys_inner(struct_def: TokenStream2) -> TokenStream2 { let id = syn::Ident::new(&id, ident.span()); quote! { #[allow(missing_docs)] - pub(crate) fn #id(address: Address) -> Key { - Key { + pub(crate) fn #id(address: Address) -> namada_core::types::storage::Key { + namada_core::types::storage::Key { segments: vec![ - DbKeySeg::AddressSeg(address), - DbKeySeg::StringSeg(#struct_def_ident::VALUES.#ident.to_string()), + namada_core::types::storage::DbKeySeg::AddressSeg(address), + namada_core::types::storage::DbKeySeg::StringSeg(#struct_def_ident::VALUES.#ident.to_string()), ], } } From 58a17fa16af88a79a99969dc469a18b92cc7cf80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 20 Dec 2023 16:45:57 +0000 Subject: [PATCH 052/118] account: fix build --- Cargo.lock | 2 ++ account/Cargo.toml | 3 +++ account/src/lib.rs | 7 ++----- account/src/storage.rs | 26 +++++++++++++++++++++----- account/src/storage_key.rs | 16 ---------------- 5 files changed, 28 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 41130a65e5..b1d6adb171 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4252,9 +4252,11 @@ dependencies = [ name = "namada_account" version = "0.28.1" dependencies = [ + "borsh", "namada_core", "namada_macros", "namada_storage", + "serde 1.0.193", ] [[package]] diff --git a/account/Cargo.toml b/account/Cargo.toml index 77a53de081..01fa06c504 100644 --- a/account/Cargo.toml +++ b/account/Cargo.toml @@ -16,3 +16,6 @@ version.workspace = true namada_core = { path = "../core", default-features = false } namada_macros = { path = "../macros" } namada_storage = { path = "../storage" } + +borsh.workspace = true +serde.workspace = true diff --git a/account/src/lib.rs b/account/src/lib.rs index 5a29aa3616..be393d9288 100644 --- a/account/src/lib.rs +++ b/account/src/lib.rs @@ -5,12 +5,9 @@ mod storage; mod storage_key; -use std::collections::{BTreeMap, HashMap}; - use borsh::{BorshDeserialize, BorshSerialize}; -use namada_core::address::Address; -use namada_core::hints; -use namada_core::key::{common, RefTo}; +use namada_core::types::address::Address; +use namada_core::types::key::{common, AccountPublicKeysMap}; use serde::{Deserialize, Serialize}; pub use storage::*; pub use storage_key::*; diff --git a/account/src/storage.rs b/account/src/storage.rs index 5fa0abb8f9..50b56e7808 100644 --- a/account/src/storage.rs +++ b/account/src/storage.rs @@ -1,10 +1,26 @@ //! Cryptographic signature keys storage API +use namada_core::types::address::Address; +use namada_core::types::key::common; +use namada_core::types::storage; +use namada_storage::{Result, StorageRead, StorageWrite}; + use super::*; -use crate::types::account::AccountPublicKeysMap; -use crate::types::address::Address; -use crate::types::key::*; -use crate::types::storage::Key; + +/// Reveal a PK of an implicit account - the PK is written into the storage +/// of the address derived from the PK. +pub fn reveal_pk( + storage: &mut S, + public_key: &common::PublicKey, +) -> Result<()> +where + S: StorageWrite + StorageRead, +{ + let owner: Address = public_key.into(); + pks_handle(&owner).insert(storage, 0, public_key.clone())?; + + Ok(()) +} /// Init the subspace of a new account pub fn init_account_storage( @@ -72,7 +88,7 @@ where { match owner { Address::Established(_) => { - let vp_key = Key::validity_predicate(owner); + let vp_key = storage::Key::validity_predicate(owner); storage.has_key(&vp_key) } Address::Implicit(_) => Ok(true), diff --git a/account/src/storage_key.rs b/account/src/storage_key.rs index 44a348aa43..20e02906b8 100644 --- a/account/src/storage_key.rs +++ b/account/src/storage_key.rs @@ -4,22 +4,6 @@ use namada_core::types::storage::{self, DbKeySeg}; use namada_macros::StorageKeys; use namada_storage::collections::lazy_map::LazyMap; use namada_storage::collections::{lazy_map, LazyCollection}; -use namada_storage::{StorageRead, StorageWrite}; - -/// Reveal a PK of an implicit account - the PK is written into the storage -/// of the address derived from the PK. -pub fn reveal_pk( - storage: &mut S, - public_key: &common::PublicKey, -) -> Result<()> -where - S: StorageWrite + StorageRead, -{ - let owner: Address = public_key.into(); - pks_handle(&owner).insert(storage, 0, public_key.clone())?; - - Ok(()) -} /// Storage keys for account. #[derive(StorageKeys)] From 3c4e9d4908a965461359669473fe9bf2566c535a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 20 Dec 2023 18:10:55 +0000 Subject: [PATCH 053/118] governance: fix build --- Cargo.lock | 8 + core/src/types/governance.rs | 367 -------------------------- core/src/types/mod.rs | 2 +- core/src/types/sign.rs | 95 +++++++ governance/Cargo.toml | 19 +- governance/src/cli/offline.rs | 8 +- governance/src/cli/onchain.rs | 151 +++++++++++ governance/src/parameters.rs | 6 +- governance/src/pgf/cli/steward.rs | 5 +- governance/src/pgf/mod.rs | 2 +- governance/src/pgf/parameters.rs | 10 +- governance/src/pgf/storage/keys.rs | 14 +- governance/src/pgf/storage/mod.rs | 51 ++-- governance/src/pgf/storage/steward.rs | 5 +- governance/src/storage/mod.rs | 57 ++-- governance/src/storage/proposal.rs | 339 +++++++++++++++++++++++- governance/src/storage/vote.rs | 69 +++++ governance/src/utils.rs | 2 +- tx/src/data/governance.rs | 216 --------------- tx/src/data/mod.rs | 2 - tx/src/types.rs | 72 +---- 21 files changed, 746 insertions(+), 754 deletions(-) delete mode 100644 core/src/types/governance.rs create mode 100644 core/src/types/sign.rs delete mode 100644 tx/src/data/governance.rs diff --git a/Cargo.lock b/Cargo.lock index b1d6adb171..4922a9207f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4485,8 +4485,16 @@ dependencies = [ name = "namada_governance" version = "0.29.0" dependencies = [ + "borsh", "namada_core", + "namada_macros", + "namada_storage", "namada_trans_token", + "proptest", + "serde 1.0.193", + "serde_json", + "thiserror", + "tracing", ] [[package]] diff --git a/core/src/types/governance.rs b/core/src/types/governance.rs deleted file mode 100644 index d260476e91..0000000000 --- a/core/src/types/governance.rs +++ /dev/null @@ -1,367 +0,0 @@ -//! Governance proposal and voting types - -use std::collections::{BTreeMap, HashSet}; - -use borsh::{BorshDeserialize, BorshSerialize}; -use serde::{Deserialize, Serialize}; -use thiserror::Error; - -use crate::types::address::Address; -use crate::types::hash::Hash; -use crate::types::storage::Epoch; -use crate::types::token; - -/// Pgf default proposal -#[derive( - Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, -)] -pub struct DefaultProposal { - /// The proposal data - pub proposal: OnChainProposal, - /// The default proposal extra data - pub data: Option>, -} - -#[derive( - Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, -)] -/// The proposal structure -pub struct OnChainProposal { - /// The proposal id - pub id: Option, - /// The proposal content - pub content: BTreeMap, - /// The proposal author address - pub author: Address, - /// The epoch from which voting is allowed - pub voting_start_epoch: Epoch, - /// The epoch from which voting is stopped - pub voting_end_epoch: Epoch, - /// The epoch from which this changes are executed - pub grace_epoch: Epoch, -} - -/// Pgf funding proposal -#[derive( - Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, -)] -pub struct PgfFundingProposal { - /// The proposal data - pub proposal: OnChainProposal, - /// The Pgf funding proposal extra data - pub data: PgfFunding, -} - -/// Pgf fundings -#[derive( - Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, -)] -pub struct PgfFunding { - /// Pgf continous funding - pub continous: Vec, - /// pgf retro fundings - pub retro: Vec, -} - -/// Pgf continous funding -#[derive( - Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, -)] -pub struct PgfContinous { - /// Pgf target - pub target: PgfFundingTarget, - /// Pgf action - pub action: PgfAction, -} - -/// Pgf retro funding -#[derive( - Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, -)] -pub struct PgfRetro { - /// Pgf retro target - pub target: PgfFundingTarget, -} - -/// Pgf Target -#[derive( - Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, -)] -pub struct PgfFundingTarget { - /// Target amount - pub amount: token::Amount, - /// Target address - pub address: Address, -} - -/// Represent an proposal vote -#[derive( - Debug, - Clone, - BorshSerialize, - BorshDeserialize, - Serialize, - Deserialize, - PartialEq, -)] -pub enum ProposalVote { - /// Represent an yay proposal vote - Yay, - /// Represent an nay proposal vote - Nay, - /// Represent an abstain proposal vote - Abstain, -} - -/// Pgf action -#[derive( - Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, -)] -pub enum PgfAction { - /// Add action - Add, - /// Remove action - Remove, -} - -/// Pgf stewards proposal -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct PgfStewardProposal { - /// The proposal data - pub proposal: OnChainProposal, - /// The Pgf steward proposal extra data - pub data: StewardsUpdate, -} - -/// Pgf steward proposal extra data -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct StewardsUpdate { - /// The optional steward to add - pub add: Option
, - /// The stewards to remove - pub remove: Vec
, -} - -#[allow(missing_docs)] -#[derive(Debug, Error)] -pub enum ProposalTypeError { - #[error("Invalid proposal type.")] - InvalidProposalType, -} - -impl TryFrom for HashSet> { - type Error = ProposalTypeError; - - fn try_from(value: StewardsUpdate) -> Result { - let mut data = HashSet::default(); - - if value.add.is_some() { - data.insert(AddRemove::Add(value.add.unwrap())); - } - for steward in value.remove { - data.insert(AddRemove::Remove(steward)); - } - Ok(data) - } -} - -/// An add or remove action for PGF -#[derive( - Debug, - Clone, - Hash, - PartialEq, - Eq, - PartialOrd, - Ord, - BorshSerialize, - BorshDeserialize, - Serialize, - Deserialize, -)] -pub enum AddRemove { - /// Add - Add(T), - /// Remove - Remove(T), -} - -/// The target of a PGF payment -#[derive( - Debug, - Clone, - PartialEq, - BorshSerialize, - BorshDeserialize, - Serialize, - Deserialize, - Ord, - Eq, - PartialOrd, -)] -pub struct PGFTarget { - /// The target address - pub target: Address, - /// The amount of token to fund the target address - pub amount: token::Amount, -} - -/// The actions that a PGF Steward can propose to execute -#[derive( - Debug, - Clone, - PartialEq, - BorshSerialize, - BorshDeserialize, - Serialize, - Deserialize, -)] -pub enum PGFAction { - /// A continuous payment - Continuous(AddRemove), - /// A retro payment - Retro(PGFTarget), -} - -/// The type of a Proposal -#[derive( - Debug, - Clone, - PartialEq, - BorshSerialize, - BorshDeserialize, - Serialize, - Deserialize, -)] -pub enum ProposalType { - /// Default governance proposal with the optional wasm code - Default(Option), - /// PGF stewards proposal - PGFSteward(HashSet>), - /// PGF funding proposal - PGFPayment(Vec), -} - -#[derive( - Debug, - Clone, - PartialEq, - BorshSerialize, - BorshDeserialize, - Eq, - Serialize, - Deserialize, -)] -/// The vote for a proposal -pub enum StorageProposalVote { - /// Yes - Yay(VoteType), - /// No - Nay, - /// Abstain - Abstain, -} - -/// The type of a governance vote with the optional associated Memo -#[derive( - Debug, - Clone, - PartialEq, - BorshSerialize, - BorshDeserialize, - Eq, - Serialize, - Deserialize, -)] -pub enum VoteType { - /// A default vote without Memo - Default, - /// A vote for the PGF stewards - PGFSteward, - /// A vote for a PGF payment proposal - PGFPayment, -} - -#[cfg(any(test, feature = "testing"))] -/// Testing helpers and and strategies for governance proposals -pub mod testing { - use proptest::prelude::*; - use proptest::{collection, option}; - - use super::*; - use crate::types::address::testing::arb_non_internal_address; - use crate::types::hash::testing::arb_hash; - use crate::types::token::testing::arb_amount; - - /// Generate an arbitrary add or removal of what's generated by the supplied - /// strategy - pub fn arb_add_remove( - strategy: X, - ) -> impl Strategy::Value>> { - (0..2, strategy).prop_map(|(discriminant, val)| match discriminant { - 0 => AddRemove::Add(val), - 1 => AddRemove::Remove(val), - _ => unreachable!(), - }) - } - - prop_compose! { - /// Generate an arbitrary PGF target - pub fn arb_pgf_target()( - target in arb_non_internal_address(), - amount in arb_amount(), - ) -> PGFTarget { - PGFTarget { - target, - amount, - } - } - } - - /// Generate an arbitrary PGF action - pub fn arb_pgf_action() -> impl Strategy { - arb_add_remove(arb_pgf_target()) - .prop_map(PGFAction::Continuous) - .boxed() - .prop_union(arb_pgf_target().prop_map(PGFAction::Retro).boxed()) - } - - /// Generate an arbitrary proposal type - pub fn arb_proposal_type() -> impl Strategy { - option::of(arb_hash()) - .prop_map(ProposalType::Default) - .boxed() - .prop_union( - collection::hash_set( - arb_add_remove(arb_non_internal_address()), - 0..10, - ) - .prop_map(ProposalType::PGFSteward) - .boxed(), - ) - .or(collection::vec(arb_pgf_action(), 0..10) - .prop_map(ProposalType::PGFPayment) - .boxed()) - } - - prop_compose! { - /// Geerate an arbitrary vote type - pub fn arb_vote_type()(discriminant in 0..3) -> VoteType { - match discriminant { - 0 => VoteType::Default, - 1 => VoteType::PGFSteward, - 2 => VoteType::PGFPayment, - _ => unreachable!(), - } - } - } - - /// Generate an arbitrary proposal vote - pub fn arb_proposal_vote() -> impl Strategy { - arb_vote_type() - .prop_map(StorageProposalVote::Yay) - .boxed() - .prop_union(Just(StorageProposalVote::Nay).boxed()) - .or(Just(StorageProposalVote::Abstain).boxed()) - } -} diff --git a/core/src/types/mod.rs b/core/src/types/mod.rs index b2531f3a81..5c2c99454c 100644 --- a/core/src/types/mod.rs +++ b/core/src/types/mod.rs @@ -7,13 +7,13 @@ pub mod eth_abi; pub mod eth_bridge_pool; pub mod ethereum_events; pub mod ethereum_structs; -pub mod governance; pub mod hash; pub mod ibc; pub mod internal; pub mod keccak; pub mod key; pub mod masp; +pub mod sign; pub mod storage; pub mod string_encoding; pub mod time; diff --git a/core/src/types/sign.rs b/core/src/types/sign.rs new file mode 100644 index 0000000000..e6a2b2e3e6 --- /dev/null +++ b/core/src/types/sign.rs @@ -0,0 +1,95 @@ +//! Types for signing + +use std::cmp::Ordering; + +use data_encoding::HEXUPPER; +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +use super::address::Address; +use super::key::common; +use crate::borsh::{ + BorshDeserialize, BorshSchema, BorshSerialize, BorshSerializeExt, +}; + +#[allow(missing_docs)] +#[derive(Error, Debug)] +pub enum SigIndexDecodeError { + #[error("Error deserializing transaction field bytes: {0}")] + TxDeserializingError(std::io::Error), + #[error("Error deserializing transaction")] + OfflineTxDeserializationError, +} + +#[derive( + Clone, + Debug, + BorshSerialize, + BorshDeserialize, + BorshSchema, + Serialize, + Deserialize, + Eq, + PartialEq, +)] +/// Signature index within a multisig +pub struct SignatureIndex { + /// PK that can be used to verify signature + pub pubkey: common::PublicKey, + /// Index in multisig + pub index: Option<(Address, u8)>, + /// Signature + pub signature: common::Signature, +} + +impl SignatureIndex { + /// Instantiate from a single signature and a matching PK. + pub fn from_single_signature( + pubkey: common::PublicKey, + signature: common::Signature, + ) -> Self { + Self { + pubkey, + signature, + index: None, + } + } + + /// Convert to a vector + pub fn to_vec(&self) -> Vec { + vec![self.clone()] + } + + /// Serialize as a string. + pub fn serialize(&self) -> String { + let signature_bytes = self.serialize_to_vec(); + HEXUPPER.encode(&signature_bytes) + } + + /// Deserialize from a string slice + pub fn deserialize(data: &[u8]) -> Result { + if let Ok(hex) = serde_json::from_slice::(data) { + match HEXUPPER.decode(hex.as_bytes()) { + Ok(bytes) => Self::try_from_slice(&bytes) + .map_err(SigIndexDecodeError::TxDeserializingError), + Err(_) => { + Err(SigIndexDecodeError::OfflineTxDeserializationError) + } + } + } else { + Err(SigIndexDecodeError::OfflineTxDeserializationError) + } + } +} + +impl Ord for SignatureIndex { + fn cmp(&self, other: &Self) -> Ordering { + self.pubkey.cmp(&other.pubkey) + } +} + +impl PartialOrd for SignatureIndex { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} diff --git a/governance/Cargo.toml b/governance/Cargo.toml index 39994f3a6f..1e3c8b075f 100644 --- a/governance/Cargo.toml +++ b/governance/Cargo.toml @@ -12,6 +12,23 @@ readme.workspace = true repository.workspace = true version.workspace = true +[features] +testing = [] + [dependencies] namada_core = {path = "../core", default-features = false} -namada_trans_token = {path = "../trans_token"} \ No newline at end of file +namada_macros = {path = "../macros"} +namada_storage = {path = "../storage"} +namada_trans_token = {path = "../trans_token"} + +borsh.workspace = true +serde_json.workspace = true +serde.workspace = true +thiserror.workspace = true +tracing.workspace = true + + +[dev-dependencies] +namada_core = {path = "../core", default-features = false, features = ["testing"]} + +proptest.workspace = true \ No newline at end of file diff --git a/governance/src/cli/offline.rs b/governance/src/cli/offline.rs index c800ef7264..6938b3fca9 100644 --- a/governance/src/cli/offline.rs +++ b/governance/src/cli/offline.rs @@ -2,13 +2,11 @@ use std::collections::{BTreeMap, BTreeSet}; use std::fs::{File, ReadDir}; use std::path::PathBuf; -use borsh::{BorshDeserialize, BorshSerialize}; -use borsh_ext::BorshSerializeExt; -use namada_core::proto::SignatureIndex; -use namada_core::types::account::AccountPublicKeysMap; +use namada_core::borsh::{BorshDeserialize, BorshSerialize, BorshSerializeExt}; use namada_core::types::address::Address; use namada_core::types::hash::Hash; -use namada_core::types::key::{common, RefTo, SigScheme}; +use namada_core::types::key::{common, AccountPublicKeysMap, RefTo, SigScheme}; +use namada_core::types::sign::SignatureIndex; use namada_core::types::storage::Epoch; use serde::{Deserialize, Serialize}; diff --git a/governance/src/cli/onchain.rs b/governance/src/cli/onchain.rs index af9e3e0f65..8c9eef2ec2 100644 --- a/governance/src/cli/onchain.rs +++ b/governance/src/cli/onchain.rs @@ -14,6 +14,36 @@ use super::validation::{ }; use crate::parameters::GovernanceParameters; +#[derive( + Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, +)] +/// The proposal structure +pub struct OnChainProposal { + /// The proposal id + pub id: Option, + /// The proposal content + pub content: BTreeMap, + /// The proposal author address + pub author: Address, + /// The epoch from which voting is allowed + pub voting_start_epoch: Epoch, + /// The epoch from which voting is stopped + pub voting_end_epoch: Epoch, + /// The epoch from which this changes are executed + pub grace_epoch: Epoch, +} + +/// Pgf default proposal +#[derive( + Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, +)] +pub struct DefaultProposal { + /// The proposal data + pub proposal: OnChainProposal, + /// The default proposal extra data + pub data: Option>, +} + impl DefaultProposal { /// Validate a default funding proposal pub fn validate( @@ -74,6 +104,24 @@ impl TryFrom<&[u8]> for DefaultProposal { } } +/// Pgf stewards proposal +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PgfStewardProposal { + /// The proposal data + pub proposal: OnChainProposal, + /// The Pgf steward proposal extra data + pub data: StewardsUpdate, +} + +/// Pgf steward proposal extra data +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct StewardsUpdate { + /// The optional steward to add + pub add: Option
, + /// The stewards to remove + pub remove: Vec
, +} + impl PgfStewardProposal { /// Validate a Pgf stewards proposal pub fn validate( @@ -131,6 +179,17 @@ impl TryFrom<&[u8]> for PgfStewardProposal { } } +/// Pgf funding proposal +#[derive( + Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, +)] +pub struct PgfFundingProposal { + /// The proposal data + pub proposal: OnChainProposal, + /// The Pgf funding proposal extra data + pub data: PgfFunding, +} + impl PgfFundingProposal { /// Validate a Pgf funding proposal pub fn validate( @@ -194,6 +253,37 @@ pub struct PgfSteward { pub address: Address, } +/// The target of a PGF payment +#[derive( + Debug, + Clone, + PartialEq, + BorshSerialize, + BorshDeserialize, + Serialize, + Deserialize, + Ord, + Eq, + PartialOrd, +)] +pub struct PGFTarget { + /// The target address + pub target: Address, + /// The amount of token to fund the target address + pub amount: token::Amount, +} + +/// Pgf action +#[derive( + Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, +)] +pub enum PgfAction { + /// Add action + Add, + /// Remove action + Remove, +} + impl PgfAction { /// Check if a pgf action is adding a steward pub fn is_add(&self) -> bool { @@ -201,6 +291,67 @@ impl PgfAction { } } +/// Pgf fundings +#[derive( + Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, +)] +pub struct PgfFunding { + /// Pgf continuous funding + pub continuous: Vec, + /// pgf retro fundings + pub retro: Vec, +} + +/// Pgf continous funding +#[derive( + Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, +)] +pub struct PgfContinous { + /// Pgf target + pub target: PgfFundingTarget, + /// Pgf action + pub action: PgfAction, +} + +/// Pgf retro funding +#[derive( + Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, +)] +pub struct PgfRetro { + /// Pgf retro target + pub target: PgfFundingTarget, +} + +/// Pgf Target +#[derive( + Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, +)] +pub struct PgfFundingTarget { + /// Target amount + pub amount: token::Amount, + /// Target address + pub address: Address, +} + +/// Represent an proposal vote +#[derive( + Debug, + Clone, + BorshSerialize, + BorshDeserialize, + Serialize, + Deserialize, + PartialEq, +)] +pub enum ProposalVote { + /// Represent an yay proposal vote + Yay, + /// Represent an nay proposal vote + Nay, + /// Represent an abstain proposal vote + Abstain, +} + impl TryFrom for ProposalVote { type Error = String; diff --git a/governance/src/parameters.rs b/governance/src/parameters.rs index 25a14073cf..ee8686854c 100644 --- a/governance/src/parameters.rs +++ b/governance/src/parameters.rs @@ -1,6 +1,6 @@ -use borsh::{BorshDeserialize, BorshSerialize}; -use namada_core::ledger::storage_api::{self, StorageRead, StorageWrite}; +use namada_core::borsh::{BorshDeserialize, BorshSerialize}; use namada_core::types::token; +use namada_storage::{Result, StorageRead, StorageWrite}; use super::storage::keys as goverance_storage; @@ -46,7 +46,7 @@ impl Default for GovernanceParameters { impl GovernanceParameters { /// Initialize governance parameters into storage - pub fn init_storage(&self, storage: &mut S) -> storage_api::Result<()> + pub fn init_storage(&self, storage: &mut S) -> Result<()> where S: StorageRead + StorageWrite, { diff --git a/governance/src/pgf/cli/steward.rs b/governance/src/pgf/cli/steward.rs index 5ebbd40621..dd411ac190 100644 --- a/governance/src/pgf/cli/steward.rs +++ b/governance/src/pgf/cli/steward.rs @@ -1,10 +1,9 @@ use std::collections::HashMap; +use namada_core::types::address::Address; +use namada_core::types::dec::Dec; use serde::{Deserialize, Serialize}; -use crate::types::address::Address; -use crate::types::dec::Dec; - #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] /// Struct holding data about a steward commission pub struct Commission { diff --git a/governance/src/pgf/mod.rs b/governance/src/pgf/mod.rs index 5d7d7eef24..a9398a75d9 100644 --- a/governance/src/pgf/mod.rs +++ b/governance/src/pgf/mod.rs @@ -1,6 +1,6 @@ //! Pgf library code -use crate::types::address::{Address, InternalAddress}; +use namada_core::types::address::{Address, InternalAddress}; /// Pgf CLI pub mod cli; diff --git a/governance/src/pgf/parameters.rs b/governance/src/pgf/parameters.rs index 1d45ea79d2..7ca973dbbf 100644 --- a/governance/src/pgf/parameters.rs +++ b/governance/src/pgf/parameters.rs @@ -1,13 +1,13 @@ use std::collections::BTreeSet; -use borsh::{BorshDeserialize, BorshSerialize}; +use namada_core::borsh::{BorshDeserialize, BorshSerialize}; +use namada_core::types::address::Address; +use namada_core::types::dec::Dec; +use namada_storage::{Result, StorageRead, StorageWrite}; use serde::{Deserialize, Serialize}; use super::storage::keys as pgf_storage; use super::storage::steward::StewardDetail; -use crate::ledger::storage_api::{self, StorageRead, StorageWrite}; -use crate::types::address::Address; -use crate::types::dec::Dec; #[derive( Clone, @@ -44,7 +44,7 @@ impl Default for PgfParameters { impl PgfParameters { /// Initialize governance parameters into storage - pub fn init_storage(&self, storage: &mut S) -> storage_api::Result<()> + pub fn init_storage(&self, storage: &mut S) -> Result<()> where S: StorageRead + StorageWrite, { diff --git a/governance/src/pgf/storage/keys.rs b/governance/src/pgf/storage/keys.rs index 96768ac494..ef1a65dd7f 100644 --- a/governance/src/pgf/storage/keys.rs +++ b/governance/src/pgf/storage/keys.rs @@ -1,13 +1,11 @@ +use namada_core::types::address::Address; +use namada_core::types::storage::{DbKeySeg, Key, KeySeg}; use namada_macros::StorageKeys; +use namada_storage::collections::{lazy_map, LazyCollection, LazyMap}; -use super::steward::StewardDetail; -use crate::ledger::governance::storage::proposal::StoragePgfFunding; -use crate::ledger::pgf::ADDRESS; -use crate::ledger::storage_api::collections::{ - lazy_map, LazyCollection, LazyMap, -}; -use crate::types::address::Address; -use crate::types::storage::{DbKeySeg, Key, KeySeg}; +use crate::pgf::storage::steward::StewardDetail; +use crate::pgf::ADDRESS; +use crate::storage::proposal::StoragePgfFunding; /// Storage keys for pgf internal address. #[derive(StorageKeys)] diff --git a/governance/src/pgf/storage/mod.rs b/governance/src/pgf/storage/mod.rs index 15238dd9b1..63ea00b3a7 100644 --- a/governance/src/pgf/storage/mod.rs +++ b/governance/src/pgf/storage/mod.rs @@ -7,18 +7,19 @@ pub mod steward; use std::collections::HashMap; -use crate::ledger::governance::storage::proposal::StoragePgfFunding; -use crate::ledger::pgf::parameters::PgfParameters; -use crate::ledger::pgf::storage::keys as pgf_keys; -use crate::ledger::pgf::storage::steward::StewardDetail; -use crate::ledger::storage_api::{self}; -use crate::types::address::Address; -use crate::types::dec::Dec; +use namada_core::types::address::Address; +use namada_core::types::dec::Dec; +use namada_storage::{Result, StorageRead, StorageWrite}; + +use crate::pgf::parameters::PgfParameters; +use crate::pgf::storage::keys as pgf_keys; +use crate::pgf::storage::steward::StewardDetail; +use crate::storage::proposal::StoragePgfFunding; /// Query the current pgf steward set -pub fn get_stewards(storage: &S) -> storage_api::Result> +pub fn get_stewards(storage: &S) -> Result> where - S: storage_api::StorageRead, + S: StorageRead, { let stewards = pgf_keys::stewards_handle() .iter(storage)? @@ -35,31 +36,25 @@ where pub fn get_steward( storage: &S, address: &Address, -) -> storage_api::Result> +) -> Result> where - S: storage_api::StorageRead, + S: StorageRead, { pgf_keys::stewards_handle().get(storage, address) } /// Check if an address is a steward -pub fn is_steward( - storage: &S, - address: &Address, -) -> storage_api::Result +pub fn is_steward(storage: &S, address: &Address) -> Result where - S: storage_api::StorageRead, + S: StorageRead, { pgf_keys::stewards_handle().contains(storage, address) } /// Remove a steward -pub fn remove_steward( - storage: &mut S, - address: &Address, -) -> storage_api::Result<()> +pub fn remove_steward(storage: &mut S, address: &Address) -> Result<()> where - S: storage_api::StorageRead + storage_api::StorageWrite, + S: StorageRead + StorageWrite, { pgf_keys::stewards_handle().remove(storage, address)?; @@ -67,11 +62,9 @@ where } /// Query the current pgf continous payments -pub fn get_payments( - storage: &S, -) -> storage_api::Result> +pub fn get_payments(storage: &S) -> Result> where - S: storage_api::StorageRead, + S: StorageRead, { let fundings = pgf_keys::fundings_handle() .iter(storage)? @@ -85,9 +78,9 @@ where } /// Query the pgf parameters -pub fn get_parameters(storage: &S) -> storage_api::Result +pub fn get_parameters(storage: &S) -> Result where - S: storage_api::StorageRead, + S: StorageRead, { let pgf_inflation_rate_key = pgf_keys::get_pgf_inflation_rate_key(); let stewards_inflation_rate_key = @@ -112,9 +105,9 @@ pub fn update_commission( storage: &mut S, address: Address, reward_distribution: HashMap, -) -> storage_api::Result<()> +) -> Result<()> where - S: storage_api::StorageRead + storage_api::StorageWrite, + S: StorageRead + StorageWrite, { pgf_keys::stewards_handle().insert( storage, diff --git a/governance/src/pgf/storage/steward.rs b/governance/src/pgf/storage/steward.rs index d93be50b83..2ed4b41111 100644 --- a/governance/src/pgf/storage/steward.rs +++ b/governance/src/pgf/storage/steward.rs @@ -1,9 +1,8 @@ use std::collections::HashMap; use borsh::{BorshDeserialize, BorshSerialize}; - -use crate::types::address::Address; -use crate::types::dec::Dec; +use namada_core::types::address::Address; +use namada_core::types::dec::Dec; #[derive(Clone, Debug, BorshSerialize, BorshDeserialize, PartialEq)] /// Struct holding data about a pgf steward diff --git a/governance/src/storage/mod.rs b/governance/src/storage/mod.rs index 0f2fc59332..6e30941ee4 100644 --- a/governance/src/storage/mod.rs +++ b/governance/src/storage/mod.rs @@ -9,15 +9,13 @@ pub mod vote; use std::collections::BTreeMap; -use borsh::BorshDeserialize; -use namada_core::ledger::storage_api::{self, StorageRead, StorageWrite}; +use namada_core::borsh::BorshDeserialize; use namada_core::types::address::Address; use namada_core::types::storage::Epoch; -use namada_core::types::transaction::governance::{ - InitProposalData, VoteProposalData, -}; +use namada_storage::{iter_prefix, Error, Result, StorageRead, StorageWrite}; +use namada_trans_token as token; -use super::token; +use self::proposal::{InitProposalData, VoteProposalData}; use crate::parameters::GovernanceParameters; use crate::storage::keys as governance_keys; use crate::storage::proposal::{ProposalType, StorageProposal}; @@ -31,7 +29,7 @@ pub fn init_proposal( data: InitProposalData, content: Vec, code: Option>, -) -> storage_api::Result<()> +) -> Result<()> where S: StorageRead + StorageWrite, { @@ -55,9 +53,9 @@ where storage.write(&proposal_type_key, ProposalType::Default(None))?; let proposal_code_key = governance_keys::get_proposal_code_key(proposal_id); - let proposal_code = code.clone().ok_or( - storage_api::Error::new_const("Missing proposal code"), - )?; + let proposal_code = code + .clone() + .ok_or(Error::new_const("Missing proposal code"))?; storage.write_bytes(&proposal_code_key, proposal_code)? } _ => storage.write(&proposal_type_key, data.r#type.clone())?, @@ -78,7 +76,7 @@ where let proposal_code_key = governance_keys::get_proposal_code_key(proposal_id); let proposal_code = - code.ok_or(storage_api::Error::new_const("Missing proposal code"))?; + code.ok_or(Error::new_const("Missing proposal code"))?; storage.write_bytes(&proposal_code_key, proposal_code)?; } @@ -109,10 +107,7 @@ where } /// A proposal vote transaction. -pub fn vote_proposal( - storage: &mut S, - data: VoteProposalData, -) -> storage_api::Result<()> +pub fn vote_proposal(storage: &mut S, data: VoteProposalData) -> Result<()> where S: StorageRead + StorageWrite, { @@ -131,7 +126,7 @@ where pub fn get_proposal_by_id( storage: &S, id: u64, -) -> storage_api::Result> +) -> Result> where S: StorageRead, { @@ -164,19 +159,14 @@ where } /// Query all the votes for a proposal_id -pub fn get_proposal_votes( - storage: &S, - proposal_id: u64, -) -> storage_api::Result> +pub fn get_proposal_votes(storage: &S, proposal_id: u64) -> Result> where - S: storage_api::StorageRead, + S: StorageRead, { let vote_prefix_key = governance_keys::get_proposal_vote_prefix_key(proposal_id); - let vote_iter = storage_api::iter_prefix::( - storage, - &vote_prefix_key, - )?; + let vote_iter = + iter_prefix::(storage, &vote_prefix_key)?; let votes = vote_iter .filter_map(|vote_result| { @@ -205,12 +195,9 @@ where } /// Check if an accepted proposal is being executed -pub fn is_proposal_accepted( - storage: &S, - tx_data: &[u8], -) -> storage_api::Result +pub fn is_proposal_accepted(storage: &S, tx_data: &[u8]) -> Result where - S: storage_api::StorageRead, + S: StorageRead, { let proposal_id = u64::try_from_slice(tx_data).ok(); match proposal_id { @@ -224,11 +211,9 @@ where } /// Get governance parameters -pub fn get_parameters( - storage: &S, -) -> storage_api::Result +pub fn get_parameters(storage: &S) -> Result where - S: storage_api::StorageRead, + S: StorageRead, { let key = governance_keys::get_max_proposal_code_size_key(); let max_proposal_code_size: u64 = @@ -263,9 +248,9 @@ where } /// Get governance "max_proposal_period" parameter -pub fn get_max_proposal_period(storage: &S) -> storage_api::Result +pub fn get_max_proposal_period(storage: &S) -> Result where - S: storage_api::StorageRead, + S: StorageRead, { let key = governance_keys::get_max_proposal_period_key(); let max_proposal_period: u64 = diff --git a/governance/src/storage/proposal.rs b/governance/src/storage/proposal.rs index 98aaedbfa3..6ff9bd7c30 100644 --- a/governance/src/storage/proposal.rs +++ b/governance/src/storage/proposal.rs @@ -5,15 +5,166 @@ use borsh::{BorshDeserialize, BorshSerialize}; use namada_core::types::address::Address; use namada_core::types::hash::Hash; use namada_core::types::storage::Epoch; -use namada_core::types::token; use serde::{Deserialize, Serialize}; use thiserror::Error; +use super::vote::StorageProposalVote; use crate::cli::onchain::{ - PgfAction, PgfContinous, PgfRetro, PgfSteward, StewardsUpdate, + DefaultProposal, PGFTarget, PgfAction, PgfContinous, PgfFundingProposal, + PgfRetro, PgfSteward, PgfStewardProposal, StewardsUpdate, }; use crate::utils::{ProposalStatus, TallyType}; +#[allow(missing_docs)] +#[derive(Debug, Error)] +pub enum ProposalError { + #[error("Invalid proposal data.")] + InvalidProposalData, +} + +/// A tx data type to hold proposal data +#[derive( + Debug, + Clone, + PartialEq, + BorshSerialize, + BorshDeserialize, + Serialize, + Deserialize, +)] +pub struct InitProposalData { + /// The proposal id + pub id: Option, + /// The proposal content + pub content: Hash, + /// The proposal author address + pub author: Address, + /// The proposal type + pub r#type: ProposalType, + /// The epoch from which voting is allowed + pub voting_start_epoch: Epoch, + /// The epoch from which voting is stopped + pub voting_end_epoch: Epoch, + /// The epoch from which this changes are executed + pub grace_epoch: Epoch, +} + +impl InitProposalData { + /// Get the hash of the corresponding extra data section + pub fn get_section_code_hash(&self) -> Option { + match self.r#type { + ProposalType::Default(hash) => hash, + _ => None, + } + } +} + +/// A tx data type to hold vote proposal data +#[derive( + Debug, + Clone, + PartialEq, + BorshSerialize, + BorshDeserialize, + Serialize, + Deserialize, +)] +pub struct VoteProposalData { + /// The proposal id + pub id: u64, + /// The proposal vote + pub vote: StorageProposalVote, + /// The proposal author address + pub voter: Address, + /// Delegator addreses + pub delegations: Vec
, +} + +impl TryFrom for InitProposalData { + type Error = ProposalError; + + fn try_from(value: DefaultProposal) -> Result { + Ok(InitProposalData { + id: value.proposal.id, + content: Hash::default(), + author: value.proposal.author, + r#type: ProposalType::Default(None), + voting_start_epoch: value.proposal.voting_start_epoch, + voting_end_epoch: value.proposal.voting_end_epoch, + grace_epoch: value.proposal.grace_epoch, + }) + } +} + +impl TryFrom for InitProposalData { + type Error = ProposalError; + + fn try_from(value: PgfStewardProposal) -> Result { + let extra_data = + HashSet::>::try_from(value.data).unwrap(); + + Ok(InitProposalData { + id: value.proposal.id, + content: Hash::default(), + author: value.proposal.author, + r#type: ProposalType::PGFSteward(extra_data), + voting_start_epoch: value.proposal.voting_start_epoch, + voting_end_epoch: value.proposal.voting_end_epoch, + grace_epoch: value.proposal.grace_epoch, + }) + } +} + +impl TryFrom for InitProposalData { + type Error = ProposalError; + + fn try_from(value: PgfFundingProposal) -> Result { + let continous_fundings = value + .data + .continuous + .iter() + .cloned() + .map(|funding| { + let target = PGFTarget { + target: funding.address, + amount: funding.amount, + }; + if funding.amount.is_zero() { + PGFAction::Continuous(AddRemove::Remove(target)) + } else { + PGFAction::Continuous(AddRemove::Add(target)) + } + }) + .collect::>(); + + let retro_fundings = value + .data + .retro + .iter() + .cloned() + .map(|funding| { + let target = PGFTarget { + target: funding.address, + amount: funding.amount, + }; + PGFAction::Retro(target) + }) + .collect::>(); + + let extra_data = [continous_fundings, retro_fundings].concat(); + + Ok(InitProposalData { + id: value.proposal.id, + content: Hash::default(), + author: value.proposal.author, + r#type: ProposalType::PGFPayment(extra_data), + voting_start_epoch: value.proposal.voting_start_epoch, + voting_end_epoch: value.proposal.voting_end_epoch, + grace_epoch: value.proposal.grace_epoch, + }) + } +} + /// Storage struture for pgf fundings #[derive( Debug, @@ -40,6 +191,63 @@ impl StoragePgfFunding { } } +/// The type of a Proposal +#[derive( + Debug, + Clone, + PartialEq, + BorshSerialize, + BorshDeserialize, + Serialize, + Deserialize, +)] +pub enum ProposalType { + /// Default governance proposal with the optional wasm code + Default(Option), + /// PGF stewards proposal + PGFSteward(HashSet>), + /// PGF funding proposal + PGFPayment(Vec), +} + +/// An add or remove action for PGF +#[derive( + Debug, + Clone, + Hash, + PartialEq, + Eq, + PartialOrd, + Ord, + BorshSerialize, + BorshDeserialize, + Serialize, + Deserialize, +)] +pub enum AddRemove { + /// Add + Add(T), + /// Remove + Remove(T), +} + +/// The actions that a PGF Steward can propose to execute +#[derive( + Debug, + Clone, + PartialEq, + BorshSerialize, + BorshDeserialize, + Serialize, + Deserialize, +)] +pub enum PGFAction { + /// A continuous payment + Continuous(AddRemove), + /// A retro payment + Retro(PGFTarget), +} + impl ProposalType { /// Check if the proposal type is default pub fn is_default(&self) -> bool { @@ -57,6 +265,29 @@ impl Display for ProposalType { } } +#[allow(missing_docs)] +#[derive(Debug, Error)] +pub enum ProposalTypeError { + #[error("Invalid proposal type.")] + InvalidProposalType, +} + +impl TryFrom for HashSet> { + type Error = ProposalTypeError; + + fn try_from(value: StewardsUpdate) -> Result { + let mut data = HashSet::default(); + + if value.add.is_some() { + data.insert(AddRemove::Add(value.add.unwrap())); + } + for steward in value.remove { + data.insert(AddRemove::Remove(steward)); + } + Ok(data) + } +} + impl TryFrom for AddRemove
{ type Error = ProposalTypeError; @@ -211,3 +442,107 @@ impl Display for StorageProposal { ) } } + +#[cfg(any(test, feature = "testing"))] +/// Testing helpers and and strategies for governance proposals +pub mod testing { + use namada_core::types::address::testing::arb_non_internal_address; + use namada_core::types::hash::testing::arb_hash; + use namada_core::types::storage::testing::arb_epoch; + use namada_core::types::token::testing::arb_amount; + use proptest::prelude::*; + use proptest::{collection, option, prop_compose}; + + use super::*; + + /// Generate an arbitrary add or removal of what's generated by the supplied + /// strategy + pub fn arb_add_remove( + strategy: X, + ) -> impl Strategy::Value>> { + (0..2, strategy).prop_map(|(discriminant, val)| match discriminant { + 0 => AddRemove::Add(val), + 1 => AddRemove::Remove(val), + _ => unreachable!(), + }) + } + + prop_compose! { + /// Generate an arbitrary PGF target + pub fn arb_pgf_target()( + target in arb_non_internal_address(), + amount in arb_amount(), + ) -> PGFTarget { + PGFTarget { + target, + amount, + } + } + } + + /// Generate an arbitrary PGF action + pub fn arb_pgf_action() -> impl Strategy { + arb_add_remove(arb_pgf_target()) + .prop_map(PGFAction::Continuous) + .boxed() + .prop_union(arb_pgf_target().prop_map(PGFAction::Retro).boxed()) + } + + /// Generate an arbitrary proposal type + pub fn arb_proposal_type() -> impl Strategy { + option::of(arb_hash()) + .prop_map(ProposalType::Default) + .boxed() + .prop_union( + collection::hash_set( + arb_add_remove(arb_non_internal_address()), + 0..10, + ) + .prop_map(ProposalType::PGFSteward) + .boxed(), + ) + .or(collection::vec(arb_pgf_action(), 0..10) + .prop_map(ProposalType::PGFPayment) + .boxed()) + } + + prop_compose! { + /// Generate a proposal initialization + pub fn arb_init_proposal()( + id: Option, + content in arb_hash(), + author in arb_non_internal_address(), + r#type in arb_proposal_type(), + voting_start_epoch in arb_epoch(), + voting_end_epoch in arb_epoch(), + grace_epoch in arb_epoch(), + ) -> InitProposalData { + InitProposalData { + id, + content, + author, + r#type, + voting_start_epoch, + voting_end_epoch, + grace_epoch, + } + } + } + + prop_compose! { + /// Generate an arbitrary vote proposal + pub fn arb_vote_proposal()( + id: u64, + vote in arb_proposal_vote(), + voter in arb_non_internal_address(), + delegations in collection::vec(arb_non_internal_address(), 0..10), + ) -> VoteProposalData { + VoteProposalData { + id, + vote, + voter, + delegations, + } + } + } +} diff --git a/governance/src/storage/vote.rs b/governance/src/storage/vote.rs index 8e838b4200..67e940017a 100644 --- a/governance/src/storage/vote.rs +++ b/governance/src/storage/vote.rs @@ -6,6 +6,46 @@ use serde::{Deserialize, Serialize}; use super::super::cli::onchain::ProposalVote; use super::proposal::ProposalType; +#[derive( + Debug, + Clone, + PartialEq, + BorshSerialize, + BorshDeserialize, + Eq, + Serialize, + Deserialize, +)] +/// The vote for a proposal +pub enum StorageProposalVote { + /// Yes + Yay(VoteType), + /// No + Nay, + /// Abstain + Abstain, +} + +/// The type of a governance vote with the optional associated Memo +#[derive( + Debug, + Clone, + PartialEq, + BorshSerialize, + BorshDeserialize, + Eq, + Serialize, + Deserialize, +)] +pub enum VoteType { + /// A default vote without Memo + Default, + /// A vote for the PGF stewards + PGFSteward, + /// A vote for a PGF payment proposal + PGFPayment, +} + impl StorageProposalVote { /// Check if a vote is yay pub fn is_yay(&self) -> bool { @@ -104,3 +144,32 @@ impl PartialEq for ProposalType { } } } + +#[cfg(any(test, feature = "testing"))] +/// Testing helpers and and strategies for governance proposals +pub mod testing { + use proptest::prelude::*; + + use super::*; + + prop_compose! { + /// Geerate an arbitrary vote type + pub fn arb_vote_type()(discriminant in 0..3) -> VoteType { + match discriminant { + 0 => VoteType::Default, + 1 => VoteType::PGFSteward, + 2 => VoteType::PGFPayment, + _ => unreachable!(), + } + } + } + + /// Generate an arbitrary proposal vote + pub fn arb_proposal_vote() -> impl Strategy { + arb_vote_type() + .prop_map(StorageProposalVote::Yay) + .boxed() + .prop_union(Just(StorageProposalVote::Nay).boxed()) + .or(Just(StorageProposalVote::Abstain).boxed()) + } +} diff --git a/governance/src/utils.rs b/governance/src/utils.rs index fe6db8091d..fb5bf942de 100644 --- a/governance/src/utils.rs +++ b/governance/src/utils.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use std::fmt::Display; -use borsh::{BorshDeserialize, BorshSerialize}; +use namada_core::borsh::{BorshDeserialize, BorshSerialize}; use namada_core::types::address::Address; use namada_core::types::storage::Epoch; use namada_core::types::token; diff --git a/tx/src/data/governance.rs b/tx/src/data/governance.rs deleted file mode 100644 index f2c63a4345..0000000000 --- a/tx/src/data/governance.rs +++ /dev/null @@ -1,216 +0,0 @@ -use std::collections::HashSet; - -use namada_core::borsh::{BorshDeserialize, BorshSerialize}; -use namada_core::types::address::Address; -use namada_core::types::governance::{ - AddRemove, DefaultProposal, PGFAction, PGFTarget, PgfFundingProposal, - PgfStewardProposal, ProposalType, StorageProposalVote, -}; -use namada_core::types::hash::Hash; -use namada_core::types::storage::Epoch; -use serde::{Deserialize, Serialize}; -use thiserror::Error; - -#[allow(missing_docs)] -#[derive(Debug, Error)] -pub enum ProposalError { - #[error("Invalid proposal data.")] - InvalidProposalData, -} - -/// A tx data type to hold proposal data -#[derive( - Debug, - Clone, - PartialEq, - BorshSerialize, - BorshDeserialize, - Serialize, - Deserialize, -)] -pub struct InitProposalData { - /// The proposal id - pub id: Option, - /// The proposal content - pub content: Hash, - /// The proposal author address - pub author: Address, - /// The proposal type - pub r#type: ProposalType, - /// The epoch from which voting is allowed - pub voting_start_epoch: Epoch, - /// The epoch from which voting is stopped - pub voting_end_epoch: Epoch, - /// The epoch from which this changes are executed - pub grace_epoch: Epoch, -} - -impl InitProposalData { - /// Get the hash of the corresponding extra data section - pub fn get_section_code_hash(&self) -> Option { - match self.r#type { - ProposalType::Default(hash) => hash, - _ => None, - } - } -} - -/// A tx data type to hold vote proposal data -#[derive( - Debug, - Clone, - PartialEq, - BorshSerialize, - BorshDeserialize, - Serialize, - Deserialize, -)] -pub struct VoteProposalData { - /// The proposal id - pub id: u64, - /// The proposal vote - pub vote: StorageProposalVote, - /// The proposal author address - pub voter: Address, - /// Delegator addresses - pub delegations: Vec
, -} - -impl TryFrom for InitProposalData { - type Error = ProposalError; - - fn try_from(value: DefaultProposal) -> Result { - Ok(InitProposalData { - id: value.proposal.id, - content: Hash::default(), - author: value.proposal.author, - r#type: ProposalType::Default(None), - voting_start_epoch: value.proposal.voting_start_epoch, - voting_end_epoch: value.proposal.voting_end_epoch, - grace_epoch: value.proposal.grace_epoch, - }) - } -} - -impl TryFrom for InitProposalData { - type Error = ProposalError; - - fn try_from(value: PgfStewardProposal) -> Result { - let extra_data = - HashSet::>::try_from(value.data).unwrap(); - - Ok(InitProposalData { - id: value.proposal.id, - content: Hash::default(), - author: value.proposal.author, - r#type: ProposalType::PGFSteward(extra_data), - voting_start_epoch: value.proposal.voting_start_epoch, - voting_end_epoch: value.proposal.voting_end_epoch, - grace_epoch: value.proposal.grace_epoch, - }) - } -} - -impl TryFrom for InitProposalData { - type Error = ProposalError; - - fn try_from(value: PgfFundingProposal) -> Result { - let continuous_fundings = value - .data - .continuous - .iter() - .cloned() - .map(|funding| { - let target = PGFTarget { - target: funding.address, - amount: funding.amount, - }; - if funding.amount.is_zero() { - PGFAction::Continuous(AddRemove::Remove(target)) - } else { - PGFAction::Continuous(AddRemove::Add(target)) - } - }) - .collect::>(); - - let retro_fundings = value - .data - .retro - .iter() - .cloned() - .map(|funding| { - let target = PGFTarget { - target: funding.address, - amount: funding.amount, - }; - PGFAction::Retro(target) - }) - .collect::>(); - - let extra_data = [continuous_fundings, retro_fundings].concat(); - - Ok(InitProposalData { - id: value.proposal.id, - content: Hash::default(), - author: value.proposal.author, - r#type: ProposalType::PGFPayment(extra_data), - voting_start_epoch: value.proposal.voting_start_epoch, - voting_end_epoch: value.proposal.voting_end_epoch, - grace_epoch: value.proposal.grace_epoch, - }) - } -} - -#[cfg(any(test, feature = "testing"))] -/// Tests and strategies for governance -pub mod tests { - use namada_core::types::address::testing::arb_non_internal_address; - use namada_core::types::governance::testing::{ - arb_proposal_type, arb_proposal_vote, - }; - use namada_core::types::hash::testing::arb_hash; - use namada_core::types::storage::testing::arb_epoch; - use proptest::{collection, prop_compose}; - - use super::*; - - prop_compose! { - /// Generate a proposal initialization - pub fn arb_init_proposal()( - id: Option, - content in arb_hash(), - author in arb_non_internal_address(), - r#type in arb_proposal_type(), - voting_start_epoch in arb_epoch(), - voting_end_epoch in arb_epoch(), - grace_epoch in arb_epoch(), - ) -> InitProposalData { - InitProposalData { - id, - content, - author, - r#type, - voting_start_epoch, - voting_end_epoch, - grace_epoch, - } - } - } - - prop_compose! { - /// Generate an arbitrary vote proposal - pub fn arb_vote_proposal()( - id: u64, - vote in arb_proposal_vote(), - voter in arb_non_internal_address(), - delegations in collection::vec(arb_non_internal_address(), 0..10), - ) -> VoteProposalData { - VoteProposalData { - id, - vote, - voter, - delegations, - } - } - } -} diff --git a/tx/src/data/mod.rs b/tx/src/data/mod.rs index 4b408bb850..b421902abd 100644 --- a/tx/src/data/mod.rs +++ b/tx/src/data/mod.rs @@ -6,8 +6,6 @@ pub mod account; /// non-decryptability pub mod decrypted; pub mod eval_vp; -/// txs to manage governance -pub mod governance; /// txs to manage pgf pub mod pgf; /// txs to manage pos diff --git a/tx/src/types.rs b/tx/src/types.rs index a32e06ed10..e969ba4bd8 100644 --- a/tx/src/types.rs +++ b/tx/src/types.rs @@ -39,14 +39,11 @@ pub enum VerifySigError { Gas(#[from] namada_gas::Error), } +#[allow(missing_docs)] #[derive(Error, Debug)] pub enum Error { #[error("Error decoding a transaction from bytes: {0}")] TxDecodingError(prost::DecodeError), - #[error("Error deserializing transaction field bytes: {0}")] - TxDeserializingError(std::io::Error), - #[error("Error deserializing transaction")] - OfflineTxDeserializationError, #[error("Timestamp is empty")] NoTimestampError, #[error("Timestamp is invalid: {0}")] @@ -57,12 +54,8 @@ pub enum Error { InvalidJSONDeserialization(String), #[error("The wrapper signature is invalid.")] InvalidWrapperSignature, - #[error("Signature verification went out of gas: {0}")] - OutOfGas(namada_gas::Error), } -pub type Result = std::result::Result; - /// This can be used to sign an arbitrary tx. The signature is produced and /// verified on the tx data concatenated with the tx code, however the tx code /// itself is not part of this structure. @@ -345,69 +338,6 @@ impl Code { } } -#[derive( - Clone, - Debug, - BorshSerialize, - BorshDeserialize, - BorshSchema, - Serialize, - Deserialize, - Eq, - PartialEq, -)] -pub struct SignatureIndex { - pub pubkey: common::PublicKey, - pub index: Option<(Address, u8)>, - pub signature: common::Signature, -} - -impl SignatureIndex { - pub fn from_single_signature( - pubkey: common::PublicKey, - signature: common::Signature, - ) -> Self { - Self { - pubkey, - signature, - index: None, - } - } - - pub fn to_vec(&self) -> Vec { - vec![self.clone()] - } - - pub fn serialize(&self) -> String { - let signature_bytes = self.serialize_to_vec(); - HEXUPPER.encode(&signature_bytes) - } - - pub fn deserialize(data: &[u8]) -> Result { - if let Ok(hex) = serde_json::from_slice::(data) { - match HEXUPPER.decode(hex.as_bytes()) { - Ok(bytes) => Self::try_from_slice(&bytes) - .map_err(Error::TxDeserializingError), - Err(_) => Err(Error::OfflineTxDeserializationError), - } - } else { - Err(Error::OfflineTxDeserializationError) - } - } -} - -impl Ord for SignatureIndex { - fn cmp(&self, other: &Self) -> Ordering { - self.pubkey.cmp(&other.pubkey) - } -} - -impl PartialOrd for SignatureIndex { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - /// Indicates the list of public keys against which signatures will be verified #[derive( Clone, From b8cecab63a3621a4fde9f039af2a439a2518be29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 20 Dec 2023 18:41:24 +0000 Subject: [PATCH 054/118] move ics23_specs from state into merkle_tree --- {state => merkle_tree}/src/ics23_specs.rs | 0 merkle_tree/src/lib.rs | 3 +++ state/src/lib.rs | 1 - 3 files changed, 3 insertions(+), 1 deletion(-) rename {state => merkle_tree}/src/ics23_specs.rs (100%) diff --git a/state/src/ics23_specs.rs b/merkle_tree/src/ics23_specs.rs similarity index 100% rename from state/src/ics23_specs.rs rename to merkle_tree/src/ics23_specs.rs diff --git a/merkle_tree/src/lib.rs b/merkle_tree/src/lib.rs index b7e448b14e..ac983f66cf 100644 --- a/merkle_tree/src/lib.rs +++ b/merkle_tree/src/lib.rs @@ -1,4 +1,7 @@ //! The merkle tree in the storage + +pub mod ics23_specs; + use std::fmt; use std::str::FromStr; diff --git a/state/src/lib.rs b/state/src/lib.rs index b58b0b6664..6d3108da41 100644 --- a/state/src/lib.rs +++ b/state/src/lib.rs @@ -1,6 +1,5 @@ //! Ledger's state storage with key-value backed store and a merkle tree -pub mod ics23_specs; #[cfg(any(test, feature = "testing"))] pub mod mockdb; pub mod traits; From 0bba4e4064b2afe342c93cd2e07313ab5a8f41a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Thu, 21 Dec 2023 10:58:04 +0000 Subject: [PATCH 055/118] proof_of_stake: wip fix build --- Cargo.lock | 5 + proof_of_stake/Cargo.toml | 12 +- proof_of_stake/src/epoched.rs | 74 +- proof_of_stake/src/error.rs | 26 +- proof_of_stake/src/lib.rs | 111 +- proof_of_stake/src/parameters.rs | 2 +- proof_of_stake/src/pos_queries.rs | 10 +- proof_of_stake/src/tests.rs | 6885 ++++++++++++++++++ proof_of_stake/src/tests/state_machine.rs | 10 +- proof_of_stake/src/tests/state_machine_v2.rs | 8 +- proof_of_stake/src/types/mod.rs | 6 +- 11 files changed, 7022 insertions(+), 127 deletions(-) create mode 100644 proof_of_stake/src/tests.rs diff --git a/Cargo.lock b/Cargo.lock index 4922a9207f..99b711d36c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4553,7 +4553,12 @@ dependencies = [ "data-encoding", "derivative", "itertools 0.10.5", + "namada_account", "namada_core", + "namada_governance", + "namada_parameters", + "namada_storage", + "namada_trans_token", "once_cell", "pretty_assertions", "proptest", diff --git a/proof_of_stake/Cargo.toml b/proof_of_stake/Cargo.toml index 391c8ddb8f..53edb78f1c 100644 --- a/proof_of_stake/Cargo.toml +++ b/proof_of_stake/Cargo.toml @@ -18,19 +18,25 @@ default = [] testing = ["proptest"] [dependencies] -namada_core = {path = "../core", default-features = false} +namada_account = { path = "../account" } +namada_core = { path = "../core", default-features = false } +namada_governance = { path = "../governance" } +namada_storage = { path = "../storage" } +namada_parameters = { path = "../parameters" } +namada_trans_token = { path = "../trans_token" } + borsh.workspace = true data-encoding.workspace = true derivative.workspace = true once_cell.workspace = true -proptest = {workspace = true, optional = true} +proptest = { workspace = true, optional = true } serde.workspace = true thiserror.workspace = true tracing.workspace = true [dev-dependencies] -namada_core = {path = "../core", features = ["testing"]} +namada_core = { path = "../core", features = ["testing"] } assert_matches.workspace = true itertools.workspace = true proptest.workspace = true diff --git a/proof_of_stake/src/epoched.rs b/proof_of_stake/src/epoched.rs index fd1c248c74..50160421ab 100644 --- a/proof_of_stake/src/epoched.rs +++ b/proof_of_stake/src/epoched.rs @@ -7,13 +7,11 @@ use std::marker::PhantomData; use std::{cmp, ops}; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; -use namada_core::ledger::storage_api; -use namada_core::ledger::storage_api::collections::lazy_map::{ - LazyMap, NestedMap, -}; -use namada_core::ledger::storage_api::collections::{self, LazyCollection}; -use namada_core::ledger::storage_api::{StorageRead, StorageWrite}; use namada_core::types::storage::{self, Epoch}; +use namada_storage; +use namada_storage::collections::lazy_map::{LazyMap, NestedMap}; +use namada_storage::collections::{self, LazyCollection}; +use namada_storage::{StorageRead, StorageWrite}; use crate::parameters::PosParams; use crate::read_pos_params; @@ -80,7 +78,7 @@ where storage: &mut S, value: Data, current_epoch: Epoch, - ) -> storage_api::Result<()> + ) -> namada_storage::Result<()> where S: StorageWrite + StorageRead, { @@ -96,7 +94,7 @@ where storage: &S, epoch: Epoch, params: &PosParams, - ) -> storage_api::Result> + ) -> namada_storage::Result> where S: StorageRead, { @@ -138,7 +136,7 @@ where value: Data, current_epoch: Epoch, offset: u64, - ) -> storage_api::Result<()> + ) -> namada_storage::Result<()> where S: StorageWrite + StorageRead, { @@ -153,7 +151,7 @@ where value: Data, current_epoch: Epoch, offset: u64, - ) -> storage_api::Result<()> + ) -> namada_storage::Result<()> where S: StorageWrite + StorageRead, { @@ -173,7 +171,7 @@ where storage: &mut S, params: &PosParams, current_epoch: Epoch, - ) -> storage_api::Result<()> + ) -> namada_storage::Result<()> where S: StorageWrite + StorageRead, { @@ -248,7 +246,7 @@ where fn get_last_update( &self, storage: &S, - ) -> storage_api::Result> + ) -> namada_storage::Result> where S: StorageRead, { @@ -279,7 +277,7 @@ where fn get_oldest_epoch( &self, storage: &S, - ) -> storage_api::Result> + ) -> namada_storage::Result> where S: StorageRead, { @@ -291,7 +289,7 @@ where &self, storage: &mut S, new_oldest_epoch: Epoch, - ) -> storage_api::Result<()> + ) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -326,7 +324,7 @@ where &self, storage: &mut S, epoch: Epoch, - ) -> storage_api::Result<()> + ) -> namada_storage::Result<()> where S: StorageWrite + StorageRead, { @@ -345,7 +343,7 @@ where pub fn get_last_update( &self, storage: &S, - ) -> storage_api::Result> + ) -> namada_storage::Result> where S: StorageRead, { @@ -358,7 +356,7 @@ where &self, storage: &mut S, current_epoch: Epoch, - ) -> storage_api::Result<()> + ) -> namada_storage::Result<()> where S: StorageWrite + StorageRead, { @@ -375,7 +373,7 @@ where fn get_oldest_epoch( &self, storage: &S, - ) -> storage_api::Result> + ) -> namada_storage::Result> where S: StorageRead, { @@ -387,7 +385,7 @@ where &self, storage: &mut S, new_oldest_epoch: Epoch, - ) -> storage_api::Result<()> + ) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -407,7 +405,7 @@ where storage: &mut S, params: &PosParams, current_epoch: Epoch, - ) -> storage_api::Result<()> + ) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -487,7 +485,7 @@ where storage: &mut S, value: Data, current_epoch: Epoch, - ) -> storage_api::Result<()> + ) -> namada_storage::Result<()> where S: StorageWrite + StorageRead, { @@ -502,7 +500,7 @@ where &self, storage: &S, epoch: Epoch, - ) -> storage_api::Result> + ) -> namada_storage::Result> where S: StorageRead, { @@ -515,7 +513,7 @@ where storage: &S, epoch: Epoch, params: &PosParams, - ) -> storage_api::Result> + ) -> namada_storage::Result> where S: StorageRead, { @@ -557,7 +555,7 @@ where value: Data, current_epoch: Epoch, offset: u64, - ) -> storage_api::Result<()> + ) -> namada_storage::Result<()> where S: StorageWrite + StorageRead, Data: Default, @@ -577,7 +575,7 @@ where value: Data, current_epoch: Epoch, offset: u64, - ) -> storage_api::Result<()> + ) -> namada_storage::Result<()> where S: StorageWrite + StorageRead, { @@ -592,7 +590,7 @@ where value: Data, current_epoch: Epoch, offset: u64, - ) -> storage_api::Result<()> + ) -> namada_storage::Result<()> where S: StorageWrite + StorageRead, { @@ -610,7 +608,7 @@ where storage: &mut S, params: &PosParams, current_epoch: Epoch, - ) -> storage_api::Result<()> + ) -> namada_storage::Result<()> where S: StorageWrite + StorageRead, { @@ -692,7 +690,7 @@ where pub fn get_last_update( &self, storage: &S, - ) -> storage_api::Result> + ) -> namada_storage::Result> where S: StorageRead, { @@ -713,7 +711,7 @@ where pub fn to_hashmap( &self, storage: &S, - ) -> storage_api::Result> + ) -> namada_storage::Result> where S: StorageRead, { @@ -736,7 +734,7 @@ where fn get_oldest_epoch( &self, storage: &S, - ) -> storage_api::Result> + ) -> namada_storage::Result> where S: StorageRead, { @@ -748,7 +746,7 @@ where &self, storage: &mut S, new_oldest_epoch: Epoch, - ) -> storage_api::Result<()> + ) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -1097,7 +1095,7 @@ mod test { use crate::types::GenesisValidator; #[test] - fn test_epoched_data_trimming() -> storage_api::Result<()> { + fn test_epoched_data_trimming() -> namada_storage::Result<()> { let mut s = init_storage()?; let key_prefix = storage::Key::parse("test").unwrap(); @@ -1168,7 +1166,7 @@ mod test { } #[test] - fn test_epoched_without_data_trimming() -> storage_api::Result<()> { + fn test_epoched_without_data_trimming() -> namada_storage::Result<()> { let mut s = init_storage()?; let key_prefix = storage::Key::parse("test").unwrap(); @@ -1236,7 +1234,7 @@ mod test { } #[test] - fn test_epoched_delta_data_trimming() -> storage_api::Result<()> { + fn test_epoched_delta_data_trimming() -> namada_storage::Result<()> { let mut s = init_storage()?; let key_prefix = storage::Key::parse("test").unwrap(); @@ -1309,7 +1307,8 @@ mod test { } #[test] - fn test_epoched_delta_without_data_trimming() -> storage_api::Result<()> { + fn test_epoched_delta_without_data_trimming() -> namada_storage::Result<()> + { let mut s = init_storage()?; // Nothing should ever get trimmed @@ -1393,9 +1392,10 @@ mod test { Ok(()) } - fn init_storage() -> storage_api::Result { + fn init_storage() -> namada_storage::Result { let mut s = TestWlStorage::default(); - let gov_params = namada_core::ledger::governance::parameters::GovernanceParameters::default(); + let gov_params = + namada_governance::parameters::GovernanceParameters::default(); gov_params.init_storage(&mut s)?; crate::test_utils::init_genesis_helper( &mut s, diff --git a/proof_of_stake/src/error.rs b/proof_of_stake/src/error.rs index 9cc7caa282..ec8e7c9733 100644 --- a/proof_of_stake/src/error.rs +++ b/proof_of_stake/src/error.rs @@ -1,10 +1,10 @@ /// Custom error types use std::num::TryFromIntError; -use namada_core::ledger::storage_api; use namada_core::types::address::Address; use namada_core::types::dec::Dec; use namada_core::types::storage::Epoch; +use namada_storage; use thiserror::Error; use crate::rewards; @@ -177,73 +177,73 @@ pub enum ConsensusKeyChangeError { MustBeEd25519, } -impl From for storage_api::Error { +impl From for namada_storage::Error { fn from(err: BecomeValidatorError) -> Self { Self::new(err) } } -impl From for storage_api::Error { +impl From for namada_storage::Error { fn from(err: BondError) -> Self { Self::new(err) } } -impl From for storage_api::Error { +impl From for namada_storage::Error { fn from(err: UnbondError) -> Self { Self::new(err) } } -impl From for storage_api::Error { +impl From for namada_storage::Error { fn from(err: WithdrawError) -> Self { Self::new(err) } } -impl From for storage_api::Error { +impl From for namada_storage::Error { fn from(err: CommissionRateChangeError) -> Self { Self::new(err) } } -impl From for storage_api::Error { +impl From for namada_storage::Error { fn from(err: InflationError) -> Self { Self::new(err) } } -impl From for storage_api::Error { +impl From for namada_storage::Error { fn from(err: UnjailValidatorError) -> Self { Self::new(err) } } -impl From for storage_api::Error { +impl From for namada_storage::Error { fn from(err: RedelegationError) -> Self { Self::new(err) } } -impl From for storage_api::Error { +impl From for namada_storage::Error { fn from(err: DeactivationError) -> Self { Self::new(err) } } -impl From for storage_api::Error { +impl From for namada_storage::Error { fn from(err: ReactivationError) -> Self { Self::new(err) } } -impl From for storage_api::Error { +impl From for namada_storage::Error { fn from(err: MetadataError) -> Self { Self::new(err) } } -impl From for storage_api::Error { +impl From for namada_storage::Error { fn from(err: ConsensusKeyChangeError) -> Self { Self::new(err) } diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index a390d6ec00..280060b06e 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -27,17 +27,21 @@ use std::cmp::{self}; use std::collections::{BTreeMap, BTreeSet, HashSet}; pub use error::*; -use namada_core::ledger::storage_api::collections::lazy_map::{ - Collectable, LazyMap, NestedSubKey, SubKey, -}; -use namada_core::ledger::storage_api::{ - self, token, StorageRead, StorageWrite, -}; -use namada_core::types::address::{Address, InternalAddress}; +use namada_account::protocol_pk_key; +use namada_core::types::address::{self, Address, InternalAddress}; use namada_core::types::dec::Dec; -use namada_core::types::key::common; +use namada_core::types::key::{ + common, tm_consensus_key_raw_hash, PublicKeyTmRawHash, +}; use namada_core::types::storage::BlockHeight; pub use namada_core::types::storage::{Epoch, Key, KeySeg}; +use namada_storage::collections::lazy_map::{ + Collectable, LazyMap, NestedMap, NestedSubKey, SubKey, +}; +use namada_storage::collections::{LazyCollection, LazySet}; +use namada_storage::{ResultExt, StorageRead, StorageWrite}; +pub use namada_trans_token as token; +use once_cell::unsync::Lazy; pub use parameters::{OwnedPosParams, PosParams}; use crate::queries::{find_bonds, has_bonds}; @@ -106,7 +110,7 @@ pub fn init_genesis( storage: &mut S, params: &OwnedPosParams, current_epoch: Epoch, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -127,7 +131,7 @@ pub fn copy_genesis_validator_sets( storage: &mut S, params: &OwnedPosParams, current_epoch: Epoch, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -149,7 +153,7 @@ where pub fn is_validator( storage: &S, address: &Address, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead, { @@ -165,14 +169,14 @@ pub fn is_delegator( storage: &S, address: &Address, epoch: Option, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead, { let prefix = bonds_for_source_prefix(address); match epoch { Some(epoch) => { - let iter = storage_api::iter_prefix_bytes(storage, &prefix)?; + let iter = namada_storage::iter_prefix_bytes(storage, &prefix)?; for res in iter { let (key, _) = res?; if let Some((bond_id, bond_epoch)) = is_bond_key(&key) { @@ -186,7 +190,7 @@ where Ok(false) } None => { - let iter = storage_api::iter_prefix_bytes(storage, &prefix)?; + let iter = namada_storage::iter_prefix_bytes(storage, &prefix)?; for res in iter { let (key, _) = res?; if let Some((bond_id, _epoch)) = is_bond_key(&key) { @@ -210,7 +214,7 @@ pub fn bond_tokens( amount: token::Amount, current_epoch: Epoch, offset_opt: Option, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -307,7 +311,7 @@ where fn compute_total_consensus_stake( storage: &S, epoch: Epoch, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead, { @@ -333,7 +337,7 @@ where pub fn compute_and_store_total_consensus_stake( storage: &mut S, epoch: Epoch, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -366,7 +370,7 @@ pub fn unbond_tokens( amount: token::Amount, current_epoch: Epoch, is_redelegation: bool, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead + StorageWrite, { @@ -826,7 +830,7 @@ fn find_bonds_to_remove( storage: &S, bonds_handle: &LazyMap, amount: token::Amount, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead, { @@ -870,7 +874,7 @@ fn compute_modified_redelegation( redelegated_bonds: &RedelegatedTokens, start_epoch: Epoch, amount_to_unbond: token::Amount, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead, { @@ -910,7 +914,7 @@ where let (_, amount) = res?; Ok(amount) }) - .sum::>()?; + .sum::>()?; // TODO: move this into the `if total_redelegated <= remaining` branch // below, then we don't have to remove it in `fn @@ -965,7 +969,7 @@ fn update_redelegated_bonds( storage: &mut S, redelegated_bonds: &RedelegatedTokens, modified_redelegation: &ModifiedRedelegation, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -1041,7 +1045,7 @@ fn compute_new_redelegated_unbonds( redelegated_bonds: &RedelegatedBondsOrUnbonds, epochs_to_remove: &BTreeSet, modified: &ModifiedRedelegation, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead + StorageWrite, { @@ -1197,7 +1201,7 @@ pub struct BecomeValidator<'a> { pub fn become_validator( storage: &mut S, args: BecomeValidator<'_>, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -1217,14 +1221,14 @@ where let offset = offset_opt.unwrap_or(params.pipeline_len); if !address.is_established() { - return Err(storage_api::Error::new_const( + return Err(namada_storage::Error::new_const( "The given address {address} is not established. Only an \ established address can become a validator.", )); } if is_validator(storage, address)? { - return Err(storage_api::Error::new_const( + return Err(namada_storage::Error::new_const( "The given address is already a validator", )); } @@ -1232,7 +1236,7 @@ where // If the address is not yet a validator, it cannot have self-bonds, but it // may have delegations. if has_bonds(storage, address)? { - return Err(storage_api::Error::new_const( + return Err(namada_storage::Error::new_const( "The given address has delegations and therefore cannot become a \ validator. Unbond first.", )); @@ -1320,7 +1324,7 @@ pub fn change_consensus_key( validator: &Address, consensus_key: &common::PublicKey, current_epoch: Epoch, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -1358,7 +1362,7 @@ pub fn withdraw_tokens( source: Option<&Address>, validator: &Address, current_epoch: Epoch, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead + StorageWrite, { @@ -1497,7 +1501,7 @@ pub fn change_validator_commission_rate( validator: &Address, new_rate: Dec, current_epoch: Epoch, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -1559,7 +1563,7 @@ pub fn bond_amount( storage: &S, bond_id: &BondId, epoch: Epoch, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead, { @@ -1704,7 +1708,7 @@ pub fn bond_amounts_for_rewards( bond_id: &BondId, claim_start: Epoch, claim_end: Epoch, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -1799,7 +1803,7 @@ pub fn genesis_validator_set_tendermint( params: &PosParams, current_epoch: Epoch, mut f: impl FnMut(ValidatorSetUpdate) -> T, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -1832,7 +1836,7 @@ pub fn unjail_validator( storage: &mut S, validator: &Address, current_epoch: Epoch, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -1897,7 +1901,7 @@ pub fn is_validator_frozen( validator: &Address, current_epoch: Epoch, params: &PosParams, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead, { @@ -1918,7 +1922,7 @@ pub fn get_total_consensus_stake( storage: &S, epoch: Epoch, params: &PosParams, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead, { @@ -1935,7 +1939,7 @@ pub fn redelegate_tokens( dest_validator: &Address, current_epoch: Epoch, amount: token::Amount, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -2141,7 +2145,7 @@ pub fn deactivate_validator( storage: &mut S, validator: &Address, current_epoch: Epoch, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -2221,7 +2225,7 @@ pub fn reactivate_validator( storage: &mut S, validator: &Address, current_epoch: Epoch, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -2289,7 +2293,7 @@ where pub fn prune_liveness_data( storage: &mut S, current_epoch: Epoch, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -2326,7 +2330,7 @@ pub fn record_liveness_data( votes_epoch: Epoch, votes_height: BlockHeight, pos_params: &PosParams, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -2403,7 +2407,7 @@ pub fn jail_for_liveness( params: &PosParams, current_epoch: Epoch, jail_epoch: Epoch, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -2412,7 +2416,7 @@ where * params.liveness_window_check) .to_uint() .ok_or_else(|| { - storage_api::Error::SimpleMessage( + namada_storage::Error::SimpleMessage( "Found negative liveness threshold", ) })? @@ -2456,9 +2460,9 @@ where #[cfg(any(test, feature = "testing"))] /// PoS related utility functions to help set up tests. pub mod test_utils { - use namada_core::ledger::storage_api; - use namada_core::ledger::storage_api::token::credit_tokens; - use namada_core::ledger::storage_api::{StorageRead, StorageWrite}; + use namada_namada_trans_token::credit_tokens; + use namada_storage; + use namada_storage::{StorageRead, StorageWrite}; use super::*; use crate::parameters::PosParams; @@ -2472,7 +2476,7 @@ pub mod test_utils { params: &PosParams, validators: impl Iterator, current_epoch: namada_core::types::storage::Epoch, - ) -> storage_api::Result<()> + ) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -2535,11 +2539,12 @@ pub mod test_utils { owned: OwnedPosParams, validators: impl Iterator + Clone, current_epoch: namada_core::types::storage::Epoch, - ) -> storage_api::Result + ) -> namada_storage::Result where S: StorageRead + StorageWrite, { - let gov_params = namada_core::ledger::governance::parameters::GovernanceParameters::default(); + let gov_params = + namada_governance::parameters::GovernanceParameters::default(); gov_params.init_storage(storage)?; let params = read_non_pos_owned_params(storage, owned)?; init_genesis_helper(storage, ¶ms, validators, current_epoch)?; @@ -2560,7 +2565,7 @@ pub fn change_validator_metadata( discord_handle: Option, commission_rate: Option, current_epoch: Epoch, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -2594,7 +2599,7 @@ pub fn claim_reward_tokens( source: Option<&Address>, validator: &Address, current_epoch: Epoch, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead + StorageWrite, { @@ -2629,7 +2634,7 @@ pub fn query_reward_tokens( source: Option<&Address>, validator: &Address, current_epoch: Epoch, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead, { @@ -2656,7 +2661,7 @@ fn jail_validator( validator: &Address, current_epoch: Epoch, validator_set_update_epoch: Epoch, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { diff --git a/proof_of_stake/src/parameters.rs b/proof_of_stake/src/parameters.rs index cb963d9e36..7678416cbc 100644 --- a/proof_of_stake/src/parameters.rs +++ b/proof_of_stake/src/parameters.rs @@ -3,11 +3,11 @@ use std::str::FromStr; use borsh::{BorshDeserialize, BorshSerialize}; -use namada_core::ledger::governance::parameters::GovernanceParameters; use namada_core::types::dec::Dec; use namada_core::types::storage::Epoch; use namada_core::types::token; use namada_core::types::uint::Uint; +use namada_governance::parameters::GovernanceParameters; use thiserror::Error; /// Proof-of-Stake system parameters. This includes parameters that are used in diff --git a/proof_of_stake/src/pos_queries.rs b/proof_of_stake/src/pos_queries.rs index 6effbcfc51..8c5e3cdab5 100644 --- a/proof_of_stake/src/pos_queries.rs +++ b/proof_of_stake/src/pos_queries.rs @@ -1,14 +1,12 @@ //! Storage API for querying data about Proof-of-stake related //! data. This includes validator and epoch related data. -use namada_core::ledger::parameters::storage::get_max_proposal_bytes_key; -use namada_core::ledger::storage::WlStorage; -use namada_core::ledger::storage_api::collections::lazy_map::NestedSubKey; -use namada_core::ledger::{storage, storage_api}; use namada_core::types::address::Address; use namada_core::types::chain::ProposalBytes; use namada_core::types::storage::{BlockHeight, Epoch}; use namada_core::types::{key, token}; +use namada_parameters::storage::get_max_proposal_bytes_key; +use namada_storage::collections::lazy_map::NestedSubKey; use thiserror::Error; use crate::storage::find_validator_by_raw_hash; @@ -24,7 +22,7 @@ use crate::{ pub enum Error { /// A storage error occurred. #[error("Storage error: {0}")] - Storage(storage_api::Error), + Storage(namada_storage::Error), /// The given address is not among the set of consensus validators for /// the corresponding epoch. #[error( @@ -264,7 +262,7 @@ where /// Retrieve the `max_proposal_bytes` consensus parameter from storage. pub fn get_max_proposal_bytes(self) -> ProposalBytes { - storage_api::StorageRead::read( + namada_storage::StorageRead::read( self.wl_storage, &get_max_proposal_bytes_key(), ) diff --git a/proof_of_stake/src/tests.rs b/proof_of_stake/src/tests.rs new file mode 100644 index 0000000000..590673eb50 --- /dev/null +++ b/proof_of_stake/src/tests.rs @@ -0,0 +1,6885 @@ +//! PoS system tests + +mod state_machine; +mod state_machine_v2; +mod utils; + +use std::cmp::{max, min}; +use std::collections::{BTreeMap, BTreeSet, HashSet}; +use std::ops::{Deref, Range}; +use std::str::FromStr; + +use assert_matches::assert_matches; +use namada_core::ledger::storage::testing::TestWlStorage; +use namada_core::types::address::testing::{ + address_from_simple_seed, arb_established_address, established_address_1, + established_address_2, established_address_3, +}; +use namada_core::types::address::{Address, EstablishedAddressGen}; +use namada_core::types::dec::Dec; +use namada_core::types::key::common::{PublicKey, SecretKey}; +use namada_core::types::key::testing::{ + arb_common_keypair, common_sk_from_simple_seed, gen_keypair, +}; +use namada_core::types::key::RefTo; +use namada_core::types::storage::{BlockHeight, Epoch, Key}; +use namada_core::types::token::testing::arb_amount_non_zero_ceiled; +use namada_core::types::token::NATIVE_MAX_DECIMAL_PLACES; +use namada_core::types::{address, key, token}; +use namada_namada_trans_token::{credit_tokens, read_balance}; +use namada_storage::collections::lazy_map::{self, Collectable, NestedMap}; +use namada_storage::collections::LazyCollection; +use namada_storage::StorageRead; +use proptest::prelude::*; +use proptest::test_runner::Config; +// Use `RUST_LOG=info` (or another tracing level) and `--nocapture` to see +// `tracing` logs from tests +use test_log::test; + +use crate::epoched::DEFAULT_NUM_PAST_EPOCHS; +use crate::parameters::testing::arb_pos_params; +use crate::parameters::{OwnedPosParams, PosParams}; +use crate::rewards::PosRewardsCalculator; +use crate::test_utils::test_init_genesis; +use crate::types::{ + into_tm_voting_power, BondDetails, BondId, BondsAndUnbondsDetails, + ConsensusValidator, EagerRedelegatedBondsMap, GenesisValidator, Position, + RedelegatedTokens, ReverseOrdTokenAmount, Slash, SlashType, UnbondDetails, + ValidatorSetUpdate, ValidatorState, VoteInfo, WeightedValidator, +}; +use crate::{ + apply_list_slashes, become_validator, below_capacity_validator_set_handle, + bond_handle, bond_tokens, bonds_and_unbonds, change_consensus_key, + compute_amount_after_slashing_unbond, + compute_amount_after_slashing_withdraw, + compute_and_store_total_consensus_stake, compute_bond_at_epoch, + compute_modified_redelegation, compute_new_redelegated_unbonds, + compute_slash_bond_at_epoch, compute_slashable_amount, + consensus_validator_set_handle, copy_validator_sets_and_positions, + delegator_redelegated_bonds_handle, delegator_redelegated_unbonds_handle, + find_bonds_to_remove, find_validator_by_raw_hash, + fold_and_slash_redelegated_bonds, get_consensus_key_set, + get_num_consensus_validators, insert_validator_into_validator_set, + is_validator, process_slashes, + read_below_capacity_validator_set_addresses_with_stake, + read_below_threshold_validator_set_addresses, + read_consensus_validator_set_addresses_with_stake, read_total_stake, + read_validator_deltas_value, read_validator_stake, slash, + slash_redelegation, slash_validator, slash_validator_redelegation, + staking_token_address, total_bonded_handle, total_deltas_handle, + total_unbonded_handle, unbond_handle, unbond_tokens, unjail_validator, + update_validator_deltas, update_validator_set, + validator_consensus_key_handle, validator_incoming_redelegations_handle, + validator_outgoing_redelegations_handle, validator_set_positions_handle, + validator_set_update_tendermint, validator_slashes_handle, + validator_state_handle, validator_total_redelegated_bonded_handle, + validator_total_redelegated_unbonded_handle, withdraw_tokens, + write_pos_params, write_validator_address_raw_hash, BecomeValidator, + EagerRedelegatedUnbonds, FoldRedelegatedBondsResult, ModifiedRedelegation, + RedelegationError, +}; + +proptest! { + // Generate arb valid input for `test_test_init_genesis_aux` + #![proptest_config(Config { + cases: 100, + .. Config::default() + })] + #[test] + fn test_test_init_genesis( + + (pos_params, genesis_validators) in arb_params_and_genesis_validators(Some(5), 1..10), + start_epoch in (0_u64..1000).prop_map(Epoch), + + ) { + test_test_init_genesis_aux(pos_params, start_epoch, genesis_validators) + } +} + +proptest! { + // Generate arb valid input for `test_bonds_aux` + #![proptest_config(Config { + cases: 100, + .. Config::default() + })] + #[test] + fn test_bonds( + + (pos_params, genesis_validators) in arb_params_and_genesis_validators(Some(5), 1..3), + + ) { + test_bonds_aux(pos_params, genesis_validators) + } +} + +proptest! { + // Generate arb valid input for `test_become_validator_aux` + #![proptest_config(Config { + cases: 100, + .. Config::default() + })] + #[test] + fn test_become_validator( + + (pos_params, genesis_validators) in arb_params_and_genesis_validators(Some(5), 1..3), + new_validator in arb_established_address().prop_map(Address::Established), + new_validator_consensus_key in arb_common_keypair(), + + ) { + test_become_validator_aux(pos_params, new_validator, + new_validator_consensus_key, genesis_validators) + } +} + +proptest! { + // Generate arb valid input for `test_slashes_with_unbonding_aux` + #![proptest_config(Config { + cases: 100, + .. Config::default() + })] + #[test] + fn test_slashes_with_unbonding( + (params, genesis_validators, unbond_delay) + in test_slashes_with_unbonding_params() + ) { + test_slashes_with_unbonding_aux( + params, genesis_validators, unbond_delay) + } +} + +proptest! { + // Generate arb valid input for `test_unjail_validator_aux` + #![proptest_config(Config { + cases: 100, + .. Config::default() + })] + #[test] + fn test_unjail_validator( + (pos_params, genesis_validators) + in arb_params_and_genesis_validators(Some(4),6..9) + ) { + test_unjail_validator_aux(pos_params, + genesis_validators) + } +} + +proptest! { + // Generate arb valid input for `test_simple_redelegation_aux` + #![proptest_config(Config { + cases: 100, + .. Config::default() + })] + #[test] + fn test_simple_redelegation( + + genesis_validators in arb_genesis_validators(2..4, None), + (amount_delegate, amount_redelegate, amount_unbond) in arb_redelegation_amounts(20) + + ) { + test_simple_redelegation_aux(genesis_validators, amount_delegate, amount_redelegate, amount_unbond) + } +} + +proptest! { + // Generate arb valid input for `test_simple_redelegation_aux` + #![proptest_config(Config { + cases: 100, + .. Config::default() + })] + #[test] + fn test_redelegation_with_slashing( + + genesis_validators in arb_genesis_validators(2..4, None), + (amount_delegate, amount_redelegate, amount_unbond) in arb_redelegation_amounts(20) + + ) { + test_redelegation_with_slashing_aux(genesis_validators, amount_delegate, amount_redelegate, amount_unbond) + } +} + +proptest! { + // Generate arb valid input for `test_chain_redelegations_aux` + #![proptest_config(Config { + cases: 100, + .. Config::default() + })] + #[test] + fn test_chain_redelegations( + + genesis_validators in arb_genesis_validators(3..4, None), + + ) { + test_chain_redelegations_aux(genesis_validators) + } +} + +proptest! { + // Generate arb valid input for `test_overslashing_aux` + #![proptest_config(Config { + cases: 1, + .. Config::default() + })] + #[test] + fn test_overslashing( + + genesis_validators in arb_genesis_validators(4..5, None), + + ) { + test_overslashing_aux(genesis_validators) + } +} + +proptest! { + // Generate arb valid input for `test_unslashed_bond_amount_aux` + #![proptest_config(Config { + cases: 1, + .. Config::default() + })] + #[test] + fn test_unslashed_bond_amount( + + genesis_validators in arb_genesis_validators(4..5, None), + + ) { + test_unslashed_bond_amount_aux(genesis_validators) + } +} + +proptest! { + // Generate arb valid input for `test_slashed_bond_amount_aux` + #![proptest_config(Config { + cases: 1, + .. Config::default() + })] + #[test] + fn test_slashed_bond_amount( + + genesis_validators in arb_genesis_validators(4..5, None), + + ) { + test_slashed_bond_amount_aux(genesis_validators) + } +} + +proptest! { + // Generate arb valid input for `test_log_block_rewards_aux` + #![proptest_config(Config { + cases: 1, + .. Config::default() + })] + #[test] + fn test_log_block_rewards( + genesis_validators in arb_genesis_validators(4..10, None), + params in arb_pos_params(Some(5)) + + ) { + test_log_block_rewards_aux(genesis_validators, params) + } +} + +proptest! { + // Generate arb valid input for `test_update_rewards_products_aux` + #![proptest_config(Config { + cases: 1, + .. Config::default() + })] + #[test] + fn test_update_rewards_products( + genesis_validators in arb_genesis_validators(4..10, None), + + ) { + test_update_rewards_products_aux(genesis_validators) + } +} + +proptest! { + // Generate arb valid input for `test_consensus_key_change` + #![proptest_config(Config { + cases: 1, + .. Config::default() + })] + #[test] + fn test_consensus_key_change( + + genesis_validators in arb_genesis_validators(1..2, None), + + ) { + test_consensus_key_change_aux(genesis_validators) + } +} + +proptest! { + // Generate arb valid input for `test_is_delegator` + #![proptest_config(Config { + cases: 100, + .. Config::default() + })] + #[test] + fn test_is_delegator( + + genesis_validators in arb_genesis_validators(2..3, None), + + ) { + test_is_delegator_aux(genesis_validators) + } +} + +fn arb_params_and_genesis_validators( + num_max_validator_slots: Option, + val_size: Range, +) -> impl Strategy)> { + let params = arb_pos_params(num_max_validator_slots); + params.prop_flat_map(move |params| { + let validators = arb_genesis_validators( + val_size.clone(), + Some(params.validator_stake_threshold), + ); + (Just(params), validators) + }) +} + +fn test_slashes_with_unbonding_params() +-> impl Strategy, u64)> { + let params = arb_pos_params(Some(5)); + params.prop_flat_map(|params| { + let unbond_delay = 0..(params.slash_processing_epoch_offset() * 2); + // Must have at least 4 validators so we can slash one and the cubic + // slash rate will be less than 100% + let validators = arb_genesis_validators(4..10, None); + (Just(params), validators, unbond_delay) + }) +} + +/// Test genesis initialization +fn test_test_init_genesis_aux( + params: OwnedPosParams, + start_epoch: Epoch, + mut validators: Vec, +) { + println!( + "Test inputs: {params:?}, {start_epoch}, genesis validators: \ + {validators:#?}" + ); + let mut s = TestWlStorage::default(); + s.storage.block.epoch = start_epoch; + + validators.sort_by(|a, b| b.tokens.cmp(&a.tokens)); + let params = test_init_genesis( + &mut s, + params, + validators.clone().into_iter(), + start_epoch, + ) + .unwrap(); + + let mut bond_details = bonds_and_unbonds(&s, None, None).unwrap(); + assert!(bond_details.iter().all(|(_id, details)| { + details.unbonds.is_empty() && details.slashes.is_empty() + })); + + for (i, validator) in validators.into_iter().enumerate() { + let addr = &validator.address; + let self_bonds = bond_details + .remove(&BondId { + source: addr.clone(), + validator: addr.clone(), + }) + .unwrap(); + assert_eq!(self_bonds.bonds.len(), 1); + assert_eq!( + self_bonds.bonds[0], + BondDetails { + start: start_epoch, + amount: validator.tokens, + slashed_amount: None, + } + ); + + let state = validator_state_handle(&validator.address) + .get(&s, start_epoch, ¶ms) + .unwrap(); + if (i as u64) < params.max_validator_slots + && validator.tokens >= params.validator_stake_threshold + { + // should be in consensus set + let handle = consensus_validator_set_handle().at(&start_epoch); + assert!(handle.at(&validator.tokens).iter(&s).unwrap().any( + |result| { + let (_pos, addr) = result.unwrap(); + addr == validator.address + } + )); + assert_eq!(state, Some(ValidatorState::Consensus)); + } else if validator.tokens >= params.validator_stake_threshold { + // Should be in below-capacity set if its tokens are greater than + // `validator_stake_threshold` + let handle = below_capacity_validator_set_handle().at(&start_epoch); + assert!(handle.at(&validator.tokens.into()).iter(&s).unwrap().any( + |result| { + let (_pos, addr) = result.unwrap(); + addr == validator.address + } + )); + assert_eq!(state, Some(ValidatorState::BelowCapacity)); + } else { + // Should be in below-threshold + let bt_addresses = + read_below_threshold_validator_set_addresses(&s, start_epoch) + .unwrap(); + assert!( + bt_addresses + .into_iter() + .any(|addr| { addr == validator.address }) + ); + assert_eq!(state, Some(ValidatorState::BelowThreshold)); + } + } +} + +/// Test bonding +/// NOTE: copy validator sets each time we advance the epoch +fn test_bonds_aux(params: OwnedPosParams, validators: Vec) { + // This can be useful for debugging: + // params.pipeline_len = 2; + // params.unbonding_len = 4; + println!("\nTest inputs: {params:?}, genesis validators: {validators:#?}"); + let mut s = TestWlStorage::default(); + + // Genesis + let start_epoch = s.storage.block.epoch; + let mut current_epoch = s.storage.block.epoch; + let params = test_init_genesis( + &mut s, + params, + validators.clone().into_iter(), + current_epoch, + ) + .unwrap(); + s.commit_block().unwrap(); + + // Advance to epoch 1 + current_epoch = advance_epoch(&mut s, ¶ms); + let self_bond_epoch = current_epoch; + + let validator = validators.first().unwrap(); + + // Read some data before submitting bond + let pipeline_epoch = current_epoch + params.pipeline_len; + let staking_token = staking_token_address(&s); + let pos_balance_pre = s + .read::(&token::balance_key( + &staking_token, + &super::ADDRESS, + )) + .unwrap() + .unwrap_or_default(); + let total_stake_before = + read_total_stake(&s, ¶ms, pipeline_epoch).unwrap(); + + // Self-bond + let amount_self_bond = token::Amount::from_uint(100_500_000, 0).unwrap(); + credit_tokens(&mut s, &staking_token, &validator.address, amount_self_bond) + .unwrap(); + bond_tokens( + &mut s, + None, + &validator.address, + amount_self_bond, + current_epoch, + None, + ) + .unwrap(); + + // Check the bond delta + let self_bond = bond_handle(&validator.address, &validator.address); + let delta = self_bond.get_delta_val(&s, pipeline_epoch).unwrap(); + assert_eq!(delta, Some(amount_self_bond)); + + // Check the validator in the validator set + let set = + read_consensus_validator_set_addresses_with_stake(&s, pipeline_epoch) + .unwrap(); + assert!(set.into_iter().any( + |WeightedValidator { + bonded_stake, + address, + }| { + address == validator.address + && bonded_stake == validator.tokens + amount_self_bond + } + )); + + let val_deltas = + read_validator_deltas_value(&s, &validator.address, &pipeline_epoch) + .unwrap(); + assert_eq!(val_deltas, Some(amount_self_bond.change())); + + let total_deltas_handle = total_deltas_handle(); + assert_eq!( + current_epoch, + total_deltas_handle.get_last_update(&s).unwrap().unwrap() + ); + let total_stake_after = + read_total_stake(&s, ¶ms, pipeline_epoch).unwrap(); + assert_eq!(total_stake_before + amount_self_bond, total_stake_after); + + // Check bond details after self-bond + let self_bond_id = BondId { + source: validator.address.clone(), + validator: validator.address.clone(), + }; + let check_bond_details = |ix, bond_details: BondsAndUnbondsDetails| { + println!("Check index {ix}"); + let details = bond_details.get(&self_bond_id).unwrap(); + assert_eq!( + details.bonds.len(), + 2, + "Contains genesis and newly added self-bond" + ); + dbg!(&details.bonds); + assert_eq!( + details.bonds[0], + BondDetails { + start: start_epoch, + amount: validator.tokens, + slashed_amount: None + }, + ); + assert_eq!( + details.bonds[1], + BondDetails { + start: pipeline_epoch, + amount: amount_self_bond, + slashed_amount: None + }, + ); + }; + // Try to call it with different combinations of owner/validator args + check_bond_details(0, bonds_and_unbonds(&s, None, None).unwrap()); + check_bond_details( + 1, + bonds_and_unbonds(&s, Some(validator.address.clone()), None).unwrap(), + ); + check_bond_details( + 2, + bonds_and_unbonds(&s, None, Some(validator.address.clone())).unwrap(), + ); + check_bond_details( + 3, + bonds_and_unbonds( + &s, + Some(validator.address.clone()), + Some(validator.address.clone()), + ) + .unwrap(), + ); + + // Get a non-validating account with tokens + let delegator = address::testing::gen_implicit_address(); + let amount_del = token::Amount::from_uint(201_000_000, 0).unwrap(); + credit_tokens(&mut s, &staking_token, &delegator, amount_del).unwrap(); + let balance_key = token::balance_key(&staking_token, &delegator); + let balance = s + .read::(&balance_key) + .unwrap() + .unwrap_or_default(); + assert_eq!(balance, amount_del); + + // Advance to epoch 3 + advance_epoch(&mut s, ¶ms); + current_epoch = advance_epoch(&mut s, ¶ms); + let pipeline_epoch = current_epoch + params.pipeline_len; + + // Delegation + let delegation_epoch = current_epoch; + bond_tokens( + &mut s, + Some(&delegator), + &validator.address, + amount_del, + current_epoch, + None, + ) + .unwrap(); + let val_stake_pre = read_validator_stake( + &s, + ¶ms, + &validator.address, + pipeline_epoch.prev(), + ) + .unwrap(); + let val_stake_post = + read_validator_stake(&s, ¶ms, &validator.address, pipeline_epoch) + .unwrap(); + assert_eq!(validator.tokens + amount_self_bond, val_stake_pre); + assert_eq!( + validator.tokens + amount_self_bond + amount_del, + val_stake_post + ); + let delegation = bond_handle(&delegator, &validator.address); + assert_eq!( + delegation + .get_sum(&s, pipeline_epoch.prev(), ¶ms) + .unwrap() + .unwrap_or_default(), + token::Amount::zero() + ); + assert_eq!( + delegation + .get_sum(&s, pipeline_epoch, ¶ms) + .unwrap() + .unwrap_or_default(), + amount_del + ); + + // Check delegation bonds details after delegation + let delegation_bond_id = BondId { + source: delegator.clone(), + validator: validator.address.clone(), + }; + let check_bond_details = |ix, bond_details: BondsAndUnbondsDetails| { + println!("Check index {ix}"); + assert_eq!(bond_details.len(), 1); + let details = bond_details.get(&delegation_bond_id).unwrap(); + assert_eq!(details.bonds.len(), 1,); + dbg!(&details.bonds); + assert_eq!( + details.bonds[0], + BondDetails { + start: pipeline_epoch, + amount: amount_del, + slashed_amount: None + }, + ); + }; + // Try to call it with different combinations of owner/validator args + check_bond_details( + 0, + bonds_and_unbonds(&s, Some(delegator.clone()), None).unwrap(), + ); + check_bond_details( + 1, + bonds_and_unbonds( + &s, + Some(delegator.clone()), + Some(validator.address.clone()), + ) + .unwrap(), + ); + + // Check all bond details (self-bonds and delegation) + let check_bond_details = |ix, bond_details: BondsAndUnbondsDetails| { + println!("Check index {ix}"); + let self_bond_details = bond_details.get(&self_bond_id).unwrap(); + let delegation_details = bond_details.get(&delegation_bond_id).unwrap(); + assert_eq!( + self_bond_details.bonds.len(), + 2, + "Contains genesis and newly added self-bond" + ); + assert_eq!( + self_bond_details.bonds[0], + BondDetails { + start: start_epoch, + amount: validator.tokens, + slashed_amount: None + }, + ); + assert_eq!(self_bond_details.bonds[1].amount, amount_self_bond); + assert_eq!( + delegation_details.bonds[0], + BondDetails { + start: pipeline_epoch, + amount: amount_del, + slashed_amount: None + }, + ); + }; + // Try to call it with different combinations of owner/validator args + check_bond_details(0, bonds_and_unbonds(&s, None, None).unwrap()); + check_bond_details( + 1, + bonds_and_unbonds(&s, None, Some(validator.address.clone())).unwrap(), + ); + + // Advance to epoch 5 + for _ in 0..2 { + current_epoch = advance_epoch(&mut s, ¶ms); + } + let pipeline_epoch = current_epoch + params.pipeline_len; + + // Unbond the self-bond with an amount that will remove all of the self-bond + // executed after genesis and some of the genesis bond + let amount_self_unbond: token::Amount = + amount_self_bond + (validator.tokens / 2); + // When the difference is 0, only the non-genesis self-bond is unbonded + let unbonded_genesis_self_bond = + amount_self_unbond - amount_self_bond != token::Amount::zero(); + dbg!( + amount_self_unbond, + amount_self_bond, + unbonded_genesis_self_bond + ); + let self_unbond_epoch = s.storage.block.epoch; + + unbond_tokens( + &mut s, + None, + &validator.address, + amount_self_unbond, + current_epoch, + false, + ) + .unwrap(); + + let val_stake_pre = read_validator_stake( + &s, + ¶ms, + &validator.address, + pipeline_epoch.prev(), + ) + .unwrap(); + + let val_stake_post = + read_validator_stake(&s, ¶ms, &validator.address, pipeline_epoch) + .unwrap(); + + let val_delta = + read_validator_deltas_value(&s, &validator.address, &pipeline_epoch) + .unwrap(); + let unbond = unbond_handle(&validator.address, &validator.address); + + assert_eq!(val_delta, Some(-amount_self_unbond.change())); + assert_eq!( + unbond + .at(&Epoch::default()) + .get( + &s, + &(pipeline_epoch + + params.unbonding_len + + params.cubic_slashing_window_length) + ) + .unwrap(), + if unbonded_genesis_self_bond { + Some(amount_self_unbond - amount_self_bond) + } else { + None + } + ); + assert_eq!( + unbond + .at(&(self_bond_epoch + params.pipeline_len)) + .get( + &s, + &(pipeline_epoch + + params.unbonding_len + + params.cubic_slashing_window_length) + ) + .unwrap(), + Some(amount_self_bond) + ); + assert_eq!( + val_stake_pre, + validator.tokens + amount_self_bond + amount_del + ); + assert_eq!( + val_stake_post, + validator.tokens + amount_self_bond + amount_del - amount_self_unbond + ); + + // Check all bond and unbond details (self-bonds and delegation) + let check_bond_details = |ix, bond_details: BondsAndUnbondsDetails| { + println!("Check index {ix}"); + dbg!(&bond_details); + assert_eq!(bond_details.len(), 2); + let self_bond_details = bond_details.get(&self_bond_id).unwrap(); + let delegation_details = bond_details.get(&delegation_bond_id).unwrap(); + assert_eq!( + self_bond_details.bonds.len(), + 1, + "Contains only part of the genesis bond now" + ); + assert_eq!( + self_bond_details.bonds[0], + BondDetails { + start: start_epoch, + amount: validator.tokens + amount_self_bond + - amount_self_unbond, + slashed_amount: None + }, + ); + assert_eq!( + delegation_details.bonds[0], + BondDetails { + start: delegation_epoch + params.pipeline_len, + amount: amount_del, + slashed_amount: None + }, + ); + assert_eq!( + self_bond_details.unbonds.len(), + if unbonded_genesis_self_bond { 2 } else { 1 }, + "Contains a full unbond of the last self-bond and an unbond from \ + the genesis bond" + ); + if unbonded_genesis_self_bond { + assert_eq!( + self_bond_details.unbonds[0], + UnbondDetails { + start: start_epoch, + withdraw: self_unbond_epoch + + params.pipeline_len + + params.unbonding_len + + params.cubic_slashing_window_length, + amount: amount_self_unbond - amount_self_bond, + slashed_amount: None + } + ); + } + assert_eq!( + self_bond_details.unbonds[usize::from(unbonded_genesis_self_bond)], + UnbondDetails { + start: self_bond_epoch + params.pipeline_len, + withdraw: self_unbond_epoch + + params.pipeline_len + + params.unbonding_len + + params.cubic_slashing_window_length, + amount: amount_self_bond, + slashed_amount: None + } + ); + }; + check_bond_details( + 0, + bonds_and_unbonds(&s, None, Some(validator.address.clone())).unwrap(), + ); + + // Unbond delegation + let amount_undel = token::Amount::from_uint(1_000_000, 0).unwrap(); + unbond_tokens( + &mut s, + Some(&delegator), + &validator.address, + amount_undel, + current_epoch, + false, + ) + .unwrap(); + + let val_stake_pre = read_validator_stake( + &s, + ¶ms, + &validator.address, + pipeline_epoch.prev(), + ) + .unwrap(); + let val_stake_post = + read_validator_stake(&s, ¶ms, &validator.address, pipeline_epoch) + .unwrap(); + let val_delta = + read_validator_deltas_value(&s, &validator.address, &pipeline_epoch) + .unwrap(); + let unbond = unbond_handle(&delegator, &validator.address); + + assert_eq!( + val_delta, + Some(-(amount_self_unbond + amount_undel).change()) + ); + assert_eq!( + unbond + .at(&(delegation_epoch + params.pipeline_len)) + .get( + &s, + &(pipeline_epoch + + params.unbonding_len + + params.cubic_slashing_window_length) + ) + .unwrap(), + Some(amount_undel) + ); + assert_eq!( + val_stake_pre, + validator.tokens + amount_self_bond + amount_del + ); + assert_eq!( + val_stake_post, + validator.tokens + amount_self_bond - amount_self_unbond + amount_del + - amount_undel + ); + + let withdrawable_offset = params.unbonding_len + + params.pipeline_len + + params.cubic_slashing_window_length; + + // Advance to withdrawable epoch + for _ in 0..withdrawable_offset { + current_epoch = advance_epoch(&mut s, ¶ms); + } + + dbg!(current_epoch); + + let pos_balance = s + .read::(&token::balance_key( + &staking_token, + &super::ADDRESS, + )) + .unwrap(); + + assert_eq!( + Some(pos_balance_pre + amount_self_bond + amount_del), + pos_balance + ); + + // Withdraw the self-unbond + withdraw_tokens(&mut s, None, &validator.address, current_epoch).unwrap(); + let unbond = unbond_handle(&validator.address, &validator.address); + let unbond_iter = unbond.iter(&s).unwrap().next(); + assert!(unbond_iter.is_none()); + + let pos_balance = s + .read::(&token::balance_key( + &staking_token, + &super::ADDRESS, + )) + .unwrap(); + assert_eq!( + Some( + pos_balance_pre + amount_self_bond - amount_self_unbond + + amount_del + ), + pos_balance + ); + + // Withdraw the delegation unbond + withdraw_tokens( + &mut s, + Some(&delegator), + &validator.address, + current_epoch, + ) + .unwrap(); + let unbond = unbond_handle(&delegator, &validator.address); + let unbond_iter = unbond.iter(&s).unwrap().next(); + assert!(unbond_iter.is_none()); + + let pos_balance = s + .read::(&token::balance_key( + &staking_token, + &super::ADDRESS, + )) + .unwrap(); + assert_eq!( + Some( + pos_balance_pre + amount_self_bond - amount_self_unbond + + amount_del + - amount_undel + ), + pos_balance + ); +} + +/// Test validator initialization. +fn test_become_validator_aux( + params: OwnedPosParams, + new_validator: Address, + new_validator_consensus_key: SecretKey, + validators: Vec, +) { + println!( + "Test inputs: {params:?}, new validator: {new_validator}, genesis \ + validators: {validators:#?}" + ); + + let mut s = TestWlStorage::default(); + + // Genesis + let mut current_epoch = s.storage.block.epoch; + let params = test_init_genesis( + &mut s, + params, + validators.clone().into_iter(), + current_epoch, + ) + .unwrap(); + s.commit_block().unwrap(); + + // Advance to epoch 1 + current_epoch = advance_epoch(&mut s, ¶ms); + + let num_consensus_before = + get_num_consensus_validators(&s, current_epoch + params.pipeline_len) + .unwrap(); + let num_validators_over_thresh = validators + .iter() + .filter(|validator| { + validator.tokens >= params.validator_stake_threshold + }) + .count(); + + assert_eq!( + min( + num_validators_over_thresh as u64, + params.max_validator_slots + ), + num_consensus_before + ); + assert!(!is_validator(&s, &new_validator).unwrap()); + + // Credit the `new_validator` account + let staking_token = staking_token_address(&s); + let amount = token::Amount::from_uint(100_500_000, 0).unwrap(); + // Credit twice the amount as we're gonna bond it in delegation first, then + // self-bond + credit_tokens(&mut s, &staking_token, &new_validator, amount * 2).unwrap(); + + // Add a delegation from `new_validator` to `genesis_validator` + let genesis_validator = &validators.first().unwrap().address; + bond_tokens( + &mut s, + Some(&new_validator), + genesis_validator, + amount, + current_epoch, + None, + ) + .unwrap(); + + let consensus_key = new_validator_consensus_key.to_public(); + let protocol_sk = common_sk_from_simple_seed(0); + let protocol_key = protocol_sk.to_public(); + let eth_hot_key = key::common::PublicKey::Secp256k1( + key::testing::gen_keypair::().ref_to(), + ); + let eth_cold_key = key::common::PublicKey::Secp256k1( + key::testing::gen_keypair::().ref_to(), + ); + + // Try to become a validator - it should fail as there is a delegation + let result = become_validator( + &mut s, + BecomeValidator { + params: ¶ms, + address: &new_validator, + consensus_key: &consensus_key, + protocol_key: &protocol_key, + eth_cold_key: ð_cold_key, + eth_hot_key: ð_hot_key, + current_epoch, + commission_rate: Dec::new(5, 2).expect("Dec creation failed"), + max_commission_rate_change: Dec::new(5, 2) + .expect("Dec creation failed"), + metadata: Default::default(), + offset_opt: None, + }, + ); + assert!(result.is_err()); + assert!(!is_validator(&s, &new_validator).unwrap()); + + // Unbond the delegation + unbond_tokens( + &mut s, + Some(&new_validator), + genesis_validator, + amount, + current_epoch, + false, + ) + .unwrap(); + + // Try to become a validator account again - it should pass now + become_validator( + &mut s, + BecomeValidator { + params: ¶ms, + address: &new_validator, + consensus_key: &consensus_key, + protocol_key: &protocol_key, + eth_cold_key: ð_cold_key, + eth_hot_key: ð_hot_key, + current_epoch, + commission_rate: Dec::new(5, 2).expect("Dec creation failed"), + max_commission_rate_change: Dec::new(5, 2) + .expect("Dec creation failed"), + metadata: Default::default(), + offset_opt: None, + }, + ) + .unwrap(); + assert!(is_validator(&s, &new_validator).unwrap()); + + let num_consensus_after = + get_num_consensus_validators(&s, current_epoch + params.pipeline_len) + .unwrap(); + // The new validator is initialized with no stake and thus is in the + // below-threshold set + assert_eq!(num_consensus_before, num_consensus_after); + + // Advance to epoch 2 + current_epoch = advance_epoch(&mut s, ¶ms); + + // Self-bond to the new validator + bond_tokens(&mut s, None, &new_validator, amount, current_epoch, None) + .unwrap(); + + // Check the bond delta + let bond_handle = bond_handle(&new_validator, &new_validator); + let pipeline_epoch = current_epoch + params.pipeline_len; + let delta = bond_handle.get_delta_val(&s, pipeline_epoch).unwrap(); + assert_eq!(delta, Some(amount)); + + // Check the validator in the validator set - + // If the consensus validator slots are full and all the genesis validators + // have stake GTE the new validator's self-bond amount, the validator should + // be added to the below-capacity set, or the consensus otherwise + if params.max_validator_slots <= validators.len() as u64 + && validators + .iter() + .all(|validator| validator.tokens >= amount) + { + let set = read_below_capacity_validator_set_addresses_with_stake( + &s, + pipeline_epoch, + ) + .unwrap(); + assert!(set.into_iter().any( + |WeightedValidator { + bonded_stake, + address, + }| { + address == new_validator && bonded_stake == amount + } + )); + } else { + let set = read_consensus_validator_set_addresses_with_stake( + &s, + pipeline_epoch, + ) + .unwrap(); + assert!(set.into_iter().any( + |WeightedValidator { + bonded_stake, + address, + }| { + address == new_validator && bonded_stake == amount + } + )); + } + + // Advance to epoch 3 + current_epoch = advance_epoch(&mut s, ¶ms); + + // Unbond the self-bond + unbond_tokens(&mut s, None, &new_validator, amount, current_epoch, false) + .unwrap(); + + let withdrawable_offset = params.unbonding_len + params.pipeline_len; + + // Advance to withdrawable epoch + for _ in 0..withdrawable_offset { + current_epoch = advance_epoch(&mut s, ¶ms); + } + + // Withdraw the self-bond + withdraw_tokens(&mut s, None, &new_validator, current_epoch).unwrap(); +} + +fn test_slashes_with_unbonding_aux( + mut params: OwnedPosParams, + validators: Vec, + unbond_delay: u64, +) { + // This can be useful for debugging: + params.pipeline_len = 2; + params.unbonding_len = 4; + println!("\nTest inputs: {params:?}, genesis validators: {validators:#?}"); + let mut s = TestWlStorage::default(); + + // Find the validator with the least stake to avoid the cubic slash rate + // going to 100% + let validator = + itertools::Itertools::sorted_by_key(validators.iter(), |v| v.tokens) + .next() + .unwrap(); + let val_addr = &validator.address; + let val_tokens = validator.tokens; + println!( + "Validator that will misbehave addr {val_addr}, tokens {}", + val_tokens.to_string_native() + ); + + // Genesis + // let start_epoch = s.storage.block.epoch; + let mut current_epoch = s.storage.block.epoch; + let params = test_init_genesis( + &mut s, + params, + validators.clone().into_iter(), + current_epoch, + ) + .unwrap(); + s.commit_block().unwrap(); + + current_epoch = advance_epoch(&mut s, ¶ms); + super::process_slashes(&mut s, current_epoch).unwrap(); + + // Discover first slash + let slash_0_evidence_epoch = current_epoch; + // let slash_0_processing_epoch = + // slash_0_evidence_epoch + params.slash_processing_epoch_offset(); + let evidence_block_height = BlockHeight(0); // doesn't matter for slashing logic + let slash_0_type = SlashType::DuplicateVote; + slash( + &mut s, + ¶ms, + current_epoch, + slash_0_evidence_epoch, + evidence_block_height, + slash_0_type, + val_addr, + current_epoch.next(), + ) + .unwrap(); + + // Advance to an epoch in which we can unbond + let unfreeze_epoch = + slash_0_evidence_epoch + params.slash_processing_epoch_offset(); + while current_epoch < unfreeze_epoch { + current_epoch = advance_epoch(&mut s, ¶ms); + super::process_slashes(&mut s, current_epoch).unwrap(); + } + + // Advance more epochs randomly from the generated delay + for _ in 0..unbond_delay { + current_epoch = advance_epoch(&mut s, ¶ms); + } + + // Unbond half of the tokens + let unbond_amount = Dec::new(5, 1).unwrap() * val_tokens; + println!("Going to unbond {}", unbond_amount.to_string_native()); + let unbond_epoch = current_epoch; + unbond_tokens(&mut s, None, val_addr, unbond_amount, unbond_epoch, false) + .unwrap(); + + // Discover second slash + let slash_1_evidence_epoch = current_epoch; + // Ensure that both slashes happen before `unbond_epoch + pipeline` + let _slash_1_processing_epoch = + slash_1_evidence_epoch + params.slash_processing_epoch_offset(); + let evidence_block_height = BlockHeight(0); // doesn't matter for slashing logic + let slash_1_type = SlashType::DuplicateVote; + slash( + &mut s, + ¶ms, + current_epoch, + slash_1_evidence_epoch, + evidence_block_height, + slash_1_type, + val_addr, + current_epoch.next(), + ) + .unwrap(); + + // Advance to an epoch in which we can withdraw + let withdraw_epoch = unbond_epoch + params.withdrawable_epoch_offset(); + while current_epoch < withdraw_epoch { + current_epoch = advance_epoch(&mut s, ¶ms); + super::process_slashes(&mut s, current_epoch).unwrap(); + } + let token = staking_token_address(&s); + let val_balance_pre = read_balance(&s, &token, val_addr).unwrap(); + + let bond_id = BondId { + source: val_addr.clone(), + validator: val_addr.clone(), + }; + let binding = + super::bonds_and_unbonds(&s, None, Some(val_addr.clone())).unwrap(); + let details = binding.get(&bond_id).unwrap(); + let exp_withdraw_from_details = details.unbonds[0].amount + - details.unbonds[0].slashed_amount.unwrap_or_default(); + + withdraw_tokens(&mut s, None, val_addr, current_epoch).unwrap(); + + let val_balance_post = read_balance(&s, &token, val_addr).unwrap(); + let withdrawn_tokens = val_balance_post - val_balance_pre; + println!("Withdrew {} tokens", withdrawn_tokens.to_string_native()); + + assert_eq!(exp_withdraw_from_details, withdrawn_tokens); + + let slash_rate_0 = validator_slashes_handle(val_addr) + .get(&s, 0) + .unwrap() + .unwrap() + .rate; + let slash_rate_1 = validator_slashes_handle(val_addr) + .get(&s, 1) + .unwrap() + .unwrap() + .rate; + println!("Slash 0 rate {slash_rate_0}, slash 1 rate {slash_rate_1}"); + + let expected_withdrawn_amount = Dec::from( + (Dec::one() - slash_rate_1) + * (Dec::one() - slash_rate_0) + * unbond_amount, + ); + // Allow some rounding error, 1 NAMNAM per each slash + let rounding_error_tolerance = + Dec::new(2, NATIVE_MAX_DECIMAL_PLACES).unwrap(); + assert!( + dbg!(expected_withdrawn_amount.abs_diff(&Dec::from(withdrawn_tokens))) + <= rounding_error_tolerance + ); + + // TODO: finish once implemented + // let slash_0 = decimal_mult_amount(slash_rate_0, val_tokens); + // let slash_1 = decimal_mult_amount(slash_rate_1, val_tokens - slash_0); + // let expected_slash_pool = slash_0 + slash_1; + // let slash_pool_balance = + // read_balance(&s, &token, &SLASH_POOL_ADDRESS).unwrap(); + // assert_eq!(expected_slash_pool, slash_pool_balance); +} + +#[test] +fn test_validator_raw_hash() { + let mut storage = TestWlStorage::default(); + let address = address::testing::established_address_1(); + let consensus_sk = key::testing::keypair_1(); + let consensus_pk = consensus_sk.to_public(); + let expected_raw_hash = key::tm_consensus_key_raw_hash(&consensus_pk); + + assert!( + find_validator_by_raw_hash(&storage, &expected_raw_hash) + .unwrap() + .is_none() + ); + write_validator_address_raw_hash(&mut storage, &address, &consensus_pk) + .unwrap(); + let found = + find_validator_by_raw_hash(&storage, &expected_raw_hash).unwrap(); + assert_eq!(found, Some(address)); +} + +#[test] +fn test_validator_sets() { + let mut s = TestWlStorage::default(); + // Only 3 consensus validator slots + let params = OwnedPosParams { + max_validator_slots: 3, + ..Default::default() + }; + let addr_seed = "seed"; + let mut address_gen = EstablishedAddressGen::new(addr_seed); + let mut sk_seed = 0; + let mut gen_validator = || { + let res = ( + address_gen.generate_address(addr_seed), + key::testing::common_sk_from_simple_seed(sk_seed).to_public(), + ); + // bump the sk seed + sk_seed += 1; + res + }; + + // Create genesis validators + let ((val1, pk1), stake1) = + (gen_validator(), token::Amount::native_whole(1)); + let ((val2, pk2), stake2) = + (gen_validator(), token::Amount::native_whole(1)); + let ((val3, pk3), stake3) = + (gen_validator(), token::Amount::native_whole(10)); + let ((val4, pk4), stake4) = + (gen_validator(), token::Amount::native_whole(1)); + let ((val5, pk5), stake5) = + (gen_validator(), token::Amount::native_whole(100)); + let ((val6, pk6), stake6) = + (gen_validator(), token::Amount::native_whole(1)); + let ((val7, pk7), stake7) = + (gen_validator(), token::Amount::native_whole(1)); + println!("\nval1: {val1}, {pk1}, {}", stake1.to_string_native()); + println!("val2: {val2}, {pk2}, {}", stake2.to_string_native()); + println!("val3: {val3}, {pk3}, {}", stake3.to_string_native()); + println!("val4: {val4}, {pk4}, {}", stake4.to_string_native()); + println!("val5: {val5}, {pk5}, {}", stake5.to_string_native()); + println!("val6: {val6}, {pk6}, {}", stake6.to_string_native()); + println!("val7: {val7}, {pk7}, {}", stake7.to_string_native()); + + let start_epoch = Epoch::default(); + let epoch = start_epoch; + + let protocol_sk_1 = common_sk_from_simple_seed(0); + let protocol_sk_2 = common_sk_from_simple_seed(1); + + let params = test_init_genesis( + &mut s, + params, + [ + GenesisValidator { + address: val1.clone(), + tokens: stake1, + consensus_key: pk1.clone(), + protocol_key: protocol_sk_1.to_public(), + eth_hot_key: key::common::PublicKey::Secp256k1( + key::testing::gen_keypair::() + .ref_to(), + ), + eth_cold_key: key::common::PublicKey::Secp256k1( + key::testing::gen_keypair::() + .ref_to(), + ), + commission_rate: Dec::new(1, 1).expect("Dec creation failed"), + max_commission_rate_change: Dec::new(1, 1) + .expect("Dec creation failed"), + metadata: Default::default(), + }, + GenesisValidator { + address: val2.clone(), + tokens: stake2, + consensus_key: pk2.clone(), + protocol_key: protocol_sk_2.to_public(), + eth_hot_key: key::common::PublicKey::Secp256k1( + key::testing::gen_keypair::() + .ref_to(), + ), + eth_cold_key: key::common::PublicKey::Secp256k1( + key::testing::gen_keypair::() + .ref_to(), + ), + commission_rate: Dec::new(1, 1).expect("Dec creation failed"), + max_commission_rate_change: Dec::new(1, 1) + .expect("Dec creation failed"), + metadata: Default::default(), + }, + ] + .into_iter(), + epoch, + ) + .unwrap(); + + // A helper to insert a non-genesis validator + let insert_validator = |s: &mut TestWlStorage, + addr, + pk: &PublicKey, + stake: token::Amount, + epoch: Epoch| { + insert_validator_into_validator_set( + s, + ¶ms, + addr, + stake, + epoch, + params.pipeline_len, + ) + .unwrap(); + + update_validator_deltas(s, ¶ms, addr, stake.change(), epoch, None) + .unwrap(); + + // Set their consensus key (needed for + // `validator_set_update_tendermint` fn) + validator_consensus_key_handle(addr) + .set(s, pk.clone(), epoch, params.pipeline_len) + .unwrap(); + }; + + // Advance to EPOCH 1 + // + // We cannot call `get_tendermint_set_updates` for the genesis state as + // `validator_set_update_tendermint` is only called 2 blocks before the + // start of an epoch and so we need to give it a predecessor epoch (see + // `get_tendermint_set_updates`), which we cannot have on the first + // epoch. In any way, the initial validator set is given to Tendermint + // from InitChain, so `validator_set_update_tendermint` is + // not being used for it. + let epoch = advance_epoch(&mut s, ¶ms); + let pipeline_epoch = epoch + params.pipeline_len; + + // Insert another validator with the greater stake 10 NAM + insert_validator(&mut s, &val3, &pk3, stake3, epoch); + // Insert validator with stake 1 NAM + insert_validator(&mut s, &val4, &pk4, stake4, epoch); + + // Validator `val3` and `val4` will be added at pipeline offset (2) - epoch + // 3 + let val3_and_4_epoch = pipeline_epoch; + + let consensus_vals: Vec<_> = consensus_validator_set_handle() + .at(&pipeline_epoch) + .iter(&s) + .unwrap() + .map(Result::unwrap) + .collect(); + + assert_eq!(consensus_vals.len(), 3); + assert!(matches!( + &consensus_vals[0], + (lazy_map::NestedSubKey::Data { + key: stake, + nested_sub_key: lazy_map::SubKey::Data(position), + }, address) + if address == &val1 && stake == &stake1 && *position == Position(0) + )); + assert!(matches!( + &consensus_vals[1], + (lazy_map::NestedSubKey::Data { + key: stake, + nested_sub_key: lazy_map::SubKey::Data(position), + }, address) + if address == &val2 && stake == &stake2 && *position == Position(1) + )); + assert!(matches!( + &consensus_vals[2], + (lazy_map::NestedSubKey::Data { + key: stake, + nested_sub_key: lazy_map::SubKey::Data(position), + }, address) + if address == &val3 && stake == &stake3 && *position == Position(0) + )); + + // Check tendermint validator set updates - there should be none + let tm_updates = get_tendermint_set_updates(&s, ¶ms, epoch); + assert!(tm_updates.is_empty()); + + // Advance to EPOCH 2 + let epoch = advance_epoch(&mut s, ¶ms); + let pipeline_epoch = epoch + params.pipeline_len; + + // Insert another validator with a greater stake still 1000 NAM. It should + // replace 2nd consensus validator with stake 1, which should become + // below-capacity + insert_validator(&mut s, &val5, &pk5, stake5, epoch); + // Validator `val5` will be added at pipeline offset (2) - epoch 4 + let val5_epoch = pipeline_epoch; + + let consensus_vals: Vec<_> = consensus_validator_set_handle() + .at(&pipeline_epoch) + .iter(&s) + .unwrap() + .map(Result::unwrap) + .collect(); + + assert_eq!(consensus_vals.len(), 3); + assert!(matches!( + &consensus_vals[0], + (lazy_map::NestedSubKey::Data { + key: stake, + nested_sub_key: lazy_map::SubKey::Data(position), + }, address) + if address == &val1 && stake == &stake1 && *position == Position(0) + )); + assert!(matches!( + &consensus_vals[1], + (lazy_map::NestedSubKey::Data { + key: stake, + nested_sub_key: lazy_map::SubKey::Data(position), + }, address) + if address == &val3 && stake == &stake3 && *position == Position(0) + )); + assert!(matches!( + &consensus_vals[2], + (lazy_map::NestedSubKey::Data { + key: stake, + nested_sub_key: lazy_map::SubKey::Data(position), + }, address) + if address == &val5 && stake == &stake5 && *position == Position(0) + )); + + let below_capacity_vals: Vec<_> = below_capacity_validator_set_handle() + .at(&pipeline_epoch) + .iter(&s) + .unwrap() + .map(Result::unwrap) + .collect(); + + assert_eq!(below_capacity_vals.len(), 2); + assert!(matches!( + &below_capacity_vals[0], + (lazy_map::NestedSubKey::Data { + key: ReverseOrdTokenAmount(stake), + nested_sub_key: lazy_map::SubKey::Data(position), + }, address) + if address == &val4 && stake == &stake4 && *position == Position(0) + )); + assert!(matches!( + &below_capacity_vals[1], + (lazy_map::NestedSubKey::Data { + key: ReverseOrdTokenAmount(stake), + nested_sub_key: lazy_map::SubKey::Data(position), + }, address) + if address == &val2 && stake == &stake2 && *position == Position(1) + )); + + // Advance to EPOCH 3 + let epoch = advance_epoch(&mut s, ¶ms); + let pipeline_epoch = epoch + params.pipeline_len; + + // Check tendermint validator set updates + assert_eq!( + val3_and_4_epoch, epoch, + "val3 and val4 are in the validator sets now" + ); + let tm_updates = get_tendermint_set_updates(&s, ¶ms, epoch); + // `val4` is newly added below-capacity, must be skipped in updated in TM + assert_eq!(tm_updates.len(), 1); + assert_eq!( + tm_updates[0], + ValidatorSetUpdate::Consensus(ConsensusValidator { + consensus_key: pk3, + bonded_stake: stake3, + }) + ); + + // Insert another validator with a stake 1 NAM. It should be added to the + // below-capacity set + insert_validator(&mut s, &val6, &pk6, stake6, epoch); + // Validator `val6` will be added at pipeline offset (2) - epoch 5 + let val6_epoch = pipeline_epoch; + + let below_capacity_vals: Vec<_> = below_capacity_validator_set_handle() + .at(&pipeline_epoch) + .iter(&s) + .unwrap() + .map(Result::unwrap) + .collect(); + + assert_eq!(below_capacity_vals.len(), 3); + assert!(matches!( + &below_capacity_vals[0], + (lazy_map::NestedSubKey::Data { + key: ReverseOrdTokenAmount(stake), + nested_sub_key: lazy_map::SubKey::Data(position), + }, address) + if address == &val4 && stake == &stake4 && *position == Position(0) + )); + assert!(matches!( + &below_capacity_vals[1], + (lazy_map::NestedSubKey::Data { + key: ReverseOrdTokenAmount(stake), + nested_sub_key: lazy_map::SubKey::Data(position), + }, address) + if address == &val2 && stake == &stake2 && *position == Position(1) + )); + assert!(matches!( + &below_capacity_vals[2], + (lazy_map::NestedSubKey::Data { + key: ReverseOrdTokenAmount(stake), + nested_sub_key: lazy_map::SubKey::Data(position), + }, address) + if address == &val6 && stake == &stake6 && *position == Position(2) + )); + + // Advance to EPOCH 4 + let epoch = advance_epoch(&mut s, ¶ms); + let pipeline_epoch = epoch + params.pipeline_len; + + // Check tendermint validator set updates + assert_eq!(val5_epoch, epoch, "val5 is in the validator sets now"); + let tm_updates = get_tendermint_set_updates(&s, ¶ms, epoch); + assert_eq!(tm_updates.len(), 2); + assert_eq!( + tm_updates[0], + ValidatorSetUpdate::Consensus(ConsensusValidator { + consensus_key: pk5, + bonded_stake: stake5, + }) + ); + assert_eq!(tm_updates[1], ValidatorSetUpdate::Deactivated(pk2)); + + // Unbond some stake from val1, it should be be swapped with the greatest + // below-capacity validator val2 into the below-capacity set. The stake of + // val1 will go below 1 NAM, which is the validator_stake_threshold, so it + // will enter the below-threshold validator set. + let unbond = token::Amount::from_uint(500_000, 0).unwrap(); + let stake1 = stake1 - unbond; + println!("val1 {val1} new stake {}", stake1.to_string_native()); + // Because `update_validator_set` and `update_validator_deltas` are + // effective from pipeline offset, we use pipeline epoch for the rest of the + // checks + update_validator_set(&mut s, ¶ms, &val1, -unbond.change(), epoch, None) + .unwrap(); + update_validator_deltas( + &mut s, + ¶ms, + &val1, + -unbond.change(), + epoch, + None, + ) + .unwrap(); + // Epoch 6 + let val1_unbond_epoch = pipeline_epoch; + + let consensus_vals: Vec<_> = consensus_validator_set_handle() + .at(&pipeline_epoch) + .iter(&s) + .unwrap() + .map(Result::unwrap) + .collect(); + + assert_eq!(consensus_vals.len(), 3); + assert!(matches!( + &consensus_vals[0], + (lazy_map::NestedSubKey::Data { + key: stake, + nested_sub_key: lazy_map::SubKey::Data(position), + }, address) + if address == &val4 && stake == &stake4 && *position == Position(0) + )); + assert!(matches!( + &consensus_vals[1], + (lazy_map::NestedSubKey::Data { + key: stake, + nested_sub_key: lazy_map::SubKey::Data(position), + }, address) + if address == &val3 && stake == &stake3 && *position == Position(0) + )); + assert!(matches!( + &consensus_vals[2], + (lazy_map::NestedSubKey::Data { + key: stake, + nested_sub_key: lazy_map::SubKey::Data(position), + }, address) + if address == &val5 && stake == &stake5 && *position == Position(0) + )); + + let below_capacity_vals: Vec<_> = below_capacity_validator_set_handle() + .at(&pipeline_epoch) + .iter(&s) + .unwrap() + .map(Result::unwrap) + .collect(); + + assert_eq!(below_capacity_vals.len(), 2); + assert!(matches!( + &below_capacity_vals[0], + (lazy_map::NestedSubKey::Data { + key: ReverseOrdTokenAmount(stake), + nested_sub_key: lazy_map::SubKey::Data(position), + }, address) + if address == &val2 && stake == &stake2 && *position == Position(1) + )); + assert!(matches!( + &below_capacity_vals[1], + (lazy_map::NestedSubKey::Data { + key: ReverseOrdTokenAmount(stake), + nested_sub_key: lazy_map::SubKey::Data(position), + }, address) + if address == &val6 && stake == &stake6 && *position == Position(2) + )); + + let below_threshold_vals = + read_below_threshold_validator_set_addresses(&s, pipeline_epoch) + .unwrap() + .into_iter() + .collect::>(); + + assert_eq!(below_threshold_vals.len(), 1); + assert_eq!(&below_threshold_vals[0], &val1); + + // Advance to EPOCH 5 + let epoch = advance_epoch(&mut s, ¶ms); + let pipeline_epoch = epoch + params.pipeline_len; + + // Check tendermint validator set updates + assert_eq!(val6_epoch, epoch, "val6 is in the validator sets now"); + let tm_updates = get_tendermint_set_updates(&s, ¶ms, epoch); + assert!(tm_updates.is_empty()); + + // Insert another validator with stake 1 - it should be added to below + // capacity set + insert_validator(&mut s, &val7, &pk7, stake7, epoch); + // Epoch 7 + let val7_epoch = pipeline_epoch; + + let consensus_vals: Vec<_> = consensus_validator_set_handle() + .at(&pipeline_epoch) + .iter(&s) + .unwrap() + .map(Result::unwrap) + .collect(); + + assert_eq!(consensus_vals.len(), 3); + assert!(matches!( + &consensus_vals[0], + (lazy_map::NestedSubKey::Data { + key: stake, + nested_sub_key: lazy_map::SubKey::Data(position), + }, address) + if address == &val4 && stake == &stake4 && *position == Position(0) + )); + assert!(matches!( + &consensus_vals[1], + (lazy_map::NestedSubKey::Data { + key: stake, + nested_sub_key: lazy_map::SubKey::Data(position), + }, address) + if address == &val3 && stake == &stake3 && *position == Position(0) + )); + assert!(matches!( + &consensus_vals[2], + (lazy_map::NestedSubKey::Data { + key: stake, + nested_sub_key: lazy_map::SubKey::Data(position), + }, address) + if address == &val5 && stake == &stake5 && *position == Position(0) + )); + + let below_capacity_vals: Vec<_> = below_capacity_validator_set_handle() + .at(&pipeline_epoch) + .iter(&s) + .unwrap() + .map(Result::unwrap) + .collect(); + + assert_eq!(below_capacity_vals.len(), 3); + assert!(matches!( + &below_capacity_vals[0], + (lazy_map::NestedSubKey::Data { + key: ReverseOrdTokenAmount(stake), + nested_sub_key: lazy_map::SubKey::Data(position), + }, address) + if address == &val2 && stake == &stake2 && *position == Position(1) + )); + assert!(matches!( + &below_capacity_vals[1], + (lazy_map::NestedSubKey::Data { + key: ReverseOrdTokenAmount(stake), + nested_sub_key: lazy_map::SubKey::Data(position), + }, address) + if address == &val6 && stake == &stake6 && *position == Position(2) + )); + assert!(matches!( + &below_capacity_vals[2], + ( + lazy_map::NestedSubKey::Data { + key: ReverseOrdTokenAmount(stake), + nested_sub_key: lazy_map::SubKey::Data(position), + }, + address + ) + if address == &val7 && stake == &stake7 && *position == Position(3) + )); + + let below_threshold_vals = + read_below_threshold_validator_set_addresses(&s, pipeline_epoch) + .unwrap() + .into_iter() + .collect::>(); + + assert_eq!(below_threshold_vals.len(), 1); + assert_eq!(&below_threshold_vals[0], &val1); + + // Advance to EPOCH 6 + let epoch = advance_epoch(&mut s, ¶ms); + let pipeline_epoch = epoch + params.pipeline_len; + + // Check tendermint validator set updates + assert_eq!(val1_unbond_epoch, epoch, "val1's unbond is applied now"); + let tm_updates = get_tendermint_set_updates(&s, ¶ms, epoch); + assert_eq!(tm_updates.len(), 2); + assert_eq!( + tm_updates[0], + ValidatorSetUpdate::Consensus(ConsensusValidator { + consensus_key: pk4.clone(), + bonded_stake: stake4, + }) + ); + assert_eq!(tm_updates[1], ValidatorSetUpdate::Deactivated(pk1)); + + // Bond some stake to val6, it should be be swapped with the lowest + // consensus validator val2 into the consensus set + let bond = token::Amount::from_uint(500_000, 0).unwrap(); + let stake6 = stake6 + bond; + println!("val6 {val6} new stake {}", stake6.to_string_native()); + update_validator_set(&mut s, ¶ms, &val6, bond.change(), epoch, None) + .unwrap(); + update_validator_deltas(&mut s, ¶ms, &val6, bond.change(), epoch, None) + .unwrap(); + let val6_bond_epoch = pipeline_epoch; + + let consensus_vals: Vec<_> = consensus_validator_set_handle() + .at(&pipeline_epoch) + .iter(&s) + .unwrap() + .map(Result::unwrap) + .collect(); + + assert_eq!(consensus_vals.len(), 3); + assert!(matches!( + &consensus_vals[0], + (lazy_map::NestedSubKey::Data { + key: stake, + nested_sub_key: lazy_map::SubKey::Data(position), + }, address) + if address == &val6 && stake == &stake6 && *position == Position(0) + )); + assert!(matches!( + &consensus_vals[1], + (lazy_map::NestedSubKey::Data { + key: stake, + nested_sub_key: lazy_map::SubKey::Data(position), + }, address) + if address == &val3 && stake == &stake3 && *position == Position(0) + )); + assert!(matches!( + &consensus_vals[2], + (lazy_map::NestedSubKey::Data { + key: stake, + nested_sub_key: lazy_map::SubKey::Data(position), + }, address) + if address == &val5 && stake == &stake5 && *position == Position(0) + )); + + let below_capacity_vals: Vec<_> = below_capacity_validator_set_handle() + .at(&pipeline_epoch) + .iter(&s) + .unwrap() + .map(Result::unwrap) + .collect(); + + assert_eq!(below_capacity_vals.len(), 3); + dbg!(&below_capacity_vals); + assert!(matches!( + &below_capacity_vals[0], + (lazy_map::NestedSubKey::Data { + key: ReverseOrdTokenAmount(stake), + nested_sub_key: lazy_map::SubKey::Data(position), + }, address) + if address == &val2 && stake == &stake2 && *position == Position(1) + )); + assert!(matches!( + &below_capacity_vals[1], + (lazy_map::NestedSubKey::Data { + key: ReverseOrdTokenAmount(stake), + nested_sub_key: lazy_map::SubKey::Data(position), + }, address) + if address == &val7 && stake == &stake7 && *position == Position(3) + )); + assert!(matches!( + &below_capacity_vals[2], + ( + lazy_map::NestedSubKey::Data { + key: ReverseOrdTokenAmount(stake), + nested_sub_key: lazy_map::SubKey::Data(position), + }, + address + ) + if address == &val4 && stake == &stake4 && *position == Position(4) + )); + + let below_threshold_vals = + read_below_threshold_validator_set_addresses(&s, pipeline_epoch) + .unwrap() + .into_iter() + .collect::>(); + + assert_eq!(below_threshold_vals.len(), 1); + assert_eq!(&below_threshold_vals[0], &val1); + + // Advance to EPOCH 7 + let epoch = advance_epoch(&mut s, ¶ms); + assert_eq!(val7_epoch, epoch, "val6 is in the validator sets now"); + + // Check tendermint validator set updates + let tm_updates = get_tendermint_set_updates(&s, ¶ms, epoch); + assert!(tm_updates.is_empty()); + + // Advance to EPOCH 8 + let epoch = advance_epoch(&mut s, ¶ms); + + // Check tendermint validator set updates + assert_eq!(val6_bond_epoch, epoch, "val5's bond is applied now"); + let tm_updates = get_tendermint_set_updates(&s, ¶ms, epoch); + dbg!(&tm_updates); + assert_eq!(tm_updates.len(), 2); + assert_eq!( + tm_updates[0], + ValidatorSetUpdate::Consensus(ConsensusValidator { + consensus_key: pk6, + bonded_stake: stake6, + }) + ); + assert_eq!(tm_updates[1], ValidatorSetUpdate::Deactivated(pk4)); + + // Check that the below-capacity validator set was purged for the old epochs + // but that the consensus_validator_set was not + let last_epoch = epoch; + for e in Epoch::iter_bounds_inclusive( + start_epoch, + last_epoch + .sub_or_default(Epoch(DEFAULT_NUM_PAST_EPOCHS)) + .sub_or_default(Epoch(1)), + ) { + assert!( + !consensus_validator_set_handle() + .at(&e) + .is_empty(&s) + .unwrap() + ); + assert!( + below_capacity_validator_set_handle() + .at(&e) + .is_empty(&s) + .unwrap() + ); + } +} + +/// When a consensus set validator with 0 voting power adds a bond in the same +/// epoch as another below-capacity set validator with 0 power, but who adds +/// more bonds than the validator who is in the consensus set, they get swapped +/// in the sets. But if both of their new voting powers are still 0 after +/// bonding, the newly below-capacity validator must not be given to tendermint +/// with 0 voting power, because it wasn't it its set before +#[test] +fn test_validator_sets_swap() { + let mut s = TestWlStorage::default(); + // Only 2 consensus validator slots + let params = OwnedPosParams { + max_validator_slots: 2, + // Set the stake threshold to 0 so no validators are in the + // below-threshold set + validator_stake_threshold: token::Amount::zero(), + // Set 0.1 votes per token + tm_votes_per_token: Dec::new(1, 1).expect("Dec creation failed"), + ..Default::default() + }; + + let addr_seed = "seed"; + let mut address_gen = EstablishedAddressGen::new(addr_seed); + let mut sk_seed = 0; + let mut gen_validator = || { + let res = ( + address_gen.generate_address(addr_seed), + key::testing::common_sk_from_simple_seed(sk_seed).to_public(), + ); + // bump the sk seed + sk_seed += 1; + res + }; + + // Start with two genesis validators, one with 1 voting power and other 0 + let epoch = Epoch::default(); + // 1M voting power + let ((val1, pk1), stake1) = + (gen_validator(), token::Amount::native_whole(10)); + // 0 voting power + let ((val2, pk2), stake2) = + (gen_validator(), token::Amount::from_uint(5, 0).unwrap()); + // 0 voting power + let ((val3, pk3), stake3) = + (gen_validator(), token::Amount::from_uint(5, 0).unwrap()); + println!("val1: {val1}, {pk1}, {}", stake1.to_string_native()); + println!("val2: {val2}, {pk2}, {}", stake2.to_string_native()); + println!("val3: {val3}, {pk3}, {}", stake3.to_string_native()); + + let protocol_sk_1 = common_sk_from_simple_seed(0); + let protocol_sk_2 = common_sk_from_simple_seed(1); + + let params = test_init_genesis( + &mut s, + params, + [ + GenesisValidator { + address: val1, + tokens: stake1, + consensus_key: pk1, + protocol_key: protocol_sk_1.to_public(), + eth_hot_key: key::common::PublicKey::Secp256k1( + key::testing::gen_keypair::() + .ref_to(), + ), + eth_cold_key: key::common::PublicKey::Secp256k1( + key::testing::gen_keypair::() + .ref_to(), + ), + commission_rate: Dec::new(1, 1).expect("Dec creation failed"), + max_commission_rate_change: Dec::new(1, 1) + .expect("Dec creation failed"), + metadata: Default::default(), + }, + GenesisValidator { + address: val2.clone(), + tokens: stake2, + consensus_key: pk2, + protocol_key: protocol_sk_2.to_public(), + eth_hot_key: key::common::PublicKey::Secp256k1( + key::testing::gen_keypair::() + .ref_to(), + ), + eth_cold_key: key::common::PublicKey::Secp256k1( + key::testing::gen_keypair::() + .ref_to(), + ), + commission_rate: Dec::new(1, 1).expect("Dec creation failed"), + max_commission_rate_change: Dec::new(1, 1) + .expect("Dec creation failed"), + metadata: Default::default(), + }, + ] + .into_iter(), + epoch, + ) + .unwrap(); + + // A helper to insert a non-genesis validator + let insert_validator = |s: &mut TestWlStorage, + addr, + pk: &PublicKey, + stake: token::Amount, + epoch: Epoch| { + insert_validator_into_validator_set( + s, + ¶ms, + addr, + stake, + epoch, + params.pipeline_len, + ) + .unwrap(); + + update_validator_deltas(s, ¶ms, addr, stake.change(), epoch, None) + .unwrap(); + + // Set their consensus key (needed for + // `validator_set_update_tendermint` fn) + validator_consensus_key_handle(addr) + .set(s, pk.clone(), epoch, params.pipeline_len) + .unwrap(); + }; + + // Advance to EPOCH 1 + let epoch = advance_epoch(&mut s, ¶ms); + let pipeline_epoch = epoch + params.pipeline_len; + + // Insert another validator with 0 voting power + insert_validator(&mut s, &val3, &pk3, stake3, epoch); + + assert_eq!(stake2, stake3); + + // Add 2 bonds, one for val2 and greater one for val3 + let bonds_epoch_1 = pipeline_epoch; + let bond2 = token::Amount::from_uint(1, 0).unwrap(); + let stake2 = stake2 + bond2; + let bond3 = token::Amount::from_uint(4, 0).unwrap(); + let stake3 = stake3 + bond3; + + assert!(stake2 < stake3); + assert_eq!(into_tm_voting_power(params.tm_votes_per_token, stake2), 0); + assert_eq!(into_tm_voting_power(params.tm_votes_per_token, stake3), 0); + + update_validator_set(&mut s, ¶ms, &val2, bond2.change(), epoch, None) + .unwrap(); + update_validator_deltas( + &mut s, + ¶ms, + &val2, + bond2.change(), + epoch, + None, + ) + .unwrap(); + + update_validator_set(&mut s, ¶ms, &val3, bond3.change(), epoch, None) + .unwrap(); + update_validator_deltas( + &mut s, + ¶ms, + &val3, + bond3.change(), + epoch, + None, + ) + .unwrap(); + + // Advance to EPOCH 2 + let epoch = advance_epoch(&mut s, ¶ms); + let pipeline_epoch = epoch + params.pipeline_len; + + // Add 2 more bonds, same amount for `val2` and val3` + let bonds_epoch_2 = pipeline_epoch; + let bonds = token::Amount::native_whole(1); + let stake2 = stake2 + bonds; + let stake3 = stake3 + bonds; + assert!(stake2 < stake3); + assert_eq!( + into_tm_voting_power(params.tm_votes_per_token, stake2), + into_tm_voting_power(params.tm_votes_per_token, stake3) + ); + + update_validator_set(&mut s, ¶ms, &val2, bonds.change(), epoch, None) + .unwrap(); + update_validator_deltas( + &mut s, + ¶ms, + &val2, + bonds.change(), + epoch, + None, + ) + .unwrap(); + + update_validator_set(&mut s, ¶ms, &val3, bonds.change(), epoch, None) + .unwrap(); + update_validator_deltas( + &mut s, + ¶ms, + &val3, + bonds.change(), + epoch, + None, + ) + .unwrap(); + + // Advance to EPOCH 3 + let epoch = advance_epoch(&mut s, ¶ms); + + // Check tendermint validator set updates + assert_eq!(bonds_epoch_1, epoch); + let tm_updates = get_tendermint_set_updates(&s, ¶ms, epoch); + // `val2` must not be given to tendermint - even though it was in the + // consensus set, its voting power was 0, so it wasn't in TM set before the + // bond + assert!(tm_updates.is_empty()); + + // Advance to EPOCH 4 + let epoch = advance_epoch(&mut s, ¶ms); + + // Check tendermint validator set updates + assert_eq!(bonds_epoch_2, epoch); + let tm_updates = get_tendermint_set_updates(&s, ¶ms, epoch); + dbg!(&tm_updates); + assert_eq!(tm_updates.len(), 1); + // `val2` must not be given to tendermint as it was and still is below + // capacity + assert_eq!( + tm_updates[0], + ValidatorSetUpdate::Consensus(ConsensusValidator { + consensus_key: pk3, + bonded_stake: stake3, + }) + ); +} + +fn get_tendermint_set_updates( + s: &TestWlStorage, + params: &PosParams, + Epoch(epoch): Epoch, +) -> Vec { + // Because the `validator_set_update_tendermint` is called 2 blocks before + // the start of a new epoch, it expects to receive the epoch that is before + // the start of a new one too and so we give it the predecessor of the + // current epoch here to actually get the update for the current epoch. + let epoch = Epoch(epoch - 1); + validator_set_update_tendermint(s, params, epoch, |update| update).unwrap() +} + +/// Advance to the next epoch. Returns the new epoch. +fn advance_epoch(s: &mut TestWlStorage, params: &PosParams) -> Epoch { + s.storage.block.epoch = s.storage.block.epoch.next(); + let current_epoch = s.storage.block.epoch; + compute_and_store_total_consensus_stake(s, current_epoch).unwrap(); + copy_validator_sets_and_positions( + s, + params, + current_epoch, + current_epoch + params.pipeline_len, + ) + .unwrap(); + // purge_validator_sets_for_old_epoch(s, current_epoch).unwrap(); + // process_slashes(s, current_epoch).unwrap(); + // dbg!(current_epoch); + current_epoch +} + +fn arb_genesis_validators( + size: Range, + threshold: Option, +) -> impl Strategy> { + let threshold = threshold + .unwrap_or_else(|| PosParams::default().validator_stake_threshold); + let tokens: Vec<_> = (0..size.end) + .map(|ix| { + if ix == 0 { + // Make sure that at least one validator has at least a stake + // greater or equal to the threshold to avoid having an empty + // consensus set. + threshold.raw_amount().as_u64()..=10_000_000_u64 + } else { + 1..=10_000_000_u64 + } + .prop_map(token::Amount::from) + }) + .collect(); + (size, tokens) + .prop_map(|(size, token_amounts)| { + // use unique seeds to generate validators' address and consensus + // key + let seeds = (0_u64..).take(size); + seeds + .zip(token_amounts) + .map(|(seed, tokens)| { + let address = address_from_simple_seed(seed); + let consensus_sk = common_sk_from_simple_seed(seed); + let consensus_key = consensus_sk.to_public(); + + let protocol_sk = common_sk_from_simple_seed(seed); + let protocol_key = protocol_sk.to_public(); + + let eth_hot_key = key::common::PublicKey::Secp256k1( + key::testing::gen_keypair::( + ) + .ref_to(), + ); + let eth_cold_key = key::common::PublicKey::Secp256k1( + key::testing::gen_keypair::( + ) + .ref_to(), + ); + + let commission_rate = Dec::new(5, 2).expect("Test failed"); + let max_commission_rate_change = + Dec::new(1, 2).expect("Test failed"); + GenesisValidator { + address, + tokens, + consensus_key, + protocol_key, + eth_hot_key, + eth_cold_key, + commission_rate, + max_commission_rate_change, + metadata: Default::default(), + } + }) + .collect() + }) + .prop_filter( + "Must have at least one genesis validator with stake above the \ + provided threshold, if any.", + move |gen_vals: &Vec| { + gen_vals.iter().any(|val| val.tokens >= threshold) + }, + ) +} + +fn test_unjail_validator_aux( + params: OwnedPosParams, + mut validators: Vec, +) { + println!("\nTest inputs: {params:?}, genesis validators: {validators:#?}"); + let mut s = TestWlStorage::default(); + + // Find the validator with the most stake and 100x his stake to keep the + // cubic slash rate small + let num_vals = validators.len(); + validators.sort_by_key(|a| a.tokens); + validators[num_vals - 1].tokens = 100 * validators[num_vals - 1].tokens; + + // Get second highest stake validator tomisbehave + let val_addr = &validators[num_vals - 2].address; + let val_tokens = validators[num_vals - 2].tokens; + println!( + "Validator that will misbehave addr {val_addr}, tokens {}", + val_tokens.to_string_native() + ); + + // Genesis + let mut current_epoch = s.storage.block.epoch; + let params = test_init_genesis( + &mut s, + params, + validators.clone().into_iter(), + current_epoch, + ) + .unwrap(); + s.commit_block().unwrap(); + + current_epoch = advance_epoch(&mut s, ¶ms); + super::process_slashes(&mut s, current_epoch).unwrap(); + + // Discover first slash + let slash_0_evidence_epoch = current_epoch; + let evidence_block_height = BlockHeight(0); // doesn't matter for slashing logic + let slash_0_type = SlashType::DuplicateVote; + slash( + &mut s, + ¶ms, + current_epoch, + slash_0_evidence_epoch, + evidence_block_height, + slash_0_type, + val_addr, + current_epoch.next(), + ) + .unwrap(); + + assert_eq!( + validator_state_handle(val_addr) + .get(&s, current_epoch, ¶ms) + .unwrap(), + Some(ValidatorState::Consensus) + ); + + for epoch in Epoch::iter_bounds_inclusive( + current_epoch.next(), + current_epoch + params.pipeline_len, + ) { + // Check the validator state + assert_eq!( + validator_state_handle(val_addr) + .get(&s, epoch, ¶ms) + .unwrap(), + Some(ValidatorState::Jailed) + ); + // Check the validator set positions + assert!( + validator_set_positions_handle() + .at(&epoch) + .get(&s, val_addr) + .unwrap() + .is_none(), + ); + } + + // Advance past an epoch in which we can unbond + let unfreeze_epoch = + slash_0_evidence_epoch + params.slash_processing_epoch_offset(); + while current_epoch < unfreeze_epoch + 4u64 { + current_epoch = advance_epoch(&mut s, ¶ms); + super::process_slashes(&mut s, current_epoch).unwrap(); + } + + // Unjail the validator + unjail_validator(&mut s, val_addr, current_epoch).unwrap(); + + // Check the validator state + for epoch in + Epoch::iter_bounds_inclusive(current_epoch, current_epoch.next()) + { + assert_eq!( + validator_state_handle(val_addr) + .get(&s, epoch, ¶ms) + .unwrap(), + Some(ValidatorState::Jailed) + ); + } + + assert_eq!( + validator_state_handle(val_addr) + .get(&s, current_epoch + params.pipeline_len, ¶ms) + .unwrap(), + Some(ValidatorState::Consensus) + ); + assert!( + validator_set_positions_handle() + .at(&(current_epoch + params.pipeline_len)) + .get(&s, val_addr) + .unwrap() + .is_some(), + ); + + // Advance another epoch + current_epoch = advance_epoch(&mut s, ¶ms); + super::process_slashes(&mut s, current_epoch).unwrap(); + + let second_att = unjail_validator(&mut s, val_addr, current_epoch); + assert!(second_att.is_err()); +} + +/// `iterateBondsUpToAmountTest` +#[test] +fn test_find_bonds_to_remove() { + let mut storage = TestWlStorage::default(); + let gov_params = + namada_governance::parameters::GovernanceParameters::default(); + gov_params.init_storage(&mut storage).unwrap(); + write_pos_params(&mut storage, &OwnedPosParams::default()).unwrap(); + + let source = established_address_1(); + let validator = established_address_2(); + let bond_handle = bond_handle(&source, &validator); + + let (e1, e2, e6) = (Epoch(1), Epoch(2), Epoch(6)); + + bond_handle + .set(&mut storage, token::Amount::from(5), e1, 0) + .unwrap(); + bond_handle + .set(&mut storage, token::Amount::from(3), e2, 0) + .unwrap(); + bond_handle + .set(&mut storage, token::Amount::from(8), e6, 0) + .unwrap(); + + // Test 1 + let bonds_for_removal = find_bonds_to_remove( + &storage, + &bond_handle.get_data_handler(), + token::Amount::from(8), + ) + .unwrap(); + assert_eq!( + bonds_for_removal.epochs, + vec![e6].into_iter().collect::>() + ); + assert!(bonds_for_removal.new_entry.is_none()); + + // Test 2 + let bonds_for_removal = find_bonds_to_remove( + &storage, + &bond_handle.get_data_handler(), + token::Amount::from(10), + ) + .unwrap(); + assert_eq!( + bonds_for_removal.epochs, + vec![e6].into_iter().collect::>() + ); + assert_eq!( + bonds_for_removal.new_entry, + Some((Epoch(2), token::Amount::from(1))) + ); + + // Test 3 + let bonds_for_removal = find_bonds_to_remove( + &storage, + &bond_handle.get_data_handler(), + token::Amount::from(11), + ) + .unwrap(); + assert_eq!( + bonds_for_removal.epochs, + vec![e6, e2].into_iter().collect::>() + ); + assert!(bonds_for_removal.new_entry.is_none()); + + // Test 4 + let bonds_for_removal = find_bonds_to_remove( + &storage, + &bond_handle.get_data_handler(), + token::Amount::from(12), + ) + .unwrap(); + assert_eq!( + bonds_for_removal.epochs, + vec![e6, e2].into_iter().collect::>() + ); + assert_eq!( + bonds_for_removal.new_entry, + Some((Epoch(1), token::Amount::from(4))) + ); +} + +/// `computeModifiedRedelegationTest` +#[test] +fn test_compute_modified_redelegation() { + let mut storage = TestWlStorage::default(); + let validator1 = established_address_1(); + let validator2 = established_address_2(); + let owner = established_address_3(); + let outer_epoch = Epoch(0); + + let mut alice = validator1.clone(); + let mut bob = validator2.clone(); + + // Ensure a ranking order of alice > bob + if bob > alice { + alice = validator2; + bob = validator1; + } + println!("\n\nalice = {}\nbob = {}\n", &alice, &bob); + + // Fill redelegated bonds in storage + let redelegated_bonds_map = delegator_redelegated_bonds_handle(&owner) + .at(&alice) + .at(&outer_epoch); + redelegated_bonds_map + .at(&alice) + .insert(&mut storage, Epoch(2), token::Amount::from(6)) + .unwrap(); + redelegated_bonds_map + .at(&alice) + .insert(&mut storage, Epoch(4), token::Amount::from(7)) + .unwrap(); + redelegated_bonds_map + .at(&bob) + .insert(&mut storage, Epoch(1), token::Amount::from(5)) + .unwrap(); + redelegated_bonds_map + .at(&bob) + .insert(&mut storage, Epoch(4), token::Amount::from(7)) + .unwrap(); + + // Test cases 1 and 2 + let mr1 = compute_modified_redelegation( + &storage, + &redelegated_bonds_map, + Epoch(5), + token::Amount::from(25), + ) + .unwrap(); + let mr2 = compute_modified_redelegation( + &storage, + &redelegated_bonds_map, + Epoch(5), + token::Amount::from(30), + ) + .unwrap(); + + let exp_mr = ModifiedRedelegation { + epoch: Some(Epoch(5)), + ..Default::default() + }; + + assert_eq!(mr1, exp_mr); + assert_eq!(mr2, exp_mr); + + // Test case 3 + let mr3 = compute_modified_redelegation( + &storage, + &redelegated_bonds_map, + Epoch(5), + token::Amount::from(7), + ) + .unwrap(); + + let exp_mr = ModifiedRedelegation { + epoch: Some(Epoch(5)), + validators_to_remove: BTreeSet::from_iter([bob.clone()]), + validator_to_modify: Some(bob.clone()), + epochs_to_remove: BTreeSet::from_iter([Epoch(4)]), + ..Default::default() + }; + assert_eq!(mr3, exp_mr); + + // Test case 4 + let mr4 = compute_modified_redelegation( + &storage, + &redelegated_bonds_map, + Epoch(5), + token::Amount::from(8), + ) + .unwrap(); + + let exp_mr = ModifiedRedelegation { + epoch: Some(Epoch(5)), + validators_to_remove: BTreeSet::from_iter([bob.clone()]), + validator_to_modify: Some(bob.clone()), + epochs_to_remove: BTreeSet::from_iter([Epoch(1), Epoch(4)]), + epoch_to_modify: Some(Epoch(1)), + new_amount: Some(4.into()), + }; + assert_eq!(mr4, exp_mr); + + // Test case 5 + let mr5 = compute_modified_redelegation( + &storage, + &redelegated_bonds_map, + Epoch(5), + 12.into(), + ) + .unwrap(); + + let exp_mr = ModifiedRedelegation { + epoch: Some(Epoch(5)), + validators_to_remove: BTreeSet::from_iter([bob.clone()]), + ..Default::default() + }; + assert_eq!(mr5, exp_mr); + + // Test case 6 + let mr6 = compute_modified_redelegation( + &storage, + &redelegated_bonds_map, + Epoch(5), + 14.into(), + ) + .unwrap(); + + let exp_mr = ModifiedRedelegation { + epoch: Some(Epoch(5)), + validators_to_remove: BTreeSet::from_iter([alice.clone(), bob.clone()]), + validator_to_modify: Some(alice.clone()), + epochs_to_remove: BTreeSet::from_iter([Epoch(4)]), + epoch_to_modify: Some(Epoch(4)), + new_amount: Some(5.into()), + }; + assert_eq!(mr6, exp_mr); + + // Test case 7 + let mr7 = compute_modified_redelegation( + &storage, + &redelegated_bonds_map, + Epoch(5), + 19.into(), + ) + .unwrap(); + + let exp_mr = ModifiedRedelegation { + epoch: Some(Epoch(5)), + validators_to_remove: BTreeSet::from_iter([alice.clone(), bob.clone()]), + validator_to_modify: Some(alice.clone()), + epochs_to_remove: BTreeSet::from_iter([Epoch(4)]), + ..Default::default() + }; + assert_eq!(mr7, exp_mr); + + // Test case 8 + let mr8 = compute_modified_redelegation( + &storage, + &redelegated_bonds_map, + Epoch(5), + 21.into(), + ) + .unwrap(); + + let exp_mr = ModifiedRedelegation { + epoch: Some(Epoch(5)), + validators_to_remove: BTreeSet::from_iter([alice.clone(), bob]), + validator_to_modify: Some(alice), + epochs_to_remove: BTreeSet::from_iter([Epoch(2), Epoch(4)]), + epoch_to_modify: Some(Epoch(2)), + new_amount: Some(4.into()), + }; + assert_eq!(mr8, exp_mr); +} + +/// `computeBondAtEpochTest` +#[test] +fn test_compute_bond_at_epoch() { + let mut storage = TestWlStorage::default(); + let params = OwnedPosParams { + pipeline_len: 2, + unbonding_len: 4, + cubic_slashing_window_length: 1, + ..Default::default() + }; + let alice = established_address_1(); + let bob = established_address_2(); + + // Test 1 + let res = compute_bond_at_epoch( + &storage, + ¶ms, + &bob, + 12.into(), + 3.into(), + 23.into(), + Some(&Default::default()), + ) + .unwrap(); + + pretty_assertions::assert_eq!(res, 23.into()); + + // Test 2 + validator_slashes_handle(&bob) + .push( + &mut storage, + Slash { + epoch: 4.into(), + block_height: 0, + r#type: SlashType::DuplicateVote, + rate: Dec::one(), + }, + ) + .unwrap(); + let res = compute_bond_at_epoch( + &storage, + ¶ms, + &bob, + 12.into(), + 3.into(), + 23.into(), + Some(&Default::default()), + ) + .unwrap(); + + pretty_assertions::assert_eq!(res, 0.into()); + + // Test 3 + validator_slashes_handle(&bob).pop(&mut storage).unwrap(); + let mut redel_bonds = EagerRedelegatedBondsMap::default(); + redel_bonds.insert( + alice.clone(), + BTreeMap::from_iter([(Epoch(1), token::Amount::from(5))]), + ); + let res = compute_bond_at_epoch( + &storage, + ¶ms, + &bob, + 12.into(), + 3.into(), + 23.into(), + Some(&redel_bonds), + ) + .unwrap(); + + pretty_assertions::assert_eq!(res, 23.into()); + + // Test 4 + validator_slashes_handle(&bob) + .push( + &mut storage, + Slash { + epoch: 4.into(), + block_height: 0, + r#type: SlashType::DuplicateVote, + rate: Dec::one(), + }, + ) + .unwrap(); + let res = compute_bond_at_epoch( + &storage, + ¶ms, + &bob, + 12.into(), + 3.into(), + 23.into(), + Some(&redel_bonds), + ) + .unwrap(); + + pretty_assertions::assert_eq!(res, 0.into()); + + // Test 5 + validator_slashes_handle(&bob).pop(&mut storage).unwrap(); + validator_slashes_handle(&alice) + .push( + &mut storage, + Slash { + epoch: 6.into(), + block_height: 0, + r#type: SlashType::DuplicateVote, + rate: Dec::one(), + }, + ) + .unwrap(); + let res = compute_bond_at_epoch( + &storage, + ¶ms, + &bob, + 12.into(), + 3.into(), + 23.into(), + Some(&redel_bonds), + ) + .unwrap(); + + pretty_assertions::assert_eq!(res, 23.into()); + + // Test 6 + validator_slashes_handle(&alice).pop(&mut storage).unwrap(); + validator_slashes_handle(&alice) + .push( + &mut storage, + Slash { + epoch: 4.into(), + block_height: 0, + r#type: SlashType::DuplicateVote, + rate: Dec::one(), + }, + ) + .unwrap(); + let res = compute_bond_at_epoch( + &storage, + ¶ms, + &bob, + 18.into(), + 9.into(), + 23.into(), + Some(&redel_bonds), + ) + .unwrap(); + + pretty_assertions::assert_eq!(res, 18.into()); +} + +/// `computeSlashBondAtEpochTest` +#[test] +fn test_compute_slash_bond_at_epoch() { + let mut storage = TestWlStorage::default(); + let params = OwnedPosParams { + pipeline_len: 2, + unbonding_len: 4, + cubic_slashing_window_length: 1, + ..Default::default() + }; + let alice = established_address_1(); + let bob = established_address_2(); + + let current_epoch = Epoch(20); + let infraction_epoch = + current_epoch - params.slash_processing_epoch_offset(); + + let redelegated_bond = BTreeMap::from_iter([( + alice, + BTreeMap::from_iter([(infraction_epoch - 4, token::Amount::from(10))]), + )]); + + // Test 1 + let res = compute_slash_bond_at_epoch( + &storage, + ¶ms, + &bob, + current_epoch.next(), + infraction_epoch, + infraction_epoch - 2, + 30.into(), + Some(&Default::default()), + Dec::one(), + ) + .unwrap(); + + pretty_assertions::assert_eq!(res, 30.into()); + + // Test 2 + let res = compute_slash_bond_at_epoch( + &storage, + ¶ms, + &bob, + current_epoch.next(), + infraction_epoch, + infraction_epoch - 2, + 30.into(), + Some(&redelegated_bond), + Dec::one(), + ) + .unwrap(); + + pretty_assertions::assert_eq!(res, 30.into()); + + // Test 3 + validator_slashes_handle(&bob) + .push( + &mut storage, + Slash { + epoch: infraction_epoch.prev(), + block_height: 0, + r#type: SlashType::DuplicateVote, + rate: Dec::one(), + }, + ) + .unwrap(); + let res = compute_slash_bond_at_epoch( + &storage, + ¶ms, + &bob, + current_epoch.next(), + infraction_epoch, + infraction_epoch - 2, + 30.into(), + Some(&Default::default()), + Dec::one(), + ) + .unwrap(); + + pretty_assertions::assert_eq!(res, 0.into()); + + // Test 4 + let res = compute_slash_bond_at_epoch( + &storage, + ¶ms, + &bob, + current_epoch.next(), + infraction_epoch, + infraction_epoch - 2, + 30.into(), + Some(&redelegated_bond), + Dec::one(), + ) + .unwrap(); + + pretty_assertions::assert_eq!(res, 0.into()); +} + +/// `computeNewRedelegatedUnbondsTest` +#[test] +fn test_compute_new_redelegated_unbonds() { + let mut storage = TestWlStorage::default(); + let alice = established_address_1(); + let bob = established_address_2(); + + let key = Key::parse("testing").unwrap(); + let redelegated_bonds = NestedMap::::open(key); + + // Populate the lazy and eager maps + let (ep1, ep2, ep4, ep5, ep6, ep7) = + (Epoch(1), Epoch(2), Epoch(4), Epoch(5), Epoch(6), Epoch(7)); + let keys_and_values = vec![ + (ep5, alice.clone(), ep2, 1), + (ep5, alice.clone(), ep4, 1), + (ep7, alice.clone(), ep2, 1), + (ep7, alice.clone(), ep4, 1), + (ep5, bob.clone(), ep1, 1), + (ep5, bob.clone(), ep4, 2), + (ep7, bob.clone(), ep1, 1), + (ep7, bob.clone(), ep4, 2), + ]; + let mut eager_map = BTreeMap::::new(); + for (outer_ep, address, inner_ep, amount) in keys_and_values { + redelegated_bonds + .at(&outer_ep) + .at(&address) + .insert(&mut storage, inner_ep, token::Amount::from(amount)) + .unwrap(); + eager_map + .entry(outer_ep) + .or_default() + .entry(address.clone()) + .or_default() + .insert(inner_ep, token::Amount::from(amount)); + } + + // Different ModifiedRedelegation objects for testing + let empty_mr = ModifiedRedelegation::default(); + let all_mr = ModifiedRedelegation { + epoch: Some(ep7), + validators_to_remove: BTreeSet::from_iter([alice.clone(), bob.clone()]), + validator_to_modify: None, + epochs_to_remove: Default::default(), + epoch_to_modify: None, + new_amount: None, + }; + let mod_val_mr = ModifiedRedelegation { + epoch: Some(ep7), + validators_to_remove: BTreeSet::from_iter([alice.clone()]), + validator_to_modify: None, + epochs_to_remove: Default::default(), + epoch_to_modify: None, + new_amount: None, + }; + let mod_val_partial_mr = ModifiedRedelegation { + epoch: Some(ep7), + validators_to_remove: BTreeSet::from_iter([alice.clone(), bob.clone()]), + validator_to_modify: Some(bob.clone()), + epochs_to_remove: BTreeSet::from_iter([ep1]), + epoch_to_modify: None, + new_amount: None, + }; + let mod_epoch_partial_mr = ModifiedRedelegation { + epoch: Some(ep7), + validators_to_remove: BTreeSet::from_iter([alice, bob.clone()]), + validator_to_modify: Some(bob.clone()), + epochs_to_remove: BTreeSet::from_iter([ep1, ep4]), + epoch_to_modify: Some(ep4), + new_amount: Some(token::Amount::from(1)), + }; + + // Test case 1 + let res = compute_new_redelegated_unbonds( + &storage, + &redelegated_bonds, + &Default::default(), + &empty_mr, + ) + .unwrap(); + assert_eq!(res, Default::default()); + + let set5 = BTreeSet::::from_iter([ep5]); + let set56 = BTreeSet::::from_iter([ep5, ep6]); + + // Test case 2 + let res = compute_new_redelegated_unbonds( + &storage, + &redelegated_bonds, + &set5, + &empty_mr, + ) + .unwrap(); + let mut exp_res = eager_map.clone(); + exp_res.remove(&ep7); + assert_eq!(res, exp_res); + + // Test case 3 + let res = compute_new_redelegated_unbonds( + &storage, + &redelegated_bonds, + &set56, + &empty_mr, + ) + .unwrap(); + assert_eq!(res, exp_res); + + // Test case 4 + println!("\nTEST CASE 4\n"); + let res = compute_new_redelegated_unbonds( + &storage, + &redelegated_bonds, + &set56, + &all_mr, + ) + .unwrap(); + assert_eq!(res, eager_map); + + // Test case 5 + let res = compute_new_redelegated_unbonds( + &storage, + &redelegated_bonds, + &set56, + &mod_val_mr, + ) + .unwrap(); + exp_res = eager_map.clone(); + exp_res.entry(ep7).or_default().remove(&bob); + assert_eq!(res, exp_res); + + // Test case 6 + let res = compute_new_redelegated_unbonds( + &storage, + &redelegated_bonds, + &set56, + &mod_val_partial_mr, + ) + .unwrap(); + exp_res = eager_map.clone(); + exp_res + .entry(ep7) + .or_default() + .entry(bob.clone()) + .or_default() + .remove(&ep4); + assert_eq!(res, exp_res); + + // Test case 7 + let res = compute_new_redelegated_unbonds( + &storage, + &redelegated_bonds, + &set56, + &mod_epoch_partial_mr, + ) + .unwrap(); + exp_res + .entry(ep7) + .or_default() + .entry(bob) + .or_default() + .insert(ep4, token::Amount::from(1)); + assert_eq!(res, exp_res); +} + +/// `applyListSlashesTest` +#[test] +fn test_apply_list_slashes() { + let init_epoch = Epoch(2); + let params = OwnedPosParams { + unbonding_len: 4, + ..Default::default() + }; + // let unbonding_len = 4u64; + // let cubic_offset = 1u64; + + let slash1 = Slash { + epoch: init_epoch, + block_height: Default::default(), + r#type: SlashType::DuplicateVote, + rate: Dec::one(), + }; + let slash2 = Slash { + epoch: init_epoch + + params.unbonding_len + + params.cubic_slashing_window_length + + 1u64, + block_height: Default::default(), + r#type: SlashType::DuplicateVote, + rate: Dec::one(), + }; + + let list1 = vec![slash1.clone()]; + let list2 = vec![slash1.clone(), slash2.clone()]; + let list3 = vec![slash1.clone(), slash1.clone()]; + let list4 = vec![slash1.clone(), slash1, slash2]; + + let res = apply_list_slashes(¶ms, &[], token::Amount::from(100)); + assert_eq!(res, token::Amount::from(100)); + + let res = apply_list_slashes(¶ms, &list1, token::Amount::from(100)); + assert_eq!(res, token::Amount::zero()); + + let res = apply_list_slashes(¶ms, &list2, token::Amount::from(100)); + assert_eq!(res, token::Amount::zero()); + + let res = apply_list_slashes(¶ms, &list3, token::Amount::from(100)); + assert_eq!(res, token::Amount::zero()); + + let res = apply_list_slashes(¶ms, &list4, token::Amount::from(100)); + assert_eq!(res, token::Amount::zero()); +} + +/// `computeSlashableAmountTest` +#[test] +fn test_compute_slashable_amount() { + let init_epoch = Epoch(2); + let params = OwnedPosParams { + unbonding_len: 4, + ..Default::default() + }; + + let slash1 = Slash { + epoch: init_epoch + + params.unbonding_len + + params.cubic_slashing_window_length, + block_height: Default::default(), + r#type: SlashType::DuplicateVote, + rate: Dec::one(), + }; + + let slash2 = Slash { + epoch: init_epoch + + params.unbonding_len + + params.cubic_slashing_window_length + + 1u64, + block_height: Default::default(), + r#type: SlashType::DuplicateVote, + rate: Dec::one(), + }; + + let test_map = vec![(init_epoch, token::Amount::from(50))] + .into_iter() + .collect::>(); + + let res = compute_slashable_amount( + ¶ms, + &slash1, + token::Amount::from(100), + &BTreeMap::new(), + ); + assert_eq!(res, token::Amount::from(100)); + + let res = compute_slashable_amount( + ¶ms, + &slash2, + token::Amount::from(100), + &test_map, + ); + assert_eq!(res, token::Amount::from(50)); + + let res = compute_slashable_amount( + ¶ms, + &slash1, + token::Amount::from(100), + &test_map, + ); + assert_eq!(res, token::Amount::from(100)); +} + +/// `foldAndSlashRedelegatedBondsMapTest` +#[test] +fn test_fold_and_slash_redelegated_bonds() { + let mut storage = TestWlStorage::default(); + let params = OwnedPosParams { + unbonding_len: 4, + ..Default::default() + }; + let start_epoch = Epoch(7); + + let alice = established_address_1(); + let bob = established_address_2(); + + println!("\n\nAlice: {}", alice); + println!("Bob: {}\n", bob); + + let test_slash = Slash { + epoch: Default::default(), + block_height: Default::default(), + r#type: SlashType::DuplicateVote, + rate: Dec::one(), + }; + + let test_data = vec![ + (alice.clone(), vec![(2, 1), (4, 1)]), + (bob, vec![(1, 1), (4, 2)]), + ]; + let mut eager_redel_bonds = EagerRedelegatedBondsMap::default(); + for (address, pair) in test_data { + for (epoch, amount) in pair { + eager_redel_bonds + .entry(address.clone()) + .or_default() + .insert(Epoch(epoch), token::Amount::from(amount)); + } + } + + // Test case 1 + let res = fold_and_slash_redelegated_bonds( + &storage, + ¶ms, + &eager_redel_bonds, + start_epoch, + &[], + |_| true, + ); + assert_eq!( + res, + FoldRedelegatedBondsResult { + total_redelegated: token::Amount::from(5), + total_after_slashing: token::Amount::from(5), + } + ); + + // Test case 2 + let res = fold_and_slash_redelegated_bonds( + &storage, + ¶ms, + &eager_redel_bonds, + start_epoch, + &[test_slash], + |_| true, + ); + assert_eq!( + res, + FoldRedelegatedBondsResult { + total_redelegated: token::Amount::from(5), + total_after_slashing: token::Amount::zero(), + } + ); + + // Test case 3 + let alice_slash = Slash { + epoch: Epoch(6), + block_height: Default::default(), + r#type: SlashType::DuplicateVote, + rate: Dec::one(), + }; + validator_slashes_handle(&alice) + .push(&mut storage, alice_slash) + .unwrap(); + + let res = fold_and_slash_redelegated_bonds( + &storage, + ¶ms, + &eager_redel_bonds, + start_epoch, + &[], + |_| true, + ); + assert_eq!( + res, + FoldRedelegatedBondsResult { + total_redelegated: token::Amount::from(5), + total_after_slashing: token::Amount::from(3), + } + ); +} + +/// `slashRedelegationTest` +#[test] +fn test_slash_redelegation() { + let mut storage = TestWlStorage::default(); + let params = OwnedPosParams { + unbonding_len: 4, + ..Default::default() + }; + let alice = established_address_1(); + + let total_redelegated_unbonded = + validator_total_redelegated_unbonded_handle(&alice); + total_redelegated_unbonded + .at(&Epoch(13)) + .at(&Epoch(10)) + .at(&alice) + .insert(&mut storage, Epoch(7), token::Amount::from(2)) + .unwrap(); + + let slashes = validator_slashes_handle(&alice); + + let mut slashed_amounts_map = BTreeMap::from_iter([ + (Epoch(15), token::Amount::zero()), + (Epoch(16), token::Amount::zero()), + ]); + let empty_slash_amounts = slashed_amounts_map.clone(); + + // Test case 1 + slash_redelegation( + &storage, + ¶ms, + token::Amount::from(7), + Epoch(7), + Epoch(10), + &alice, + Epoch(14), + &slashes, + &total_redelegated_unbonded, + Dec::one(), + &mut slashed_amounts_map, + ) + .unwrap(); + assert_eq!( + slashed_amounts_map, + BTreeMap::from_iter([ + (Epoch(15), token::Amount::from(5)), + (Epoch(16), token::Amount::from(5)), + ]) + ); + + // Test case 2 + slashed_amounts_map = empty_slash_amounts.clone(); + slash_redelegation( + &storage, + ¶ms, + token::Amount::from(7), + Epoch(7), + Epoch(11), + &alice, + Epoch(14), + &slashes, + &total_redelegated_unbonded, + Dec::one(), + &mut slashed_amounts_map, + ) + .unwrap(); + assert_eq!( + slashed_amounts_map, + BTreeMap::from_iter([ + (Epoch(15), token::Amount::from(7)), + (Epoch(16), token::Amount::from(7)), + ]) + ); + + // Test case 3 + slashed_amounts_map = BTreeMap::from_iter([ + (Epoch(15), token::Amount::from(2)), + (Epoch(16), token::Amount::from(3)), + ]); + slash_redelegation( + &storage, + ¶ms, + token::Amount::from(7), + Epoch(7), + Epoch(10), + &alice, + Epoch(14), + &slashes, + &total_redelegated_unbonded, + Dec::one(), + &mut slashed_amounts_map, + ) + .unwrap(); + assert_eq!( + slashed_amounts_map, + BTreeMap::from_iter([ + (Epoch(15), token::Amount::from(7)), + (Epoch(16), token::Amount::from(8)), + ]) + ); + + // Test case 4 + slashes + .push( + &mut storage, + Slash { + epoch: Epoch(8), + block_height: Default::default(), + r#type: SlashType::DuplicateVote, + rate: Dec::one(), + }, + ) + .unwrap(); + slashed_amounts_map = empty_slash_amounts.clone(); + slash_redelegation( + &storage, + ¶ms, + token::Amount::from(7), + Epoch(7), + Epoch(10), + &alice, + Epoch(14), + &slashes, + &total_redelegated_unbonded, + Dec::one(), + &mut slashed_amounts_map, + ) + .unwrap(); + assert_eq!(slashed_amounts_map, empty_slash_amounts); + + // Test case 5 + slashes.pop(&mut storage).unwrap(); + slashes + .push( + &mut storage, + Slash { + epoch: Epoch(9), + block_height: Default::default(), + r#type: SlashType::DuplicateVote, + rate: Dec::one(), + }, + ) + .unwrap(); + slash_redelegation( + &storage, + ¶ms, + token::Amount::from(7), + Epoch(7), + Epoch(10), + &alice, + Epoch(14), + &slashes, + &total_redelegated_unbonded, + Dec::one(), + &mut slashed_amounts_map, + ) + .unwrap(); + assert_eq!(slashed_amounts_map, empty_slash_amounts); + + // Test case 6 + slashes + .push( + &mut storage, + Slash { + epoch: Epoch(8), + block_height: Default::default(), + r#type: SlashType::DuplicateVote, + rate: Dec::one(), + }, + ) + .unwrap(); + slash_redelegation( + &storage, + ¶ms, + token::Amount::from(7), + Epoch(7), + Epoch(10), + &alice, + Epoch(14), + &slashes, + &total_redelegated_unbonded, + Dec::one(), + &mut slashed_amounts_map, + ) + .unwrap(); + assert_eq!(slashed_amounts_map, empty_slash_amounts); +} + +/// `slashValidatorRedelegationTest` +#[test] +fn test_slash_validator_redelegation() { + let mut storage = TestWlStorage::default(); + let params = OwnedPosParams { + unbonding_len: 4, + ..Default::default() + }; + let gov_params = + namada_governance::parameters::GovernanceParameters::default(); + gov_params.init_storage(&mut storage).unwrap(); + write_pos_params(&mut storage, ¶ms).unwrap(); + + let alice = established_address_1(); + let bob = established_address_2(); + + let total_redelegated_unbonded = + validator_total_redelegated_unbonded_handle(&alice); + total_redelegated_unbonded + .at(&Epoch(13)) + .at(&Epoch(10)) + .at(&alice) + .insert(&mut storage, Epoch(7), token::Amount::from(2)) + .unwrap(); + + let outgoing_redelegations = + validator_outgoing_redelegations_handle(&alice).at(&bob); + + let slashes = validator_slashes_handle(&alice); + + let mut slashed_amounts_map = BTreeMap::from_iter([ + (Epoch(15), token::Amount::zero()), + (Epoch(16), token::Amount::zero()), + ]); + let empty_slash_amounts = slashed_amounts_map.clone(); + + // Test case 1 + slash_validator_redelegation( + &storage, + ¶ms, + &alice, + Epoch(14), + &outgoing_redelegations, + &slashes, + &total_redelegated_unbonded, + Dec::one(), + &mut slashed_amounts_map, + ) + .unwrap(); + assert_eq!(slashed_amounts_map, empty_slash_amounts); + + // Test case 2 + total_redelegated_unbonded + .remove_all(&mut storage, &Epoch(13)) + .unwrap(); + slash_validator_redelegation( + &storage, + ¶ms, + &alice, + Epoch(14), + &outgoing_redelegations, + &slashes, + &total_redelegated_unbonded, + Dec::one(), + &mut slashed_amounts_map, + ) + .unwrap(); + assert_eq!(slashed_amounts_map, empty_slash_amounts); + + // Test case 3 + total_redelegated_unbonded + .at(&Epoch(13)) + .at(&Epoch(10)) + .at(&alice) + .insert(&mut storage, Epoch(7), token::Amount::from(2)) + .unwrap(); + outgoing_redelegations + .at(&Epoch(6)) + .insert(&mut storage, Epoch(8), token::Amount::from(7)) + .unwrap(); + slash_validator_redelegation( + &storage, + ¶ms, + &alice, + Epoch(14), + &outgoing_redelegations, + &slashes, + &total_redelegated_unbonded, + Dec::one(), + &mut slashed_amounts_map, + ) + .unwrap(); + assert_eq!( + slashed_amounts_map, + BTreeMap::from_iter([ + (Epoch(15), token::Amount::from(7)), + (Epoch(16), token::Amount::from(7)), + ]) + ); + + // Test case 4 + slashed_amounts_map = empty_slash_amounts.clone(); + outgoing_redelegations + .remove_all(&mut storage, &Epoch(6)) + .unwrap(); + outgoing_redelegations + .at(&Epoch(7)) + .insert(&mut storage, Epoch(8), token::Amount::from(7)) + .unwrap(); + slash_validator_redelegation( + &storage, + ¶ms, + &alice, + Epoch(14), + &outgoing_redelegations, + &slashes, + &total_redelegated_unbonded, + Dec::one(), + &mut slashed_amounts_map, + ) + .unwrap(); + assert_eq!( + slashed_amounts_map, + BTreeMap::from_iter([ + (Epoch(15), token::Amount::from(5)), + (Epoch(16), token::Amount::from(5)), + ]) + ); + + // Test case 5 + slashed_amounts_map = BTreeMap::from_iter([ + (Epoch(15), token::Amount::from(2)), + (Epoch(16), token::Amount::from(3)), + ]); + slash_validator_redelegation( + &storage, + ¶ms, + &alice, + Epoch(14), + &outgoing_redelegations, + &slashes, + &total_redelegated_unbonded, + Dec::one(), + &mut slashed_amounts_map, + ) + .unwrap(); + assert_eq!( + slashed_amounts_map, + BTreeMap::from_iter([ + (Epoch(15), token::Amount::from(7)), + (Epoch(16), token::Amount::from(8)), + ]) + ); + + // Test case 6 + slashed_amounts_map = empty_slash_amounts.clone(); + slashes + .push( + &mut storage, + Slash { + epoch: Epoch(8), + block_height: Default::default(), + r#type: SlashType::DuplicateVote, + rate: Dec::one(), + }, + ) + .unwrap(); + slash_validator_redelegation( + &storage, + ¶ms, + &alice, + Epoch(14), + &outgoing_redelegations, + &slashes, + &total_redelegated_unbonded, + Dec::one(), + &mut slashed_amounts_map, + ) + .unwrap(); + assert_eq!(slashed_amounts_map, empty_slash_amounts); +} + +/// `slashValidatorTest` +#[test] +fn test_slash_validator() { + let mut storage = TestWlStorage::default(); + let params = OwnedPosParams { + unbonding_len: 4, + ..Default::default() + }; + let gov_params = + namada_governance::parameters::GovernanceParameters::default(); + gov_params.init_storage(&mut storage).unwrap(); + write_pos_params(&mut storage, ¶ms).unwrap(); + + let alice = established_address_1(); + let bob = established_address_2(); + + let total_bonded = total_bonded_handle(&bob); + let total_unbonded = total_unbonded_handle(&bob); + let total_redelegated_bonded = + validator_total_redelegated_bonded_handle(&bob); + let total_redelegated_unbonded = + validator_total_redelegated_unbonded_handle(&bob); + + let infraction_stake = token::Amount::from(23); + + let initial_stakes = BTreeMap::from_iter([ + (Epoch(11), infraction_stake), + (Epoch(12), infraction_stake), + (Epoch(13), infraction_stake), + ]); + let mut exp_res = initial_stakes.clone(); + + let current_epoch = Epoch(10); + let infraction_epoch = + current_epoch - params.slash_processing_epoch_offset(); + let processing_epoch = current_epoch.next(); + let slash_rate = Dec::one(); + + // Test case 1 + println!("\nTEST 1:"); + + total_bonded + .set(&mut storage, 23.into(), infraction_epoch - 2, 0) + .unwrap(); + let res = slash_validator( + &storage, + ¶ms, + &bob, + slash_rate, + processing_epoch, + &Default::default(), + ) + .unwrap(); + assert_eq!(res, exp_res); + + // Test case 2 + println!("\nTEST 2:"); + total_bonded + .set(&mut storage, 17.into(), infraction_epoch - 2, 0) + .unwrap(); + total_unbonded + .at(&(current_epoch + params.pipeline_len)) + .insert(&mut storage, infraction_epoch - 2, 6.into()) + .unwrap(); + let res = slash_validator( + &storage, + ¶ms, + &bob, + slash_rate, + processing_epoch, + &Default::default(), + ) + .unwrap(); + exp_res.insert(Epoch(12), 17.into()); + exp_res.insert(Epoch(13), 17.into()); + assert_eq!(res, exp_res); + + // Test case 3 + println!("\nTEST 3:"); + total_redelegated_bonded + .at(&infraction_epoch.prev()) + .at(&alice) + .insert(&mut storage, Epoch(2), 5.into()) + .unwrap(); + total_redelegated_bonded + .at(&infraction_epoch.prev()) + .at(&alice) + .insert(&mut storage, Epoch(3), 1.into()) + .unwrap(); + + let res = slash_validator( + &storage, + ¶ms, + &bob, + slash_rate, + processing_epoch, + &Default::default(), + ) + .unwrap(); + assert_eq!(res, exp_res); + + // Test case 4 + println!("\nTEST 4:"); + total_unbonded_handle(&bob) + .at(&(current_epoch + params.pipeline_len)) + .remove(&mut storage, &(infraction_epoch - 2)) + .unwrap(); + total_unbonded_handle(&bob) + .at(&(current_epoch + params.pipeline_len)) + .insert(&mut storage, infraction_epoch - 1, 6.into()) + .unwrap(); + total_redelegated_unbonded + .at(&(current_epoch + params.pipeline_len)) + .at(&infraction_epoch.prev()) + .at(&alice) + .insert(&mut storage, Epoch(2), 5.into()) + .unwrap(); + total_redelegated_unbonded + .at(&(current_epoch + params.pipeline_len)) + .at(&infraction_epoch.prev()) + .at(&alice) + .insert(&mut storage, Epoch(3), 1.into()) + .unwrap(); + let res = slash_validator( + &storage, + ¶ms, + &bob, + slash_rate, + processing_epoch, + &Default::default(), + ) + .unwrap(); + assert_eq!(res, exp_res); + + // Test case 5 + println!("\nTEST 5:"); + total_bonded_handle(&bob) + .set(&mut storage, 19.into(), infraction_epoch - 2, 0) + .unwrap(); + total_unbonded_handle(&bob) + .at(&(current_epoch + params.pipeline_len)) + .insert(&mut storage, infraction_epoch - 1, 4.into()) + .unwrap(); + total_redelegated_bonded + .at(¤t_epoch) + .at(&alice) + .insert(&mut storage, Epoch(2), token::Amount::from(1)) + .unwrap(); + total_redelegated_unbonded + .at(&(current_epoch + params.pipeline_len)) + .at(&infraction_epoch.prev()) + .at(&alice) + .remove(&mut storage, &Epoch(3)) + .unwrap(); + total_redelegated_unbonded + .at(&(current_epoch + params.pipeline_len)) + .at(&infraction_epoch.prev()) + .at(&alice) + .insert(&mut storage, Epoch(2), 4.into()) + .unwrap(); + let res = slash_validator( + &storage, + ¶ms, + &bob, + slash_rate, + processing_epoch, + &Default::default(), + ) + .unwrap(); + exp_res.insert(Epoch(12), 19.into()); + exp_res.insert(Epoch(13), 19.into()); + assert_eq!(res, exp_res); + + // Test case 6 + println!("\nTEST 6:"); + total_unbonded_handle(&bob) + .remove_all(&mut storage, &(current_epoch + params.pipeline_len)) + .unwrap(); + total_redelegated_unbonded + .remove_all(&mut storage, &(current_epoch + params.pipeline_len)) + .unwrap(); + total_redelegated_bonded + .remove_all(&mut storage, ¤t_epoch) + .unwrap(); + total_bonded_handle(&bob) + .set(&mut storage, 23.into(), infraction_epoch - 2, 0) + .unwrap(); + total_bonded_handle(&bob) + .set(&mut storage, 6.into(), current_epoch, 0) + .unwrap(); + + let res = slash_validator( + &storage, + ¶ms, + &bob, + slash_rate, + processing_epoch, + &Default::default(), + ) + .unwrap(); + exp_res = initial_stakes; + assert_eq!(res, exp_res); + + // Test case 7 + println!("\nTEST 7:"); + total_bonded + .get_data_handler() + .remove(&mut storage, ¤t_epoch) + .unwrap(); + total_unbonded + .at(¤t_epoch.next()) + .insert(&mut storage, current_epoch, 6.into()) + .unwrap(); + let res = slash_validator( + &storage, + ¶ms, + &bob, + slash_rate, + processing_epoch, + &Default::default(), + ) + .unwrap(); + assert_eq!(res, exp_res); + + // Test case 8 + println!("\nTEST 8:"); + total_bonded + .get_data_handler() + .insert(&mut storage, current_epoch, 3.into()) + .unwrap(); + total_unbonded + .at(¤t_epoch.next()) + .insert(&mut storage, current_epoch, 3.into()) + .unwrap(); + let res = slash_validator( + &storage, + ¶ms, + &bob, + slash_rate, + processing_epoch, + &Default::default(), + ) + .unwrap(); + assert_eq!(res, exp_res); + + // Test case 9 + println!("\nTEST 9:"); + total_unbonded + .remove_all(&mut storage, ¤t_epoch.next()) + .unwrap(); + total_bonded + .set(&mut storage, 6.into(), current_epoch, 0) + .unwrap(); + total_redelegated_bonded + .at(¤t_epoch) + .at(&alice) + .insert(&mut storage, 2.into(), 5.into()) + .unwrap(); + total_redelegated_bonded + .at(¤t_epoch) + .at(&alice) + .insert(&mut storage, 3.into(), 1.into()) + .unwrap(); + let res = slash_validator( + &storage, + ¶ms, + &bob, + slash_rate, + processing_epoch, + &Default::default(), + ) + .unwrap(); + assert_eq!(res, exp_res); + + // Test case 10 + println!("\nTEST 10:"); + total_redelegated_bonded + .remove_all(&mut storage, ¤t_epoch) + .unwrap(); + total_bonded + .get_data_handler() + .remove(&mut storage, ¤t_epoch) + .unwrap(); + total_redelegated_unbonded + .at(¤t_epoch.next()) + .at(¤t_epoch) + .at(&alice) + .insert(&mut storage, 2.into(), 5.into()) + .unwrap(); + total_redelegated_unbonded + .at(¤t_epoch.next()) + .at(¤t_epoch) + .at(&alice) + .insert(&mut storage, 3.into(), 1.into()) + .unwrap(); + let res = slash_validator( + &storage, + ¶ms, + &bob, + slash_rate, + processing_epoch, + &Default::default(), + ) + .unwrap(); + assert_eq!(res, exp_res); + + // Test case 11 + println!("\nTEST 11:"); + total_bonded + .set(&mut storage, 2.into(), current_epoch, 0) + .unwrap(); + total_redelegated_unbonded + .at(¤t_epoch.next()) + .at(¤t_epoch) + .at(&alice) + .insert(&mut storage, 2.into(), 4.into()) + .unwrap(); + total_redelegated_unbonded + .at(¤t_epoch.next()) + .at(¤t_epoch) + .at(&alice) + .remove(&mut storage, &3.into()) + .unwrap(); + total_redelegated_bonded + .at(¤t_epoch) + .at(&alice) + .insert(&mut storage, 2.into(), 1.into()) + .unwrap(); + total_redelegated_bonded + .at(¤t_epoch) + .at(&alice) + .insert(&mut storage, 3.into(), 1.into()) + .unwrap(); + let res = slash_validator( + &storage, + ¶ms, + &bob, + slash_rate, + processing_epoch, + &Default::default(), + ) + .unwrap(); + assert_eq!(res, exp_res); + + // Test case 12 + println!("\nTEST 12:"); + total_bonded + .set(&mut storage, 6.into(), current_epoch, 0) + .unwrap(); + total_bonded + .set(&mut storage, 2.into(), current_epoch.next(), 0) + .unwrap(); + total_redelegated_bonded + .remove_all(&mut storage, ¤t_epoch) + .unwrap(); + total_redelegated_bonded + .at(¤t_epoch.next()) + .at(&alice) + .insert(&mut storage, 2.into(), 1.into()) + .unwrap(); + total_redelegated_bonded + .at(¤t_epoch.next()) + .at(&alice) + .insert(&mut storage, 3.into(), 1.into()) + .unwrap(); + let res = slash_validator( + &storage, + ¶ms, + &bob, + slash_rate, + processing_epoch, + &Default::default(), + ) + .unwrap(); + assert_eq!(res, exp_res); + + // Test case 13 + println!("\nTEST 13:"); + validator_slashes_handle(&bob) + .push( + &mut storage, + Slash { + epoch: infraction_epoch.prev(), + block_height: 0, + r#type: SlashType::DuplicateVote, + rate: Dec::one(), + }, + ) + .unwrap(); + total_redelegated_unbonded + .remove_all(&mut storage, ¤t_epoch.next()) + .unwrap(); + total_bonded + .get_data_handler() + .remove(&mut storage, ¤t_epoch.next()) + .unwrap(); + total_redelegated_bonded + .remove_all(&mut storage, ¤t_epoch.next()) + .unwrap(); + let res = slash_validator( + &storage, + ¶ms, + &bob, + slash_rate, + processing_epoch, + &Default::default(), + ) + .unwrap(); + exp_res.insert(Epoch(11), 0.into()); + exp_res.insert(Epoch(12), 0.into()); + exp_res.insert(Epoch(13), 0.into()); + assert_eq!(res, exp_res); +} + +/// `computeAmountAfterSlashingUnbondTest` +#[test] +fn compute_amount_after_slashing_unbond_test() { + let mut storage = TestWlStorage::default(); + let params = OwnedPosParams { + unbonding_len: 4, + ..Default::default() + }; + + // Test data + let alice = established_address_1(); + let bob = established_address_2(); + let unbonds: BTreeMap = BTreeMap::from_iter([ + ((Epoch(2)), token::Amount::from(5)), + ((Epoch(4)), token::Amount::from(6)), + ]); + let redelegated_unbonds: EagerRedelegatedUnbonds = BTreeMap::from_iter([( + Epoch(2), + BTreeMap::from_iter([( + alice.clone(), + BTreeMap::from_iter([(Epoch(1), token::Amount::from(1))]), + )]), + )]); + + // Test case 1 + let slashes = vec![]; + let result = compute_amount_after_slashing_unbond( + &storage, + ¶ms, + &unbonds, + &redelegated_unbonds, + slashes, + ) + .unwrap(); + assert_eq!(result.sum, 11.into()); + itertools::assert_equal( + result.epoch_map, + [(2.into(), 5.into()), (4.into(), 6.into())], + ); + + // Test case 2 + let bob_slash = Slash { + epoch: Epoch(5), + block_height: Default::default(), + r#type: SlashType::DuplicateVote, + rate: Dec::one(), + }; + let slashes = vec![bob_slash.clone()]; + validator_slashes_handle(&bob) + .push(&mut storage, bob_slash) + .unwrap(); + let result = compute_amount_after_slashing_unbond( + &storage, + ¶ms, + &unbonds, + &redelegated_unbonds, + slashes, + ) + .unwrap(); + assert_eq!(result.sum, 0.into()); + itertools::assert_equal( + result.epoch_map, + [(2.into(), 0.into()), (4.into(), 0.into())], + ); + + // Test case 3 + let alice_slash = Slash { + epoch: Epoch(0), + block_height: Default::default(), + r#type: SlashType::DuplicateVote, + rate: Dec::one(), + }; + let slashes = vec![alice_slash.clone()]; + validator_slashes_handle(&alice) + .push(&mut storage, alice_slash) + .unwrap(); + validator_slashes_handle(&bob).pop(&mut storage).unwrap(); + let result = compute_amount_after_slashing_unbond( + &storage, + ¶ms, + &unbonds, + &redelegated_unbonds, + slashes, + ) + .unwrap(); + assert_eq!(result.sum, 11.into()); + itertools::assert_equal( + result.epoch_map, + [(2.into(), 5.into()), (4.into(), 6.into())], + ); + + // Test case 4 + let alice_slash = Slash { + epoch: Epoch(1), + block_height: Default::default(), + r#type: SlashType::DuplicateVote, + rate: Dec::one(), + }; + let slashes = vec![alice_slash.clone()]; + validator_slashes_handle(&alice).pop(&mut storage).unwrap(); + validator_slashes_handle(&alice) + .push(&mut storage, alice_slash) + .unwrap(); + let result = compute_amount_after_slashing_unbond( + &storage, + ¶ms, + &unbonds, + &redelegated_unbonds, + slashes, + ) + .unwrap(); + assert_eq!(result.sum, 10.into()); + itertools::assert_equal( + result.epoch_map, + [(2.into(), 4.into()), (4.into(), 6.into())], + ); +} + +/// `computeAmountAfterSlashingWithdrawTest` +#[test] +fn compute_amount_after_slashing_withdraw_test() { + let mut storage = TestWlStorage::default(); + let params = OwnedPosParams { + unbonding_len: 4, + ..Default::default() + }; + + // Test data + let alice = established_address_1(); + let bob = established_address_2(); + let unbonds_and_redelegated_unbonds: BTreeMap< + (Epoch, Epoch), + (token::Amount, EagerRedelegatedBondsMap), + > = BTreeMap::from_iter([ + ( + (Epoch(2), Epoch(20)), + ( + // unbond + token::Amount::from(5), + // redelegations + BTreeMap::from_iter([( + alice.clone(), + BTreeMap::from_iter([(Epoch(1), token::Amount::from(1))]), + )]), + ), + ), + ( + (Epoch(4), Epoch(20)), + ( + // unbond + token::Amount::from(6), + // redelegations + BTreeMap::default(), + ), + ), + ]); + + // Test case 1 + let slashes = vec![]; + let result = compute_amount_after_slashing_withdraw( + &storage, + ¶ms, + &unbonds_and_redelegated_unbonds, + slashes, + ) + .unwrap(); + assert_eq!(result.sum, 11.into()); + itertools::assert_equal( + result.epoch_map, + [(2.into(), 5.into()), (4.into(), 6.into())], + ); + + // Test case 2 + let bob_slash = Slash { + epoch: Epoch(5), + block_height: Default::default(), + r#type: SlashType::DuplicateVote, + rate: Dec::one(), + }; + let slashes = vec![bob_slash.clone()]; + validator_slashes_handle(&bob) + .push(&mut storage, bob_slash) + .unwrap(); + let result = compute_amount_after_slashing_withdraw( + &storage, + ¶ms, + &unbonds_and_redelegated_unbonds, + slashes, + ) + .unwrap(); + assert_eq!(result.sum, 0.into()); + itertools::assert_equal( + result.epoch_map, + [(2.into(), 0.into()), (4.into(), 0.into())], + ); + + // Test case 3 + let alice_slash = Slash { + epoch: Epoch(0), + block_height: Default::default(), + r#type: SlashType::DuplicateVote, + rate: Dec::one(), + }; + let slashes = vec![alice_slash.clone()]; + validator_slashes_handle(&alice) + .push(&mut storage, alice_slash) + .unwrap(); + validator_slashes_handle(&bob).pop(&mut storage).unwrap(); + let result = compute_amount_after_slashing_withdraw( + &storage, + ¶ms, + &unbonds_and_redelegated_unbonds, + slashes, + ) + .unwrap(); + assert_eq!(result.sum, 11.into()); + itertools::assert_equal( + result.epoch_map, + [(2.into(), 5.into()), (4.into(), 6.into())], + ); + + // Test case 4 + let alice_slash = Slash { + epoch: Epoch(1), + block_height: Default::default(), + r#type: SlashType::DuplicateVote, + rate: Dec::one(), + }; + let slashes = vec![alice_slash.clone()]; + validator_slashes_handle(&alice).pop(&mut storage).unwrap(); + validator_slashes_handle(&alice) + .push(&mut storage, alice_slash) + .unwrap(); + let result = compute_amount_after_slashing_withdraw( + &storage, + ¶ms, + &unbonds_and_redelegated_unbonds, + slashes, + ) + .unwrap(); + assert_eq!(result.sum, 10.into()); + itertools::assert_equal( + result.epoch_map, + [(2.into(), 4.into()), (4.into(), 6.into())], + ); +} + +fn arb_redelegation_amounts( + max_delegation: u64, +) -> impl Strategy { + let arb_delegation = arb_amount_non_zero_ceiled(max_delegation); + let amounts = arb_delegation.prop_flat_map(move |amount_delegate| { + let amount_redelegate = arb_amount_non_zero_ceiled(max( + 1, + u64::try_from(amount_delegate.raw_amount()).unwrap() - 1, + )); + (Just(amount_delegate), amount_redelegate) + }); + amounts.prop_flat_map(move |(amount_delegate, amount_redelegate)| { + let amount_unbond = arb_amount_non_zero_ceiled(max( + 1, + u64::try_from(amount_redelegate.raw_amount()).unwrap() - 1, + )); + ( + Just(amount_delegate), + Just(amount_redelegate), + amount_unbond, + ) + }) +} + +fn test_simple_redelegation_aux( + mut validators: Vec, + amount_delegate: token::Amount, + amount_redelegate: token::Amount, + amount_unbond: token::Amount, +) { + validators.sort_by(|a, b| b.tokens.cmp(&a.tokens)); + + let src_validator = validators[0].address.clone(); + let dest_validator = validators[1].address.clone(); + + let mut storage = TestWlStorage::default(); + let params = OwnedPosParams { + unbonding_len: 4, + ..Default::default() + }; + + // Genesis + let mut current_epoch = storage.storage.block.epoch; + let params = test_init_genesis( + &mut storage, + params, + validators.clone().into_iter(), + current_epoch, + ) + .unwrap(); + storage.commit_block().unwrap(); + + // Get a delegator with some tokens + let staking_token = staking_token_address(&storage); + let delegator = address::testing::gen_implicit_address(); + let del_balance = token::Amount::from_uint(1_000_000, 0).unwrap(); + credit_tokens(&mut storage, &staking_token, &delegator, del_balance) + .unwrap(); + + // Ensure that we cannot redelegate with the same src and dest validator + let err = super::redelegate_tokens( + &mut storage, + &delegator, + &src_validator, + &src_validator, + current_epoch, + amount_redelegate, + ) + .unwrap_err(); + let err_str = err.to_string(); + assert_matches!( + err.downcast::().unwrap().deref(), + RedelegationError::RedelegationSrcEqDest, + "Redelegation with the same src and dest validator must be rejected, \ + got {err_str}", + ); + + for _ in 0..5 { + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + } + + let init_epoch = current_epoch; + + // Delegate in epoch 1 to src_validator + println!( + "\nBONDING {} TOKENS TO {}\n", + amount_delegate.to_string_native(), + &src_validator + ); + super::bond_tokens( + &mut storage, + Some(&delegator), + &src_validator, + amount_delegate, + current_epoch, + None, + ) + .unwrap(); + + println!("\nAFTER DELEGATION\n"); + let bonds = bond_handle(&delegator, &src_validator) + .get_data_handler() + .collect_map(&storage) + .unwrap(); + let bonds_dest = bond_handle(&delegator, &dest_validator) + .get_data_handler() + .collect_map(&storage) + .unwrap(); + let unbonds = unbond_handle(&delegator, &src_validator) + .collect_map(&storage) + .unwrap(); + let tot_bonds = total_bonded_handle(&src_validator) + .get_data_handler() + .collect_map(&storage) + .unwrap(); + let tot_unbonds = total_unbonded_handle(&src_validator) + .collect_map(&storage) + .unwrap(); + dbg!(&bonds, &bonds_dest, &unbonds, &tot_bonds, &tot_unbonds); + + // Advance three epochs + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + + // Redelegate in epoch 3 + println!( + "\nREDELEGATING {} TOKENS TO {}\n", + amount_redelegate.to_string_native(), + &dest_validator + ); + + super::redelegate_tokens( + &mut storage, + &delegator, + &src_validator, + &dest_validator, + current_epoch, + amount_redelegate, + ) + .unwrap(); + + println!("\nAFTER REDELEGATION\n"); + println!("\nDELEGATOR\n"); + let bonds_src = bond_handle(&delegator, &src_validator) + .get_data_handler() + .collect_map(&storage) + .unwrap(); + let bonds_dest = bond_handle(&delegator, &dest_validator) + .get_data_handler() + .collect_map(&storage) + .unwrap(); + let unbonds_src = unbond_handle(&delegator, &src_validator) + .collect_map(&storage) + .unwrap(); + let unbonds_dest = unbond_handle(&delegator, &dest_validator) + .collect_map(&storage) + .unwrap(); + let redel_bonds = delegator_redelegated_bonds_handle(&delegator) + .collect_map(&storage) + .unwrap(); + let redel_unbonds = delegator_redelegated_unbonds_handle(&delegator) + .collect_map(&storage) + .unwrap(); + + dbg!( + &bonds_src, + &bonds_dest, + &unbonds_src, + &unbonds_dest, + &redel_bonds, + &redel_unbonds + ); + + // Dest val + println!("\nDEST VALIDATOR\n"); + + let incoming_redels_dest = + validator_incoming_redelegations_handle(&dest_validator) + .collect_map(&storage) + .unwrap(); + let outgoing_redels_dest = + validator_outgoing_redelegations_handle(&dest_validator) + .collect_map(&storage) + .unwrap(); + let tot_bonds_dest = total_bonded_handle(&dest_validator) + .get_data_handler() + .collect_map(&storage) + .unwrap(); + let tot_unbonds_dest = total_unbonded_handle(&dest_validator) + .collect_map(&storage) + .unwrap(); + let tot_redel_bonds_dest = + validator_total_redelegated_bonded_handle(&dest_validator) + .collect_map(&storage) + .unwrap(); + let tot_redel_unbonds_dest = + validator_total_redelegated_unbonded_handle(&dest_validator) + .collect_map(&storage) + .unwrap(); + dbg!( + &incoming_redels_dest, + &outgoing_redels_dest, + &tot_bonds_dest, + &tot_unbonds_dest, + &tot_redel_bonds_dest, + &tot_redel_unbonds_dest + ); + + // Src val + println!("\nSRC VALIDATOR\n"); + + let incoming_redels_src = + validator_incoming_redelegations_handle(&src_validator) + .collect_map(&storage) + .unwrap(); + let outgoing_redels_src = + validator_outgoing_redelegations_handle(&src_validator) + .collect_map(&storage) + .unwrap(); + let tot_bonds_src = total_bonded_handle(&src_validator) + .get_data_handler() + .collect_map(&storage) + .unwrap(); + let tot_unbonds_src = total_unbonded_handle(&src_validator) + .collect_map(&storage) + .unwrap(); + let tot_redel_bonds_src = + validator_total_redelegated_bonded_handle(&src_validator) + .collect_map(&storage) + .unwrap(); + let tot_redel_unbonds_src = + validator_total_redelegated_unbonded_handle(&src_validator) + .collect_map(&storage) + .unwrap(); + dbg!( + &incoming_redels_src, + &outgoing_redels_src, + &tot_bonds_src, + &tot_unbonds_src, + &tot_redel_bonds_src, + &tot_redel_unbonds_src + ); + + // Checks + let redelegated = delegator_redelegated_bonds_handle(&delegator) + .at(&dest_validator) + .at(&(current_epoch + params.pipeline_len)) + .at(&src_validator) + .get(&storage, &(init_epoch + params.pipeline_len)) + .unwrap() + .unwrap(); + assert_eq!(redelegated, amount_redelegate); + + let redel_start_epoch = + validator_incoming_redelegations_handle(&dest_validator) + .get(&storage, &delegator) + .unwrap() + .unwrap(); + assert_eq!(redel_start_epoch, current_epoch + params.pipeline_len); + + let redelegated = validator_outgoing_redelegations_handle(&src_validator) + .at(&dest_validator) + .at(¤t_epoch.prev()) + .get(&storage, ¤t_epoch) + .unwrap() + .unwrap(); + assert_eq!(redelegated, amount_redelegate); + + // Advance three epochs + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + + // Unbond in epoch 5 from dest_validator + println!( + "\nUNBONDING {} TOKENS FROM {}\n", + amount_unbond.to_string_native(), + &dest_validator + ); + let _ = unbond_tokens( + &mut storage, + Some(&delegator), + &dest_validator, + amount_unbond, + current_epoch, + false, + ) + .unwrap(); + + println!("\nAFTER UNBONDING\n"); + println!("\nDELEGATOR\n"); + + let bonds_src = bond_handle(&delegator, &src_validator) + .get_data_handler() + .collect_map(&storage) + .unwrap(); + let bonds_dest = bond_handle(&delegator, &dest_validator) + .get_data_handler() + .collect_map(&storage) + .unwrap(); + let unbonds_src = unbond_handle(&delegator, &src_validator) + .collect_map(&storage) + .unwrap(); + let unbonds_dest = unbond_handle(&delegator, &dest_validator) + .collect_map(&storage) + .unwrap(); + let redel_bonds = delegator_redelegated_bonds_handle(&delegator) + .collect_map(&storage) + .unwrap(); + let redel_unbonds = delegator_redelegated_unbonds_handle(&delegator) + .collect_map(&storage) + .unwrap(); + + dbg!( + &bonds_src, + &bonds_dest, + &unbonds_src, + &unbonds_dest, + &redel_bonds, + &redel_unbonds + ); + + println!("\nDEST VALIDATOR\n"); + + let incoming_redels_dest = + validator_incoming_redelegations_handle(&dest_validator) + .collect_map(&storage) + .unwrap(); + let outgoing_redels_dest = + validator_outgoing_redelegations_handle(&dest_validator) + .collect_map(&storage) + .unwrap(); + let tot_bonds_dest = total_bonded_handle(&dest_validator) + .get_data_handler() + .collect_map(&storage) + .unwrap(); + let tot_unbonds_dest = total_unbonded_handle(&dest_validator) + .collect_map(&storage) + .unwrap(); + let tot_redel_bonds_dest = + validator_total_redelegated_bonded_handle(&dest_validator) + .collect_map(&storage) + .unwrap(); + let tot_redel_unbonds_dest = + validator_total_redelegated_unbonded_handle(&dest_validator) + .collect_map(&storage) + .unwrap(); + dbg!( + &incoming_redels_dest, + &outgoing_redels_dest, + &tot_bonds_dest, + &tot_unbonds_dest, + &tot_redel_bonds_dest, + &tot_redel_unbonds_dest + ); + + let bond_start = init_epoch + params.pipeline_len; + let redelegation_end = bond_start + params.pipeline_len + 1u64; + let unbond_end = + redelegation_end + params.withdrawable_epoch_offset() + 1u64; + let unbond_materialized = redelegation_end + params.pipeline_len + 1u64; + + // Checks + let redelegated_remaining = delegator_redelegated_bonds_handle(&delegator) + .at(&dest_validator) + .at(&redelegation_end) + .at(&src_validator) + .get(&storage, &bond_start) + .unwrap() + .unwrap_or_default(); + assert_eq!(redelegated_remaining, amount_redelegate - amount_unbond); + + let redel_unbonded = delegator_redelegated_unbonds_handle(&delegator) + .at(&dest_validator) + .at(&redelegation_end) + .at(&unbond_end) + .at(&src_validator) + .get(&storage, &bond_start) + .unwrap() + .unwrap(); + assert_eq!(redel_unbonded, amount_unbond); + + dbg!(unbond_materialized, redelegation_end, bond_start); + let total_redel_unbonded = + validator_total_redelegated_unbonded_handle(&dest_validator) + .at(&unbond_materialized) + .at(&redelegation_end) + .at(&src_validator) + .get(&storage, &bond_start) + .unwrap() + .unwrap(); + assert_eq!(total_redel_unbonded, amount_unbond); + + // Advance to withdrawal epoch + loop { + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + if current_epoch == unbond_end { + break; + } + } + + // Withdraw + withdraw_tokens( + &mut storage, + Some(&delegator), + &dest_validator, + current_epoch, + ) + .unwrap(); + + assert!( + delegator_redelegated_unbonds_handle(&delegator) + .at(&dest_validator) + .is_empty(&storage) + .unwrap() + ); + + let delegator_balance = storage + .read::(&token::balance_key(&staking_token, &delegator)) + .unwrap() + .unwrap_or_default(); + assert_eq!( + delegator_balance, + del_balance - amount_delegate + amount_unbond + ); +} + +fn test_redelegation_with_slashing_aux( + mut validators: Vec, + amount_delegate: token::Amount, + amount_redelegate: token::Amount, + amount_unbond: token::Amount, +) { + validators.sort_by(|a, b| b.tokens.cmp(&a.tokens)); + + let src_validator = validators[0].address.clone(); + let dest_validator = validators[1].address.clone(); + + let mut storage = TestWlStorage::default(); + let params = OwnedPosParams { + unbonding_len: 4, + // Avoid empty consensus set by removing the threshold + validator_stake_threshold: token::Amount::zero(), + ..Default::default() + }; + + // Genesis + let mut current_epoch = storage.storage.block.epoch; + let params = test_init_genesis( + &mut storage, + params, + validators.clone().into_iter(), + current_epoch, + ) + .unwrap(); + storage.commit_block().unwrap(); + + // Get a delegator with some tokens + let staking_token = staking_token_address(&storage); + let delegator = address::testing::gen_implicit_address(); + let del_balance = token::Amount::from_uint(1_000_000, 0).unwrap(); + credit_tokens(&mut storage, &staking_token, &delegator, del_balance) + .unwrap(); + + for _ in 0..5 { + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + } + + let init_epoch = current_epoch; + + // Delegate in epoch 5 to src_validator + println!( + "\nBONDING {} TOKENS TO {}\n", + amount_delegate.to_string_native(), + &src_validator + ); + super::bond_tokens( + &mut storage, + Some(&delegator), + &src_validator, + amount_delegate, + current_epoch, + None, + ) + .unwrap(); + + println!("\nAFTER DELEGATION\n"); + let bonds = bond_handle(&delegator, &src_validator) + .get_data_handler() + .collect_map(&storage) + .unwrap(); + let bonds_dest = bond_handle(&delegator, &dest_validator) + .get_data_handler() + .collect_map(&storage) + .unwrap(); + let unbonds = unbond_handle(&delegator, &src_validator) + .collect_map(&storage) + .unwrap(); + let tot_bonds = total_bonded_handle(&src_validator) + .get_data_handler() + .collect_map(&storage) + .unwrap(); + let tot_unbonds = total_unbonded_handle(&src_validator) + .collect_map(&storage) + .unwrap(); + dbg!(&bonds, &bonds_dest, &unbonds, &tot_bonds, &tot_unbonds); + + // Advance three epochs + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + + // Redelegate in epoch 8 + println!( + "\nREDELEGATING {} TOKENS TO {}\n", + amount_redelegate.to_string_native(), + &dest_validator + ); + + super::redelegate_tokens( + &mut storage, + &delegator, + &src_validator, + &dest_validator, + current_epoch, + amount_redelegate, + ) + .unwrap(); + + println!("\nAFTER REDELEGATION\n"); + println!("\nDELEGATOR\n"); + let bonds_src = bond_handle(&delegator, &src_validator) + .get_data_handler() + .collect_map(&storage) + .unwrap(); + let bonds_dest = bond_handle(&delegator, &dest_validator) + .get_data_handler() + .collect_map(&storage) + .unwrap(); + let unbonds_src = unbond_handle(&delegator, &src_validator) + .collect_map(&storage) + .unwrap(); + let unbonds_dest = unbond_handle(&delegator, &dest_validator) + .collect_map(&storage) + .unwrap(); + let redel_bonds = delegator_redelegated_bonds_handle(&delegator) + .collect_map(&storage) + .unwrap(); + let redel_unbonds = delegator_redelegated_unbonds_handle(&delegator) + .collect_map(&storage) + .unwrap(); + + dbg!( + &bonds_src, + &bonds_dest, + &unbonds_src, + &unbonds_dest, + &redel_bonds, + &redel_unbonds + ); + + // Dest val + println!("\nDEST VALIDATOR\n"); + + let incoming_redels_dest = + validator_incoming_redelegations_handle(&dest_validator) + .collect_map(&storage) + .unwrap(); + let outgoing_redels_dest = + validator_outgoing_redelegations_handle(&dest_validator) + .collect_map(&storage) + .unwrap(); + let tot_bonds_dest = total_bonded_handle(&dest_validator) + .get_data_handler() + .collect_map(&storage) + .unwrap(); + let tot_unbonds_dest = total_unbonded_handle(&dest_validator) + .collect_map(&storage) + .unwrap(); + let tot_redel_bonds_dest = + validator_total_redelegated_bonded_handle(&dest_validator) + .collect_map(&storage) + .unwrap(); + let tot_redel_unbonds_dest = + validator_total_redelegated_unbonded_handle(&dest_validator) + .collect_map(&storage) + .unwrap(); + dbg!( + &incoming_redels_dest, + &outgoing_redels_dest, + &tot_bonds_dest, + &tot_unbonds_dest, + &tot_redel_bonds_dest, + &tot_redel_unbonds_dest + ); + + // Src val + println!("\nSRC VALIDATOR\n"); + + let incoming_redels_src = + validator_incoming_redelegations_handle(&src_validator) + .collect_map(&storage) + .unwrap(); + let outgoing_redels_src = + validator_outgoing_redelegations_handle(&src_validator) + .collect_map(&storage) + .unwrap(); + let tot_bonds_src = total_bonded_handle(&src_validator) + .get_data_handler() + .collect_map(&storage) + .unwrap(); + let tot_unbonds_src = total_unbonded_handle(&src_validator) + .collect_map(&storage) + .unwrap(); + let tot_redel_bonds_src = + validator_total_redelegated_bonded_handle(&src_validator) + .collect_map(&storage) + .unwrap(); + let tot_redel_unbonds_src = + validator_total_redelegated_unbonded_handle(&src_validator) + .collect_map(&storage) + .unwrap(); + dbg!( + &incoming_redels_src, + &outgoing_redels_src, + &tot_bonds_src, + &tot_unbonds_src, + &tot_redel_bonds_src, + &tot_redel_unbonds_src + ); + + // Checks + let redelegated = delegator_redelegated_bonds_handle(&delegator) + .at(&dest_validator) + .at(&(current_epoch + params.pipeline_len)) + .at(&src_validator) + .get(&storage, &(init_epoch + params.pipeline_len)) + .unwrap() + .unwrap(); + assert_eq!(redelegated, amount_redelegate); + + let redel_start_epoch = + validator_incoming_redelegations_handle(&dest_validator) + .get(&storage, &delegator) + .unwrap() + .unwrap(); + assert_eq!(redel_start_epoch, current_epoch + params.pipeline_len); + + let redelegated = validator_outgoing_redelegations_handle(&src_validator) + .at(&dest_validator) + .at(¤t_epoch.prev()) + .get(&storage, ¤t_epoch) + .unwrap() + .unwrap(); + assert_eq!(redelegated, amount_redelegate); + + // Advance three epochs + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + + // Unbond in epoch 11 from dest_validator + println!( + "\nUNBONDING {} TOKENS FROM {}\n", + amount_unbond.to_string_native(), + &dest_validator + ); + let _ = unbond_tokens( + &mut storage, + Some(&delegator), + &dest_validator, + amount_unbond, + current_epoch, + false, + ) + .unwrap(); + + println!("\nAFTER UNBONDING\n"); + println!("\nDELEGATOR\n"); + + let bonds_src = bond_handle(&delegator, &src_validator) + .get_data_handler() + .collect_map(&storage) + .unwrap(); + let bonds_dest = bond_handle(&delegator, &dest_validator) + .get_data_handler() + .collect_map(&storage) + .unwrap(); + let unbonds_src = unbond_handle(&delegator, &src_validator) + .collect_map(&storage) + .unwrap(); + let unbonds_dest = unbond_handle(&delegator, &dest_validator) + .collect_map(&storage) + .unwrap(); + let redel_bonds = delegator_redelegated_bonds_handle(&delegator) + .collect_map(&storage) + .unwrap(); + let redel_unbonds = delegator_redelegated_unbonds_handle(&delegator) + .collect_map(&storage) + .unwrap(); + + dbg!( + &bonds_src, + &bonds_dest, + &unbonds_src, + &unbonds_dest, + &redel_bonds, + &redel_unbonds + ); + + println!("\nDEST VALIDATOR\n"); + + let incoming_redels_dest = + validator_incoming_redelegations_handle(&dest_validator) + .collect_map(&storage) + .unwrap(); + let outgoing_redels_dest = + validator_outgoing_redelegations_handle(&dest_validator) + .collect_map(&storage) + .unwrap(); + let tot_bonds_dest = total_bonded_handle(&dest_validator) + .get_data_handler() + .collect_map(&storage) + .unwrap(); + let tot_unbonds_dest = total_unbonded_handle(&dest_validator) + .collect_map(&storage) + .unwrap(); + let tot_redel_bonds_dest = + validator_total_redelegated_bonded_handle(&dest_validator) + .collect_map(&storage) + .unwrap(); + let tot_redel_unbonds_dest = + validator_total_redelegated_unbonded_handle(&dest_validator) + .collect_map(&storage) + .unwrap(); + dbg!( + &incoming_redels_dest, + &outgoing_redels_dest, + &tot_bonds_dest, + &tot_unbonds_dest, + &tot_redel_bonds_dest, + &tot_redel_unbonds_dest + ); + + // Advance one epoch + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + + // Discover evidence + slash( + &mut storage, + ¶ms, + current_epoch, + init_epoch + 2 * params.pipeline_len, + 0u64, + SlashType::DuplicateVote, + &src_validator, + current_epoch.next(), + ) + .unwrap(); + + let bond_start = init_epoch + params.pipeline_len; + let redelegation_end = bond_start + params.pipeline_len + 1u64; + let unbond_end = + redelegation_end + params.withdrawable_epoch_offset() + 1u64; + let unbond_materialized = redelegation_end + params.pipeline_len + 1u64; + + // Checks + let redelegated_remaining = delegator_redelegated_bonds_handle(&delegator) + .at(&dest_validator) + .at(&redelegation_end) + .at(&src_validator) + .get(&storage, &bond_start) + .unwrap() + .unwrap_or_default(); + assert_eq!(redelegated_remaining, amount_redelegate - amount_unbond); + + let redel_unbonded = delegator_redelegated_unbonds_handle(&delegator) + .at(&dest_validator) + .at(&redelegation_end) + .at(&unbond_end) + .at(&src_validator) + .get(&storage, &bond_start) + .unwrap() + .unwrap(); + assert_eq!(redel_unbonded, amount_unbond); + + dbg!(unbond_materialized, redelegation_end, bond_start); + let total_redel_unbonded = + validator_total_redelegated_unbonded_handle(&dest_validator) + .at(&unbond_materialized) + .at(&redelegation_end) + .at(&src_validator) + .get(&storage, &bond_start) + .unwrap() + .unwrap(); + assert_eq!(total_redel_unbonded, amount_unbond); + + // Advance to withdrawal epoch + loop { + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + if current_epoch == unbond_end { + break; + } + } + + // Withdraw + withdraw_tokens( + &mut storage, + Some(&delegator), + &dest_validator, + current_epoch, + ) + .unwrap(); + + assert!( + delegator_redelegated_unbonds_handle(&delegator) + .at(&dest_validator) + .is_empty(&storage) + .unwrap() + ); + + let delegator_balance = storage + .read::(&token::balance_key(&staking_token, &delegator)) + .unwrap() + .unwrap_or_default(); + assert_eq!(delegator_balance, del_balance - amount_delegate); +} + +fn test_chain_redelegations_aux(mut validators: Vec) { + validators.sort_by(|a, b| b.tokens.cmp(&a.tokens)); + + let src_validator = validators[0].address.clone(); + let _init_stake_src = validators[0].tokens; + let dest_validator = validators[1].address.clone(); + let _init_stake_dest = validators[1].tokens; + let dest_validator_2 = validators[2].address.clone(); + let _init_stake_dest_2 = validators[2].tokens; + + let mut storage = TestWlStorage::default(); + let params = OwnedPosParams { + unbonding_len: 4, + ..Default::default() + }; + + // Genesis + let mut current_epoch = storage.storage.block.epoch; + let params = test_init_genesis( + &mut storage, + params, + validators.clone().into_iter(), + current_epoch, + ) + .unwrap(); + storage.commit_block().unwrap(); + + // Get a delegator with some tokens + let staking_token = staking_token_address(&storage); + let delegator = address::testing::gen_implicit_address(); + let del_balance = token::Amount::from_uint(1_000_000, 0).unwrap(); + credit_tokens(&mut storage, &staking_token, &delegator, del_balance) + .unwrap(); + + // Delegate in epoch 0 to src_validator + let bond_amount: token::Amount = 100.into(); + super::bond_tokens( + &mut storage, + Some(&delegator), + &src_validator, + bond_amount, + current_epoch, + None, + ) + .unwrap(); + + let bond_start = current_epoch + params.pipeline_len; + + // Advance one epoch + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + + // Redelegate in epoch 1 to dest_validator + let redel_amount_1: token::Amount = 58.into(); + super::redelegate_tokens( + &mut storage, + &delegator, + &src_validator, + &dest_validator, + current_epoch, + redel_amount_1, + ) + .unwrap(); + + let redel_start = current_epoch; + let redel_end = current_epoch + params.pipeline_len; + + // Checks ---------------- + + // Dest validator should have an incoming redelegation + let incoming_redelegation = + validator_incoming_redelegations_handle(&dest_validator) + .get(&storage, &delegator) + .unwrap(); + assert_eq!(incoming_redelegation, Some(redel_end)); + + // Src validator should have an outoging redelegation + let outgoing_redelegation = + validator_outgoing_redelegations_handle(&src_validator) + .at(&dest_validator) + .at(&bond_start) + .get(&storage, &redel_start) + .unwrap(); + assert_eq!(outgoing_redelegation, Some(redel_amount_1)); + + // Delegator should have redelegated bonds + let del_total_redelegated_bonded = + delegator_redelegated_bonds_handle(&delegator) + .at(&dest_validator) + .at(&redel_end) + .at(&src_validator) + .get(&storage, &bond_start) + .unwrap() + .unwrap_or_default(); + assert_eq!(del_total_redelegated_bonded, redel_amount_1); + + // There should be delegator bonds for both src and dest validators + let bonded_src = bond_handle(&delegator, &src_validator); + let bonded_dest = bond_handle(&delegator, &dest_validator); + assert_eq!( + bonded_src + .get_delta_val(&storage, bond_start) + .unwrap() + .unwrap_or_default(), + bond_amount - redel_amount_1 + ); + assert_eq!( + bonded_dest + .get_delta_val(&storage, redel_end) + .unwrap() + .unwrap_or_default(), + redel_amount_1 + ); + + // The dest validator should have total redelegated bonded tokens + let dest_total_redelegated_bonded = + validator_total_redelegated_bonded_handle(&dest_validator) + .at(&redel_end) + .at(&src_validator) + .get(&storage, &bond_start) + .unwrap() + .unwrap_or_default(); + assert_eq!(dest_total_redelegated_bonded, redel_amount_1); + + // The dest validator's total bonded should have an entry for the genesis + // bond and the redelegation + let dest_total_bonded = total_bonded_handle(&dest_validator) + .get_data_handler() + .collect_map(&storage) + .unwrap(); + assert!( + dest_total_bonded.len() == 2 + && dest_total_bonded.contains_key(&Epoch::default()) + ); + assert_eq!( + dest_total_bonded + .get(&redel_end) + .cloned() + .unwrap_or_default(), + redel_amount_1 + ); + + // The src validator should have a total bonded entry for the original bond + // accounting for the redelegation + assert_eq!( + total_bonded_handle(&src_validator) + .get_delta_val(&storage, bond_start) + .unwrap() + .unwrap_or_default(), + bond_amount - redel_amount_1 + ); + + // The src validator should have a total unbonded entry due to the + // redelegation + let src_total_unbonded = total_unbonded_handle(&src_validator) + .at(&redel_end) + .get(&storage, &bond_start) + .unwrap() + .unwrap_or_default(); + assert_eq!(src_total_unbonded, redel_amount_1); + + // Attempt to redelegate in epoch 3 to dest_validator + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + + let redel_amount_2: token::Amount = 23.into(); + let redel_att = super::redelegate_tokens( + &mut storage, + &delegator, + &dest_validator, + &dest_validator_2, + current_epoch, + redel_amount_2, + ); + assert!(redel_att.is_err()); + + // Advance to right before the redelegation can be redelegated again + assert_eq!(redel_end, current_epoch); + let epoch_can_redel = + redel_end.prev() + params.slash_processing_epoch_offset(); + loop { + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + if current_epoch == epoch_can_redel.prev() { + break; + } + } + + // Attempt to redelegate in epoch before we actually are able to + let redel_att = super::redelegate_tokens( + &mut storage, + &delegator, + &dest_validator, + &dest_validator_2, + current_epoch, + redel_amount_2, + ); + assert!(redel_att.is_err()); + + // Advance one more epoch + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + + // Redelegate from dest_validator to dest_validator_2 now + super::redelegate_tokens( + &mut storage, + &delegator, + &dest_validator, + &dest_validator_2, + current_epoch, + redel_amount_2, + ) + .unwrap(); + + let redel_2_start = current_epoch; + let redel_2_end = current_epoch + params.pipeline_len; + + // Checks ----------------------------------- + + // Both the dest validator and dest validator 2 should have incoming + // redelegations + let incoming_redelegation_1 = + validator_incoming_redelegations_handle(&dest_validator) + .get(&storage, &delegator) + .unwrap(); + assert_eq!(incoming_redelegation_1, Some(redel_end)); + let incoming_redelegation_2 = + validator_incoming_redelegations_handle(&dest_validator_2) + .get(&storage, &delegator) + .unwrap(); + assert_eq!(incoming_redelegation_2, Some(redel_2_end)); + + // Both the src validator and dest validator should have outgoing + // redelegations + let outgoing_redelegation_1 = + validator_outgoing_redelegations_handle(&src_validator) + .at(&dest_validator) + .at(&bond_start) + .get(&storage, &redel_start) + .unwrap(); + assert_eq!(outgoing_redelegation_1, Some(redel_amount_1)); + + let outgoing_redelegation_2 = + validator_outgoing_redelegations_handle(&dest_validator) + .at(&dest_validator_2) + .at(&redel_end) + .get(&storage, &redel_2_start) + .unwrap(); + assert_eq!(outgoing_redelegation_2, Some(redel_amount_2)); + + // All three validators should have bonds + let bonded_dest2 = bond_handle(&delegator, &dest_validator_2); + assert_eq!( + bonded_src + .get_delta_val(&storage, bond_start) + .unwrap() + .unwrap_or_default(), + bond_amount - redel_amount_1 + ); + assert_eq!( + bonded_dest + .get_delta_val(&storage, redel_end) + .unwrap() + .unwrap_or_default(), + redel_amount_1 - redel_amount_2 + ); + assert_eq!( + bonded_dest2 + .get_delta_val(&storage, redel_2_end) + .unwrap() + .unwrap_or_default(), + redel_amount_2 + ); + + // There should be no unbond entries + let unbond_src = unbond_handle(&delegator, &src_validator); + let unbond_dest = unbond_handle(&delegator, &dest_validator); + assert!(unbond_src.is_empty(&storage).unwrap()); + assert!(unbond_dest.is_empty(&storage).unwrap()); + + // The dest validator should have some total unbonded due to the second + // redelegation + let dest_total_unbonded = total_unbonded_handle(&dest_validator) + .at(&redel_2_end) + .get(&storage, &redel_end) + .unwrap(); + assert_eq!(dest_total_unbonded, Some(redel_amount_2)); + + // Delegator should have redelegated bonds due to both redelegations + let del_redelegated_bonds = delegator_redelegated_bonds_handle(&delegator); + assert_eq!( + Some(redel_amount_1 - redel_amount_2), + del_redelegated_bonds + .at(&dest_validator) + .at(&redel_end) + .at(&src_validator) + .get(&storage, &bond_start) + .unwrap() + ); + assert_eq!( + Some(redel_amount_2), + del_redelegated_bonds + .at(&dest_validator_2) + .at(&redel_2_end) + .at(&dest_validator) + .get(&storage, &redel_end) + .unwrap() + ); + + // Delegator redelegated unbonds should be empty + assert!( + delegator_redelegated_unbonds_handle(&delegator) + .is_empty(&storage) + .unwrap() + ); + + // Both the dest validator and dest validator 2 should have total + // redelegated bonds + let dest_redelegated_bonded = + validator_total_redelegated_bonded_handle(&dest_validator) + .at(&redel_end) + .at(&src_validator) + .get(&storage, &bond_start) + .unwrap() + .unwrap_or_default(); + let dest2_redelegated_bonded = + validator_total_redelegated_bonded_handle(&dest_validator_2) + .at(&redel_2_end) + .at(&dest_validator) + .get(&storage, &redel_end) + .unwrap() + .unwrap_or_default(); + assert_eq!(dest_redelegated_bonded, redel_amount_1 - redel_amount_2); + assert_eq!(dest2_redelegated_bonded, redel_amount_2); + + // Total redelegated unbonded should be empty for src_validator and + // dest_validator_2 + assert!( + validator_total_redelegated_unbonded_handle(&dest_validator_2) + .is_empty(&storage) + .unwrap() + ); + assert!( + validator_total_redelegated_unbonded_handle(&src_validator) + .is_empty(&storage) + .unwrap() + ); + + // The dest_validator should have total_redelegated unbonded + let tot_redel_unbonded = + validator_total_redelegated_unbonded_handle(&dest_validator) + .at(&redel_2_end) + .at(&redel_end) + .at(&src_validator) + .get(&storage, &bond_start) + .unwrap() + .unwrap_or_default(); + assert_eq!(tot_redel_unbonded, redel_amount_2); +} + +/// SM test case 1 from Brent +#[test] +fn test_from_sm_case_1() { + use namada_core::types::address::testing::established_address_4; + + let mut storage = TestWlStorage::default(); + let gov_params = + namada_governance::parameters::GovernanceParameters::default(); + gov_params.init_storage(&mut storage).unwrap(); + write_pos_params(&mut storage, &OwnedPosParams::default()).unwrap(); + + let validator = established_address_1(); + let redeleg_src_1 = established_address_2(); + let redeleg_src_2 = established_address_3(); + let owner = established_address_4(); + let unbond_amount = token::Amount::from(3130688); + println!( + "Owner: {owner}\nValidator: {validator}\nRedeleg src 1: \ + {redeleg_src_1}\nRedeleg src 2: {redeleg_src_2}" + ); + + // Validator's incoming redelegations + let outer_epoch_1 = Epoch(27); + // from redeleg_src_1 + let epoch_1_redeleg_1 = token::Amount::from(8516); + // from redeleg_src_2 + let epoch_1_redeleg_2 = token::Amount::from(5704386); + let outer_epoch_2 = Epoch(30); + // from redeleg_src_2 + let epoch_2_redeleg_2 = token::Amount::from(1035191); + + // Insert the data - bonds and redelegated bonds + let bonds_handle = bond_handle(&owner, &validator); + bonds_handle + .add( + &mut storage, + epoch_1_redeleg_1 + epoch_1_redeleg_2, + outer_epoch_1, + 0, + ) + .unwrap(); + bonds_handle + .add(&mut storage, epoch_2_redeleg_2, outer_epoch_2, 0) + .unwrap(); + + let redelegated_bonds_map_1 = delegator_redelegated_bonds_handle(&owner) + .at(&validator) + .at(&outer_epoch_1); + redelegated_bonds_map_1 + .at(&redeleg_src_1) + .insert(&mut storage, Epoch(14), epoch_1_redeleg_1) + .unwrap(); + redelegated_bonds_map_1 + .at(&redeleg_src_2) + .insert(&mut storage, Epoch(18), epoch_1_redeleg_2) + .unwrap(); + let redelegated_bonds_map_1 = delegator_redelegated_bonds_handle(&owner) + .at(&validator) + .at(&outer_epoch_1); + + let redelegated_bonds_map_2 = delegator_redelegated_bonds_handle(&owner) + .at(&validator) + .at(&outer_epoch_2); + redelegated_bonds_map_2 + .at(&redeleg_src_2) + .insert(&mut storage, Epoch(18), epoch_2_redeleg_2) + .unwrap(); + + // Find the modified redelegation the same way as `unbond_tokens` + let bonds_to_unbond = find_bonds_to_remove( + &storage, + &bonds_handle.get_data_handler(), + unbond_amount, + ) + .unwrap(); + dbg!(&bonds_to_unbond); + + let (new_entry_epoch, new_bond_amount) = bonds_to_unbond.new_entry.unwrap(); + assert_eq!(outer_epoch_1, new_entry_epoch); + // The modified bond should be sum of all redelegations less the unbonded + // amouunt + assert_eq!( + epoch_1_redeleg_1 + epoch_1_redeleg_2 + epoch_2_redeleg_2 + - unbond_amount, + new_bond_amount + ); + // The current bond should be sum of redelegations fom the modified epoch + let cur_bond_amount = bonds_handle + .get_delta_val(&storage, new_entry_epoch) + .unwrap() + .unwrap_or_default(); + assert_eq!(epoch_1_redeleg_1 + epoch_1_redeleg_2, cur_bond_amount); + + let mr = compute_modified_redelegation( + &storage, + &redelegated_bonds_map_1, + new_entry_epoch, + cur_bond_amount - new_bond_amount, + ) + .unwrap(); + + let exp_mr = ModifiedRedelegation { + epoch: Some(Epoch(27)), + validators_to_remove: BTreeSet::from_iter([redeleg_src_2.clone()]), + validator_to_modify: Some(redeleg_src_2), + epochs_to_remove: BTreeSet::from_iter([Epoch(18)]), + epoch_to_modify: Some(Epoch(18)), + new_amount: Some(token::Amount::from(3608889)), + }; + + pretty_assertions::assert_eq!(mr, exp_mr); +} + +/// Test precisely that we are not overslashing, as originally discovered by Tomas in this issue: https://github.com/informalsystems/partnership-heliax/issues/74 +fn test_overslashing_aux(mut validators: Vec) { + assert_eq!(validators.len(), 4); + + let params = OwnedPosParams { + unbonding_len: 4, + ..Default::default() + }; + + let offending_stake = token::Amount::native_whole(110); + let other_stake = token::Amount::native_whole(100); + + // Set stakes so we know we will get a slashing rate between 0.5 -1.0 + validators[0].tokens = offending_stake; + validators[1].tokens = other_stake; + validators[2].tokens = other_stake; + validators[3].tokens = other_stake; + + // Get the offending validator + let validator = validators[0].address.clone(); + + println!("\nTest inputs: {params:?}, genesis validators: {validators:#?}"); + let mut storage = TestWlStorage::default(); + + // Genesis + let mut current_epoch = storage.storage.block.epoch; + let params = test_init_genesis( + &mut storage, + params, + validators.clone().into_iter(), + current_epoch, + ) + .unwrap(); + storage.commit_block().unwrap(); + + // Get a delegator with some tokens + let staking_token = storage.storage.native_token.clone(); + let delegator = address::testing::gen_implicit_address(); + let amount_del = token::Amount::native_whole(5); + credit_tokens(&mut storage, &staking_token, &delegator, amount_del) + .unwrap(); + + // Delegate tokens in epoch 0 to validator + bond_tokens( + &mut storage, + Some(&delegator), + &validator, + amount_del, + current_epoch, + None, + ) + .unwrap(); + + let self_bond_epoch = current_epoch; + let delegation_epoch = current_epoch + params.pipeline_len; + + // Advance to pipeline epoch + for _ in 0..params.pipeline_len { + current_epoch = advance_epoch(&mut storage, ¶ms); + } + assert_eq!(delegation_epoch, current_epoch); + + // Find a misbehavior committed in epoch 0 + slash( + &mut storage, + ¶ms, + current_epoch, + self_bond_epoch, + 0_u64, + SlashType::DuplicateVote, + &validator, + current_epoch.next(), + ) + .unwrap(); + + // Find a misbehavior committed in current epoch + slash( + &mut storage, + ¶ms, + current_epoch, + delegation_epoch, + 0_u64, + SlashType::DuplicateVote, + &validator, + current_epoch.next(), + ) + .unwrap(); + + let processing_epoch_1 = + self_bond_epoch + params.slash_processing_epoch_offset(); + let processing_epoch_2 = + delegation_epoch + params.slash_processing_epoch_offset(); + + // Advance to processing epoch 1 + loop { + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + if current_epoch == processing_epoch_1 { + break; + } + } + + let total_stake_1 = offending_stake + 3 * other_stake; + let stake_frac = Dec::from(offending_stake) / Dec::from(total_stake_1); + let slash_rate_1 = Dec::from_str("9.0").unwrap() * stake_frac * stake_frac; + dbg!(&slash_rate_1); + + let exp_slashed_1 = offending_stake.mul_ceil(slash_rate_1); + + // Check that the proper amount was slashed + let epoch = current_epoch.next(); + let validator_stake = + read_validator_stake(&storage, ¶ms, &validator, epoch).unwrap(); + let exp_validator_stake = offending_stake - exp_slashed_1 + amount_del; + assert_eq!(validator_stake, exp_validator_stake); + + let total_stake = read_total_stake(&storage, ¶ms, epoch).unwrap(); + let exp_total_stake = + offending_stake - exp_slashed_1 + amount_del + 3 * other_stake; + assert_eq!(total_stake, exp_total_stake); + + let self_bond_id = BondId { + source: validator.clone(), + validator: validator.clone(), + }; + let bond_amount = + crate::bond_amount(&storage, &self_bond_id, epoch).unwrap(); + let exp_bond_amount = offending_stake - exp_slashed_1; + assert_eq!(bond_amount, exp_bond_amount); + + // Advance to processing epoch 2 + loop { + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + if current_epoch == processing_epoch_2 { + break; + } + } + + let total_stake_2 = offending_stake + amount_del + 3 * other_stake; + let stake_frac = + Dec::from(offending_stake + amount_del) / Dec::from(total_stake_2); + let slash_rate_2 = Dec::from_str("9.0").unwrap() * stake_frac * stake_frac; + dbg!(&slash_rate_2); + + let exp_slashed_from_delegation = amount_del.mul_ceil(slash_rate_2); + + // Check that the proper amount was slashed. We expect that all of the + // validator self-bond has been slashed and some of the delegation has been + // slashed due to the second infraction. + let epoch = current_epoch.next(); + + let validator_stake = + read_validator_stake(&storage, ¶ms, &validator, epoch).unwrap(); + let exp_validator_stake = amount_del - exp_slashed_from_delegation; + assert_eq!(validator_stake, exp_validator_stake); + + let total_stake = read_total_stake(&storage, ¶ms, epoch).unwrap(); + let exp_total_stake = + amount_del - exp_slashed_from_delegation + 3 * other_stake; + assert_eq!(total_stake, exp_total_stake); + + let delegation_id = BondId { + source: delegator.clone(), + validator: validator.clone(), + }; + let delegation_amount = + crate::bond_amount(&storage, &delegation_id, epoch).unwrap(); + let exp_del_amount = amount_del - exp_slashed_from_delegation; + assert_eq!(delegation_amount, exp_del_amount); + + let self_bond_amount = + crate::bond_amount(&storage, &self_bond_id, epoch).unwrap(); + let exp_bond_amount = token::Amount::zero(); + assert_eq!(self_bond_amount, exp_bond_amount); +} + +fn test_unslashed_bond_amount_aux(validators: Vec) { + let mut storage = TestWlStorage::default(); + let params = OwnedPosParams { + unbonding_len: 4, + ..Default::default() + }; + + // Genesis + let mut current_epoch = storage.storage.block.epoch; + let params = test_init_genesis( + &mut storage, + params, + validators.clone().into_iter(), + current_epoch, + ) + .unwrap(); + storage.commit_block().unwrap(); + + let validator1 = validators[0].address.clone(); + let validator2 = validators[1].address.clone(); + + // Get a delegator with some tokens + let staking_token = staking_token_address(&storage); + let delegator = address::testing::gen_implicit_address(); + let del_balance = token::Amount::from_uint(1_000_000, 0).unwrap(); + credit_tokens(&mut storage, &staking_token, &delegator, del_balance) + .unwrap(); + + // Bond to validator 1 + super::bond_tokens( + &mut storage, + Some(&delegator), + &validator1, + 10_000.into(), + current_epoch, + None, + ) + .unwrap(); + + // Unbond some from validator 1 + super::unbond_tokens( + &mut storage, + Some(&delegator), + &validator1, + 1_342.into(), + current_epoch, + false, + ) + .unwrap(); + + // Redelegate some from validator 1 -> 2 + super::redelegate_tokens( + &mut storage, + &delegator, + &validator1, + &validator2, + current_epoch, + 1_875.into(), + ) + .unwrap(); + + // Unbond some from validator 2 + super::unbond_tokens( + &mut storage, + Some(&delegator), + &validator2, + 584.into(), + current_epoch, + false, + ) + .unwrap(); + + // Advance an epoch + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + + // Bond to validator 1 + super::bond_tokens( + &mut storage, + Some(&delegator), + &validator1, + 384.into(), + current_epoch, + None, + ) + .unwrap(); + + // Unbond some from validator 1 + super::unbond_tokens( + &mut storage, + Some(&delegator), + &validator1, + 144.into(), + current_epoch, + false, + ) + .unwrap(); + + // Redelegate some from validator 1 -> 2 + super::redelegate_tokens( + &mut storage, + &delegator, + &validator1, + &validator2, + current_epoch, + 3_448.into(), + ) + .unwrap(); + + // Unbond some from validator 2 + super::unbond_tokens( + &mut storage, + Some(&delegator), + &validator2, + 699.into(), + current_epoch, + false, + ) + .unwrap(); + + // Advance an epoch + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + + // Bond to validator 1 + super::bond_tokens( + &mut storage, + Some(&delegator), + &validator1, + 4_384.into(), + current_epoch, + None, + ) + .unwrap(); + + // Redelegate some from validator 1 -> 2 + super::redelegate_tokens( + &mut storage, + &delegator, + &validator1, + &validator2, + current_epoch, + 1_008.into(), + ) + .unwrap(); + + // Unbond some from validator 2 + super::unbond_tokens( + &mut storage, + Some(&delegator), + &validator2, + 3_500.into(), + current_epoch, + false, + ) + .unwrap(); + + // Checks + let val1_init_stake = validators[0].tokens; + + for epoch in Epoch::iter_bounds_inclusive( + Epoch(0), + current_epoch + params.pipeline_len, + ) { + let bond_amount = crate::bond_amount( + &storage, + &BondId { + source: delegator.clone(), + validator: validator1.clone(), + }, + epoch, + ) + .unwrap_or_default(); + + let val_stake = + crate::read_validator_stake(&storage, ¶ms, &validator1, epoch) + .unwrap(); + // dbg!(&bond_amount); + assert_eq!(val_stake - val1_init_stake, bond_amount); + } +} + +fn test_log_block_rewards_aux( + validators: Vec, + params: OwnedPosParams, +) { + tracing::info!( + "New case with {} validators: {:#?}", + validators.len(), + validators + .iter() + .map(|v| (&v.address, v.tokens.to_string_native())) + .collect::>() + ); + let mut s = TestWlStorage::default(); + // Init genesis + let current_epoch = s.storage.block.epoch; + let params = test_init_genesis( + &mut s, + params, + validators.clone().into_iter(), + current_epoch, + ) + .unwrap(); + s.commit_block().unwrap(); + let total_stake = + crate::get_total_consensus_stake(&s, current_epoch, ¶ms).unwrap(); + let consensus_set = + crate::read_consensus_validator_set_addresses(&s, current_epoch) + .unwrap(); + let proposer_address = consensus_set.iter().next().unwrap().clone(); + + tracing::info!( + ?params.block_proposer_reward, + ?params.block_vote_reward, + ); + tracing::info!(?proposer_address,); + + // Rewards accumulator should be empty at first + let rewards_handle = crate::rewards_accumulator_handle(); + assert!(rewards_handle.is_empty(&s).unwrap()); + + let mut last_rewards = BTreeMap::default(); + + let num_blocks = 100; + // Loop through `num_blocks`, log rewards & check results + for i in 0..num_blocks { + tracing::info!(""); + tracing::info!("Block {}", i + 1); + + // A helper closure to prepare minimum required votes + let prep_votes = |epoch| { + // Ceil of 2/3 of total stake + let min_required_votes = total_stake.mul_ceil(Dec::two() / 3); + + let mut total_votes = token::Amount::zero(); + let mut non_voters = HashSet::
::default(); + let mut prep_vote = |validator| { + // Add validator vote if it's in consensus set and if we don't + // yet have min required votes + if consensus_set.contains(validator) + && total_votes < min_required_votes + { + let stake = + read_validator_stake(&s, ¶ms, validator, epoch) + .unwrap(); + total_votes += stake; + let validator_vp = + into_tm_voting_power(params.tm_votes_per_token, stake) + as u64; + tracing::info!("Validator {validator} signed"); + Some(VoteInfo { + validator_address: validator.clone(), + validator_vp, + }) + } else { + non_voters.insert(validator.clone()); + None + } + }; + + let votes: Vec = validators + .iter() + .rev() + .filter_map(|validator| prep_vote(&validator.address)) + .collect(); + (votes, total_votes, non_voters) + }; + + let (votes, signing_stake, non_voters) = prep_votes(current_epoch); + crate::log_block_rewards( + &mut s, + current_epoch, + &proposer_address, + votes.clone(), + ) + .unwrap(); + + assert!(!rewards_handle.is_empty(&s).unwrap()); + + let rewards_calculator = PosRewardsCalculator { + proposer_reward: params.block_proposer_reward, + signer_reward: params.block_vote_reward, + signing_stake, + total_stake, + }; + let coeffs = rewards_calculator.get_reward_coeffs().unwrap(); + tracing::info!(?coeffs); + + // Check proposer reward + let stake = + read_validator_stake(&s, ¶ms, &proposer_address, current_epoch) + .unwrap(); + let proposer_signing_reward = votes.iter().find_map(|vote| { + if vote.validator_address == proposer_address { + let signing_fraction = + Dec::from(stake) / Dec::from(signing_stake); + Some(coeffs.signer_coeff * signing_fraction) + } else { + None + } + }); + let expected_proposer_rewards = last_rewards.get(&proposer_address).copied().unwrap_or_default() + + // Proposer reward + coeffs.proposer_coeff + // Consensus validator reward + + (coeffs.active_val_coeff + * (Dec::from(stake) / Dec::from(total_stake))) + // Signing reward (if proposer voted) + + proposer_signing_reward + .unwrap_or_default(); + tracing::info!( + "Expected proposer rewards: {expected_proposer_rewards}. Signed \ + block: {}", + proposer_signing_reward.is_some() + ); + assert_eq!( + rewards_handle.get(&s, &proposer_address).unwrap(), + Some(expected_proposer_rewards) + ); + + // Check voters rewards + for VoteInfo { + validator_address, .. + } in votes.iter() + { + // Skip proposer, in case voted - already checked + if validator_address == &proposer_address { + continue; + } + + let stake = read_validator_stake( + &s, + ¶ms, + validator_address, + current_epoch, + ) + .unwrap(); + let signing_fraction = Dec::from(stake) / Dec::from(signing_stake); + let expected_signer_rewards = last_rewards + .get(validator_address) + .copied() + .unwrap_or_default() + + coeffs.signer_coeff * signing_fraction + + (coeffs.active_val_coeff + * (Dec::from(stake) / Dec::from(total_stake))); + tracing::info!( + "Expected signer {validator_address} rewards: \ + {expected_signer_rewards}" + ); + assert_eq!( + rewards_handle.get(&s, validator_address).unwrap(), + Some(expected_signer_rewards) + ); + } + + // Check non-voters rewards, if any + for address in non_voters { + // Skip proposer, in case it didn't vote - already checked + if address == proposer_address { + continue; + } + + if consensus_set.contains(&address) { + let stake = + read_validator_stake(&s, ¶ms, &address, current_epoch) + .unwrap(); + let expected_non_signer_rewards = + last_rewards.get(&address).copied().unwrap_or_default() + + coeffs.active_val_coeff + * (Dec::from(stake) / Dec::from(total_stake)); + tracing::info!( + "Expected non-signer {address} rewards: \ + {expected_non_signer_rewards}" + ); + assert_eq!( + rewards_handle.get(&s, &address).unwrap(), + Some(expected_non_signer_rewards) + ); + } else { + let last_reward = last_rewards.get(&address).copied(); + assert_eq!( + rewards_handle.get(&s, &address).unwrap(), + last_reward + ); + } + } + s.commit_block().unwrap(); + + last_rewards = + crate::rewards_accumulator_handle().collect_map(&s).unwrap(); + + let rewards_sum: Dec = last_rewards.values().copied().sum(); + let expected_sum = Dec::one() * (i as u64 + 1); + let err_tolerance = Dec::new(1, 9).unwrap(); + let fail_msg = format!( + "Expected rewards sum at block {} to be {expected_sum}, got \ + {rewards_sum}. Error tolerance {err_tolerance}.", + i + 1 + ); + assert!(expected_sum <= rewards_sum + err_tolerance, "{fail_msg}"); + assert!(rewards_sum <= expected_sum, "{fail_msg}"); + } +} + +fn test_update_rewards_products_aux(validators: Vec) { + tracing::info!( + "New case with {} validators: {:#?}", + validators.len(), + validators + .iter() + .map(|v| (&v.address, v.tokens.to_string_native())) + .collect::>() + ); + let mut s = TestWlStorage::default(); + // Init genesis + let current_epoch = s.storage.block.epoch; + let params = OwnedPosParams::default(); + let params = test_init_genesis( + &mut s, + params, + validators.into_iter(), + current_epoch, + ) + .unwrap(); + s.commit_block().unwrap(); + + let staking_token = staking_token_address(&s); + let consensus_set = + crate::read_consensus_validator_set_addresses(&s, current_epoch) + .unwrap(); + + // Start a new epoch + let current_epoch = advance_epoch(&mut s, ¶ms); + + // Read some data before applying rewards + let pos_balance_pre = + read_balance(&s, &staking_token, &address::POS).unwrap(); + let gov_balance_pre = + read_balance(&s, &staking_token, &address::GOV).unwrap(); + + let num_consensus_validators = consensus_set.len() as u64; + let accum_val = Dec::one() / num_consensus_validators; + let num_blocks_in_last_epoch = 1000; + + // Assign some reward accumulator values to consensus validator + for validator in &consensus_set { + crate::rewards_accumulator_handle() + .insert( + &mut s, + validator.clone(), + accum_val * num_blocks_in_last_epoch, + ) + .unwrap(); + } + + // Distribute inflation into rewards + let last_epoch = current_epoch.prev(); + let inflation = token::Amount::native_whole(10_000_000); + crate::update_rewards_products_and_mint_inflation( + &mut s, + ¶ms, + last_epoch, + num_blocks_in_last_epoch, + inflation, + &staking_token, + ) + .unwrap(); + + let pos_balance_post = + read_balance(&s, &staking_token, &address::POS).unwrap(); + let gov_balance_post = + read_balance(&s, &staking_token, &address::GOV).unwrap(); + + assert_eq!( + pos_balance_pre + gov_balance_pre + inflation, + pos_balance_post + gov_balance_post, + "Expected inflation to be minted to PoS and left-over amount to Gov" + ); + + let pos_credit = pos_balance_post - pos_balance_pre; + let gov_credit = gov_balance_post - gov_balance_pre; + assert!( + pos_credit > gov_credit, + "PoS must receive more tokens than Gov, but got {} in PoS and {} in \ + Gov", + pos_credit.to_string_native(), + gov_credit.to_string_native() + ); + + // Rewards accumulator must be cleared out + let rewards_handle = crate::rewards_accumulator_handle(); + assert!(rewards_handle.is_empty(&s).unwrap()); +} + +fn test_slashed_bond_amount_aux(validators: Vec) { + let mut storage = TestWlStorage::default(); + let params = OwnedPosParams { + unbonding_len: 4, + ..Default::default() + }; + + let init_tot_stake = validators + .clone() + .into_iter() + .fold(token::Amount::zero(), |acc, v| acc + v.tokens); + let val1_init_stake = validators[0].tokens; + + let mut validators = validators; + validators[0].tokens = (init_tot_stake - val1_init_stake) / 30; + + // Genesis + let mut current_epoch = storage.storage.block.epoch; + let params = test_init_genesis( + &mut storage, + params, + validators.clone().into_iter(), + current_epoch, + ) + .unwrap(); + storage.commit_block().unwrap(); + + let validator1 = validators[0].address.clone(); + let validator2 = validators[1].address.clone(); + + // Get a delegator with some tokens + let staking_token = staking_token_address(&storage); + let delegator = address::testing::gen_implicit_address(); + let del_balance = token::Amount::from_uint(1_000_000, 0).unwrap(); + credit_tokens(&mut storage, &staking_token, &delegator, del_balance) + .unwrap(); + + // Bond to validator 1 + super::bond_tokens( + &mut storage, + Some(&delegator), + &validator1, + 10_000.into(), + current_epoch, + None, + ) + .unwrap(); + + // Unbond some from validator 1 + super::unbond_tokens( + &mut storage, + Some(&delegator), + &validator1, + 1_342.into(), + current_epoch, + false, + ) + .unwrap(); + + // Redelegate some from validator 1 -> 2 + super::redelegate_tokens( + &mut storage, + &delegator, + &validator1, + &validator2, + current_epoch, + 1_875.into(), + ) + .unwrap(); + + // Unbond some from validator 2 + super::unbond_tokens( + &mut storage, + Some(&delegator), + &validator2, + 584.into(), + current_epoch, + false, + ) + .unwrap(); + + // Advance an epoch to 1 + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + + // Bond to validator 1 + super::bond_tokens( + &mut storage, + Some(&delegator), + &validator1, + 384.into(), + current_epoch, + None, + ) + .unwrap(); + + // Unbond some from validator 1 + super::unbond_tokens( + &mut storage, + Some(&delegator), + &validator1, + 144.into(), + current_epoch, + false, + ) + .unwrap(); + + // Redelegate some from validator 1 -> 2 + super::redelegate_tokens( + &mut storage, + &delegator, + &validator1, + &validator2, + current_epoch, + 3_448.into(), + ) + .unwrap(); + + // Unbond some from validator 2 + super::unbond_tokens( + &mut storage, + Some(&delegator), + &validator2, + 699.into(), + current_epoch, + false, + ) + .unwrap(); + + // Advance an epoch to ep 2 + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + + // Bond to validator 1 + super::bond_tokens( + &mut storage, + Some(&delegator), + &validator1, + 4_384.into(), + current_epoch, + None, + ) + .unwrap(); + + // Redelegate some from validator 1 -> 2 + super::redelegate_tokens( + &mut storage, + &delegator, + &validator1, + &validator2, + current_epoch, + 1_008.into(), + ) + .unwrap(); + + // Unbond some from validator 2 + super::unbond_tokens( + &mut storage, + Some(&delegator), + &validator2, + 3_500.into(), + current_epoch, + false, + ) + .unwrap(); + + // Advance two epochs to ep 4 + for _ in 0..2 { + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + } + + // Find some slashes committed in various epochs + super::slash( + &mut storage, + ¶ms, + current_epoch, + Epoch(1), + 1_u64, + SlashType::DuplicateVote, + &validator1, + current_epoch, + ) + .unwrap(); + super::slash( + &mut storage, + ¶ms, + current_epoch, + Epoch(2), + 1_u64, + SlashType::DuplicateVote, + &validator1, + current_epoch, + ) + .unwrap(); + super::slash( + &mut storage, + ¶ms, + current_epoch, + Epoch(2), + 1_u64, + SlashType::DuplicateVote, + &validator1, + current_epoch, + ) + .unwrap(); + super::slash( + &mut storage, + ¶ms, + current_epoch, + Epoch(3), + 1_u64, + SlashType::DuplicateVote, + &validator1, + current_epoch, + ) + .unwrap(); + + // Advance such that these slashes are all processed + for _ in 0..params.slash_processing_epoch_offset() { + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + } + + let pipeline_epoch = current_epoch + params.pipeline_len; + + let del_bond_amount = crate::bond_amount( + &storage, + &BondId { + source: delegator.clone(), + validator: validator1.clone(), + }, + pipeline_epoch, + ) + .unwrap_or_default(); + + let self_bond_amount = crate::bond_amount( + &storage, + &BondId { + source: validator1.clone(), + validator: validator1.clone(), + }, + pipeline_epoch, + ) + .unwrap_or_default(); + + let val_stake = crate::read_validator_stake( + &storage, + ¶ms, + &validator1, + pipeline_epoch, + ) + .unwrap(); + // dbg!(&val_stake); + // dbg!(&del_bond_amount); + // dbg!(&self_bond_amount); + + let diff = val_stake - self_bond_amount - del_bond_amount; + assert!(diff <= 2.into()); +} + +fn test_consensus_key_change_aux(validators: Vec) { + assert_eq!(validators.len(), 1); + + let params = OwnedPosParams { + unbonding_len: 4, + ..Default::default() + }; + let validator = validators[0].address.clone(); + + println!("\nTest inputs: {params:?}, genesis validators: {validators:#?}"); + let mut storage = TestWlStorage::default(); + + // Genesis + let mut current_epoch = storage.storage.block.epoch; + let params = test_init_genesis( + &mut storage, + params, + validators.into_iter(), + current_epoch, + ) + .unwrap(); + storage.commit_block().unwrap(); + + // Check that there is one consensus key in the network + let consensus_keys = get_consensus_key_set(&storage).unwrap(); + assert_eq!(consensus_keys.len(), 1); + let ck = consensus_keys.first().cloned().unwrap(); + let og_ck = validator_consensus_key_handle(&validator) + .get(&storage, current_epoch, ¶ms) + .unwrap() + .unwrap(); + assert_eq!(ck, og_ck); + + // Attempt to change to a new secp256k1 consensus key (disallowed) + let secp_ck = gen_keypair::(); + let secp_ck = key::common::SecretKey::Secp256k1(secp_ck).ref_to(); + let res = + change_consensus_key(&mut storage, &validator, &secp_ck, current_epoch); + assert!(res.is_err()); + + // Change consensus keys + let ck_2 = common_sk_from_simple_seed(1).ref_to(); + change_consensus_key(&mut storage, &validator, &ck_2, current_epoch) + .unwrap(); + + // Check that there is a new consensus key + let consensus_keys = get_consensus_key_set(&storage).unwrap(); + assert_eq!(consensus_keys.len(), 2); + + for epoch in current_epoch.iter_range(params.pipeline_len) { + let ck = validator_consensus_key_handle(&validator) + .get(&storage, epoch, ¶ms) + .unwrap() + .unwrap(); + assert_eq!(ck, og_ck); + } + let pipeline_epoch = current_epoch + params.pipeline_len; + let ck = validator_consensus_key_handle(&validator) + .get(&storage, pipeline_epoch, ¶ms) + .unwrap() + .unwrap(); + assert_eq!(ck, ck_2); + + // Advance to the pipeline epoch + loop { + current_epoch = advance_epoch(&mut storage, ¶ms); + if current_epoch == pipeline_epoch { + break; + } + } + + // Check the consensus keys again + let consensus_keys = get_consensus_key_set(&storage).unwrap(); + assert_eq!(consensus_keys.len(), 2); + + for epoch in current_epoch.iter_range(params.pipeline_len + 1) { + let ck = validator_consensus_key_handle(&validator) + .get(&storage, epoch, ¶ms) + .unwrap() + .unwrap(); + assert_eq!(ck, ck_2); + } + + // Now change the consensus key again and bond in the same epoch + let ck_3 = common_sk_from_simple_seed(3).ref_to(); + change_consensus_key(&mut storage, &validator, &ck_3, current_epoch) + .unwrap(); + + let staking_token = storage.storage.native_token.clone(); + let amount_del = token::Amount::native_whole(5); + credit_tokens(&mut storage, &staking_token, &validator, amount_del) + .unwrap(); + bond_tokens( + &mut storage, + None, + &validator, + token::Amount::native_whole(1), + current_epoch, + None, + ) + .unwrap(); + + // Check consensus keys again + let consensus_keys = get_consensus_key_set(&storage).unwrap(); + assert_eq!(consensus_keys.len(), 3); + + for epoch in current_epoch.iter_range(params.pipeline_len) { + let ck = validator_consensus_key_handle(&validator) + .get(&storage, epoch, ¶ms) + .unwrap() + .unwrap(); + assert_eq!(ck, ck_2); + } + let pipeline_epoch = current_epoch + params.pipeline_len; + let ck = validator_consensus_key_handle(&validator) + .get(&storage, pipeline_epoch, ¶ms) + .unwrap() + .unwrap(); + assert_eq!(ck, ck_3); + + // Advance to the pipeline epoch to ensure that the validator set updates to + // tendermint will work + loop { + current_epoch = advance_epoch(&mut storage, ¶ms); + if current_epoch == pipeline_epoch { + break; + } + } + assert_eq!(current_epoch.0, 2 * params.pipeline_len); +} + +fn test_is_delegator_aux(mut validators: Vec) { + validators.sort_by(|a, b| b.tokens.cmp(&a.tokens)); + + let validator1 = validators[0].address.clone(); + let validator2 = validators[1].address.clone(); + + let mut storage = TestWlStorage::default(); + let params = OwnedPosParams { + unbonding_len: 4, + ..Default::default() + }; + + // Genesis + let mut current_epoch = storage.storage.block.epoch; + let params = test_init_genesis( + &mut storage, + params, + validators.clone().into_iter(), + current_epoch, + ) + .unwrap(); + storage.commit_block().unwrap(); + + // Get delegators with some tokens + let staking_token = staking_token_address(&storage); + let delegator1 = address::testing::gen_implicit_address(); + let delegator2 = address::testing::gen_implicit_address(); + let del_balance = token::Amount::native_whole(1000); + credit_tokens(&mut storage, &staking_token, &delegator1, del_balance) + .unwrap(); + credit_tokens(&mut storage, &staking_token, &delegator2, del_balance) + .unwrap(); + + // Advance to epoch 1 + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + + // Delegate in epoch 1 to validator1 + let del1_epoch = current_epoch; + super::bond_tokens( + &mut storage, + Some(&delegator1), + &validator1, + 1000.into(), + current_epoch, + None, + ) + .unwrap(); + + // Advance to epoch 2 + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + + // Delegate in epoch 2 to validator2 + let del2_epoch = current_epoch; + super::bond_tokens( + &mut storage, + Some(&delegator2), + &validator2, + 1000.into(), + current_epoch, + None, + ) + .unwrap(); + + // Checks + assert!(super::is_validator(&storage, &validator1).unwrap()); + assert!(super::is_validator(&storage, &validator2).unwrap()); + assert!(!super::is_delegator(&storage, &validator1, None).unwrap()); + assert!(!super::is_delegator(&storage, &validator2, None).unwrap()); + + assert!(!super::is_validator(&storage, &delegator1).unwrap()); + assert!(!super::is_validator(&storage, &delegator2).unwrap()); + assert!(super::is_delegator(&storage, &delegator1, None).unwrap()); + assert!(super::is_delegator(&storage, &delegator2, None).unwrap()); + + for epoch in Epoch::default().iter_range(del1_epoch.0 + params.pipeline_len) + { + assert!( + !super::is_delegator(&storage, &delegator1, Some(epoch)).unwrap() + ); + } + assert!( + super::is_delegator( + &storage, + &delegator1, + Some(del1_epoch + params.pipeline_len) + ) + .unwrap() + ); + for epoch in Epoch::default().iter_range(del2_epoch.0 + params.pipeline_len) + { + assert!( + !super::is_delegator(&storage, &delegator2, Some(epoch)).unwrap() + ); + } + assert!( + super::is_delegator( + &storage, + &delegator2, + Some(del2_epoch + params.pipeline_len) + ) + .unwrap() + ); +} diff --git a/proof_of_stake/src/tests/state_machine.rs b/proof_of_stake/src/tests/state_machine.rs index 1d07d6465b..8732a7e02a 100644 --- a/proof_of_stake/src/tests/state_machine.rs +++ b/proof_of_stake/src/tests/state_machine.rs @@ -7,17 +7,17 @@ use std::ops::Deref; use assert_matches::assert_matches; use itertools::Itertools; use namada_core::ledger::storage::testing::TestWlStorage; -use namada_core::ledger::storage_api::collections::lazy_map::{ - Collectable, NestedSubKey, SubKey, -}; -use namada_core::ledger::storage_api::token::read_balance; -use namada_core::ledger::storage_api::{token, StorageRead}; use namada_core::types::address::{self, Address}; use namada_core::types::dec::Dec; use namada_core::types::key; use namada_core::types::key::common::PublicKey; use namada_core::types::storage::Epoch; use namada_core::types::token::Change; +use namada_namada_trans_token::read_balance; +use namada_storage::collections::lazy_map::{ + Collectable, NestedSubKey, SubKey, +}; +use namada_storage::{token, StorageRead}; use proptest::prelude::*; use proptest::test_runner::Config; use proptest_state_machine::{ diff --git a/proof_of_stake/src/tests/state_machine_v2.rs b/proof_of_stake/src/tests/state_machine_v2.rs index c75d629995..2d3baf0ae2 100644 --- a/proof_of_stake/src/tests/state_machine_v2.rs +++ b/proof_of_stake/src/tests/state_machine_v2.rs @@ -8,17 +8,15 @@ use assert_matches::assert_matches; use derivative::Derivative; use itertools::Itertools; use namada_core::ledger::storage::testing::TestWlStorage; -use namada_core::ledger::storage_api::collections::lazy_map::{ - NestedSubKey, SubKey, -}; -use namada_core::ledger::storage_api::token::read_balance; -use namada_core::ledger::storage_api::{token, StorageRead}; use namada_core::types::address::{self, Address}; use namada_core::types::dec::Dec; use namada_core::types::key; use namada_core::types::key::common::PublicKey; use namada_core::types::storage::Epoch; use namada_core::types::token::Change; +use namada_namada_trans_token::read_balance; +use namada_storage::collections::lazy_map::{NestedSubKey, SubKey}; +use namada_storage::{token, StorageRead}; use proptest::prelude::*; use proptest::test_runner::Config; use proptest_state_machine::{ diff --git a/proof_of_stake/src/types/mod.rs b/proof_of_stake/src/types/mod.rs index 2d297bd72f..3d1d4c321a 100644 --- a/proof_of_stake/src/types/mod.rs +++ b/proof_of_stake/src/types/mod.rs @@ -10,16 +10,14 @@ use std::hash::Hash; use std::ops::Sub; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; -use namada_core::ledger::storage_api::collections::lazy_map::NestedMap; -use namada_core::ledger::storage_api::collections::{ - LazyMap, LazySet, LazyVec, -}; use namada_core::types::address::Address; use namada_core::types::dec::Dec; use namada_core::types::key::common; use namada_core::types::storage::{Epoch, KeySeg}; use namada_core::types::token; use namada_core::types::token::Amount; +use namada_storage::collections::lazy_map::NestedMap; +use namada_storage::collections::{LazyMap, LazySet, LazyVec}; pub use rev_order::ReverseOrdTokenAmount; use serde::{Deserialize, Serialize}; From d8df8964be6f9a9f0609c0d56920eab0b987f64a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Thu, 21 Dec 2023 10:58:25 +0000 Subject: [PATCH 056/118] merkle_tree: wip fix build --- Cargo.lock | 5 +++ merkle_tree/Cargo.toml | 9 ++++- merkle_tree/src/ics23_specs.rs | 3 +- merkle_tree/src/lib.rs | 74 +++++++++++++++++++++++----------- state/src/traits.rs | 30 -------------- 5 files changed, 63 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 99b711d36c..65bf769838 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4529,8 +4529,13 @@ dependencies = [ name = "namada_merkle_tree" version = "0.28.1" dependencies = [ + "borsh", + "ics23", "namada_core", + "namada_ethereum_bridge", + "prost 0.12.3", "sparse-merkle-tree", + "thiserror", ] [[package]] diff --git a/merkle_tree/Cargo.toml b/merkle_tree/Cargo.toml index 6edfa3e1d6..be842fb452 100644 --- a/merkle_tree/Cargo.toml +++ b/merkle_tree/Cargo.toml @@ -13,6 +13,11 @@ repository.workspace = true version.workspace = true [dependencies] -namada_core = {path = "../core", default-features = false} +namada_core = { path = "../core", default-features = false } +namada_ethereum_bridge = { path = "../ethereum_bridge" } -arse-merkle-tree.workspace = true \ No newline at end of file +arse-merkle-tree.workspace = true +borsh.workspace = true +ics23.workspace = true +prost.workspace = true +thiserror.workspace = true diff --git a/merkle_tree/src/ics23_specs.rs b/merkle_tree/src/ics23_specs.rs index 00691bd30e..130735d33a 100644 --- a/merkle_tree/src/ics23_specs.rs +++ b/merkle_tree/src/ics23_specs.rs @@ -2,8 +2,7 @@ use arse_merkle_tree::H256; use ics23::{HashOp, LeafOp, LengthOp, ProofSpec}; - -use super::traits::StorageHasher; +use namada_core::types::hash::StorageHasher; /// Get the leaf spec for the base tree. The key is stored after hashing, /// but the stored value is the subtree's root without hashing. diff --git a/merkle_tree/src/lib.rs b/merkle_tree/src/lib.rs index ac983f66cf..d13898a247 100644 --- a/merkle_tree/src/lib.rs +++ b/merkle_tree/src/lib.rs @@ -10,27 +10,52 @@ use arse_merkle_tree::error::Error as MtError; use arse_merkle_tree::{ Hash as SmtHash, Key as TreeKey, SparseMerkleTree as ArseMerkleTree, H256, }; -use borsh::{BorshDeserialize, BorshSerialize}; -use borsh_ext::BorshSerializeExt; use ics23::commitment_proof::Proof as Ics23Proof; use ics23::{CommitmentProof, ExistenceProof, NonExistenceProof}; -use namada_core::ledger::eth_bridge::storage::bridge_pool::BridgePoolProof; -use thiserror::Error; - -use super::traits::{StorageHasher, SubTreeRead, SubTreeWrite}; -use crate::bytes::ByteBuf; -use crate::ledger::eth_bridge::storage::bridge_pool::{ - is_pending_transfer_key, BridgePoolTree, +use ics23_specs::ibc_leaf_spec; +use namada_core::borsh::{BorshDeserialize, BorshSerialize, BorshSerializeExt}; +use namada_core::bytes::ByteBuf; +use namada_core::types::address::{Address, InternalAddress}; +use namada_core::types::hash::{Hash, StorageHasher}; +use namada_core::types::keccak::KeccakHash; +use namada_core::types::storage::{ + self, BlockHeight, DbKeySeg, Epoch, Error as StorageError, Key, KeySeg, + StringKey, TreeBytes, TreeKeyError, IBC_KEY_LIMIT, }; -use crate::ledger::storage::ics23_specs::ibc_leaf_spec; -use crate::ledger::storage::{ics23_specs, types, BlockHeight, Epoch, KeySeg}; -use crate::types::address::{Address, InternalAddress}; -use crate::types::hash::Hash; -use crate::types::keccak::KeccakHash; -use crate::types::storage::{ - self, DbKeySeg, Error as StorageError, Key, StringKey, TreeBytes, - TreeKeyError, IBC_KEY_LIMIT, +use namada_ethereum_bridge::storage::bridge_pool::{ + is_pending_transfer_key, BridgePoolProof, BridgePoolTree, }; +use thiserror::Error; + +/// Trait for reading from a merkle tree that is a sub-tree +/// of the global merkle tree. +pub trait SubTreeRead { + /// Get the root of a subtree in raw bytes. + fn root(&self) -> MerkleRoot; + /// Check if a key is present in the sub-tree + fn subtree_has_key(&self, key: &Key) -> Result; + /// Get the height at which the key is inserted + fn subtree_get(&self, key: &Key) -> Result>; + /// Get a membership proof for various key-value pairs + fn subtree_membership_proof( + &self, + keys: &[Key], + values: Vec, + ) -> Result; +} + +/// Trait for updating a merkle tree that is a sub-tree +/// of the global merkle tree +pub trait SubTreeWrite { + /// Add a key-value pair to the sub-tree + fn subtree_update( + &mut self, + key: &Key, + value: StorageBytes, + ) -> Result; + /// Delete a key from the sub-tree + fn subtree_delete(&mut self, key: &Key) -> Result; +} /// Type of membership proof from a merkle tree pub enum MembershipProof { @@ -81,7 +106,7 @@ pub enum Error { type Result = std::result::Result; /// Type alias for bytes to be put into the Merkle storage -pub(super) type StorageBytes<'a> = &'a [u8]; +pub type StorageBytes<'a> = &'a [u8]; // Type aliases for the different merkle trees and backing stores /// Sparse-merkle-tree store @@ -759,7 +784,7 @@ pub struct Proof { pub base_proof: CommitmentProof, } -impl From for crate::tendermint::merkle::proof::ProofOps { +impl From for namada_core::tendermint::merkle::proof::ProofOps { fn from( Proof { key, @@ -767,10 +792,9 @@ impl From for crate::tendermint::merkle::proof::ProofOps { base_proof, }: Proof, ) -> Self { + use namada_core::tendermint::merkle::proof::ProofOp; use prost::Message; - use crate::tendermint::merkle::proof::ProofOp; - let mut data = vec![]; sub_proof .encode(&mut data) @@ -801,11 +825,13 @@ impl From for crate::tendermint::merkle::proof::ProofOps { #[cfg(test)] mod test { use ics23::HostFunctionsManager; + use namada_core::ledger::storage::ics23_specs::{ + ibc_proof_specs, proof_specs, + }; + use namada_core::ledger::storage::traits::Sha256Hasher; + use namada_core::types::storage::KeySeg; use super::*; - use crate::ledger::storage::ics23_specs::{ibc_proof_specs, proof_specs}; - use crate::ledger::storage::traits::Sha256Hasher; - use crate::types::storage::KeySeg; #[test] fn test_crud_value() { diff --git a/state/src/traits.rs b/state/src/traits.rs index 15504404e8..d276816288 100644 --- a/state/src/traits.rs +++ b/state/src/traits.rs @@ -22,36 +22,6 @@ use crate::types::storage::{ BlockHeight, Key, MembershipProof, StringKey, TreeBytes, IBC_KEY_LIMIT, }; -/// Trait for reading from a merkle tree that is a sub-tree -/// of the global merkle tree. -pub trait SubTreeRead { - /// Get the root of a subtree in raw bytes. - fn root(&self) -> MerkleRoot; - /// Check if a key is present in the sub-tree - fn subtree_has_key(&self, key: &Key) -> Result; - /// Get the height at which the key is inserted - fn subtree_get(&self, key: &Key) -> Result, Error>; - /// Get a membership proof for various key-value pairs - fn subtree_membership_proof( - &self, - keys: &[Key], - values: Vec, - ) -> Result; -} - -/// Trait for updating a merkle tree that is a sub-tree -/// of the global merkle tree -pub trait SubTreeWrite { - /// Add a key-value pair to the sub-tree - fn subtree_update( - &mut self, - key: &Key, - value: StorageBytes, - ) -> Result; - /// Delete a key from the sub-tree - fn subtree_delete(&mut self, key: &Key) -> Result; -} - impl<'a, H: StorageHasher + Default> SubTreeRead for &'a Smt { fn root(&self) -> MerkleRoot { Smt::::root(self).into() From baa5406f6678ddb0918425bc802f09436847df8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Thu, 21 Dec 2023 17:24:06 +0000 Subject: [PATCH 057/118] rebase fixes --- Cargo.lock | 6 +++ core/src/types/sign.rs | 18 ++++---- gas/Cargo.toml | 1 + governance/Cargo.toml | 1 + governance/src/pgf/inflation.rs | 22 +++++----- trans_token/src/storage_key.rs | 4 +- tx/Cargo.toml | 6 ++- tx/src/data/mod.rs | 2 +- tx/src/lib.rs | 5 ++- tx/src/types.rs | 46 ++++++++++++-------- vp_env/Cargo.toml | 2 + vp_env/src/collection_validation/lazy_map.rs | 20 +++++---- vp_env/src/collection_validation/lazy_set.rs | 10 ++--- vp_env/src/collection_validation/lazy_vec.rs | 11 +++-- vp_env/src/collection_validation/mod.rs | 1 + vp_env/src/lib.rs | 2 + 16 files changed, 95 insertions(+), 62 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 65bf769838..852cb365a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4478,6 +4478,7 @@ dependencies = [ "namada_core", "namada_parameters", "namada_storage", + "serde 1.0.193", "thiserror", ] @@ -4488,6 +4489,7 @@ dependencies = [ "borsh", "namada_core", "namada_macros", + "namada_parameters", "namada_storage", "namada_trans_token", "proptest", @@ -4729,6 +4731,8 @@ dependencies = [ "masp_primitives", "namada_core", "namada_gas", + "num-derive", + "num-traits 0.2.17", "prost 0.12.3", "prost-types 0.12.3", "serde 1.0.193", @@ -4782,6 +4786,8 @@ dependencies = [ name = "namada_vp_env" version = "0.28.1" dependencies = [ + "derivative", + "masp_primitives", "namada_core", "namada_storage", "namada_tx", diff --git a/core/src/types/sign.rs b/core/src/types/sign.rs index e6a2b2e3e6..7b3e0d91ec 100644 --- a/core/src/types/sign.rs +++ b/core/src/types/sign.rs @@ -15,10 +15,12 @@ use crate::borsh::{ #[allow(missing_docs)] #[derive(Error, Debug)] pub enum SigIndexDecodeError { - #[error("Error deserializing transaction field bytes: {0}")] - TxDeserializingError(std::io::Error), - #[error("Error deserializing transaction")] - OfflineTxDeserializationError, + #[error("Invalid signature index bytes: {0}")] + InvalidEncoding(std::io::Error), + #[error("Invalid signature index JSON string")] + InvalidJsonString, + #[error("Invalid signature index: {0}")] + InvalidHex(data_encoding::DecodeError), } #[derive( @@ -71,13 +73,11 @@ impl SignatureIndex { if let Ok(hex) = serde_json::from_slice::(data) { match HEXUPPER.decode(hex.as_bytes()) { Ok(bytes) => Self::try_from_slice(&bytes) - .map_err(SigIndexDecodeError::TxDeserializingError), - Err(_) => { - Err(SigIndexDecodeError::OfflineTxDeserializationError) - } + .map_err(SigIndexDecodeError::InvalidEncoding), + Err(e) => Err(SigIndexDecodeError::InvalidHex(e)), } } else { - Err(SigIndexDecodeError::OfflineTxDeserializationError) + Err(SigIndexDecodeError::InvalidJsonString) } } } diff --git a/gas/Cargo.toml b/gas/Cargo.toml index f523e5e38e..ed2857462d 100644 --- a/gas/Cargo.toml +++ b/gas/Cargo.toml @@ -18,4 +18,5 @@ namada_parameters = { path = "../parameters" } namada_storage = { path = "../storage" } borsh.workspace = true +serde.workspace = true thiserror.workspace = true diff --git a/governance/Cargo.toml b/governance/Cargo.toml index 1e3c8b075f..d501ae6777 100644 --- a/governance/Cargo.toml +++ b/governance/Cargo.toml @@ -18,6 +18,7 @@ testing = [] [dependencies] namada_core = {path = "../core", default-features = false} namada_macros = {path = "../macros"} +namada_parameters = {path = "../parameters"} namada_storage = {path = "../storage"} namada_trans_token = {path = "../trans_token"} diff --git a/governance/src/pgf/inflation.rs b/governance/src/pgf/inflation.rs index 30dd4191fd..f4bcc95646 100644 --- a/governance/src/pgf/inflation.rs +++ b/governance/src/pgf/inflation.rs @@ -1,16 +1,16 @@ //! PGF lib code. -use crate::ledger::parameters::storage as params_storage; -use crate::ledger::storage_api::pgf::{ - get_parameters, get_payments, get_stewards, -}; -use crate::ledger::storage_api::token::credit_tokens; -use crate::ledger::storage_api::{self, StorageRead, StorageWrite}; -use crate::types::dec::Dec; -use crate::types::token; +use namada_core::types::dec::Dec; +use namada_core::types::token; +use namada_parameters::storage as params_storage; +use namada_storage::{StorageRead, StorageWrite}; +use namada_trans_token::credit_tokens; +use namada_trans_token::storage_key::minted_balance_key; + +use crate::pgf::storage::{get_parameters, get_payments, get_stewards}; /// Apply the PGF inflation. -pub fn apply_inflation(storage: &mut S) -> storage_api::Result<()> +pub fn apply_inflation(storage: &mut S) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -21,7 +21,7 @@ where .read(¶ms_storage::get_epochs_per_year_key())? .expect("Epochs per year should exist in storage"); let total_tokens: token::Amount = storage - .read(&token::minted_balance_key(&staking_token))? + .read(&minted_balance_key(&staking_token))? .expect("Total NAM balance should exist in storage"); let pgf_pd_rate = @@ -46,7 +46,7 @@ where pgf_fundings.sort_by(|a, b| a.id.cmp(&b.id)); for funding in pgf_fundings { - if storage_api::token::transfer( + if namada_trans_token::transfer( storage, &staking_token, &super::ADDRESS, diff --git a/trans_token/src/storage_key.rs b/trans_token/src/storage_key.rs index c38264bebc..94c5d633e6 100644 --- a/trans_token/src/storage_key.rs +++ b/trans_token/src/storage_key.rs @@ -179,7 +179,7 @@ pub fn is_masp_key(key: &storage::Key) -> bool { && (key == HEAD_TX_KEY || key.starts_with(TX_KEY_PREFIX) || key.starts_with(PIN_KEY_PREFIX) - || key.starts_with(MASP_NULLIFIERS_KEY_PREFIX))) + || key.starts_with(MASP_NULLIFIERS_KEY))) } else { false } @@ -191,7 +191,7 @@ pub fn is_masp_nullifier_key(key: &storage::Key) -> bool { [DbKeySeg::AddressSeg(addr), DbKeySeg::StringSeg(prefix), .. - ] if *addr == address::MASP && prefix == MASP_NULLIFIERS_KEY_PREFIX) + ] if *addr == address::MASP && prefix == MASP_NULLIFIERS_KEY) } /// Obtain the storage key for the last locked ratio of a token diff --git a/tx/Cargo.toml b/tx/Cargo.toml index 8aa8cbdf0a..a9df94f97b 100644 --- a/tx/Cargo.toml +++ b/tx/Cargo.toml @@ -20,10 +20,12 @@ ark-bls12-381.workspace = true borsh.workspace = true data-encoding.workspace = true masp_primitives.workspace = true -prost.workspace = true +num-derive.workspace = true +num-traits.workspace = true prost-types.workspace = true -serde.workspace = true +prost.workspace = true serde_json.workspace = true +serde.workspace = true sha2.workspace = true thiserror.workspace = true diff --git a/tx/src/data/mod.rs b/tx/src/data/mod.rs index b421902abd..3958dd9c36 100644 --- a/tx/src/data/mod.rs +++ b/tx/src/data/mod.rs @@ -151,7 +151,7 @@ impl FromStr for ResultCode { } } -impl From for crate::tendermint::abci::Code { +impl From for namada_core::tendermint::abci::Code { fn from(value: ResultCode) -> Self { Self::from(value.to_u32()) } diff --git a/tx/src/lib.rs b/tx/src/lib.rs index 9e874aaede..e40ff69533 100644 --- a/tx/src/lib.rs +++ b/tx/src/lib.rs @@ -4,10 +4,11 @@ pub mod data; pub mod proto; mod types; +pub use namada_core::types::sign::SignatureIndex; pub use types::{ standalone_signature, verify_standalone_sig, Code, Commitment, - CompressedSignature, Data, Error, Header, MaspBuilder, Section, Signature, - SignatureIndex, Signed, Signer, Tx, TxError, + CompressedSignature, Data, DecodeError, Header, MaspBuilder, Section, + Signature, Signed, Signer, Tx, TxError, }; #[cfg(test)] diff --git a/tx/src/types.rs b/tx/src/types.rs index e969ba4bd8..2d8c66d242 100644 --- a/tx/src/types.rs +++ b/tx/src/types.rs @@ -17,6 +17,7 @@ use namada_core::borsh::{ use namada_core::types::address::Address; use namada_core::types::chain::ChainId; use namada_core::types::key::{AccountPublicKeysMap, *}; +use namada_core::types::sign::SignatureIndex; use namada_core::types::storage::Epoch; use namada_core::types::time::DateTimeUtc; use namada_core::types::token::MaspDenom; @@ -37,23 +38,29 @@ pub enum VerifySigError { VerifySig(#[from] namada_core::types::key::VerifySigError), #[error("{0}")] Gas(#[from] namada_gas::Error), + #[error("The wrapper signature is invalid.")] + InvalidWrapperSignature, + #[error("The section signature is invalid: {0}")] + InvalidSectionSignature(String), } #[allow(missing_docs)] #[derive(Error, Debug)] -pub enum Error { +pub enum DecodeError { + #[error("Invalid signature index bytes: {0}")] + InvalidEncoding(std::io::Error), + #[error("Invalid signature index JSON string")] + InvalidJsonString, + #[error("Invalid signature index: {0}")] + InvalidHex(data_encoding::DecodeError), #[error("Error decoding a transaction from bytes: {0}")] TxDecodingError(prost::DecodeError), #[error("Timestamp is empty")] NoTimestampError, #[error("Timestamp is invalid: {0}")] InvalidTimestamp(prost_types::TimestampError), - #[error("The section signature is invalid: {0}")] - InvalidSectionSignature(String), #[error("Couldn't serialize transaction from JSON at {0}")] InvalidJSONDeserialization(String), - #[error("The wrapper signature is invalid.")] - InvalidWrapperSignature, } /// This can be used to sign an arbitrary tx. The signature is produced and @@ -941,14 +948,15 @@ pub struct Tx { /// Deserialize Tx from protobufs impl TryFrom<&[u8]> for Tx { - type Error = Error; + type Error = DecodeError; - fn try_from(tx_bytes: &[u8]) -> Result { + fn try_from(tx_bytes: &[u8]) -> Result { use prost::Message; - let tx = proto::Tx::decode(tx_bytes).map_err(Error::TxDecodingError)?; + let tx = proto::Tx::decode(tx_bytes) + .map_err(DecodeError::TxDecodingError)?; BorshDeserialize::try_from_slice(&tx.data) - .map_err(Error::TxDeserializingError) + .map_err(DecodeError::InvalidEncoding) } } @@ -989,15 +997,15 @@ impl Tx { } // Deserialize from hex encoding - pub fn deserialize(data: &[u8]) -> Result { + pub fn deserialize(data: &[u8]) -> Result { if let Ok(hex) = serde_json::from_slice::(data) { match HEXUPPER.decode(hex.as_bytes()) { Ok(bytes) => Tx::try_from_slice(&bytes) - .map_err(Error::TxDeserializingError), - Err(_) => Err(Error::OfflineTxDeserializationError), + .map_err(DecodeError::InvalidEncoding), + Err(e) => Err(DecodeError::InvalidHex(e)), } } else { - Err(Error::OfflineTxDeserializationError) + Err(DecodeError::InvalidJsonString) } } @@ -1143,7 +1151,7 @@ impl Tx { threshold: u8, max_signatures: Option, mut consume_verify_sig_gas: F, - ) -> std::result::Result, Error> + ) -> std::result::Result, VerifySigError> where F: FnMut() -> std::result::Result<(), namada_gas::Error>, { @@ -1166,7 +1174,7 @@ impl Tx { .all(|x| self.get_section(x).is_some()) { if signatures.total_signatures() > max_signatures { - return Err(Error::InvalidSectionSignature( + return Err(VerifySigError::InvalidSectionSignature( "too many signatures.".to_string(), )); } @@ -1180,7 +1188,7 @@ impl Tx { &mut consume_verify_sig_gas, ) .map_err(|_e| { - Error::InvalidSectionSignature( + VerifySigError::InvalidSectionSignature( "found invalid signature.".to_string(), ) }); @@ -1195,7 +1203,7 @@ impl Tx { } } } - Err(Error::InvalidSectionSignature(format!( + Err(VerifySigError::InvalidSectionSignature(format!( "signature threshold not met: ({} < {})", verified_pks.len(), threshold @@ -1211,7 +1219,7 @@ impl Tx { &self, public_key: &common::PublicKey, hashes: &[namada_core::types::hash::Hash], - ) -> Result<&Signature> { + ) -> Result<&Signature, VerifySigError> { self.verify_signatures( hashes, AccountPublicKeysMap::from_iter([public_key.clone()].into_iter()), @@ -1221,7 +1229,7 @@ impl Tx { || Ok(()), ) .map(|x| *x.first().unwrap()) - .map_err(|_| Error::InvalidWrapperSignature) + .map_err(|_| VerifySigError::InvalidWrapperSignature) } pub fn compute_section_signature( diff --git a/vp_env/Cargo.toml b/vp_env/Cargo.toml index fcb59d6362..ad2a6ba325 100644 --- a/vp_env/Cargo.toml +++ b/vp_env/Cargo.toml @@ -17,4 +17,6 @@ namada_core = { path = "../core", default-features = false } namada_storage = { path = "../storage" } namada_tx = { path = "../tx" } +derivative.workspace = true +masp_primitives.workspace = true thiserror.workspace = true diff --git a/vp_env/src/collection_validation/lazy_map.rs b/vp_env/src/collection_validation/lazy_map.rs index 57020bcaa2..78956d9db3 100644 --- a/vp_env/src/collection_validation/lazy_map.rs +++ b/vp_env/src/collection_validation/lazy_map.rs @@ -1,12 +1,16 @@ //! LazyMap validation helpers -use std::ops::Index; +use core::fmt::Debug; +use core::hash::Hash; +use std::collections::HashMap; use namada_core::borsh::{BorshDeserialize, BorshSerialize}; use namada_core::types::storage; -use namada_storage::collection::lazy_map::{LazyMap, NestedSubKey, SubKey}; +use namada_storage::collections::lazy_map::{LazyMap, NestedSubKey, SubKey}; +use namada_storage::collections::{Nested, Simple}; -use super::{read_data, Data, LazyCollectionExt, ValidationBuilder}; +use super::{read_data, Data, LazyCollectionExt}; +use crate::VpEnv; /// Possible sub-keys of a [`LazyMap`], together with their [`validation::Data`] /// that contains prior and posterior state. @@ -47,9 +51,9 @@ where K: storage::KeySeg + Clone + Hash + Eq + Debug, V: LazyCollectionExt + Debug, { - type Action = NestedAction::Action>; + type Action = NestedAction::Action>; type SubKeyWithData = - NestedSubKey::SubKeyWithData>; + NestedSubKey::SubKeyWithData>; fn read_sub_key_data( env: &ENV, @@ -65,7 +69,7 @@ where nested_sub_key, } = sub_key; // Try to read data from the nested collection - let nested_data = ::read_sub_key_data( + let nested_data = ::read_sub_key_data( env, storage_key, nested_sub_key, @@ -84,7 +88,7 @@ where // We have to group the nested sub-keys by the key from this map let mut grouped_by_key: HashMap< K, - Vec<::SubKeyWithData>, + Vec<::SubKeyWithData>, > = HashMap::new(); for NestedSubKey::Data { key, @@ -101,7 +105,7 @@ where let mut actions = vec![]; for (key, sub_keys) in grouped_by_key { let nested_actions = - ::validate_changed_sub_keys(sub_keys)?; + ::validate_changed_sub_keys(sub_keys)?; actions.extend( nested_actions .into_iter() diff --git a/vp_env/src/collection_validation/lazy_set.rs b/vp_env/src/collection_validation/lazy_set.rs index 8cfb4e6e2d..bfa5828b4a 100644 --- a/vp_env/src/collection_validation/lazy_set.rs +++ b/vp_env/src/collection_validation/lazy_set.rs @@ -1,12 +1,12 @@ //! LazySet validation helpers -use std::ops::Index; +use std::fmt::Debug; -use namada_core::borsh::{BorshDeserialize, BorshSerialize}; use namada_core::types::storage; -use namada_storage::collection::lazy_set::{LazySet, SubKey}; +use namada_storage::collections::lazy_set::{LazySet, SubKey}; -use super::{Data, LazyCollectionExt, ValidationBuilder}; +use super::LazyCollectionExt; +use crate::VpEnv; /// Possible actions that can modify a [`LazySet`]. This roughly corresponds to /// the methods that have `StorageWrite` access. @@ -49,7 +49,7 @@ pub fn determine_action( env: &ENV, storage_key: &storage::Key, parsed_key: K, -) -> crate::Result>> +) -> namada_storage::Result>> where ENV: for<'a> VpEnv<'a>, { diff --git a/vp_env/src/collection_validation/lazy_vec.rs b/vp_env/src/collection_validation/lazy_vec.rs index cabad4794d..3e8054fcc6 100644 --- a/vp_env/src/collection_validation/lazy_vec.rs +++ b/vp_env/src/collection_validation/lazy_vec.rs @@ -1,12 +1,17 @@ //! LazyVec validation helpers -use std::ops::Index; +use std::collections::BTreeSet; +use std::fmt::Debug; use namada_core::borsh::{BorshDeserialize, BorshSerialize}; use namada_core::types::storage; -use namada_storage::collection::lazy_vec::{LazyVec, SubKey}; +use namada_storage::collections::lazy_vec::{ + Index, LazyVec, SubKey, ValidationError, +}; +use namada_storage::ResultExt; -use super::{read_data, Data, LazyCollectionExt, ValidationBuilder}; +use super::{read_data, Data, LazyCollectionExt}; +use crate::VpEnv; /// Possible sub-keys of a [`LazyVec`], together with their [`validation::Data`] /// that contains prior and posterior state. diff --git a/vp_env/src/collection_validation/mod.rs b/vp_env/src/collection_validation/mod.rs index 2eef49e8ae..d74053fb6c 100644 --- a/vp_env/src/collection_validation/mod.rs +++ b/vp_env/src/collection_validation/mod.rs @@ -6,6 +6,7 @@ pub mod lazy_vec; use std::fmt::Debug; +use derivative::Derivative; use namada_core::borsh::BorshDeserialize; use namada_core::types::storage; use namada_storage::collections::LazyCollection; diff --git a/vp_env/src/lib.rs b/vp_env/src/lib.rs index adca8a0ba9..57550206a8 100644 --- a/vp_env/src/lib.rs +++ b/vp_env/src/lib.rs @@ -3,6 +3,7 @@ pub mod collection_validation; +// TODO: this should be re-exported from namada_shielded_token use masp_primitives::transaction::Transaction; use namada_core::borsh::BorshDeserialize; use namada_core::types::address::Address; @@ -15,6 +16,7 @@ use namada_core::types::storage::{ }; use namada_core::types::token::Transfer; use namada_storage::{OptionExt, ResultExt, StorageRead}; +use namada_tx::Tx; /// Validity predicate's environment is available for native VPs and WASM VPs pub trait VpEnv<'view> From f247a0c8d18c96e0dda3b966968e3222b0647b2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Thu, 21 Dec 2023 17:46:40 +0000 Subject: [PATCH 058/118] proof_of_stake: fix build --- apps/src/lib/node/ledger/shell/mod.rs | 8 + .../lib/node/ledger/shell/prepare_proposal.rs | 8 +- .../lib/node/ledger/shell/process_proposal.rs | 5 +- proof_of_stake/src/lib.rs | 57 +- proof_of_stake/src/pos_queries.rs | 138 +- proof_of_stake/src/queries.rs | 45 +- proof_of_stake/src/rewards.rs | 27 +- proof_of_stake/src/slashing.rs | 50 +- proof_of_stake/src/storage.rs | 89 +- proof_of_stake/src/storage_key.rs | 2 +- proof_of_stake/src/tests.rs | 6885 ----------------- proof_of_stake/src/validator_set_update.rs | 36 +- storage/src/lib.rs | 13 + 13 files changed, 229 insertions(+), 7134 deletions(-) delete mode 100644 proof_of_stake/src/tests.rs diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index e8d5da8b83..fc4e8a85a8 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -1538,6 +1538,14 @@ where }, ) } + + /// Check if we are at a given [`BlockHeight`] offset, `height_offset`, + /// within the current [`Epoch`]. + pub fn is_deciding_offset_within_epoch(&self, height_offset: u64) -> bool { + self.wl_storage + .pos_queries() + .is_deciding_offset_within_epoch(height_offset) + } } /// for the shell diff --git a/apps/src/lib/node/ledger/shell/prepare_proposal.rs b/apps/src/lib/node/ledger/shell/prepare_proposal.rs index 1161401180..96ce52a89d 100644 --- a/apps/src/lib/node/ledger/shell/prepare_proposal.rs +++ b/apps/src/lib/node/ledger/shell/prepare_proposal.rs @@ -102,15 +102,13 @@ where /// [`EncryptedTxBatchAllocator::WithEncryptedTxs`] value. #[inline] fn get_encrypted_txs_allocator(&self) -> EncryptedTxBatchAllocator { - let pos_queries = self.wl_storage.pos_queries(); - - let is_2nd_height_off = pos_queries.is_deciding_offset_within_epoch(1); - let is_3rd_height_off = pos_queries.is_deciding_offset_within_epoch(2); + let is_2nd_height_off = self.is_deciding_offset_within_epoch(1); + let is_3rd_height_off = self.is_deciding_offset_within_epoch(2); if hints::unlikely(is_2nd_height_off || is_3rd_height_off) { tracing::warn!( proposal_height = - ?pos_queries.get_current_decision_height(), + ?self.wl_storage.storage.block.height, "No mempool txs are being included in the current proposal" ); EncryptedTxBatchAllocator::WithoutEncryptedTxs( diff --git a/apps/src/lib/node/ledger/shell/process_proposal.rs b/apps/src/lib/node/ledger/shell/process_proposal.rs index 5677e67fc0..827ea2a881 100644 --- a/apps/src/lib/node/ledger/shell/process_proposal.rs +++ b/apps/src/lib/node/ledger/shell/process_proposal.rs @@ -694,9 +694,8 @@ where /// Checks if it is not possible to include encrypted txs at the current /// block height. pub(super) fn encrypted_txs_not_allowed(&self) -> bool { - let pos_queries = self.wl_storage.pos_queries(); - let is_2nd_height_off = pos_queries.is_deciding_offset_within_epoch(1); - let is_3rd_height_off = pos_queries.is_deciding_offset_within_epoch(2); + let is_2nd_height_off = self.is_deciding_offset_within_epoch(1); + let is_3rd_height_off = self.is_deciding_offset_within_epoch(2); is_2nd_height_off || is_3rd_height_off } } diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index 280060b06e..516945c967 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -27,21 +27,14 @@ use std::cmp::{self}; use std::collections::{BTreeMap, BTreeSet, HashSet}; pub use error::*; -use namada_account::protocol_pk_key; -use namada_core::types::address::{self, Address, InternalAddress}; +use namada_core::types::address::{Address, InternalAddress}; use namada_core::types::dec::Dec; -use namada_core::types::key::{ - common, tm_consensus_key_raw_hash, PublicKeyTmRawHash, -}; +use namada_core::types::key::common; use namada_core::types::storage::BlockHeight; pub use namada_core::types::storage::{Epoch, Key, KeySeg}; -use namada_storage::collections::lazy_map::{ - Collectable, LazyMap, NestedMap, NestedSubKey, SubKey, -}; -use namada_storage::collections::{LazyCollection, LazySet}; -use namada_storage::{ResultExt, StorageRead, StorageWrite}; +use namada_storage::collections::lazy_map::{self, Collectable, LazyMap}; +use namada_storage::{StorageRead, StorageWrite}; pub use namada_trans_token as token; -use once_cell::unsync::Lazy; pub use parameters::{OwnedPosParams, PosParams}; use crate::queries::{find_bonds, has_bonds}; @@ -321,7 +314,7 @@ where .fold(Ok(token::Amount::zero()), |acc, entry| { let acc = acc?; let ( - NestedSubKey::Data { + lazy_map::NestedSubKey::Data { key: amount, nested_sub_key: _, }, @@ -884,7 +877,7 @@ where let mut total_redelegated = token::Amount::zero(); for rb in redelegated_bonds.iter(storage)? { let ( - NestedSubKey::Data { + lazy_map::NestedSubKey::Data { key: src_validator, nested_sub_key: _, }, @@ -1100,9 +1093,9 @@ where { for res in redelegated_bonds.at(&start).iter(storage).unwrap() { let ( - NestedSubKey::Data { + lazy_map::NestedSubKey::Data { key: validator, - nested_sub_key: SubKey::Data(epoch), + nested_sub_key: lazy_map::SubKey::Data(epoch), }, amount, ) = res.unwrap(); @@ -1392,9 +1385,9 @@ where for unbond in unbond_handle.iter(storage)? { let ( - NestedSubKey::Data { + lazy_map::NestedSubKey::Data { key: start_epoch, - nested_sub_key: SubKey::Data(withdraw_epoch), + nested_sub_key: lazy_map::SubKey::Data(withdraw_epoch), }, amount, ) = unbond?; @@ -1417,9 +1410,9 @@ where redelegated_unbonds.at(&start_epoch).at(&withdraw_epoch); for ub in matching_redelegated_unbonds.iter(storage)? { let ( - NestedSubKey::Data { + lazy_map::NestedSubKey::Data { key: address, - nested_sub_key: SubKey::Data(epoch), + nested_sub_key: lazy_map::SubKey::Data(epoch), }, amount, ) = ub?; @@ -1587,9 +1580,9 @@ where let unbonds = unbond_handle(&bond_id.source, &bond_id.validator); for next in unbonds.iter(storage)? { let ( - NestedSubKey::Data { + lazy_map::NestedSubKey::Data { key: start, - nested_sub_key: SubKey::Data(withdrawable_epoch), + nested_sub_key: lazy_map::SubKey::Data(withdrawable_epoch), }, delta, ) = next?; @@ -1611,15 +1604,16 @@ where delegator_redelegated_bonds_handle(&bond_id.source); for res in redelegated_bonds.iter(storage)? { let ( - NestedSubKey::Data { + lazy_map::NestedSubKey::Data { key: _dest_validator, nested_sub_key: - NestedSubKey::Data { + lazy_map::NestedSubKey::Data { key: end, nested_sub_key: - NestedSubKey::Data { + lazy_map::NestedSubKey::Data { key: src_validator, - nested_sub_key: SubKey::Data(start), + nested_sub_key: + lazy_map::SubKey::Data(start), }, }, }, @@ -1640,18 +1634,19 @@ where delegator_redelegated_unbonds_handle(&bond_id.source); for res in redelegated_unbonds.iter(storage)? { let ( - NestedSubKey::Data { + lazy_map::NestedSubKey::Data { key: _dest_validator, nested_sub_key: - NestedSubKey::Data { + lazy_map::NestedSubKey::Data { key: redelegation_epoch, nested_sub_key: - NestedSubKey::Data { + lazy_map::NestedSubKey::Data { key: _withdraw_epoch, nested_sub_key: - NestedSubKey::Data { + lazy_map::NestedSubKey::Data { key: src_validator, - nested_sub_key: SubKey::Data(start), + nested_sub_key: + lazy_map::SubKey::Data(start), }, }, }, @@ -1813,7 +1808,7 @@ where iter.map(|validator| { let ( - NestedSubKey::Data { + lazy_map::NestedSubKey::Data { key: new_stake, nested_sub_key: _, }, diff --git a/proof_of_stake/src/pos_queries.rs b/proof_of_stake/src/pos_queries.rs index 8c5e3cdab5..20e160340c 100644 --- a/proof_of_stake/src/pos_queries.rs +++ b/proof_of_stake/src/pos_queries.rs @@ -7,6 +7,7 @@ use namada_core::types::storage::{BlockHeight, Epoch}; use namada_core::types::{key, token}; use namada_parameters::storage::get_max_proposal_bytes_key; use namada_storage::collections::lazy_map::NestedSubKey; +use namada_storage::StorageRead; use thiserror::Error; use crate::storage::find_validator_by_raw_hash; @@ -22,7 +23,7 @@ use crate::{ pub enum Error { /// A storage error occurred. #[error("Storage error: {0}")] - Storage(namada_storage::Error), + Storage(#[from] namada_storage::Error), /// The given address is not among the set of consensus validators for /// the corresponding epoch. #[error( @@ -58,53 +59,50 @@ pub trait PosQueries { fn pos_queries(&self) -> PosQueriesHook<'_, Self::Storage>; } -impl PosQueries for WlStorage +impl PosQueries for S where - D: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, - H: 'static + storage::StorageHasher, + S: StorageRead, { type Storage = Self; #[inline] fn pos_queries(&self) -> PosQueriesHook<'_, Self> { - PosQueriesHook { wl_storage: self } + PosQueriesHook { storage: self } } } /// A handle to [`PosQueries`]. /// -/// This type is a wrapper around a pointer to a -/// [`WlStorage`]. +/// This type is a wrapper around a pointer to a [`impl ReadStorage`]. #[derive(Debug)] #[repr(transparent)] -pub struct PosQueriesHook<'db, DB> { - wl_storage: &'db DB, +pub struct PosQueriesHook<'db, S> { + storage: &'db S, } -impl<'db, DB> Clone for PosQueriesHook<'db, DB> { +impl<'db, S> Clone for PosQueriesHook<'db, S> { fn clone(&self) -> Self { Self { - wl_storage: self.wl_storage, + storage: self.storage, } } } -impl<'db, DB> Copy for PosQueriesHook<'db, DB> {} +impl<'db, S> Copy for PosQueriesHook<'db, S> {} -impl<'db, D, H> PosQueriesHook<'db, WlStorage> +impl<'db, S> PosQueriesHook<'db, S> where - D: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, - H: 'static + storage::StorageHasher, + S: StorageRead, { - /// Return a handle to the inner [`WlStorage`]. + /// Return a handle to the inner storage. #[inline] - pub fn storage(self) -> &'db WlStorage { - self.wl_storage + pub fn storage(self) -> &'db S { + self.storage } /// Read the proof-of-stake parameters from storage. pub fn get_pos_params(self) -> PosParams { - read_pos_params(self.wl_storage) + read_pos_params(self.storage) .expect("Should be able to read PosParams from storage") } @@ -114,11 +112,11 @@ where pub fn get_consensus_validators( self, epoch: Option, - ) -> ConsensusValidators<'db, D, H> { - let epoch = epoch - .unwrap_or_else(|| self.wl_storage.storage.get_current_epoch().0); + ) -> ConsensusValidators<'db, S> { + let epoch = + epoch.unwrap_or_else(|| self.storage.get_block_epoch().unwrap()); ConsensusValidators { - wl_storage: self.wl_storage, + wl_storage: self.storage, validator_set: consensus_validator_set_handle().at(&epoch), } } @@ -126,10 +124,10 @@ where /// Lookup the total voting power for an epoch (defaulting to the /// epoch of the current yet-to-be-committed block). pub fn get_total_voting_power(self, epoch: Option) -> token::Amount { - let epoch = epoch - .unwrap_or_else(|| self.wl_storage.storage.get_current_epoch().0); + let epoch = + epoch.unwrap_or_else(|| self.storage.get_block_epoch().unwrap()); let pos_params = self.get_pos_params(); - get_total_consensus_stake(self.wl_storage, epoch, &pos_params) + get_total_consensus_stake(self.storage, epoch, &pos_params) // NB: the only reason this call should fail is if we request // an epoch that hasn't been reached yet. let's "fail" by // returning a total stake of 0 NAM @@ -142,16 +140,17 @@ where pk: &key::common::PublicKey, epoch: Option, ) -> Result { - let params = crate::read_pos_params(self.wl_storage) + let params = crate::read_pos_params(self.storage) .expect("Failed to fetch Pos params"); let epoch = epoch - .unwrap_or_else(|| self.wl_storage.storage.get_current_epoch().0); + .map(Ok) + .unwrap_or_else(|| self.storage.get_block_epoch())?; self.get_consensus_validators(Some(epoch)) .iter() .find(|validator| { let protocol_keys = crate::validator_protocol_key_handle(&validator.address); - match protocol_keys.get(self.wl_storage, epoch, ¶ms) { + match protocol_keys.get(self.storage, epoch, ¶ms) { Ok(Some(key)) => key == *pk, _ => false, } @@ -165,10 +164,11 @@ where address: &Address, epoch: Option, ) -> Result<(token::Amount, key::common::PublicKey)> { - let params = crate::read_pos_params(self.wl_storage) + let params = crate::read_pos_params(self.storage) .expect("Failed to fetch Pos params"); let epoch = epoch - .unwrap_or_else(|| self.wl_storage.storage.get_current_epoch().0); + .map(Ok) + .unwrap_or_else(|| self.storage.get_block_epoch())?; self.get_consensus_validators(Some(epoch)) .iter() .find(|validator| address == &validator.address) @@ -176,7 +176,7 @@ where let protocol_keys = crate::validator_protocol_key_handle(&validator.address); let protocol_pk = protocol_keys - .get(self.wl_storage, epoch, ¶ms) + .get(self.storage, epoch, ¶ms) .unwrap() .expect( "Protocol public key should be set in storage after \ @@ -196,7 +196,7 @@ where tm_address: impl AsRef, ) -> Result
{ let addr_hash = tm_address.as_ref(); - let validator = find_validator_by_raw_hash(self.wl_storage, addr_hash) + let validator = find_validator_by_raw_hash(self.storage, addr_hash) .map_err(Error::Storage)?; validator.ok_or_else(|| Error::NotValidatorKeyHash(addr_hash.into())) } @@ -206,22 +206,11 @@ where pub fn is_deciding_offset_within_epoch(self, height_offset: u64) -> bool { let current_decision_height = self.get_current_decision_height(); - // NOTE: the first stored height in `fst_block_heights_of_each_epoch` - // is 0, because of a bug (should be 1), so this code needs to - // handle that case - // - // we can remove this check once that's fixed - if self.wl_storage.storage.get_current_epoch().0 == Epoch(0) { - let height_offset_within_epoch = BlockHeight(1 + height_offset); - return current_decision_height == height_offset_within_epoch; - } - - let fst_heights_of_each_epoch = self - .wl_storage + let pred_epochs = self .storage - .block - .pred_epochs - .first_block_heights(); + .get_pred_epochs() + .expect("Must be able to read pred epochs"); + let fst_heights_of_each_epoch = pred_epochs.first_block_heights(); fst_heights_of_each_epoch .last() @@ -238,7 +227,7 @@ where /// been purged from Namada, or if it is not available yet. #[inline] pub fn get_epoch(self, height: BlockHeight) -> Option { - self.wl_storage.storage.block.pred_epochs.get_epoch(height) + self.storage.get_epoch_at_height(height).unwrap() } /// Given some [`Epoch`], return the corresponding [`BlockHeight`]. @@ -247,23 +236,19 @@ where /// been purged from Namada, or if it is not available yet. #[inline] pub fn get_height(self, epoch: Epoch) -> Option { - self.wl_storage - .storage - .block - .pred_epochs - .get_start_height_of_epoch(epoch) + self.storage.get_epoch_start_height(epoch).unwrap() } /// Retrieves the [`BlockHeight`] that is currently being decided. #[inline] pub fn get_current_decision_height(self) -> BlockHeight { - self.wl_storage.storage.get_last_block_height() + 1 + self.storage.get_block_height().unwrap() } /// Retrieve the `max_proposal_bytes` consensus parameter from storage. pub fn get_max_proposal_bytes(self) -> ProposalBytes { namada_storage::StorageRead::read( - self.wl_storage, + self.storage, &get_max_proposal_bytes_key(), ) .expect("Must be able to read ProposalBytes from storage") @@ -274,22 +259,7 @@ where /// committed to storage. #[inline] pub fn get_epoch_start_height(self) -> BlockHeight { - // NOTE: the first stored height in `fst_block_heights_of_each_epoch` - // is 0, because of a bug (should be 1), so this code needs to - // handle that case - // - // we can remove this check once that's fixed - if self.wl_storage.storage.last_epoch.0 == 0 { - return BlockHeight(1); - } - self.wl_storage - .storage - .block - .pred_epochs - .first_block_heights() - .last() - .copied() - .expect("The block height of the current epoch should be known") + self.storage.get_current_epoch_start_height().unwrap() } /// Get a validator's Ethereum hot key from storage, at the given epoch, or @@ -299,11 +269,11 @@ where validator: &Address, epoch: Option, ) -> Option { - let epoch = epoch - .unwrap_or_else(|| self.wl_storage.storage.get_current_epoch().0); + let epoch = + epoch.unwrap_or_else(|| self.storage.get_block_epoch().unwrap()); let params = self.get_pos_params(); validator_eth_hot_key_handle(validator) - .get(self.wl_storage, epoch, ¶ms) + .get(self.storage, epoch, ¶ms) .ok() .flatten() } @@ -315,11 +285,11 @@ where validator: &Address, epoch: Option, ) -> Option { - let epoch = epoch - .unwrap_or_else(|| self.wl_storage.storage.get_current_epoch().0); + let epoch = + epoch.unwrap_or_else(|| self.storage.get_block_epoch().unwrap()); let params = self.get_pos_params(); validator_eth_cold_key_handle(validator) - .get(self.wl_storage, epoch, ¶ms) + .get(self.storage, epoch, ¶ms) .ok() .flatten() } @@ -327,19 +297,17 @@ where /// A handle to the set of consensus validators in Namada, /// at some given epoch. -pub struct ConsensusValidators<'db, D, H> +pub struct ConsensusValidators<'db, S> where - D: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, - H: 'static + storage::StorageHasher, + S: StorageRead, { - wl_storage: &'db WlStorage, + wl_storage: &'db S, validator_set: ConsensusValidatorSet, } -impl<'db, D, H> ConsensusValidators<'db, D, H> +impl<'db, S> ConsensusValidators<'db, S> where - D: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, - H: 'static + storage::StorageHasher, + S: StorageRead, { /// Iterate over the set of consensus validators in Namada, at some given /// epoch. diff --git a/proof_of_stake/src/queries.rs b/proof_of_stake/src/queries.rs index 137d5fdf7a..0b98810cba 100644 --- a/proof_of_stake/src/queries.rs +++ b/proof_of_stake/src/queries.rs @@ -4,14 +4,12 @@ use std::cmp; use std::collections::{BTreeMap, HashMap, HashSet}; use borsh::BorshDeserialize; -use namada_core::ledger::storage_api::collections::lazy_map::{ - NestedSubKey, SubKey, -}; -use namada_core::ledger::storage_api::{self, StorageRead}; use namada_core::types::address::Address; use namada_core::types::dec::Dec; use namada_core::types::storage::Epoch; use namada_core::types::token; +use namada_storage::collections::lazy_map::{NestedSubKey, SubKey}; +use namada_storage::{self, StorageRead}; use crate::slashing::{find_validator_slashes, get_slashed_amount}; use crate::storage::{bond_handle, read_pos_params, unbond_handle}; @@ -26,20 +24,22 @@ use crate::{storage_key, PosParams}; pub fn find_delegation_validators( storage: &S, owner: &Address, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { let bonds_prefix = storage_key::bonds_for_source_prefix(owner); let mut delegations: HashSet
= HashSet::new(); - for iter_result in storage_api::iter_prefix_bytes(storage, &bonds_prefix)? { + for iter_result in + namada_storage::iter_prefix_bytes(storage, &bonds_prefix)? + { let (key, _bond_bytes) = iter_result?; let validator_address = storage_key::get_validator_address_from_bond( &key, ) .ok_or_else(|| { - storage_api::Error::new_const( + namada_storage::Error::new_const( "Delegation key should contain validator address.", ) })?; @@ -54,7 +54,7 @@ pub fn find_delegations( storage: &S, owner: &Address, epoch: &Epoch, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -62,13 +62,15 @@ where let params = read_pos_params(storage)?; let mut delegations: HashMap = HashMap::new(); - for iter_result in storage_api::iter_prefix_bytes(storage, &bonds_prefix)? { + for iter_result in + namada_storage::iter_prefix_bytes(storage, &bonds_prefix)? + { let (key, _bond_bytes) = iter_result?; let validator_address = storage_key::get_validator_address_from_bond( &key, ) .ok_or_else(|| { - storage_api::Error::new_const( + namada_storage::Error::new_const( "Delegation key should contain validator address.", ) })?; @@ -81,7 +83,10 @@ where } /// Find if the given source address has any bonds. -pub fn has_bonds(storage: &S, source: &Address) -> storage_api::Result +pub fn has_bonds( + storage: &S, + source: &Address, +) -> namada_storage::Result where S: StorageRead, { @@ -99,7 +104,7 @@ pub fn find_bonds( storage: &S, source: &Address, validator: &Address, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -114,7 +119,7 @@ pub fn find_unbonds( storage: &S, source: &Address, validator: &Address, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -140,7 +145,7 @@ pub fn bonds_and_unbonds( storage: &S, source: Option
, validator: Option
, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead, { @@ -161,7 +166,7 @@ fn get_multiple_bonds_and_unbonds( params: &PosParams, source: Option
, validator: Option
, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead, { @@ -182,7 +187,7 @@ where }; // We have to iterate raw bytes, cause the epoched data `last_update` field // gets matched here too - let mut raw_bonds = storage_api::iter_prefix_bytes(storage, &prefix)? + let mut raw_bonds = namada_storage::iter_prefix_bytes(storage, &prefix)? .filter_map(|result| { if let Ok((key, val_bytes)) = result { if let Some((bond_id, start)) = storage_key::is_bond_key(&key) { @@ -211,7 +216,7 @@ where Some(source) => storage_key::unbonds_for_source_prefix(source), None => storage_key::unbonds_prefix(), }; - let mut raw_unbonds = storage_api::iter_prefix_bytes(storage, &prefix)? + let mut raw_unbonds = namada_storage::iter_prefix_bytes(storage, &prefix)? .filter_map(|result| { if let Ok((key, val_bytes)) = result { if let Some((bond_id, start, withdraw)) = @@ -269,7 +274,7 @@ where slashes, &mut applied_slashes, )); - Ok::<_, storage_api::Error>(()) + Ok::<_, namada_storage::Error>(()) })?; raw_unbonds.try_for_each(|(bond_id, start, withdraw, amount)| { @@ -290,7 +295,7 @@ where slashes, &mut applied_slashes, )); - Ok::<_, storage_api::Error>(()) + Ok::<_, namada_storage::Error>(()) })?; Ok(bonds_and_unbonds @@ -314,7 +319,7 @@ fn find_bonds_and_unbonds_details( params: &PosParams, source: Address, validator: Address, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead, { diff --git a/proof_of_stake/src/rewards.rs b/proof_of_stake/src/rewards.rs index 880e7404c4..7107254188 100644 --- a/proof_of_stake/src/rewards.rs +++ b/proof_of_stake/src/rewards.rs @@ -3,17 +3,16 @@ use std::collections::{HashMap, HashSet}; use namada_core::ledger::inflation; -use namada_core::ledger::parameters::storage as params_storage; -use namada_core::ledger::storage_api::collections::lazy_map::NestedSubKey; -use namada_core::ledger::storage_api::token::credit_tokens; -use namada_core::ledger::storage_api::{ - self, ResultExt, StorageRead, StorageWrite, -}; use namada_core::types::address::{self, Address}; use namada_core::types::dec::Dec; use namada_core::types::storage::Epoch; use namada_core::types::token::{self, Amount}; use namada_core::types::uint::{Uint, I256}; +use namada_parameters::storage as params_storage; +use namada_storage::collections::lazy_map::NestedSubKey; +use namada_storage::{ResultExt, StorageRead, StorageWrite}; +use namada_trans_token::credit_tokens; +use namada_trans_token::storage_key::minted_balance_key; use thiserror::Error; use crate::storage::{ @@ -131,7 +130,7 @@ pub fn log_block_rewards( epoch: impl Into, proposer_address: &Address, votes: Vec, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -264,7 +263,7 @@ pub fn apply_inflation( storage: &mut S, last_epoch: Epoch, num_blocks_in_last_epoch: u64, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -286,7 +285,7 @@ where let pos_d_gain_nom = params.rewards_gain_d; let total_tokens: token::Amount = storage - .read(&token::minted_balance_key(&staking_token))? + .read(&minted_balance_key(&staking_token))? .expect("Total NAM balance should exist in storage"); let pos_locked_supply = read_total_stake(storage, ¶ms, last_epoch)?; let pos_locked_ratio_target = params.target_staked_ratio; @@ -349,7 +348,7 @@ pub fn update_rewards_products_and_mint_inflation( num_blocks_in_last_epoch: u64, inflation: token::Amount, staking_token: &Address, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -455,7 +454,7 @@ pub fn compute_current_rewards_from_bonds( source: &Address, validator: &Address, current_epoch: Epoch, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead, { @@ -510,7 +509,7 @@ pub fn add_rewards_to_counter( source: &Address, validator: &Address, new_rewards: token::Amount, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -525,7 +524,7 @@ pub fn take_rewards_from_counter( storage: &mut S, source: &Address, validator: &Address, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead + StorageWrite, { @@ -541,7 +540,7 @@ pub fn read_rewards_counter( storage: &S, source: &Address, validator: &Address, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead, { diff --git a/proof_of_stake/src/slashing.rs b/proof_of_stake/src/slashing.rs index 4b91a84527..be206e7abc 100644 --- a/proof_of_stake/src/slashing.rs +++ b/proof_of_stake/src/slashing.rs @@ -4,15 +4,15 @@ use std::cmp::{self, Reverse}; use std::collections::{BTreeMap, BTreeSet, HashMap}; use borsh::BorshDeserialize; -use namada_core::ledger::storage_api::collections::lazy_map::{ - Collectable, NestedMap, NestedSubKey, SubKey, -}; -use namada_core::ledger::storage_api::collections::LazyMap; -use namada_core::ledger::storage_api::{self, StorageRead, StorageWrite}; use namada_core::types::address::Address; use namada_core::types::dec::Dec; use namada_core::types::storage::Epoch; use namada_core::types::token; +use namada_storage::collections::lazy_map::{ + Collectable, NestedMap, NestedSubKey, SubKey, +}; +use namada_storage::collections::LazyMap; +use namada_storage::{StorageRead, StorageWrite}; use crate::storage::{ enqueued_slashes_handle, read_pos_params, read_validator_last_slash_epoch, @@ -47,7 +47,7 @@ pub fn slash( slash_type: SlashType, validator: &Address, validator_set_update_epoch: Epoch, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -101,7 +101,7 @@ where pub fn process_slashes( storage: &mut S, current_epoch: Epoch, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -281,7 +281,7 @@ pub fn slash_validator_redelegation( dest_total_redelegated_unbonded: &TotalRedelegatedUnbonded, slash_rate: Dec, dest_slashed_amounts: &mut BTreeMap, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead, { @@ -339,7 +339,7 @@ pub fn slash_redelegation( total_redelegated_unbonded: &TotalRedelegatedUnbonded, slash_rate: Dec, slashed_amounts: &mut BTreeMap, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead, { @@ -370,7 +370,7 @@ where .unwrap_or_default(); Ok(redelegated_unbonded) }) - .sum::>()?; + .sum::>()?; for epoch in Epoch::iter_range(set_update_epoch, params.pipeline_len) { let updated_total_unbonded = { @@ -457,7 +457,7 @@ pub fn slash_validator( slash_rate: Dec, current_epoch: Epoch, slashed_amounts_map: &BTreeMap, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -583,7 +583,7 @@ pub fn compute_bond_at_epoch( start: Epoch, amount: token::Amount, redelegated_bonds: Option<&EagerRedelegatedBondsMap>, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead, { @@ -632,7 +632,7 @@ pub fn compute_slash_bond_at_epoch( bond_amount: token::Amount, redelegated_bonds: Option<&EagerRedelegatedBondsMap>, slash_rate: Dec, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead, { @@ -666,7 +666,7 @@ pub fn find_slashes_in_range( start: Epoch, end: Option, validator: &Address, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -733,12 +733,12 @@ pub fn compute_slashable_amount( /// Find all slashes and the associated validators in the PoS system pub fn find_all_slashes( storage: &S, -) -> storage_api::Result>> +) -> namada_storage::Result>> where S: StorageRead, { let mut slashes: HashMap> = HashMap::new(); - let slashes_iter = storage_api::iter_prefix_bytes( + let slashes_iter = namada_storage::iter_prefix_bytes( storage, &storage_key::slashes_prefix(), )? @@ -772,7 +772,7 @@ where pub fn find_all_enqueued_slashes( storage: &S, epoch: Epoch, -) -> storage_api::Result>>> +) -> namada_storage::Result>>> where S: StorageRead, { @@ -807,7 +807,7 @@ where pub fn find_validator_slashes( storage: &S, validator: &Address, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -821,7 +821,7 @@ pub fn get_slashed_amount( params: &PosParams, amount: token::Amount, slashes: &BTreeMap, -) -> storage_api::Result { +) -> namada_storage::Result { let mut updated_amount = amount; let mut computed_amounts = Vec::::new(); @@ -872,7 +872,7 @@ pub fn compute_amount_after_slashing_unbond( unbonds: &BTreeMap, redelegated_unbonds: &EagerRedelegatedUnbonds, slashes: Vec, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead, { @@ -929,7 +929,7 @@ pub fn compute_amount_after_slashing_withdraw( (token::Amount, EagerRedelegatedBondsMap), >, slashes: Vec, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead, { @@ -1003,7 +1003,7 @@ fn process_validator_slash( slash_rate: Dec, current_epoch: Epoch, slashed_amount_map: &mut EagerRedelegatedBondsMap, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -1042,7 +1042,7 @@ where ) = res?; Ok(dest_validator) }) - .collect::>>()?; + .collect::>>()?; for dest_validator in dest_validators { let to_modify = slashed_amount_map @@ -1079,7 +1079,7 @@ fn compute_cubic_slash_rate( storage: &S, params: &PosParams, infraction_epoch: Epoch, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead, { @@ -1103,7 +1103,7 @@ where let slashes = enqueued_slashes_handle().at(&processing_epoch); let infracting_stake = slashes.iter(storage)?.fold( Ok(Dec::zero()), - |acc: storage_api::Result, res| { + |acc: namada_storage::Result, res| { let acc = acc?; let ( NestedSubKey::Data { diff --git a/proof_of_stake/src/storage.rs b/proof_of_stake/src/storage.rs index bab677b3a0..2f28d56698 100644 --- a/proof_of_stake/src/storage.rs +++ b/proof_of_stake/src/storage.rs @@ -3,19 +3,16 @@ use std::collections::{BTreeSet, HashSet}; -use namada_core::ledger::storage_api::collections::lazy_map::NestedSubKey; -use namada_core::ledger::storage_api::collections::{LazyCollection, LazySet}; -use namada_core::ledger::storage_api::governance::get_max_proposal_period; -use namada_core::ledger::storage_api::{ - self, Result, StorageRead, StorageWrite, -}; +use namada_account::protocol_pk_key; use namada_core::types::address::Address; use namada_core::types::dec::Dec; -use namada_core::types::key::{ - common, protocol_pk_key, tm_consensus_key_raw_hash, -}; +use namada_core::types::key::{common, tm_consensus_key_raw_hash}; use namada_core::types::storage::Epoch; use namada_core::types::token; +use namada_governance::storage::get_max_proposal_period; +use namada_storage::collections::lazy_map::NestedSubKey; +use namada_storage::collections::{LazyCollection, LazySet}; +use namada_storage::{Result, StorageRead, StorageWrite}; use crate::storage_key::consensus_keys_key; use crate::types::{ @@ -249,7 +246,7 @@ pub fn liveness_sum_missed_votes_handle() -> LivenessSumMissedVotes { // ---- Storage read + write ---- /// Read PoS parameters -pub fn read_pos_params(storage: &S) -> storage_api::Result +pub fn read_pos_params(storage: &S) -> namada_storage::Result where S: StorageRead, { @@ -265,7 +262,7 @@ where pub fn read_non_pos_owned_params( storage: &S, owned: OwnedPosParams, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead, { @@ -280,7 +277,7 @@ where pub fn write_pos_params( storage: &mut S, params: &OwnedPosParams, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -292,7 +289,7 @@ where pub fn find_validator_by_raw_hash( storage: &S, raw_hash: impl AsRef, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -305,7 +302,7 @@ pub fn write_validator_address_raw_hash( storage: &mut S, validator: &Address, consensus_key: &common::PublicKey, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -320,7 +317,7 @@ where pub fn read_validator_max_commission_rate_change( storage: &S, validator: &Address, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -333,7 +330,7 @@ pub fn write_validator_max_commission_rate_change( storage: &mut S, validator: &Address, change: Dec, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -345,7 +342,7 @@ where pub fn read_validator_last_slash_epoch( storage: &S, validator: &Address, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -358,7 +355,7 @@ pub fn write_validator_last_slash_epoch( storage: &mut S, validator: &Address, epoch: Epoch, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -369,7 +366,7 @@ where /// Read last block proposer address. pub fn read_last_block_proposer_address( storage: &S, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -381,7 +378,7 @@ where pub fn write_last_block_proposer_address( storage: &mut S, address: Address, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -394,7 +391,7 @@ pub fn read_validator_deltas_value( storage: &S, validator: &Address, epoch: &namada_core::types::storage::Epoch, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -410,7 +407,7 @@ pub fn read_validator_stake( params: &PosParams, validator: &Address, epoch: namada_core::types::storage::Epoch, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead, { @@ -433,7 +430,7 @@ pub fn update_validator_deltas( delta: token::Change, current_epoch: namada_core::types::storage::Epoch, offset_opt: Option, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -456,7 +453,7 @@ pub fn read_total_stake( storage: &S, params: &PosParams, epoch: namada_core::types::storage::Epoch, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead, { @@ -475,7 +472,7 @@ where pub fn read_consensus_validator_set_addresses( storage: &S, epoch: namada_core::types::storage::Epoch, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -490,7 +487,7 @@ where pub fn read_below_capacity_validator_set_addresses( storage: &S, epoch: namada_core::types::storage::Epoch, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -505,7 +502,7 @@ where pub fn read_below_threshold_validator_set_addresses( storage: &S, epoch: namada_core::types::storage::Epoch, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -527,7 +524,7 @@ where pub fn read_consensus_validator_set_addresses_with_stake( storage: &S, epoch: namada_core::types::storage::Epoch, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -557,7 +554,7 @@ where pub fn get_num_consensus_validators( storage: &S, epoch: namada_core::types::storage::Epoch, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead, { @@ -571,7 +568,7 @@ where pub fn read_below_capacity_validator_set_addresses_with_stake( storage: &S, epoch: namada_core::types::storage::Epoch, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -601,7 +598,7 @@ where pub fn read_all_validator_addresses( storage: &S, epoch: namada_core::types::storage::Epoch, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -619,7 +616,7 @@ pub fn update_total_deltas( delta: token::Change, current_epoch: namada_core::types::storage::Epoch, offset_opt: Option, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -641,7 +638,7 @@ where pub fn read_validator_email( storage: &S, validator: &Address, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -654,7 +651,7 @@ pub fn write_validator_email( storage: &mut S, validator: &Address, email: &String, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -670,7 +667,7 @@ where pub fn read_validator_description( storage: &S, validator: &Address, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -683,7 +680,7 @@ pub fn write_validator_description( storage: &mut S, validator: &Address, description: &String, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -699,7 +696,7 @@ where pub fn read_validator_website( storage: &S, validator: &Address, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -712,7 +709,7 @@ pub fn write_validator_website( storage: &mut S, validator: &Address, website: &String, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -728,7 +725,7 @@ where pub fn read_validator_discord_handle( storage: &S, validator: &Address, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -741,7 +738,7 @@ pub fn write_validator_discord_handle( storage: &mut S, validator: &Address, discord_handle: &String, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -758,7 +755,7 @@ pub fn write_validator_metadata( storage: &mut S, validator: &Address, metadata: &ValidatorMetaData, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -782,7 +779,7 @@ pub fn get_last_reward_claim_epoch( storage: &S, delegator: &Address, validator: &Address, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -798,7 +795,7 @@ pub fn write_last_reward_claim_epoch( delegator: &Address, validator: &Address, epoch: Epoch, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -814,7 +811,7 @@ where pub fn try_insert_consensus_key( storage: &mut S, consensus_key: &common::PublicKey, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -825,7 +822,7 @@ where /// Get the unique set of consensus keys in storage pub fn get_consensus_key_set( storage: &S, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -838,7 +835,7 @@ where pub fn is_consensus_key_used( storage: &S, consensus_key: &common::PublicKey, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead, { diff --git a/proof_of_stake/src/storage_key.rs b/proof_of_stake/src/storage_key.rs index 2991526760..eaecaf7ad6 100644 --- a/proof_of_stake/src/storage_key.rs +++ b/proof_of_stake/src/storage_key.rs @@ -1,8 +1,8 @@ //! Proof-of-Stake storage keys and storage integration. -use namada_core::ledger::storage_api::collections::{lazy_map, lazy_vec}; use namada_core::types::address::Address; use namada_core::types::storage::{DbKeySeg, Epoch, Key, KeySeg}; +use namada_storage::collections::{lazy_map, lazy_vec}; use super::ADDRESS; use crate::epoched; diff --git a/proof_of_stake/src/tests.rs b/proof_of_stake/src/tests.rs deleted file mode 100644 index 590673eb50..0000000000 --- a/proof_of_stake/src/tests.rs +++ /dev/null @@ -1,6885 +0,0 @@ -//! PoS system tests - -mod state_machine; -mod state_machine_v2; -mod utils; - -use std::cmp::{max, min}; -use std::collections::{BTreeMap, BTreeSet, HashSet}; -use std::ops::{Deref, Range}; -use std::str::FromStr; - -use assert_matches::assert_matches; -use namada_core::ledger::storage::testing::TestWlStorage; -use namada_core::types::address::testing::{ - address_from_simple_seed, arb_established_address, established_address_1, - established_address_2, established_address_3, -}; -use namada_core::types::address::{Address, EstablishedAddressGen}; -use namada_core::types::dec::Dec; -use namada_core::types::key::common::{PublicKey, SecretKey}; -use namada_core::types::key::testing::{ - arb_common_keypair, common_sk_from_simple_seed, gen_keypair, -}; -use namada_core::types::key::RefTo; -use namada_core::types::storage::{BlockHeight, Epoch, Key}; -use namada_core::types::token::testing::arb_amount_non_zero_ceiled; -use namada_core::types::token::NATIVE_MAX_DECIMAL_PLACES; -use namada_core::types::{address, key, token}; -use namada_namada_trans_token::{credit_tokens, read_balance}; -use namada_storage::collections::lazy_map::{self, Collectable, NestedMap}; -use namada_storage::collections::LazyCollection; -use namada_storage::StorageRead; -use proptest::prelude::*; -use proptest::test_runner::Config; -// Use `RUST_LOG=info` (or another tracing level) and `--nocapture` to see -// `tracing` logs from tests -use test_log::test; - -use crate::epoched::DEFAULT_NUM_PAST_EPOCHS; -use crate::parameters::testing::arb_pos_params; -use crate::parameters::{OwnedPosParams, PosParams}; -use crate::rewards::PosRewardsCalculator; -use crate::test_utils::test_init_genesis; -use crate::types::{ - into_tm_voting_power, BondDetails, BondId, BondsAndUnbondsDetails, - ConsensusValidator, EagerRedelegatedBondsMap, GenesisValidator, Position, - RedelegatedTokens, ReverseOrdTokenAmount, Slash, SlashType, UnbondDetails, - ValidatorSetUpdate, ValidatorState, VoteInfo, WeightedValidator, -}; -use crate::{ - apply_list_slashes, become_validator, below_capacity_validator_set_handle, - bond_handle, bond_tokens, bonds_and_unbonds, change_consensus_key, - compute_amount_after_slashing_unbond, - compute_amount_after_slashing_withdraw, - compute_and_store_total_consensus_stake, compute_bond_at_epoch, - compute_modified_redelegation, compute_new_redelegated_unbonds, - compute_slash_bond_at_epoch, compute_slashable_amount, - consensus_validator_set_handle, copy_validator_sets_and_positions, - delegator_redelegated_bonds_handle, delegator_redelegated_unbonds_handle, - find_bonds_to_remove, find_validator_by_raw_hash, - fold_and_slash_redelegated_bonds, get_consensus_key_set, - get_num_consensus_validators, insert_validator_into_validator_set, - is_validator, process_slashes, - read_below_capacity_validator_set_addresses_with_stake, - read_below_threshold_validator_set_addresses, - read_consensus_validator_set_addresses_with_stake, read_total_stake, - read_validator_deltas_value, read_validator_stake, slash, - slash_redelegation, slash_validator, slash_validator_redelegation, - staking_token_address, total_bonded_handle, total_deltas_handle, - total_unbonded_handle, unbond_handle, unbond_tokens, unjail_validator, - update_validator_deltas, update_validator_set, - validator_consensus_key_handle, validator_incoming_redelegations_handle, - validator_outgoing_redelegations_handle, validator_set_positions_handle, - validator_set_update_tendermint, validator_slashes_handle, - validator_state_handle, validator_total_redelegated_bonded_handle, - validator_total_redelegated_unbonded_handle, withdraw_tokens, - write_pos_params, write_validator_address_raw_hash, BecomeValidator, - EagerRedelegatedUnbonds, FoldRedelegatedBondsResult, ModifiedRedelegation, - RedelegationError, -}; - -proptest! { - // Generate arb valid input for `test_test_init_genesis_aux` - #![proptest_config(Config { - cases: 100, - .. Config::default() - })] - #[test] - fn test_test_init_genesis( - - (pos_params, genesis_validators) in arb_params_and_genesis_validators(Some(5), 1..10), - start_epoch in (0_u64..1000).prop_map(Epoch), - - ) { - test_test_init_genesis_aux(pos_params, start_epoch, genesis_validators) - } -} - -proptest! { - // Generate arb valid input for `test_bonds_aux` - #![proptest_config(Config { - cases: 100, - .. Config::default() - })] - #[test] - fn test_bonds( - - (pos_params, genesis_validators) in arb_params_and_genesis_validators(Some(5), 1..3), - - ) { - test_bonds_aux(pos_params, genesis_validators) - } -} - -proptest! { - // Generate arb valid input for `test_become_validator_aux` - #![proptest_config(Config { - cases: 100, - .. Config::default() - })] - #[test] - fn test_become_validator( - - (pos_params, genesis_validators) in arb_params_and_genesis_validators(Some(5), 1..3), - new_validator in arb_established_address().prop_map(Address::Established), - new_validator_consensus_key in arb_common_keypair(), - - ) { - test_become_validator_aux(pos_params, new_validator, - new_validator_consensus_key, genesis_validators) - } -} - -proptest! { - // Generate arb valid input for `test_slashes_with_unbonding_aux` - #![proptest_config(Config { - cases: 100, - .. Config::default() - })] - #[test] - fn test_slashes_with_unbonding( - (params, genesis_validators, unbond_delay) - in test_slashes_with_unbonding_params() - ) { - test_slashes_with_unbonding_aux( - params, genesis_validators, unbond_delay) - } -} - -proptest! { - // Generate arb valid input for `test_unjail_validator_aux` - #![proptest_config(Config { - cases: 100, - .. Config::default() - })] - #[test] - fn test_unjail_validator( - (pos_params, genesis_validators) - in arb_params_and_genesis_validators(Some(4),6..9) - ) { - test_unjail_validator_aux(pos_params, - genesis_validators) - } -} - -proptest! { - // Generate arb valid input for `test_simple_redelegation_aux` - #![proptest_config(Config { - cases: 100, - .. Config::default() - })] - #[test] - fn test_simple_redelegation( - - genesis_validators in arb_genesis_validators(2..4, None), - (amount_delegate, amount_redelegate, amount_unbond) in arb_redelegation_amounts(20) - - ) { - test_simple_redelegation_aux(genesis_validators, amount_delegate, amount_redelegate, amount_unbond) - } -} - -proptest! { - // Generate arb valid input for `test_simple_redelegation_aux` - #![proptest_config(Config { - cases: 100, - .. Config::default() - })] - #[test] - fn test_redelegation_with_slashing( - - genesis_validators in arb_genesis_validators(2..4, None), - (amount_delegate, amount_redelegate, amount_unbond) in arb_redelegation_amounts(20) - - ) { - test_redelegation_with_slashing_aux(genesis_validators, amount_delegate, amount_redelegate, amount_unbond) - } -} - -proptest! { - // Generate arb valid input for `test_chain_redelegations_aux` - #![proptest_config(Config { - cases: 100, - .. Config::default() - })] - #[test] - fn test_chain_redelegations( - - genesis_validators in arb_genesis_validators(3..4, None), - - ) { - test_chain_redelegations_aux(genesis_validators) - } -} - -proptest! { - // Generate arb valid input for `test_overslashing_aux` - #![proptest_config(Config { - cases: 1, - .. Config::default() - })] - #[test] - fn test_overslashing( - - genesis_validators in arb_genesis_validators(4..5, None), - - ) { - test_overslashing_aux(genesis_validators) - } -} - -proptest! { - // Generate arb valid input for `test_unslashed_bond_amount_aux` - #![proptest_config(Config { - cases: 1, - .. Config::default() - })] - #[test] - fn test_unslashed_bond_amount( - - genesis_validators in arb_genesis_validators(4..5, None), - - ) { - test_unslashed_bond_amount_aux(genesis_validators) - } -} - -proptest! { - // Generate arb valid input for `test_slashed_bond_amount_aux` - #![proptest_config(Config { - cases: 1, - .. Config::default() - })] - #[test] - fn test_slashed_bond_amount( - - genesis_validators in arb_genesis_validators(4..5, None), - - ) { - test_slashed_bond_amount_aux(genesis_validators) - } -} - -proptest! { - // Generate arb valid input for `test_log_block_rewards_aux` - #![proptest_config(Config { - cases: 1, - .. Config::default() - })] - #[test] - fn test_log_block_rewards( - genesis_validators in arb_genesis_validators(4..10, None), - params in arb_pos_params(Some(5)) - - ) { - test_log_block_rewards_aux(genesis_validators, params) - } -} - -proptest! { - // Generate arb valid input for `test_update_rewards_products_aux` - #![proptest_config(Config { - cases: 1, - .. Config::default() - })] - #[test] - fn test_update_rewards_products( - genesis_validators in arb_genesis_validators(4..10, None), - - ) { - test_update_rewards_products_aux(genesis_validators) - } -} - -proptest! { - // Generate arb valid input for `test_consensus_key_change` - #![proptest_config(Config { - cases: 1, - .. Config::default() - })] - #[test] - fn test_consensus_key_change( - - genesis_validators in arb_genesis_validators(1..2, None), - - ) { - test_consensus_key_change_aux(genesis_validators) - } -} - -proptest! { - // Generate arb valid input for `test_is_delegator` - #![proptest_config(Config { - cases: 100, - .. Config::default() - })] - #[test] - fn test_is_delegator( - - genesis_validators in arb_genesis_validators(2..3, None), - - ) { - test_is_delegator_aux(genesis_validators) - } -} - -fn arb_params_and_genesis_validators( - num_max_validator_slots: Option, - val_size: Range, -) -> impl Strategy)> { - let params = arb_pos_params(num_max_validator_slots); - params.prop_flat_map(move |params| { - let validators = arb_genesis_validators( - val_size.clone(), - Some(params.validator_stake_threshold), - ); - (Just(params), validators) - }) -} - -fn test_slashes_with_unbonding_params() --> impl Strategy, u64)> { - let params = arb_pos_params(Some(5)); - params.prop_flat_map(|params| { - let unbond_delay = 0..(params.slash_processing_epoch_offset() * 2); - // Must have at least 4 validators so we can slash one and the cubic - // slash rate will be less than 100% - let validators = arb_genesis_validators(4..10, None); - (Just(params), validators, unbond_delay) - }) -} - -/// Test genesis initialization -fn test_test_init_genesis_aux( - params: OwnedPosParams, - start_epoch: Epoch, - mut validators: Vec, -) { - println!( - "Test inputs: {params:?}, {start_epoch}, genesis validators: \ - {validators:#?}" - ); - let mut s = TestWlStorage::default(); - s.storage.block.epoch = start_epoch; - - validators.sort_by(|a, b| b.tokens.cmp(&a.tokens)); - let params = test_init_genesis( - &mut s, - params, - validators.clone().into_iter(), - start_epoch, - ) - .unwrap(); - - let mut bond_details = bonds_and_unbonds(&s, None, None).unwrap(); - assert!(bond_details.iter().all(|(_id, details)| { - details.unbonds.is_empty() && details.slashes.is_empty() - })); - - for (i, validator) in validators.into_iter().enumerate() { - let addr = &validator.address; - let self_bonds = bond_details - .remove(&BondId { - source: addr.clone(), - validator: addr.clone(), - }) - .unwrap(); - assert_eq!(self_bonds.bonds.len(), 1); - assert_eq!( - self_bonds.bonds[0], - BondDetails { - start: start_epoch, - amount: validator.tokens, - slashed_amount: None, - } - ); - - let state = validator_state_handle(&validator.address) - .get(&s, start_epoch, ¶ms) - .unwrap(); - if (i as u64) < params.max_validator_slots - && validator.tokens >= params.validator_stake_threshold - { - // should be in consensus set - let handle = consensus_validator_set_handle().at(&start_epoch); - assert!(handle.at(&validator.tokens).iter(&s).unwrap().any( - |result| { - let (_pos, addr) = result.unwrap(); - addr == validator.address - } - )); - assert_eq!(state, Some(ValidatorState::Consensus)); - } else if validator.tokens >= params.validator_stake_threshold { - // Should be in below-capacity set if its tokens are greater than - // `validator_stake_threshold` - let handle = below_capacity_validator_set_handle().at(&start_epoch); - assert!(handle.at(&validator.tokens.into()).iter(&s).unwrap().any( - |result| { - let (_pos, addr) = result.unwrap(); - addr == validator.address - } - )); - assert_eq!(state, Some(ValidatorState::BelowCapacity)); - } else { - // Should be in below-threshold - let bt_addresses = - read_below_threshold_validator_set_addresses(&s, start_epoch) - .unwrap(); - assert!( - bt_addresses - .into_iter() - .any(|addr| { addr == validator.address }) - ); - assert_eq!(state, Some(ValidatorState::BelowThreshold)); - } - } -} - -/// Test bonding -/// NOTE: copy validator sets each time we advance the epoch -fn test_bonds_aux(params: OwnedPosParams, validators: Vec) { - // This can be useful for debugging: - // params.pipeline_len = 2; - // params.unbonding_len = 4; - println!("\nTest inputs: {params:?}, genesis validators: {validators:#?}"); - let mut s = TestWlStorage::default(); - - // Genesis - let start_epoch = s.storage.block.epoch; - let mut current_epoch = s.storage.block.epoch; - let params = test_init_genesis( - &mut s, - params, - validators.clone().into_iter(), - current_epoch, - ) - .unwrap(); - s.commit_block().unwrap(); - - // Advance to epoch 1 - current_epoch = advance_epoch(&mut s, ¶ms); - let self_bond_epoch = current_epoch; - - let validator = validators.first().unwrap(); - - // Read some data before submitting bond - let pipeline_epoch = current_epoch + params.pipeline_len; - let staking_token = staking_token_address(&s); - let pos_balance_pre = s - .read::(&token::balance_key( - &staking_token, - &super::ADDRESS, - )) - .unwrap() - .unwrap_or_default(); - let total_stake_before = - read_total_stake(&s, ¶ms, pipeline_epoch).unwrap(); - - // Self-bond - let amount_self_bond = token::Amount::from_uint(100_500_000, 0).unwrap(); - credit_tokens(&mut s, &staking_token, &validator.address, amount_self_bond) - .unwrap(); - bond_tokens( - &mut s, - None, - &validator.address, - amount_self_bond, - current_epoch, - None, - ) - .unwrap(); - - // Check the bond delta - let self_bond = bond_handle(&validator.address, &validator.address); - let delta = self_bond.get_delta_val(&s, pipeline_epoch).unwrap(); - assert_eq!(delta, Some(amount_self_bond)); - - // Check the validator in the validator set - let set = - read_consensus_validator_set_addresses_with_stake(&s, pipeline_epoch) - .unwrap(); - assert!(set.into_iter().any( - |WeightedValidator { - bonded_stake, - address, - }| { - address == validator.address - && bonded_stake == validator.tokens + amount_self_bond - } - )); - - let val_deltas = - read_validator_deltas_value(&s, &validator.address, &pipeline_epoch) - .unwrap(); - assert_eq!(val_deltas, Some(amount_self_bond.change())); - - let total_deltas_handle = total_deltas_handle(); - assert_eq!( - current_epoch, - total_deltas_handle.get_last_update(&s).unwrap().unwrap() - ); - let total_stake_after = - read_total_stake(&s, ¶ms, pipeline_epoch).unwrap(); - assert_eq!(total_stake_before + amount_self_bond, total_stake_after); - - // Check bond details after self-bond - let self_bond_id = BondId { - source: validator.address.clone(), - validator: validator.address.clone(), - }; - let check_bond_details = |ix, bond_details: BondsAndUnbondsDetails| { - println!("Check index {ix}"); - let details = bond_details.get(&self_bond_id).unwrap(); - assert_eq!( - details.bonds.len(), - 2, - "Contains genesis and newly added self-bond" - ); - dbg!(&details.bonds); - assert_eq!( - details.bonds[0], - BondDetails { - start: start_epoch, - amount: validator.tokens, - slashed_amount: None - }, - ); - assert_eq!( - details.bonds[1], - BondDetails { - start: pipeline_epoch, - amount: amount_self_bond, - slashed_amount: None - }, - ); - }; - // Try to call it with different combinations of owner/validator args - check_bond_details(0, bonds_and_unbonds(&s, None, None).unwrap()); - check_bond_details( - 1, - bonds_and_unbonds(&s, Some(validator.address.clone()), None).unwrap(), - ); - check_bond_details( - 2, - bonds_and_unbonds(&s, None, Some(validator.address.clone())).unwrap(), - ); - check_bond_details( - 3, - bonds_and_unbonds( - &s, - Some(validator.address.clone()), - Some(validator.address.clone()), - ) - .unwrap(), - ); - - // Get a non-validating account with tokens - let delegator = address::testing::gen_implicit_address(); - let amount_del = token::Amount::from_uint(201_000_000, 0).unwrap(); - credit_tokens(&mut s, &staking_token, &delegator, amount_del).unwrap(); - let balance_key = token::balance_key(&staking_token, &delegator); - let balance = s - .read::(&balance_key) - .unwrap() - .unwrap_or_default(); - assert_eq!(balance, amount_del); - - // Advance to epoch 3 - advance_epoch(&mut s, ¶ms); - current_epoch = advance_epoch(&mut s, ¶ms); - let pipeline_epoch = current_epoch + params.pipeline_len; - - // Delegation - let delegation_epoch = current_epoch; - bond_tokens( - &mut s, - Some(&delegator), - &validator.address, - amount_del, - current_epoch, - None, - ) - .unwrap(); - let val_stake_pre = read_validator_stake( - &s, - ¶ms, - &validator.address, - pipeline_epoch.prev(), - ) - .unwrap(); - let val_stake_post = - read_validator_stake(&s, ¶ms, &validator.address, pipeline_epoch) - .unwrap(); - assert_eq!(validator.tokens + amount_self_bond, val_stake_pre); - assert_eq!( - validator.tokens + amount_self_bond + amount_del, - val_stake_post - ); - let delegation = bond_handle(&delegator, &validator.address); - assert_eq!( - delegation - .get_sum(&s, pipeline_epoch.prev(), ¶ms) - .unwrap() - .unwrap_or_default(), - token::Amount::zero() - ); - assert_eq!( - delegation - .get_sum(&s, pipeline_epoch, ¶ms) - .unwrap() - .unwrap_or_default(), - amount_del - ); - - // Check delegation bonds details after delegation - let delegation_bond_id = BondId { - source: delegator.clone(), - validator: validator.address.clone(), - }; - let check_bond_details = |ix, bond_details: BondsAndUnbondsDetails| { - println!("Check index {ix}"); - assert_eq!(bond_details.len(), 1); - let details = bond_details.get(&delegation_bond_id).unwrap(); - assert_eq!(details.bonds.len(), 1,); - dbg!(&details.bonds); - assert_eq!( - details.bonds[0], - BondDetails { - start: pipeline_epoch, - amount: amount_del, - slashed_amount: None - }, - ); - }; - // Try to call it with different combinations of owner/validator args - check_bond_details( - 0, - bonds_and_unbonds(&s, Some(delegator.clone()), None).unwrap(), - ); - check_bond_details( - 1, - bonds_and_unbonds( - &s, - Some(delegator.clone()), - Some(validator.address.clone()), - ) - .unwrap(), - ); - - // Check all bond details (self-bonds and delegation) - let check_bond_details = |ix, bond_details: BondsAndUnbondsDetails| { - println!("Check index {ix}"); - let self_bond_details = bond_details.get(&self_bond_id).unwrap(); - let delegation_details = bond_details.get(&delegation_bond_id).unwrap(); - assert_eq!( - self_bond_details.bonds.len(), - 2, - "Contains genesis and newly added self-bond" - ); - assert_eq!( - self_bond_details.bonds[0], - BondDetails { - start: start_epoch, - amount: validator.tokens, - slashed_amount: None - }, - ); - assert_eq!(self_bond_details.bonds[1].amount, amount_self_bond); - assert_eq!( - delegation_details.bonds[0], - BondDetails { - start: pipeline_epoch, - amount: amount_del, - slashed_amount: None - }, - ); - }; - // Try to call it with different combinations of owner/validator args - check_bond_details(0, bonds_and_unbonds(&s, None, None).unwrap()); - check_bond_details( - 1, - bonds_and_unbonds(&s, None, Some(validator.address.clone())).unwrap(), - ); - - // Advance to epoch 5 - for _ in 0..2 { - current_epoch = advance_epoch(&mut s, ¶ms); - } - let pipeline_epoch = current_epoch + params.pipeline_len; - - // Unbond the self-bond with an amount that will remove all of the self-bond - // executed after genesis and some of the genesis bond - let amount_self_unbond: token::Amount = - amount_self_bond + (validator.tokens / 2); - // When the difference is 0, only the non-genesis self-bond is unbonded - let unbonded_genesis_self_bond = - amount_self_unbond - amount_self_bond != token::Amount::zero(); - dbg!( - amount_self_unbond, - amount_self_bond, - unbonded_genesis_self_bond - ); - let self_unbond_epoch = s.storage.block.epoch; - - unbond_tokens( - &mut s, - None, - &validator.address, - amount_self_unbond, - current_epoch, - false, - ) - .unwrap(); - - let val_stake_pre = read_validator_stake( - &s, - ¶ms, - &validator.address, - pipeline_epoch.prev(), - ) - .unwrap(); - - let val_stake_post = - read_validator_stake(&s, ¶ms, &validator.address, pipeline_epoch) - .unwrap(); - - let val_delta = - read_validator_deltas_value(&s, &validator.address, &pipeline_epoch) - .unwrap(); - let unbond = unbond_handle(&validator.address, &validator.address); - - assert_eq!(val_delta, Some(-amount_self_unbond.change())); - assert_eq!( - unbond - .at(&Epoch::default()) - .get( - &s, - &(pipeline_epoch - + params.unbonding_len - + params.cubic_slashing_window_length) - ) - .unwrap(), - if unbonded_genesis_self_bond { - Some(amount_self_unbond - amount_self_bond) - } else { - None - } - ); - assert_eq!( - unbond - .at(&(self_bond_epoch + params.pipeline_len)) - .get( - &s, - &(pipeline_epoch - + params.unbonding_len - + params.cubic_slashing_window_length) - ) - .unwrap(), - Some(amount_self_bond) - ); - assert_eq!( - val_stake_pre, - validator.tokens + amount_self_bond + amount_del - ); - assert_eq!( - val_stake_post, - validator.tokens + amount_self_bond + amount_del - amount_self_unbond - ); - - // Check all bond and unbond details (self-bonds and delegation) - let check_bond_details = |ix, bond_details: BondsAndUnbondsDetails| { - println!("Check index {ix}"); - dbg!(&bond_details); - assert_eq!(bond_details.len(), 2); - let self_bond_details = bond_details.get(&self_bond_id).unwrap(); - let delegation_details = bond_details.get(&delegation_bond_id).unwrap(); - assert_eq!( - self_bond_details.bonds.len(), - 1, - "Contains only part of the genesis bond now" - ); - assert_eq!( - self_bond_details.bonds[0], - BondDetails { - start: start_epoch, - amount: validator.tokens + amount_self_bond - - amount_self_unbond, - slashed_amount: None - }, - ); - assert_eq!( - delegation_details.bonds[0], - BondDetails { - start: delegation_epoch + params.pipeline_len, - amount: amount_del, - slashed_amount: None - }, - ); - assert_eq!( - self_bond_details.unbonds.len(), - if unbonded_genesis_self_bond { 2 } else { 1 }, - "Contains a full unbond of the last self-bond and an unbond from \ - the genesis bond" - ); - if unbonded_genesis_self_bond { - assert_eq!( - self_bond_details.unbonds[0], - UnbondDetails { - start: start_epoch, - withdraw: self_unbond_epoch - + params.pipeline_len - + params.unbonding_len - + params.cubic_slashing_window_length, - amount: amount_self_unbond - amount_self_bond, - slashed_amount: None - } - ); - } - assert_eq!( - self_bond_details.unbonds[usize::from(unbonded_genesis_self_bond)], - UnbondDetails { - start: self_bond_epoch + params.pipeline_len, - withdraw: self_unbond_epoch - + params.pipeline_len - + params.unbonding_len - + params.cubic_slashing_window_length, - amount: amount_self_bond, - slashed_amount: None - } - ); - }; - check_bond_details( - 0, - bonds_and_unbonds(&s, None, Some(validator.address.clone())).unwrap(), - ); - - // Unbond delegation - let amount_undel = token::Amount::from_uint(1_000_000, 0).unwrap(); - unbond_tokens( - &mut s, - Some(&delegator), - &validator.address, - amount_undel, - current_epoch, - false, - ) - .unwrap(); - - let val_stake_pre = read_validator_stake( - &s, - ¶ms, - &validator.address, - pipeline_epoch.prev(), - ) - .unwrap(); - let val_stake_post = - read_validator_stake(&s, ¶ms, &validator.address, pipeline_epoch) - .unwrap(); - let val_delta = - read_validator_deltas_value(&s, &validator.address, &pipeline_epoch) - .unwrap(); - let unbond = unbond_handle(&delegator, &validator.address); - - assert_eq!( - val_delta, - Some(-(amount_self_unbond + amount_undel).change()) - ); - assert_eq!( - unbond - .at(&(delegation_epoch + params.pipeline_len)) - .get( - &s, - &(pipeline_epoch - + params.unbonding_len - + params.cubic_slashing_window_length) - ) - .unwrap(), - Some(amount_undel) - ); - assert_eq!( - val_stake_pre, - validator.tokens + amount_self_bond + amount_del - ); - assert_eq!( - val_stake_post, - validator.tokens + amount_self_bond - amount_self_unbond + amount_del - - amount_undel - ); - - let withdrawable_offset = params.unbonding_len - + params.pipeline_len - + params.cubic_slashing_window_length; - - // Advance to withdrawable epoch - for _ in 0..withdrawable_offset { - current_epoch = advance_epoch(&mut s, ¶ms); - } - - dbg!(current_epoch); - - let pos_balance = s - .read::(&token::balance_key( - &staking_token, - &super::ADDRESS, - )) - .unwrap(); - - assert_eq!( - Some(pos_balance_pre + amount_self_bond + amount_del), - pos_balance - ); - - // Withdraw the self-unbond - withdraw_tokens(&mut s, None, &validator.address, current_epoch).unwrap(); - let unbond = unbond_handle(&validator.address, &validator.address); - let unbond_iter = unbond.iter(&s).unwrap().next(); - assert!(unbond_iter.is_none()); - - let pos_balance = s - .read::(&token::balance_key( - &staking_token, - &super::ADDRESS, - )) - .unwrap(); - assert_eq!( - Some( - pos_balance_pre + amount_self_bond - amount_self_unbond - + amount_del - ), - pos_balance - ); - - // Withdraw the delegation unbond - withdraw_tokens( - &mut s, - Some(&delegator), - &validator.address, - current_epoch, - ) - .unwrap(); - let unbond = unbond_handle(&delegator, &validator.address); - let unbond_iter = unbond.iter(&s).unwrap().next(); - assert!(unbond_iter.is_none()); - - let pos_balance = s - .read::(&token::balance_key( - &staking_token, - &super::ADDRESS, - )) - .unwrap(); - assert_eq!( - Some( - pos_balance_pre + amount_self_bond - amount_self_unbond - + amount_del - - amount_undel - ), - pos_balance - ); -} - -/// Test validator initialization. -fn test_become_validator_aux( - params: OwnedPosParams, - new_validator: Address, - new_validator_consensus_key: SecretKey, - validators: Vec, -) { - println!( - "Test inputs: {params:?}, new validator: {new_validator}, genesis \ - validators: {validators:#?}" - ); - - let mut s = TestWlStorage::default(); - - // Genesis - let mut current_epoch = s.storage.block.epoch; - let params = test_init_genesis( - &mut s, - params, - validators.clone().into_iter(), - current_epoch, - ) - .unwrap(); - s.commit_block().unwrap(); - - // Advance to epoch 1 - current_epoch = advance_epoch(&mut s, ¶ms); - - let num_consensus_before = - get_num_consensus_validators(&s, current_epoch + params.pipeline_len) - .unwrap(); - let num_validators_over_thresh = validators - .iter() - .filter(|validator| { - validator.tokens >= params.validator_stake_threshold - }) - .count(); - - assert_eq!( - min( - num_validators_over_thresh as u64, - params.max_validator_slots - ), - num_consensus_before - ); - assert!(!is_validator(&s, &new_validator).unwrap()); - - // Credit the `new_validator` account - let staking_token = staking_token_address(&s); - let amount = token::Amount::from_uint(100_500_000, 0).unwrap(); - // Credit twice the amount as we're gonna bond it in delegation first, then - // self-bond - credit_tokens(&mut s, &staking_token, &new_validator, amount * 2).unwrap(); - - // Add a delegation from `new_validator` to `genesis_validator` - let genesis_validator = &validators.first().unwrap().address; - bond_tokens( - &mut s, - Some(&new_validator), - genesis_validator, - amount, - current_epoch, - None, - ) - .unwrap(); - - let consensus_key = new_validator_consensus_key.to_public(); - let protocol_sk = common_sk_from_simple_seed(0); - let protocol_key = protocol_sk.to_public(); - let eth_hot_key = key::common::PublicKey::Secp256k1( - key::testing::gen_keypair::().ref_to(), - ); - let eth_cold_key = key::common::PublicKey::Secp256k1( - key::testing::gen_keypair::().ref_to(), - ); - - // Try to become a validator - it should fail as there is a delegation - let result = become_validator( - &mut s, - BecomeValidator { - params: ¶ms, - address: &new_validator, - consensus_key: &consensus_key, - protocol_key: &protocol_key, - eth_cold_key: ð_cold_key, - eth_hot_key: ð_hot_key, - current_epoch, - commission_rate: Dec::new(5, 2).expect("Dec creation failed"), - max_commission_rate_change: Dec::new(5, 2) - .expect("Dec creation failed"), - metadata: Default::default(), - offset_opt: None, - }, - ); - assert!(result.is_err()); - assert!(!is_validator(&s, &new_validator).unwrap()); - - // Unbond the delegation - unbond_tokens( - &mut s, - Some(&new_validator), - genesis_validator, - amount, - current_epoch, - false, - ) - .unwrap(); - - // Try to become a validator account again - it should pass now - become_validator( - &mut s, - BecomeValidator { - params: ¶ms, - address: &new_validator, - consensus_key: &consensus_key, - protocol_key: &protocol_key, - eth_cold_key: ð_cold_key, - eth_hot_key: ð_hot_key, - current_epoch, - commission_rate: Dec::new(5, 2).expect("Dec creation failed"), - max_commission_rate_change: Dec::new(5, 2) - .expect("Dec creation failed"), - metadata: Default::default(), - offset_opt: None, - }, - ) - .unwrap(); - assert!(is_validator(&s, &new_validator).unwrap()); - - let num_consensus_after = - get_num_consensus_validators(&s, current_epoch + params.pipeline_len) - .unwrap(); - // The new validator is initialized with no stake and thus is in the - // below-threshold set - assert_eq!(num_consensus_before, num_consensus_after); - - // Advance to epoch 2 - current_epoch = advance_epoch(&mut s, ¶ms); - - // Self-bond to the new validator - bond_tokens(&mut s, None, &new_validator, amount, current_epoch, None) - .unwrap(); - - // Check the bond delta - let bond_handle = bond_handle(&new_validator, &new_validator); - let pipeline_epoch = current_epoch + params.pipeline_len; - let delta = bond_handle.get_delta_val(&s, pipeline_epoch).unwrap(); - assert_eq!(delta, Some(amount)); - - // Check the validator in the validator set - - // If the consensus validator slots are full and all the genesis validators - // have stake GTE the new validator's self-bond amount, the validator should - // be added to the below-capacity set, or the consensus otherwise - if params.max_validator_slots <= validators.len() as u64 - && validators - .iter() - .all(|validator| validator.tokens >= amount) - { - let set = read_below_capacity_validator_set_addresses_with_stake( - &s, - pipeline_epoch, - ) - .unwrap(); - assert!(set.into_iter().any( - |WeightedValidator { - bonded_stake, - address, - }| { - address == new_validator && bonded_stake == amount - } - )); - } else { - let set = read_consensus_validator_set_addresses_with_stake( - &s, - pipeline_epoch, - ) - .unwrap(); - assert!(set.into_iter().any( - |WeightedValidator { - bonded_stake, - address, - }| { - address == new_validator && bonded_stake == amount - } - )); - } - - // Advance to epoch 3 - current_epoch = advance_epoch(&mut s, ¶ms); - - // Unbond the self-bond - unbond_tokens(&mut s, None, &new_validator, amount, current_epoch, false) - .unwrap(); - - let withdrawable_offset = params.unbonding_len + params.pipeline_len; - - // Advance to withdrawable epoch - for _ in 0..withdrawable_offset { - current_epoch = advance_epoch(&mut s, ¶ms); - } - - // Withdraw the self-bond - withdraw_tokens(&mut s, None, &new_validator, current_epoch).unwrap(); -} - -fn test_slashes_with_unbonding_aux( - mut params: OwnedPosParams, - validators: Vec, - unbond_delay: u64, -) { - // This can be useful for debugging: - params.pipeline_len = 2; - params.unbonding_len = 4; - println!("\nTest inputs: {params:?}, genesis validators: {validators:#?}"); - let mut s = TestWlStorage::default(); - - // Find the validator with the least stake to avoid the cubic slash rate - // going to 100% - let validator = - itertools::Itertools::sorted_by_key(validators.iter(), |v| v.tokens) - .next() - .unwrap(); - let val_addr = &validator.address; - let val_tokens = validator.tokens; - println!( - "Validator that will misbehave addr {val_addr}, tokens {}", - val_tokens.to_string_native() - ); - - // Genesis - // let start_epoch = s.storage.block.epoch; - let mut current_epoch = s.storage.block.epoch; - let params = test_init_genesis( - &mut s, - params, - validators.clone().into_iter(), - current_epoch, - ) - .unwrap(); - s.commit_block().unwrap(); - - current_epoch = advance_epoch(&mut s, ¶ms); - super::process_slashes(&mut s, current_epoch).unwrap(); - - // Discover first slash - let slash_0_evidence_epoch = current_epoch; - // let slash_0_processing_epoch = - // slash_0_evidence_epoch + params.slash_processing_epoch_offset(); - let evidence_block_height = BlockHeight(0); // doesn't matter for slashing logic - let slash_0_type = SlashType::DuplicateVote; - slash( - &mut s, - ¶ms, - current_epoch, - slash_0_evidence_epoch, - evidence_block_height, - slash_0_type, - val_addr, - current_epoch.next(), - ) - .unwrap(); - - // Advance to an epoch in which we can unbond - let unfreeze_epoch = - slash_0_evidence_epoch + params.slash_processing_epoch_offset(); - while current_epoch < unfreeze_epoch { - current_epoch = advance_epoch(&mut s, ¶ms); - super::process_slashes(&mut s, current_epoch).unwrap(); - } - - // Advance more epochs randomly from the generated delay - for _ in 0..unbond_delay { - current_epoch = advance_epoch(&mut s, ¶ms); - } - - // Unbond half of the tokens - let unbond_amount = Dec::new(5, 1).unwrap() * val_tokens; - println!("Going to unbond {}", unbond_amount.to_string_native()); - let unbond_epoch = current_epoch; - unbond_tokens(&mut s, None, val_addr, unbond_amount, unbond_epoch, false) - .unwrap(); - - // Discover second slash - let slash_1_evidence_epoch = current_epoch; - // Ensure that both slashes happen before `unbond_epoch + pipeline` - let _slash_1_processing_epoch = - slash_1_evidence_epoch + params.slash_processing_epoch_offset(); - let evidence_block_height = BlockHeight(0); // doesn't matter for slashing logic - let slash_1_type = SlashType::DuplicateVote; - slash( - &mut s, - ¶ms, - current_epoch, - slash_1_evidence_epoch, - evidence_block_height, - slash_1_type, - val_addr, - current_epoch.next(), - ) - .unwrap(); - - // Advance to an epoch in which we can withdraw - let withdraw_epoch = unbond_epoch + params.withdrawable_epoch_offset(); - while current_epoch < withdraw_epoch { - current_epoch = advance_epoch(&mut s, ¶ms); - super::process_slashes(&mut s, current_epoch).unwrap(); - } - let token = staking_token_address(&s); - let val_balance_pre = read_balance(&s, &token, val_addr).unwrap(); - - let bond_id = BondId { - source: val_addr.clone(), - validator: val_addr.clone(), - }; - let binding = - super::bonds_and_unbonds(&s, None, Some(val_addr.clone())).unwrap(); - let details = binding.get(&bond_id).unwrap(); - let exp_withdraw_from_details = details.unbonds[0].amount - - details.unbonds[0].slashed_amount.unwrap_or_default(); - - withdraw_tokens(&mut s, None, val_addr, current_epoch).unwrap(); - - let val_balance_post = read_balance(&s, &token, val_addr).unwrap(); - let withdrawn_tokens = val_balance_post - val_balance_pre; - println!("Withdrew {} tokens", withdrawn_tokens.to_string_native()); - - assert_eq!(exp_withdraw_from_details, withdrawn_tokens); - - let slash_rate_0 = validator_slashes_handle(val_addr) - .get(&s, 0) - .unwrap() - .unwrap() - .rate; - let slash_rate_1 = validator_slashes_handle(val_addr) - .get(&s, 1) - .unwrap() - .unwrap() - .rate; - println!("Slash 0 rate {slash_rate_0}, slash 1 rate {slash_rate_1}"); - - let expected_withdrawn_amount = Dec::from( - (Dec::one() - slash_rate_1) - * (Dec::one() - slash_rate_0) - * unbond_amount, - ); - // Allow some rounding error, 1 NAMNAM per each slash - let rounding_error_tolerance = - Dec::new(2, NATIVE_MAX_DECIMAL_PLACES).unwrap(); - assert!( - dbg!(expected_withdrawn_amount.abs_diff(&Dec::from(withdrawn_tokens))) - <= rounding_error_tolerance - ); - - // TODO: finish once implemented - // let slash_0 = decimal_mult_amount(slash_rate_0, val_tokens); - // let slash_1 = decimal_mult_amount(slash_rate_1, val_tokens - slash_0); - // let expected_slash_pool = slash_0 + slash_1; - // let slash_pool_balance = - // read_balance(&s, &token, &SLASH_POOL_ADDRESS).unwrap(); - // assert_eq!(expected_slash_pool, slash_pool_balance); -} - -#[test] -fn test_validator_raw_hash() { - let mut storage = TestWlStorage::default(); - let address = address::testing::established_address_1(); - let consensus_sk = key::testing::keypair_1(); - let consensus_pk = consensus_sk.to_public(); - let expected_raw_hash = key::tm_consensus_key_raw_hash(&consensus_pk); - - assert!( - find_validator_by_raw_hash(&storage, &expected_raw_hash) - .unwrap() - .is_none() - ); - write_validator_address_raw_hash(&mut storage, &address, &consensus_pk) - .unwrap(); - let found = - find_validator_by_raw_hash(&storage, &expected_raw_hash).unwrap(); - assert_eq!(found, Some(address)); -} - -#[test] -fn test_validator_sets() { - let mut s = TestWlStorage::default(); - // Only 3 consensus validator slots - let params = OwnedPosParams { - max_validator_slots: 3, - ..Default::default() - }; - let addr_seed = "seed"; - let mut address_gen = EstablishedAddressGen::new(addr_seed); - let mut sk_seed = 0; - let mut gen_validator = || { - let res = ( - address_gen.generate_address(addr_seed), - key::testing::common_sk_from_simple_seed(sk_seed).to_public(), - ); - // bump the sk seed - sk_seed += 1; - res - }; - - // Create genesis validators - let ((val1, pk1), stake1) = - (gen_validator(), token::Amount::native_whole(1)); - let ((val2, pk2), stake2) = - (gen_validator(), token::Amount::native_whole(1)); - let ((val3, pk3), stake3) = - (gen_validator(), token::Amount::native_whole(10)); - let ((val4, pk4), stake4) = - (gen_validator(), token::Amount::native_whole(1)); - let ((val5, pk5), stake5) = - (gen_validator(), token::Amount::native_whole(100)); - let ((val6, pk6), stake6) = - (gen_validator(), token::Amount::native_whole(1)); - let ((val7, pk7), stake7) = - (gen_validator(), token::Amount::native_whole(1)); - println!("\nval1: {val1}, {pk1}, {}", stake1.to_string_native()); - println!("val2: {val2}, {pk2}, {}", stake2.to_string_native()); - println!("val3: {val3}, {pk3}, {}", stake3.to_string_native()); - println!("val4: {val4}, {pk4}, {}", stake4.to_string_native()); - println!("val5: {val5}, {pk5}, {}", stake5.to_string_native()); - println!("val6: {val6}, {pk6}, {}", stake6.to_string_native()); - println!("val7: {val7}, {pk7}, {}", stake7.to_string_native()); - - let start_epoch = Epoch::default(); - let epoch = start_epoch; - - let protocol_sk_1 = common_sk_from_simple_seed(0); - let protocol_sk_2 = common_sk_from_simple_seed(1); - - let params = test_init_genesis( - &mut s, - params, - [ - GenesisValidator { - address: val1.clone(), - tokens: stake1, - consensus_key: pk1.clone(), - protocol_key: protocol_sk_1.to_public(), - eth_hot_key: key::common::PublicKey::Secp256k1( - key::testing::gen_keypair::() - .ref_to(), - ), - eth_cold_key: key::common::PublicKey::Secp256k1( - key::testing::gen_keypair::() - .ref_to(), - ), - commission_rate: Dec::new(1, 1).expect("Dec creation failed"), - max_commission_rate_change: Dec::new(1, 1) - .expect("Dec creation failed"), - metadata: Default::default(), - }, - GenesisValidator { - address: val2.clone(), - tokens: stake2, - consensus_key: pk2.clone(), - protocol_key: protocol_sk_2.to_public(), - eth_hot_key: key::common::PublicKey::Secp256k1( - key::testing::gen_keypair::() - .ref_to(), - ), - eth_cold_key: key::common::PublicKey::Secp256k1( - key::testing::gen_keypair::() - .ref_to(), - ), - commission_rate: Dec::new(1, 1).expect("Dec creation failed"), - max_commission_rate_change: Dec::new(1, 1) - .expect("Dec creation failed"), - metadata: Default::default(), - }, - ] - .into_iter(), - epoch, - ) - .unwrap(); - - // A helper to insert a non-genesis validator - let insert_validator = |s: &mut TestWlStorage, - addr, - pk: &PublicKey, - stake: token::Amount, - epoch: Epoch| { - insert_validator_into_validator_set( - s, - ¶ms, - addr, - stake, - epoch, - params.pipeline_len, - ) - .unwrap(); - - update_validator_deltas(s, ¶ms, addr, stake.change(), epoch, None) - .unwrap(); - - // Set their consensus key (needed for - // `validator_set_update_tendermint` fn) - validator_consensus_key_handle(addr) - .set(s, pk.clone(), epoch, params.pipeline_len) - .unwrap(); - }; - - // Advance to EPOCH 1 - // - // We cannot call `get_tendermint_set_updates` for the genesis state as - // `validator_set_update_tendermint` is only called 2 blocks before the - // start of an epoch and so we need to give it a predecessor epoch (see - // `get_tendermint_set_updates`), which we cannot have on the first - // epoch. In any way, the initial validator set is given to Tendermint - // from InitChain, so `validator_set_update_tendermint` is - // not being used for it. - let epoch = advance_epoch(&mut s, ¶ms); - let pipeline_epoch = epoch + params.pipeline_len; - - // Insert another validator with the greater stake 10 NAM - insert_validator(&mut s, &val3, &pk3, stake3, epoch); - // Insert validator with stake 1 NAM - insert_validator(&mut s, &val4, &pk4, stake4, epoch); - - // Validator `val3` and `val4` will be added at pipeline offset (2) - epoch - // 3 - let val3_and_4_epoch = pipeline_epoch; - - let consensus_vals: Vec<_> = consensus_validator_set_handle() - .at(&pipeline_epoch) - .iter(&s) - .unwrap() - .map(Result::unwrap) - .collect(); - - assert_eq!(consensus_vals.len(), 3); - assert!(matches!( - &consensus_vals[0], - (lazy_map::NestedSubKey::Data { - key: stake, - nested_sub_key: lazy_map::SubKey::Data(position), - }, address) - if address == &val1 && stake == &stake1 && *position == Position(0) - )); - assert!(matches!( - &consensus_vals[1], - (lazy_map::NestedSubKey::Data { - key: stake, - nested_sub_key: lazy_map::SubKey::Data(position), - }, address) - if address == &val2 && stake == &stake2 && *position == Position(1) - )); - assert!(matches!( - &consensus_vals[2], - (lazy_map::NestedSubKey::Data { - key: stake, - nested_sub_key: lazy_map::SubKey::Data(position), - }, address) - if address == &val3 && stake == &stake3 && *position == Position(0) - )); - - // Check tendermint validator set updates - there should be none - let tm_updates = get_tendermint_set_updates(&s, ¶ms, epoch); - assert!(tm_updates.is_empty()); - - // Advance to EPOCH 2 - let epoch = advance_epoch(&mut s, ¶ms); - let pipeline_epoch = epoch + params.pipeline_len; - - // Insert another validator with a greater stake still 1000 NAM. It should - // replace 2nd consensus validator with stake 1, which should become - // below-capacity - insert_validator(&mut s, &val5, &pk5, stake5, epoch); - // Validator `val5` will be added at pipeline offset (2) - epoch 4 - let val5_epoch = pipeline_epoch; - - let consensus_vals: Vec<_> = consensus_validator_set_handle() - .at(&pipeline_epoch) - .iter(&s) - .unwrap() - .map(Result::unwrap) - .collect(); - - assert_eq!(consensus_vals.len(), 3); - assert!(matches!( - &consensus_vals[0], - (lazy_map::NestedSubKey::Data { - key: stake, - nested_sub_key: lazy_map::SubKey::Data(position), - }, address) - if address == &val1 && stake == &stake1 && *position == Position(0) - )); - assert!(matches!( - &consensus_vals[1], - (lazy_map::NestedSubKey::Data { - key: stake, - nested_sub_key: lazy_map::SubKey::Data(position), - }, address) - if address == &val3 && stake == &stake3 && *position == Position(0) - )); - assert!(matches!( - &consensus_vals[2], - (lazy_map::NestedSubKey::Data { - key: stake, - nested_sub_key: lazy_map::SubKey::Data(position), - }, address) - if address == &val5 && stake == &stake5 && *position == Position(0) - )); - - let below_capacity_vals: Vec<_> = below_capacity_validator_set_handle() - .at(&pipeline_epoch) - .iter(&s) - .unwrap() - .map(Result::unwrap) - .collect(); - - assert_eq!(below_capacity_vals.len(), 2); - assert!(matches!( - &below_capacity_vals[0], - (lazy_map::NestedSubKey::Data { - key: ReverseOrdTokenAmount(stake), - nested_sub_key: lazy_map::SubKey::Data(position), - }, address) - if address == &val4 && stake == &stake4 && *position == Position(0) - )); - assert!(matches!( - &below_capacity_vals[1], - (lazy_map::NestedSubKey::Data { - key: ReverseOrdTokenAmount(stake), - nested_sub_key: lazy_map::SubKey::Data(position), - }, address) - if address == &val2 && stake == &stake2 && *position == Position(1) - )); - - // Advance to EPOCH 3 - let epoch = advance_epoch(&mut s, ¶ms); - let pipeline_epoch = epoch + params.pipeline_len; - - // Check tendermint validator set updates - assert_eq!( - val3_and_4_epoch, epoch, - "val3 and val4 are in the validator sets now" - ); - let tm_updates = get_tendermint_set_updates(&s, ¶ms, epoch); - // `val4` is newly added below-capacity, must be skipped in updated in TM - assert_eq!(tm_updates.len(), 1); - assert_eq!( - tm_updates[0], - ValidatorSetUpdate::Consensus(ConsensusValidator { - consensus_key: pk3, - bonded_stake: stake3, - }) - ); - - // Insert another validator with a stake 1 NAM. It should be added to the - // below-capacity set - insert_validator(&mut s, &val6, &pk6, stake6, epoch); - // Validator `val6` will be added at pipeline offset (2) - epoch 5 - let val6_epoch = pipeline_epoch; - - let below_capacity_vals: Vec<_> = below_capacity_validator_set_handle() - .at(&pipeline_epoch) - .iter(&s) - .unwrap() - .map(Result::unwrap) - .collect(); - - assert_eq!(below_capacity_vals.len(), 3); - assert!(matches!( - &below_capacity_vals[0], - (lazy_map::NestedSubKey::Data { - key: ReverseOrdTokenAmount(stake), - nested_sub_key: lazy_map::SubKey::Data(position), - }, address) - if address == &val4 && stake == &stake4 && *position == Position(0) - )); - assert!(matches!( - &below_capacity_vals[1], - (lazy_map::NestedSubKey::Data { - key: ReverseOrdTokenAmount(stake), - nested_sub_key: lazy_map::SubKey::Data(position), - }, address) - if address == &val2 && stake == &stake2 && *position == Position(1) - )); - assert!(matches!( - &below_capacity_vals[2], - (lazy_map::NestedSubKey::Data { - key: ReverseOrdTokenAmount(stake), - nested_sub_key: lazy_map::SubKey::Data(position), - }, address) - if address == &val6 && stake == &stake6 && *position == Position(2) - )); - - // Advance to EPOCH 4 - let epoch = advance_epoch(&mut s, ¶ms); - let pipeline_epoch = epoch + params.pipeline_len; - - // Check tendermint validator set updates - assert_eq!(val5_epoch, epoch, "val5 is in the validator sets now"); - let tm_updates = get_tendermint_set_updates(&s, ¶ms, epoch); - assert_eq!(tm_updates.len(), 2); - assert_eq!( - tm_updates[0], - ValidatorSetUpdate::Consensus(ConsensusValidator { - consensus_key: pk5, - bonded_stake: stake5, - }) - ); - assert_eq!(tm_updates[1], ValidatorSetUpdate::Deactivated(pk2)); - - // Unbond some stake from val1, it should be be swapped with the greatest - // below-capacity validator val2 into the below-capacity set. The stake of - // val1 will go below 1 NAM, which is the validator_stake_threshold, so it - // will enter the below-threshold validator set. - let unbond = token::Amount::from_uint(500_000, 0).unwrap(); - let stake1 = stake1 - unbond; - println!("val1 {val1} new stake {}", stake1.to_string_native()); - // Because `update_validator_set` and `update_validator_deltas` are - // effective from pipeline offset, we use pipeline epoch for the rest of the - // checks - update_validator_set(&mut s, ¶ms, &val1, -unbond.change(), epoch, None) - .unwrap(); - update_validator_deltas( - &mut s, - ¶ms, - &val1, - -unbond.change(), - epoch, - None, - ) - .unwrap(); - // Epoch 6 - let val1_unbond_epoch = pipeline_epoch; - - let consensus_vals: Vec<_> = consensus_validator_set_handle() - .at(&pipeline_epoch) - .iter(&s) - .unwrap() - .map(Result::unwrap) - .collect(); - - assert_eq!(consensus_vals.len(), 3); - assert!(matches!( - &consensus_vals[0], - (lazy_map::NestedSubKey::Data { - key: stake, - nested_sub_key: lazy_map::SubKey::Data(position), - }, address) - if address == &val4 && stake == &stake4 && *position == Position(0) - )); - assert!(matches!( - &consensus_vals[1], - (lazy_map::NestedSubKey::Data { - key: stake, - nested_sub_key: lazy_map::SubKey::Data(position), - }, address) - if address == &val3 && stake == &stake3 && *position == Position(0) - )); - assert!(matches!( - &consensus_vals[2], - (lazy_map::NestedSubKey::Data { - key: stake, - nested_sub_key: lazy_map::SubKey::Data(position), - }, address) - if address == &val5 && stake == &stake5 && *position == Position(0) - )); - - let below_capacity_vals: Vec<_> = below_capacity_validator_set_handle() - .at(&pipeline_epoch) - .iter(&s) - .unwrap() - .map(Result::unwrap) - .collect(); - - assert_eq!(below_capacity_vals.len(), 2); - assert!(matches!( - &below_capacity_vals[0], - (lazy_map::NestedSubKey::Data { - key: ReverseOrdTokenAmount(stake), - nested_sub_key: lazy_map::SubKey::Data(position), - }, address) - if address == &val2 && stake == &stake2 && *position == Position(1) - )); - assert!(matches!( - &below_capacity_vals[1], - (lazy_map::NestedSubKey::Data { - key: ReverseOrdTokenAmount(stake), - nested_sub_key: lazy_map::SubKey::Data(position), - }, address) - if address == &val6 && stake == &stake6 && *position == Position(2) - )); - - let below_threshold_vals = - read_below_threshold_validator_set_addresses(&s, pipeline_epoch) - .unwrap() - .into_iter() - .collect::>(); - - assert_eq!(below_threshold_vals.len(), 1); - assert_eq!(&below_threshold_vals[0], &val1); - - // Advance to EPOCH 5 - let epoch = advance_epoch(&mut s, ¶ms); - let pipeline_epoch = epoch + params.pipeline_len; - - // Check tendermint validator set updates - assert_eq!(val6_epoch, epoch, "val6 is in the validator sets now"); - let tm_updates = get_tendermint_set_updates(&s, ¶ms, epoch); - assert!(tm_updates.is_empty()); - - // Insert another validator with stake 1 - it should be added to below - // capacity set - insert_validator(&mut s, &val7, &pk7, stake7, epoch); - // Epoch 7 - let val7_epoch = pipeline_epoch; - - let consensus_vals: Vec<_> = consensus_validator_set_handle() - .at(&pipeline_epoch) - .iter(&s) - .unwrap() - .map(Result::unwrap) - .collect(); - - assert_eq!(consensus_vals.len(), 3); - assert!(matches!( - &consensus_vals[0], - (lazy_map::NestedSubKey::Data { - key: stake, - nested_sub_key: lazy_map::SubKey::Data(position), - }, address) - if address == &val4 && stake == &stake4 && *position == Position(0) - )); - assert!(matches!( - &consensus_vals[1], - (lazy_map::NestedSubKey::Data { - key: stake, - nested_sub_key: lazy_map::SubKey::Data(position), - }, address) - if address == &val3 && stake == &stake3 && *position == Position(0) - )); - assert!(matches!( - &consensus_vals[2], - (lazy_map::NestedSubKey::Data { - key: stake, - nested_sub_key: lazy_map::SubKey::Data(position), - }, address) - if address == &val5 && stake == &stake5 && *position == Position(0) - )); - - let below_capacity_vals: Vec<_> = below_capacity_validator_set_handle() - .at(&pipeline_epoch) - .iter(&s) - .unwrap() - .map(Result::unwrap) - .collect(); - - assert_eq!(below_capacity_vals.len(), 3); - assert!(matches!( - &below_capacity_vals[0], - (lazy_map::NestedSubKey::Data { - key: ReverseOrdTokenAmount(stake), - nested_sub_key: lazy_map::SubKey::Data(position), - }, address) - if address == &val2 && stake == &stake2 && *position == Position(1) - )); - assert!(matches!( - &below_capacity_vals[1], - (lazy_map::NestedSubKey::Data { - key: ReverseOrdTokenAmount(stake), - nested_sub_key: lazy_map::SubKey::Data(position), - }, address) - if address == &val6 && stake == &stake6 && *position == Position(2) - )); - assert!(matches!( - &below_capacity_vals[2], - ( - lazy_map::NestedSubKey::Data { - key: ReverseOrdTokenAmount(stake), - nested_sub_key: lazy_map::SubKey::Data(position), - }, - address - ) - if address == &val7 && stake == &stake7 && *position == Position(3) - )); - - let below_threshold_vals = - read_below_threshold_validator_set_addresses(&s, pipeline_epoch) - .unwrap() - .into_iter() - .collect::>(); - - assert_eq!(below_threshold_vals.len(), 1); - assert_eq!(&below_threshold_vals[0], &val1); - - // Advance to EPOCH 6 - let epoch = advance_epoch(&mut s, ¶ms); - let pipeline_epoch = epoch + params.pipeline_len; - - // Check tendermint validator set updates - assert_eq!(val1_unbond_epoch, epoch, "val1's unbond is applied now"); - let tm_updates = get_tendermint_set_updates(&s, ¶ms, epoch); - assert_eq!(tm_updates.len(), 2); - assert_eq!( - tm_updates[0], - ValidatorSetUpdate::Consensus(ConsensusValidator { - consensus_key: pk4.clone(), - bonded_stake: stake4, - }) - ); - assert_eq!(tm_updates[1], ValidatorSetUpdate::Deactivated(pk1)); - - // Bond some stake to val6, it should be be swapped with the lowest - // consensus validator val2 into the consensus set - let bond = token::Amount::from_uint(500_000, 0).unwrap(); - let stake6 = stake6 + bond; - println!("val6 {val6} new stake {}", stake6.to_string_native()); - update_validator_set(&mut s, ¶ms, &val6, bond.change(), epoch, None) - .unwrap(); - update_validator_deltas(&mut s, ¶ms, &val6, bond.change(), epoch, None) - .unwrap(); - let val6_bond_epoch = pipeline_epoch; - - let consensus_vals: Vec<_> = consensus_validator_set_handle() - .at(&pipeline_epoch) - .iter(&s) - .unwrap() - .map(Result::unwrap) - .collect(); - - assert_eq!(consensus_vals.len(), 3); - assert!(matches!( - &consensus_vals[0], - (lazy_map::NestedSubKey::Data { - key: stake, - nested_sub_key: lazy_map::SubKey::Data(position), - }, address) - if address == &val6 && stake == &stake6 && *position == Position(0) - )); - assert!(matches!( - &consensus_vals[1], - (lazy_map::NestedSubKey::Data { - key: stake, - nested_sub_key: lazy_map::SubKey::Data(position), - }, address) - if address == &val3 && stake == &stake3 && *position == Position(0) - )); - assert!(matches!( - &consensus_vals[2], - (lazy_map::NestedSubKey::Data { - key: stake, - nested_sub_key: lazy_map::SubKey::Data(position), - }, address) - if address == &val5 && stake == &stake5 && *position == Position(0) - )); - - let below_capacity_vals: Vec<_> = below_capacity_validator_set_handle() - .at(&pipeline_epoch) - .iter(&s) - .unwrap() - .map(Result::unwrap) - .collect(); - - assert_eq!(below_capacity_vals.len(), 3); - dbg!(&below_capacity_vals); - assert!(matches!( - &below_capacity_vals[0], - (lazy_map::NestedSubKey::Data { - key: ReverseOrdTokenAmount(stake), - nested_sub_key: lazy_map::SubKey::Data(position), - }, address) - if address == &val2 && stake == &stake2 && *position == Position(1) - )); - assert!(matches!( - &below_capacity_vals[1], - (lazy_map::NestedSubKey::Data { - key: ReverseOrdTokenAmount(stake), - nested_sub_key: lazy_map::SubKey::Data(position), - }, address) - if address == &val7 && stake == &stake7 && *position == Position(3) - )); - assert!(matches!( - &below_capacity_vals[2], - ( - lazy_map::NestedSubKey::Data { - key: ReverseOrdTokenAmount(stake), - nested_sub_key: lazy_map::SubKey::Data(position), - }, - address - ) - if address == &val4 && stake == &stake4 && *position == Position(4) - )); - - let below_threshold_vals = - read_below_threshold_validator_set_addresses(&s, pipeline_epoch) - .unwrap() - .into_iter() - .collect::>(); - - assert_eq!(below_threshold_vals.len(), 1); - assert_eq!(&below_threshold_vals[0], &val1); - - // Advance to EPOCH 7 - let epoch = advance_epoch(&mut s, ¶ms); - assert_eq!(val7_epoch, epoch, "val6 is in the validator sets now"); - - // Check tendermint validator set updates - let tm_updates = get_tendermint_set_updates(&s, ¶ms, epoch); - assert!(tm_updates.is_empty()); - - // Advance to EPOCH 8 - let epoch = advance_epoch(&mut s, ¶ms); - - // Check tendermint validator set updates - assert_eq!(val6_bond_epoch, epoch, "val5's bond is applied now"); - let tm_updates = get_tendermint_set_updates(&s, ¶ms, epoch); - dbg!(&tm_updates); - assert_eq!(tm_updates.len(), 2); - assert_eq!( - tm_updates[0], - ValidatorSetUpdate::Consensus(ConsensusValidator { - consensus_key: pk6, - bonded_stake: stake6, - }) - ); - assert_eq!(tm_updates[1], ValidatorSetUpdate::Deactivated(pk4)); - - // Check that the below-capacity validator set was purged for the old epochs - // but that the consensus_validator_set was not - let last_epoch = epoch; - for e in Epoch::iter_bounds_inclusive( - start_epoch, - last_epoch - .sub_or_default(Epoch(DEFAULT_NUM_PAST_EPOCHS)) - .sub_or_default(Epoch(1)), - ) { - assert!( - !consensus_validator_set_handle() - .at(&e) - .is_empty(&s) - .unwrap() - ); - assert!( - below_capacity_validator_set_handle() - .at(&e) - .is_empty(&s) - .unwrap() - ); - } -} - -/// When a consensus set validator with 0 voting power adds a bond in the same -/// epoch as another below-capacity set validator with 0 power, but who adds -/// more bonds than the validator who is in the consensus set, they get swapped -/// in the sets. But if both of their new voting powers are still 0 after -/// bonding, the newly below-capacity validator must not be given to tendermint -/// with 0 voting power, because it wasn't it its set before -#[test] -fn test_validator_sets_swap() { - let mut s = TestWlStorage::default(); - // Only 2 consensus validator slots - let params = OwnedPosParams { - max_validator_slots: 2, - // Set the stake threshold to 0 so no validators are in the - // below-threshold set - validator_stake_threshold: token::Amount::zero(), - // Set 0.1 votes per token - tm_votes_per_token: Dec::new(1, 1).expect("Dec creation failed"), - ..Default::default() - }; - - let addr_seed = "seed"; - let mut address_gen = EstablishedAddressGen::new(addr_seed); - let mut sk_seed = 0; - let mut gen_validator = || { - let res = ( - address_gen.generate_address(addr_seed), - key::testing::common_sk_from_simple_seed(sk_seed).to_public(), - ); - // bump the sk seed - sk_seed += 1; - res - }; - - // Start with two genesis validators, one with 1 voting power and other 0 - let epoch = Epoch::default(); - // 1M voting power - let ((val1, pk1), stake1) = - (gen_validator(), token::Amount::native_whole(10)); - // 0 voting power - let ((val2, pk2), stake2) = - (gen_validator(), token::Amount::from_uint(5, 0).unwrap()); - // 0 voting power - let ((val3, pk3), stake3) = - (gen_validator(), token::Amount::from_uint(5, 0).unwrap()); - println!("val1: {val1}, {pk1}, {}", stake1.to_string_native()); - println!("val2: {val2}, {pk2}, {}", stake2.to_string_native()); - println!("val3: {val3}, {pk3}, {}", stake3.to_string_native()); - - let protocol_sk_1 = common_sk_from_simple_seed(0); - let protocol_sk_2 = common_sk_from_simple_seed(1); - - let params = test_init_genesis( - &mut s, - params, - [ - GenesisValidator { - address: val1, - tokens: stake1, - consensus_key: pk1, - protocol_key: protocol_sk_1.to_public(), - eth_hot_key: key::common::PublicKey::Secp256k1( - key::testing::gen_keypair::() - .ref_to(), - ), - eth_cold_key: key::common::PublicKey::Secp256k1( - key::testing::gen_keypair::() - .ref_to(), - ), - commission_rate: Dec::new(1, 1).expect("Dec creation failed"), - max_commission_rate_change: Dec::new(1, 1) - .expect("Dec creation failed"), - metadata: Default::default(), - }, - GenesisValidator { - address: val2.clone(), - tokens: stake2, - consensus_key: pk2, - protocol_key: protocol_sk_2.to_public(), - eth_hot_key: key::common::PublicKey::Secp256k1( - key::testing::gen_keypair::() - .ref_to(), - ), - eth_cold_key: key::common::PublicKey::Secp256k1( - key::testing::gen_keypair::() - .ref_to(), - ), - commission_rate: Dec::new(1, 1).expect("Dec creation failed"), - max_commission_rate_change: Dec::new(1, 1) - .expect("Dec creation failed"), - metadata: Default::default(), - }, - ] - .into_iter(), - epoch, - ) - .unwrap(); - - // A helper to insert a non-genesis validator - let insert_validator = |s: &mut TestWlStorage, - addr, - pk: &PublicKey, - stake: token::Amount, - epoch: Epoch| { - insert_validator_into_validator_set( - s, - ¶ms, - addr, - stake, - epoch, - params.pipeline_len, - ) - .unwrap(); - - update_validator_deltas(s, ¶ms, addr, stake.change(), epoch, None) - .unwrap(); - - // Set their consensus key (needed for - // `validator_set_update_tendermint` fn) - validator_consensus_key_handle(addr) - .set(s, pk.clone(), epoch, params.pipeline_len) - .unwrap(); - }; - - // Advance to EPOCH 1 - let epoch = advance_epoch(&mut s, ¶ms); - let pipeline_epoch = epoch + params.pipeline_len; - - // Insert another validator with 0 voting power - insert_validator(&mut s, &val3, &pk3, stake3, epoch); - - assert_eq!(stake2, stake3); - - // Add 2 bonds, one for val2 and greater one for val3 - let bonds_epoch_1 = pipeline_epoch; - let bond2 = token::Amount::from_uint(1, 0).unwrap(); - let stake2 = stake2 + bond2; - let bond3 = token::Amount::from_uint(4, 0).unwrap(); - let stake3 = stake3 + bond3; - - assert!(stake2 < stake3); - assert_eq!(into_tm_voting_power(params.tm_votes_per_token, stake2), 0); - assert_eq!(into_tm_voting_power(params.tm_votes_per_token, stake3), 0); - - update_validator_set(&mut s, ¶ms, &val2, bond2.change(), epoch, None) - .unwrap(); - update_validator_deltas( - &mut s, - ¶ms, - &val2, - bond2.change(), - epoch, - None, - ) - .unwrap(); - - update_validator_set(&mut s, ¶ms, &val3, bond3.change(), epoch, None) - .unwrap(); - update_validator_deltas( - &mut s, - ¶ms, - &val3, - bond3.change(), - epoch, - None, - ) - .unwrap(); - - // Advance to EPOCH 2 - let epoch = advance_epoch(&mut s, ¶ms); - let pipeline_epoch = epoch + params.pipeline_len; - - // Add 2 more bonds, same amount for `val2` and val3` - let bonds_epoch_2 = pipeline_epoch; - let bonds = token::Amount::native_whole(1); - let stake2 = stake2 + bonds; - let stake3 = stake3 + bonds; - assert!(stake2 < stake3); - assert_eq!( - into_tm_voting_power(params.tm_votes_per_token, stake2), - into_tm_voting_power(params.tm_votes_per_token, stake3) - ); - - update_validator_set(&mut s, ¶ms, &val2, bonds.change(), epoch, None) - .unwrap(); - update_validator_deltas( - &mut s, - ¶ms, - &val2, - bonds.change(), - epoch, - None, - ) - .unwrap(); - - update_validator_set(&mut s, ¶ms, &val3, bonds.change(), epoch, None) - .unwrap(); - update_validator_deltas( - &mut s, - ¶ms, - &val3, - bonds.change(), - epoch, - None, - ) - .unwrap(); - - // Advance to EPOCH 3 - let epoch = advance_epoch(&mut s, ¶ms); - - // Check tendermint validator set updates - assert_eq!(bonds_epoch_1, epoch); - let tm_updates = get_tendermint_set_updates(&s, ¶ms, epoch); - // `val2` must not be given to tendermint - even though it was in the - // consensus set, its voting power was 0, so it wasn't in TM set before the - // bond - assert!(tm_updates.is_empty()); - - // Advance to EPOCH 4 - let epoch = advance_epoch(&mut s, ¶ms); - - // Check tendermint validator set updates - assert_eq!(bonds_epoch_2, epoch); - let tm_updates = get_tendermint_set_updates(&s, ¶ms, epoch); - dbg!(&tm_updates); - assert_eq!(tm_updates.len(), 1); - // `val2` must not be given to tendermint as it was and still is below - // capacity - assert_eq!( - tm_updates[0], - ValidatorSetUpdate::Consensus(ConsensusValidator { - consensus_key: pk3, - bonded_stake: stake3, - }) - ); -} - -fn get_tendermint_set_updates( - s: &TestWlStorage, - params: &PosParams, - Epoch(epoch): Epoch, -) -> Vec { - // Because the `validator_set_update_tendermint` is called 2 blocks before - // the start of a new epoch, it expects to receive the epoch that is before - // the start of a new one too and so we give it the predecessor of the - // current epoch here to actually get the update for the current epoch. - let epoch = Epoch(epoch - 1); - validator_set_update_tendermint(s, params, epoch, |update| update).unwrap() -} - -/// Advance to the next epoch. Returns the new epoch. -fn advance_epoch(s: &mut TestWlStorage, params: &PosParams) -> Epoch { - s.storage.block.epoch = s.storage.block.epoch.next(); - let current_epoch = s.storage.block.epoch; - compute_and_store_total_consensus_stake(s, current_epoch).unwrap(); - copy_validator_sets_and_positions( - s, - params, - current_epoch, - current_epoch + params.pipeline_len, - ) - .unwrap(); - // purge_validator_sets_for_old_epoch(s, current_epoch).unwrap(); - // process_slashes(s, current_epoch).unwrap(); - // dbg!(current_epoch); - current_epoch -} - -fn arb_genesis_validators( - size: Range, - threshold: Option, -) -> impl Strategy> { - let threshold = threshold - .unwrap_or_else(|| PosParams::default().validator_stake_threshold); - let tokens: Vec<_> = (0..size.end) - .map(|ix| { - if ix == 0 { - // Make sure that at least one validator has at least a stake - // greater or equal to the threshold to avoid having an empty - // consensus set. - threshold.raw_amount().as_u64()..=10_000_000_u64 - } else { - 1..=10_000_000_u64 - } - .prop_map(token::Amount::from) - }) - .collect(); - (size, tokens) - .prop_map(|(size, token_amounts)| { - // use unique seeds to generate validators' address and consensus - // key - let seeds = (0_u64..).take(size); - seeds - .zip(token_amounts) - .map(|(seed, tokens)| { - let address = address_from_simple_seed(seed); - let consensus_sk = common_sk_from_simple_seed(seed); - let consensus_key = consensus_sk.to_public(); - - let protocol_sk = common_sk_from_simple_seed(seed); - let protocol_key = protocol_sk.to_public(); - - let eth_hot_key = key::common::PublicKey::Secp256k1( - key::testing::gen_keypair::( - ) - .ref_to(), - ); - let eth_cold_key = key::common::PublicKey::Secp256k1( - key::testing::gen_keypair::( - ) - .ref_to(), - ); - - let commission_rate = Dec::new(5, 2).expect("Test failed"); - let max_commission_rate_change = - Dec::new(1, 2).expect("Test failed"); - GenesisValidator { - address, - tokens, - consensus_key, - protocol_key, - eth_hot_key, - eth_cold_key, - commission_rate, - max_commission_rate_change, - metadata: Default::default(), - } - }) - .collect() - }) - .prop_filter( - "Must have at least one genesis validator with stake above the \ - provided threshold, if any.", - move |gen_vals: &Vec| { - gen_vals.iter().any(|val| val.tokens >= threshold) - }, - ) -} - -fn test_unjail_validator_aux( - params: OwnedPosParams, - mut validators: Vec, -) { - println!("\nTest inputs: {params:?}, genesis validators: {validators:#?}"); - let mut s = TestWlStorage::default(); - - // Find the validator with the most stake and 100x his stake to keep the - // cubic slash rate small - let num_vals = validators.len(); - validators.sort_by_key(|a| a.tokens); - validators[num_vals - 1].tokens = 100 * validators[num_vals - 1].tokens; - - // Get second highest stake validator tomisbehave - let val_addr = &validators[num_vals - 2].address; - let val_tokens = validators[num_vals - 2].tokens; - println!( - "Validator that will misbehave addr {val_addr}, tokens {}", - val_tokens.to_string_native() - ); - - // Genesis - let mut current_epoch = s.storage.block.epoch; - let params = test_init_genesis( - &mut s, - params, - validators.clone().into_iter(), - current_epoch, - ) - .unwrap(); - s.commit_block().unwrap(); - - current_epoch = advance_epoch(&mut s, ¶ms); - super::process_slashes(&mut s, current_epoch).unwrap(); - - // Discover first slash - let slash_0_evidence_epoch = current_epoch; - let evidence_block_height = BlockHeight(0); // doesn't matter for slashing logic - let slash_0_type = SlashType::DuplicateVote; - slash( - &mut s, - ¶ms, - current_epoch, - slash_0_evidence_epoch, - evidence_block_height, - slash_0_type, - val_addr, - current_epoch.next(), - ) - .unwrap(); - - assert_eq!( - validator_state_handle(val_addr) - .get(&s, current_epoch, ¶ms) - .unwrap(), - Some(ValidatorState::Consensus) - ); - - for epoch in Epoch::iter_bounds_inclusive( - current_epoch.next(), - current_epoch + params.pipeline_len, - ) { - // Check the validator state - assert_eq!( - validator_state_handle(val_addr) - .get(&s, epoch, ¶ms) - .unwrap(), - Some(ValidatorState::Jailed) - ); - // Check the validator set positions - assert!( - validator_set_positions_handle() - .at(&epoch) - .get(&s, val_addr) - .unwrap() - .is_none(), - ); - } - - // Advance past an epoch in which we can unbond - let unfreeze_epoch = - slash_0_evidence_epoch + params.slash_processing_epoch_offset(); - while current_epoch < unfreeze_epoch + 4u64 { - current_epoch = advance_epoch(&mut s, ¶ms); - super::process_slashes(&mut s, current_epoch).unwrap(); - } - - // Unjail the validator - unjail_validator(&mut s, val_addr, current_epoch).unwrap(); - - // Check the validator state - for epoch in - Epoch::iter_bounds_inclusive(current_epoch, current_epoch.next()) - { - assert_eq!( - validator_state_handle(val_addr) - .get(&s, epoch, ¶ms) - .unwrap(), - Some(ValidatorState::Jailed) - ); - } - - assert_eq!( - validator_state_handle(val_addr) - .get(&s, current_epoch + params.pipeline_len, ¶ms) - .unwrap(), - Some(ValidatorState::Consensus) - ); - assert!( - validator_set_positions_handle() - .at(&(current_epoch + params.pipeline_len)) - .get(&s, val_addr) - .unwrap() - .is_some(), - ); - - // Advance another epoch - current_epoch = advance_epoch(&mut s, ¶ms); - super::process_slashes(&mut s, current_epoch).unwrap(); - - let second_att = unjail_validator(&mut s, val_addr, current_epoch); - assert!(second_att.is_err()); -} - -/// `iterateBondsUpToAmountTest` -#[test] -fn test_find_bonds_to_remove() { - let mut storage = TestWlStorage::default(); - let gov_params = - namada_governance::parameters::GovernanceParameters::default(); - gov_params.init_storage(&mut storage).unwrap(); - write_pos_params(&mut storage, &OwnedPosParams::default()).unwrap(); - - let source = established_address_1(); - let validator = established_address_2(); - let bond_handle = bond_handle(&source, &validator); - - let (e1, e2, e6) = (Epoch(1), Epoch(2), Epoch(6)); - - bond_handle - .set(&mut storage, token::Amount::from(5), e1, 0) - .unwrap(); - bond_handle - .set(&mut storage, token::Amount::from(3), e2, 0) - .unwrap(); - bond_handle - .set(&mut storage, token::Amount::from(8), e6, 0) - .unwrap(); - - // Test 1 - let bonds_for_removal = find_bonds_to_remove( - &storage, - &bond_handle.get_data_handler(), - token::Amount::from(8), - ) - .unwrap(); - assert_eq!( - bonds_for_removal.epochs, - vec![e6].into_iter().collect::>() - ); - assert!(bonds_for_removal.new_entry.is_none()); - - // Test 2 - let bonds_for_removal = find_bonds_to_remove( - &storage, - &bond_handle.get_data_handler(), - token::Amount::from(10), - ) - .unwrap(); - assert_eq!( - bonds_for_removal.epochs, - vec![e6].into_iter().collect::>() - ); - assert_eq!( - bonds_for_removal.new_entry, - Some((Epoch(2), token::Amount::from(1))) - ); - - // Test 3 - let bonds_for_removal = find_bonds_to_remove( - &storage, - &bond_handle.get_data_handler(), - token::Amount::from(11), - ) - .unwrap(); - assert_eq!( - bonds_for_removal.epochs, - vec![e6, e2].into_iter().collect::>() - ); - assert!(bonds_for_removal.new_entry.is_none()); - - // Test 4 - let bonds_for_removal = find_bonds_to_remove( - &storage, - &bond_handle.get_data_handler(), - token::Amount::from(12), - ) - .unwrap(); - assert_eq!( - bonds_for_removal.epochs, - vec![e6, e2].into_iter().collect::>() - ); - assert_eq!( - bonds_for_removal.new_entry, - Some((Epoch(1), token::Amount::from(4))) - ); -} - -/// `computeModifiedRedelegationTest` -#[test] -fn test_compute_modified_redelegation() { - let mut storage = TestWlStorage::default(); - let validator1 = established_address_1(); - let validator2 = established_address_2(); - let owner = established_address_3(); - let outer_epoch = Epoch(0); - - let mut alice = validator1.clone(); - let mut bob = validator2.clone(); - - // Ensure a ranking order of alice > bob - if bob > alice { - alice = validator2; - bob = validator1; - } - println!("\n\nalice = {}\nbob = {}\n", &alice, &bob); - - // Fill redelegated bonds in storage - let redelegated_bonds_map = delegator_redelegated_bonds_handle(&owner) - .at(&alice) - .at(&outer_epoch); - redelegated_bonds_map - .at(&alice) - .insert(&mut storage, Epoch(2), token::Amount::from(6)) - .unwrap(); - redelegated_bonds_map - .at(&alice) - .insert(&mut storage, Epoch(4), token::Amount::from(7)) - .unwrap(); - redelegated_bonds_map - .at(&bob) - .insert(&mut storage, Epoch(1), token::Amount::from(5)) - .unwrap(); - redelegated_bonds_map - .at(&bob) - .insert(&mut storage, Epoch(4), token::Amount::from(7)) - .unwrap(); - - // Test cases 1 and 2 - let mr1 = compute_modified_redelegation( - &storage, - &redelegated_bonds_map, - Epoch(5), - token::Amount::from(25), - ) - .unwrap(); - let mr2 = compute_modified_redelegation( - &storage, - &redelegated_bonds_map, - Epoch(5), - token::Amount::from(30), - ) - .unwrap(); - - let exp_mr = ModifiedRedelegation { - epoch: Some(Epoch(5)), - ..Default::default() - }; - - assert_eq!(mr1, exp_mr); - assert_eq!(mr2, exp_mr); - - // Test case 3 - let mr3 = compute_modified_redelegation( - &storage, - &redelegated_bonds_map, - Epoch(5), - token::Amount::from(7), - ) - .unwrap(); - - let exp_mr = ModifiedRedelegation { - epoch: Some(Epoch(5)), - validators_to_remove: BTreeSet::from_iter([bob.clone()]), - validator_to_modify: Some(bob.clone()), - epochs_to_remove: BTreeSet::from_iter([Epoch(4)]), - ..Default::default() - }; - assert_eq!(mr3, exp_mr); - - // Test case 4 - let mr4 = compute_modified_redelegation( - &storage, - &redelegated_bonds_map, - Epoch(5), - token::Amount::from(8), - ) - .unwrap(); - - let exp_mr = ModifiedRedelegation { - epoch: Some(Epoch(5)), - validators_to_remove: BTreeSet::from_iter([bob.clone()]), - validator_to_modify: Some(bob.clone()), - epochs_to_remove: BTreeSet::from_iter([Epoch(1), Epoch(4)]), - epoch_to_modify: Some(Epoch(1)), - new_amount: Some(4.into()), - }; - assert_eq!(mr4, exp_mr); - - // Test case 5 - let mr5 = compute_modified_redelegation( - &storage, - &redelegated_bonds_map, - Epoch(5), - 12.into(), - ) - .unwrap(); - - let exp_mr = ModifiedRedelegation { - epoch: Some(Epoch(5)), - validators_to_remove: BTreeSet::from_iter([bob.clone()]), - ..Default::default() - }; - assert_eq!(mr5, exp_mr); - - // Test case 6 - let mr6 = compute_modified_redelegation( - &storage, - &redelegated_bonds_map, - Epoch(5), - 14.into(), - ) - .unwrap(); - - let exp_mr = ModifiedRedelegation { - epoch: Some(Epoch(5)), - validators_to_remove: BTreeSet::from_iter([alice.clone(), bob.clone()]), - validator_to_modify: Some(alice.clone()), - epochs_to_remove: BTreeSet::from_iter([Epoch(4)]), - epoch_to_modify: Some(Epoch(4)), - new_amount: Some(5.into()), - }; - assert_eq!(mr6, exp_mr); - - // Test case 7 - let mr7 = compute_modified_redelegation( - &storage, - &redelegated_bonds_map, - Epoch(5), - 19.into(), - ) - .unwrap(); - - let exp_mr = ModifiedRedelegation { - epoch: Some(Epoch(5)), - validators_to_remove: BTreeSet::from_iter([alice.clone(), bob.clone()]), - validator_to_modify: Some(alice.clone()), - epochs_to_remove: BTreeSet::from_iter([Epoch(4)]), - ..Default::default() - }; - assert_eq!(mr7, exp_mr); - - // Test case 8 - let mr8 = compute_modified_redelegation( - &storage, - &redelegated_bonds_map, - Epoch(5), - 21.into(), - ) - .unwrap(); - - let exp_mr = ModifiedRedelegation { - epoch: Some(Epoch(5)), - validators_to_remove: BTreeSet::from_iter([alice.clone(), bob]), - validator_to_modify: Some(alice), - epochs_to_remove: BTreeSet::from_iter([Epoch(2), Epoch(4)]), - epoch_to_modify: Some(Epoch(2)), - new_amount: Some(4.into()), - }; - assert_eq!(mr8, exp_mr); -} - -/// `computeBondAtEpochTest` -#[test] -fn test_compute_bond_at_epoch() { - let mut storage = TestWlStorage::default(); - let params = OwnedPosParams { - pipeline_len: 2, - unbonding_len: 4, - cubic_slashing_window_length: 1, - ..Default::default() - }; - let alice = established_address_1(); - let bob = established_address_2(); - - // Test 1 - let res = compute_bond_at_epoch( - &storage, - ¶ms, - &bob, - 12.into(), - 3.into(), - 23.into(), - Some(&Default::default()), - ) - .unwrap(); - - pretty_assertions::assert_eq!(res, 23.into()); - - // Test 2 - validator_slashes_handle(&bob) - .push( - &mut storage, - Slash { - epoch: 4.into(), - block_height: 0, - r#type: SlashType::DuplicateVote, - rate: Dec::one(), - }, - ) - .unwrap(); - let res = compute_bond_at_epoch( - &storage, - ¶ms, - &bob, - 12.into(), - 3.into(), - 23.into(), - Some(&Default::default()), - ) - .unwrap(); - - pretty_assertions::assert_eq!(res, 0.into()); - - // Test 3 - validator_slashes_handle(&bob).pop(&mut storage).unwrap(); - let mut redel_bonds = EagerRedelegatedBondsMap::default(); - redel_bonds.insert( - alice.clone(), - BTreeMap::from_iter([(Epoch(1), token::Amount::from(5))]), - ); - let res = compute_bond_at_epoch( - &storage, - ¶ms, - &bob, - 12.into(), - 3.into(), - 23.into(), - Some(&redel_bonds), - ) - .unwrap(); - - pretty_assertions::assert_eq!(res, 23.into()); - - // Test 4 - validator_slashes_handle(&bob) - .push( - &mut storage, - Slash { - epoch: 4.into(), - block_height: 0, - r#type: SlashType::DuplicateVote, - rate: Dec::one(), - }, - ) - .unwrap(); - let res = compute_bond_at_epoch( - &storage, - ¶ms, - &bob, - 12.into(), - 3.into(), - 23.into(), - Some(&redel_bonds), - ) - .unwrap(); - - pretty_assertions::assert_eq!(res, 0.into()); - - // Test 5 - validator_slashes_handle(&bob).pop(&mut storage).unwrap(); - validator_slashes_handle(&alice) - .push( - &mut storage, - Slash { - epoch: 6.into(), - block_height: 0, - r#type: SlashType::DuplicateVote, - rate: Dec::one(), - }, - ) - .unwrap(); - let res = compute_bond_at_epoch( - &storage, - ¶ms, - &bob, - 12.into(), - 3.into(), - 23.into(), - Some(&redel_bonds), - ) - .unwrap(); - - pretty_assertions::assert_eq!(res, 23.into()); - - // Test 6 - validator_slashes_handle(&alice).pop(&mut storage).unwrap(); - validator_slashes_handle(&alice) - .push( - &mut storage, - Slash { - epoch: 4.into(), - block_height: 0, - r#type: SlashType::DuplicateVote, - rate: Dec::one(), - }, - ) - .unwrap(); - let res = compute_bond_at_epoch( - &storage, - ¶ms, - &bob, - 18.into(), - 9.into(), - 23.into(), - Some(&redel_bonds), - ) - .unwrap(); - - pretty_assertions::assert_eq!(res, 18.into()); -} - -/// `computeSlashBondAtEpochTest` -#[test] -fn test_compute_slash_bond_at_epoch() { - let mut storage = TestWlStorage::default(); - let params = OwnedPosParams { - pipeline_len: 2, - unbonding_len: 4, - cubic_slashing_window_length: 1, - ..Default::default() - }; - let alice = established_address_1(); - let bob = established_address_2(); - - let current_epoch = Epoch(20); - let infraction_epoch = - current_epoch - params.slash_processing_epoch_offset(); - - let redelegated_bond = BTreeMap::from_iter([( - alice, - BTreeMap::from_iter([(infraction_epoch - 4, token::Amount::from(10))]), - )]); - - // Test 1 - let res = compute_slash_bond_at_epoch( - &storage, - ¶ms, - &bob, - current_epoch.next(), - infraction_epoch, - infraction_epoch - 2, - 30.into(), - Some(&Default::default()), - Dec::one(), - ) - .unwrap(); - - pretty_assertions::assert_eq!(res, 30.into()); - - // Test 2 - let res = compute_slash_bond_at_epoch( - &storage, - ¶ms, - &bob, - current_epoch.next(), - infraction_epoch, - infraction_epoch - 2, - 30.into(), - Some(&redelegated_bond), - Dec::one(), - ) - .unwrap(); - - pretty_assertions::assert_eq!(res, 30.into()); - - // Test 3 - validator_slashes_handle(&bob) - .push( - &mut storage, - Slash { - epoch: infraction_epoch.prev(), - block_height: 0, - r#type: SlashType::DuplicateVote, - rate: Dec::one(), - }, - ) - .unwrap(); - let res = compute_slash_bond_at_epoch( - &storage, - ¶ms, - &bob, - current_epoch.next(), - infraction_epoch, - infraction_epoch - 2, - 30.into(), - Some(&Default::default()), - Dec::one(), - ) - .unwrap(); - - pretty_assertions::assert_eq!(res, 0.into()); - - // Test 4 - let res = compute_slash_bond_at_epoch( - &storage, - ¶ms, - &bob, - current_epoch.next(), - infraction_epoch, - infraction_epoch - 2, - 30.into(), - Some(&redelegated_bond), - Dec::one(), - ) - .unwrap(); - - pretty_assertions::assert_eq!(res, 0.into()); -} - -/// `computeNewRedelegatedUnbondsTest` -#[test] -fn test_compute_new_redelegated_unbonds() { - let mut storage = TestWlStorage::default(); - let alice = established_address_1(); - let bob = established_address_2(); - - let key = Key::parse("testing").unwrap(); - let redelegated_bonds = NestedMap::::open(key); - - // Populate the lazy and eager maps - let (ep1, ep2, ep4, ep5, ep6, ep7) = - (Epoch(1), Epoch(2), Epoch(4), Epoch(5), Epoch(6), Epoch(7)); - let keys_and_values = vec![ - (ep5, alice.clone(), ep2, 1), - (ep5, alice.clone(), ep4, 1), - (ep7, alice.clone(), ep2, 1), - (ep7, alice.clone(), ep4, 1), - (ep5, bob.clone(), ep1, 1), - (ep5, bob.clone(), ep4, 2), - (ep7, bob.clone(), ep1, 1), - (ep7, bob.clone(), ep4, 2), - ]; - let mut eager_map = BTreeMap::::new(); - for (outer_ep, address, inner_ep, amount) in keys_and_values { - redelegated_bonds - .at(&outer_ep) - .at(&address) - .insert(&mut storage, inner_ep, token::Amount::from(amount)) - .unwrap(); - eager_map - .entry(outer_ep) - .or_default() - .entry(address.clone()) - .or_default() - .insert(inner_ep, token::Amount::from(amount)); - } - - // Different ModifiedRedelegation objects for testing - let empty_mr = ModifiedRedelegation::default(); - let all_mr = ModifiedRedelegation { - epoch: Some(ep7), - validators_to_remove: BTreeSet::from_iter([alice.clone(), bob.clone()]), - validator_to_modify: None, - epochs_to_remove: Default::default(), - epoch_to_modify: None, - new_amount: None, - }; - let mod_val_mr = ModifiedRedelegation { - epoch: Some(ep7), - validators_to_remove: BTreeSet::from_iter([alice.clone()]), - validator_to_modify: None, - epochs_to_remove: Default::default(), - epoch_to_modify: None, - new_amount: None, - }; - let mod_val_partial_mr = ModifiedRedelegation { - epoch: Some(ep7), - validators_to_remove: BTreeSet::from_iter([alice.clone(), bob.clone()]), - validator_to_modify: Some(bob.clone()), - epochs_to_remove: BTreeSet::from_iter([ep1]), - epoch_to_modify: None, - new_amount: None, - }; - let mod_epoch_partial_mr = ModifiedRedelegation { - epoch: Some(ep7), - validators_to_remove: BTreeSet::from_iter([alice, bob.clone()]), - validator_to_modify: Some(bob.clone()), - epochs_to_remove: BTreeSet::from_iter([ep1, ep4]), - epoch_to_modify: Some(ep4), - new_amount: Some(token::Amount::from(1)), - }; - - // Test case 1 - let res = compute_new_redelegated_unbonds( - &storage, - &redelegated_bonds, - &Default::default(), - &empty_mr, - ) - .unwrap(); - assert_eq!(res, Default::default()); - - let set5 = BTreeSet::::from_iter([ep5]); - let set56 = BTreeSet::::from_iter([ep5, ep6]); - - // Test case 2 - let res = compute_new_redelegated_unbonds( - &storage, - &redelegated_bonds, - &set5, - &empty_mr, - ) - .unwrap(); - let mut exp_res = eager_map.clone(); - exp_res.remove(&ep7); - assert_eq!(res, exp_res); - - // Test case 3 - let res = compute_new_redelegated_unbonds( - &storage, - &redelegated_bonds, - &set56, - &empty_mr, - ) - .unwrap(); - assert_eq!(res, exp_res); - - // Test case 4 - println!("\nTEST CASE 4\n"); - let res = compute_new_redelegated_unbonds( - &storage, - &redelegated_bonds, - &set56, - &all_mr, - ) - .unwrap(); - assert_eq!(res, eager_map); - - // Test case 5 - let res = compute_new_redelegated_unbonds( - &storage, - &redelegated_bonds, - &set56, - &mod_val_mr, - ) - .unwrap(); - exp_res = eager_map.clone(); - exp_res.entry(ep7).or_default().remove(&bob); - assert_eq!(res, exp_res); - - // Test case 6 - let res = compute_new_redelegated_unbonds( - &storage, - &redelegated_bonds, - &set56, - &mod_val_partial_mr, - ) - .unwrap(); - exp_res = eager_map.clone(); - exp_res - .entry(ep7) - .or_default() - .entry(bob.clone()) - .or_default() - .remove(&ep4); - assert_eq!(res, exp_res); - - // Test case 7 - let res = compute_new_redelegated_unbonds( - &storage, - &redelegated_bonds, - &set56, - &mod_epoch_partial_mr, - ) - .unwrap(); - exp_res - .entry(ep7) - .or_default() - .entry(bob) - .or_default() - .insert(ep4, token::Amount::from(1)); - assert_eq!(res, exp_res); -} - -/// `applyListSlashesTest` -#[test] -fn test_apply_list_slashes() { - let init_epoch = Epoch(2); - let params = OwnedPosParams { - unbonding_len: 4, - ..Default::default() - }; - // let unbonding_len = 4u64; - // let cubic_offset = 1u64; - - let slash1 = Slash { - epoch: init_epoch, - block_height: Default::default(), - r#type: SlashType::DuplicateVote, - rate: Dec::one(), - }; - let slash2 = Slash { - epoch: init_epoch - + params.unbonding_len - + params.cubic_slashing_window_length - + 1u64, - block_height: Default::default(), - r#type: SlashType::DuplicateVote, - rate: Dec::one(), - }; - - let list1 = vec![slash1.clone()]; - let list2 = vec![slash1.clone(), slash2.clone()]; - let list3 = vec![slash1.clone(), slash1.clone()]; - let list4 = vec![slash1.clone(), slash1, slash2]; - - let res = apply_list_slashes(¶ms, &[], token::Amount::from(100)); - assert_eq!(res, token::Amount::from(100)); - - let res = apply_list_slashes(¶ms, &list1, token::Amount::from(100)); - assert_eq!(res, token::Amount::zero()); - - let res = apply_list_slashes(¶ms, &list2, token::Amount::from(100)); - assert_eq!(res, token::Amount::zero()); - - let res = apply_list_slashes(¶ms, &list3, token::Amount::from(100)); - assert_eq!(res, token::Amount::zero()); - - let res = apply_list_slashes(¶ms, &list4, token::Amount::from(100)); - assert_eq!(res, token::Amount::zero()); -} - -/// `computeSlashableAmountTest` -#[test] -fn test_compute_slashable_amount() { - let init_epoch = Epoch(2); - let params = OwnedPosParams { - unbonding_len: 4, - ..Default::default() - }; - - let slash1 = Slash { - epoch: init_epoch - + params.unbonding_len - + params.cubic_slashing_window_length, - block_height: Default::default(), - r#type: SlashType::DuplicateVote, - rate: Dec::one(), - }; - - let slash2 = Slash { - epoch: init_epoch - + params.unbonding_len - + params.cubic_slashing_window_length - + 1u64, - block_height: Default::default(), - r#type: SlashType::DuplicateVote, - rate: Dec::one(), - }; - - let test_map = vec![(init_epoch, token::Amount::from(50))] - .into_iter() - .collect::>(); - - let res = compute_slashable_amount( - ¶ms, - &slash1, - token::Amount::from(100), - &BTreeMap::new(), - ); - assert_eq!(res, token::Amount::from(100)); - - let res = compute_slashable_amount( - ¶ms, - &slash2, - token::Amount::from(100), - &test_map, - ); - assert_eq!(res, token::Amount::from(50)); - - let res = compute_slashable_amount( - ¶ms, - &slash1, - token::Amount::from(100), - &test_map, - ); - assert_eq!(res, token::Amount::from(100)); -} - -/// `foldAndSlashRedelegatedBondsMapTest` -#[test] -fn test_fold_and_slash_redelegated_bonds() { - let mut storage = TestWlStorage::default(); - let params = OwnedPosParams { - unbonding_len: 4, - ..Default::default() - }; - let start_epoch = Epoch(7); - - let alice = established_address_1(); - let bob = established_address_2(); - - println!("\n\nAlice: {}", alice); - println!("Bob: {}\n", bob); - - let test_slash = Slash { - epoch: Default::default(), - block_height: Default::default(), - r#type: SlashType::DuplicateVote, - rate: Dec::one(), - }; - - let test_data = vec![ - (alice.clone(), vec![(2, 1), (4, 1)]), - (bob, vec![(1, 1), (4, 2)]), - ]; - let mut eager_redel_bonds = EagerRedelegatedBondsMap::default(); - for (address, pair) in test_data { - for (epoch, amount) in pair { - eager_redel_bonds - .entry(address.clone()) - .or_default() - .insert(Epoch(epoch), token::Amount::from(amount)); - } - } - - // Test case 1 - let res = fold_and_slash_redelegated_bonds( - &storage, - ¶ms, - &eager_redel_bonds, - start_epoch, - &[], - |_| true, - ); - assert_eq!( - res, - FoldRedelegatedBondsResult { - total_redelegated: token::Amount::from(5), - total_after_slashing: token::Amount::from(5), - } - ); - - // Test case 2 - let res = fold_and_slash_redelegated_bonds( - &storage, - ¶ms, - &eager_redel_bonds, - start_epoch, - &[test_slash], - |_| true, - ); - assert_eq!( - res, - FoldRedelegatedBondsResult { - total_redelegated: token::Amount::from(5), - total_after_slashing: token::Amount::zero(), - } - ); - - // Test case 3 - let alice_slash = Slash { - epoch: Epoch(6), - block_height: Default::default(), - r#type: SlashType::DuplicateVote, - rate: Dec::one(), - }; - validator_slashes_handle(&alice) - .push(&mut storage, alice_slash) - .unwrap(); - - let res = fold_and_slash_redelegated_bonds( - &storage, - ¶ms, - &eager_redel_bonds, - start_epoch, - &[], - |_| true, - ); - assert_eq!( - res, - FoldRedelegatedBondsResult { - total_redelegated: token::Amount::from(5), - total_after_slashing: token::Amount::from(3), - } - ); -} - -/// `slashRedelegationTest` -#[test] -fn test_slash_redelegation() { - let mut storage = TestWlStorage::default(); - let params = OwnedPosParams { - unbonding_len: 4, - ..Default::default() - }; - let alice = established_address_1(); - - let total_redelegated_unbonded = - validator_total_redelegated_unbonded_handle(&alice); - total_redelegated_unbonded - .at(&Epoch(13)) - .at(&Epoch(10)) - .at(&alice) - .insert(&mut storage, Epoch(7), token::Amount::from(2)) - .unwrap(); - - let slashes = validator_slashes_handle(&alice); - - let mut slashed_amounts_map = BTreeMap::from_iter([ - (Epoch(15), token::Amount::zero()), - (Epoch(16), token::Amount::zero()), - ]); - let empty_slash_amounts = slashed_amounts_map.clone(); - - // Test case 1 - slash_redelegation( - &storage, - ¶ms, - token::Amount::from(7), - Epoch(7), - Epoch(10), - &alice, - Epoch(14), - &slashes, - &total_redelegated_unbonded, - Dec::one(), - &mut slashed_amounts_map, - ) - .unwrap(); - assert_eq!( - slashed_amounts_map, - BTreeMap::from_iter([ - (Epoch(15), token::Amount::from(5)), - (Epoch(16), token::Amount::from(5)), - ]) - ); - - // Test case 2 - slashed_amounts_map = empty_slash_amounts.clone(); - slash_redelegation( - &storage, - ¶ms, - token::Amount::from(7), - Epoch(7), - Epoch(11), - &alice, - Epoch(14), - &slashes, - &total_redelegated_unbonded, - Dec::one(), - &mut slashed_amounts_map, - ) - .unwrap(); - assert_eq!( - slashed_amounts_map, - BTreeMap::from_iter([ - (Epoch(15), token::Amount::from(7)), - (Epoch(16), token::Amount::from(7)), - ]) - ); - - // Test case 3 - slashed_amounts_map = BTreeMap::from_iter([ - (Epoch(15), token::Amount::from(2)), - (Epoch(16), token::Amount::from(3)), - ]); - slash_redelegation( - &storage, - ¶ms, - token::Amount::from(7), - Epoch(7), - Epoch(10), - &alice, - Epoch(14), - &slashes, - &total_redelegated_unbonded, - Dec::one(), - &mut slashed_amounts_map, - ) - .unwrap(); - assert_eq!( - slashed_amounts_map, - BTreeMap::from_iter([ - (Epoch(15), token::Amount::from(7)), - (Epoch(16), token::Amount::from(8)), - ]) - ); - - // Test case 4 - slashes - .push( - &mut storage, - Slash { - epoch: Epoch(8), - block_height: Default::default(), - r#type: SlashType::DuplicateVote, - rate: Dec::one(), - }, - ) - .unwrap(); - slashed_amounts_map = empty_slash_amounts.clone(); - slash_redelegation( - &storage, - ¶ms, - token::Amount::from(7), - Epoch(7), - Epoch(10), - &alice, - Epoch(14), - &slashes, - &total_redelegated_unbonded, - Dec::one(), - &mut slashed_amounts_map, - ) - .unwrap(); - assert_eq!(slashed_amounts_map, empty_slash_amounts); - - // Test case 5 - slashes.pop(&mut storage).unwrap(); - slashes - .push( - &mut storage, - Slash { - epoch: Epoch(9), - block_height: Default::default(), - r#type: SlashType::DuplicateVote, - rate: Dec::one(), - }, - ) - .unwrap(); - slash_redelegation( - &storage, - ¶ms, - token::Amount::from(7), - Epoch(7), - Epoch(10), - &alice, - Epoch(14), - &slashes, - &total_redelegated_unbonded, - Dec::one(), - &mut slashed_amounts_map, - ) - .unwrap(); - assert_eq!(slashed_amounts_map, empty_slash_amounts); - - // Test case 6 - slashes - .push( - &mut storage, - Slash { - epoch: Epoch(8), - block_height: Default::default(), - r#type: SlashType::DuplicateVote, - rate: Dec::one(), - }, - ) - .unwrap(); - slash_redelegation( - &storage, - ¶ms, - token::Amount::from(7), - Epoch(7), - Epoch(10), - &alice, - Epoch(14), - &slashes, - &total_redelegated_unbonded, - Dec::one(), - &mut slashed_amounts_map, - ) - .unwrap(); - assert_eq!(slashed_amounts_map, empty_slash_amounts); -} - -/// `slashValidatorRedelegationTest` -#[test] -fn test_slash_validator_redelegation() { - let mut storage = TestWlStorage::default(); - let params = OwnedPosParams { - unbonding_len: 4, - ..Default::default() - }; - let gov_params = - namada_governance::parameters::GovernanceParameters::default(); - gov_params.init_storage(&mut storage).unwrap(); - write_pos_params(&mut storage, ¶ms).unwrap(); - - let alice = established_address_1(); - let bob = established_address_2(); - - let total_redelegated_unbonded = - validator_total_redelegated_unbonded_handle(&alice); - total_redelegated_unbonded - .at(&Epoch(13)) - .at(&Epoch(10)) - .at(&alice) - .insert(&mut storage, Epoch(7), token::Amount::from(2)) - .unwrap(); - - let outgoing_redelegations = - validator_outgoing_redelegations_handle(&alice).at(&bob); - - let slashes = validator_slashes_handle(&alice); - - let mut slashed_amounts_map = BTreeMap::from_iter([ - (Epoch(15), token::Amount::zero()), - (Epoch(16), token::Amount::zero()), - ]); - let empty_slash_amounts = slashed_amounts_map.clone(); - - // Test case 1 - slash_validator_redelegation( - &storage, - ¶ms, - &alice, - Epoch(14), - &outgoing_redelegations, - &slashes, - &total_redelegated_unbonded, - Dec::one(), - &mut slashed_amounts_map, - ) - .unwrap(); - assert_eq!(slashed_amounts_map, empty_slash_amounts); - - // Test case 2 - total_redelegated_unbonded - .remove_all(&mut storage, &Epoch(13)) - .unwrap(); - slash_validator_redelegation( - &storage, - ¶ms, - &alice, - Epoch(14), - &outgoing_redelegations, - &slashes, - &total_redelegated_unbonded, - Dec::one(), - &mut slashed_amounts_map, - ) - .unwrap(); - assert_eq!(slashed_amounts_map, empty_slash_amounts); - - // Test case 3 - total_redelegated_unbonded - .at(&Epoch(13)) - .at(&Epoch(10)) - .at(&alice) - .insert(&mut storage, Epoch(7), token::Amount::from(2)) - .unwrap(); - outgoing_redelegations - .at(&Epoch(6)) - .insert(&mut storage, Epoch(8), token::Amount::from(7)) - .unwrap(); - slash_validator_redelegation( - &storage, - ¶ms, - &alice, - Epoch(14), - &outgoing_redelegations, - &slashes, - &total_redelegated_unbonded, - Dec::one(), - &mut slashed_amounts_map, - ) - .unwrap(); - assert_eq!( - slashed_amounts_map, - BTreeMap::from_iter([ - (Epoch(15), token::Amount::from(7)), - (Epoch(16), token::Amount::from(7)), - ]) - ); - - // Test case 4 - slashed_amounts_map = empty_slash_amounts.clone(); - outgoing_redelegations - .remove_all(&mut storage, &Epoch(6)) - .unwrap(); - outgoing_redelegations - .at(&Epoch(7)) - .insert(&mut storage, Epoch(8), token::Amount::from(7)) - .unwrap(); - slash_validator_redelegation( - &storage, - ¶ms, - &alice, - Epoch(14), - &outgoing_redelegations, - &slashes, - &total_redelegated_unbonded, - Dec::one(), - &mut slashed_amounts_map, - ) - .unwrap(); - assert_eq!( - slashed_amounts_map, - BTreeMap::from_iter([ - (Epoch(15), token::Amount::from(5)), - (Epoch(16), token::Amount::from(5)), - ]) - ); - - // Test case 5 - slashed_amounts_map = BTreeMap::from_iter([ - (Epoch(15), token::Amount::from(2)), - (Epoch(16), token::Amount::from(3)), - ]); - slash_validator_redelegation( - &storage, - ¶ms, - &alice, - Epoch(14), - &outgoing_redelegations, - &slashes, - &total_redelegated_unbonded, - Dec::one(), - &mut slashed_amounts_map, - ) - .unwrap(); - assert_eq!( - slashed_amounts_map, - BTreeMap::from_iter([ - (Epoch(15), token::Amount::from(7)), - (Epoch(16), token::Amount::from(8)), - ]) - ); - - // Test case 6 - slashed_amounts_map = empty_slash_amounts.clone(); - slashes - .push( - &mut storage, - Slash { - epoch: Epoch(8), - block_height: Default::default(), - r#type: SlashType::DuplicateVote, - rate: Dec::one(), - }, - ) - .unwrap(); - slash_validator_redelegation( - &storage, - ¶ms, - &alice, - Epoch(14), - &outgoing_redelegations, - &slashes, - &total_redelegated_unbonded, - Dec::one(), - &mut slashed_amounts_map, - ) - .unwrap(); - assert_eq!(slashed_amounts_map, empty_slash_amounts); -} - -/// `slashValidatorTest` -#[test] -fn test_slash_validator() { - let mut storage = TestWlStorage::default(); - let params = OwnedPosParams { - unbonding_len: 4, - ..Default::default() - }; - let gov_params = - namada_governance::parameters::GovernanceParameters::default(); - gov_params.init_storage(&mut storage).unwrap(); - write_pos_params(&mut storage, ¶ms).unwrap(); - - let alice = established_address_1(); - let bob = established_address_2(); - - let total_bonded = total_bonded_handle(&bob); - let total_unbonded = total_unbonded_handle(&bob); - let total_redelegated_bonded = - validator_total_redelegated_bonded_handle(&bob); - let total_redelegated_unbonded = - validator_total_redelegated_unbonded_handle(&bob); - - let infraction_stake = token::Amount::from(23); - - let initial_stakes = BTreeMap::from_iter([ - (Epoch(11), infraction_stake), - (Epoch(12), infraction_stake), - (Epoch(13), infraction_stake), - ]); - let mut exp_res = initial_stakes.clone(); - - let current_epoch = Epoch(10); - let infraction_epoch = - current_epoch - params.slash_processing_epoch_offset(); - let processing_epoch = current_epoch.next(); - let slash_rate = Dec::one(); - - // Test case 1 - println!("\nTEST 1:"); - - total_bonded - .set(&mut storage, 23.into(), infraction_epoch - 2, 0) - .unwrap(); - let res = slash_validator( - &storage, - ¶ms, - &bob, - slash_rate, - processing_epoch, - &Default::default(), - ) - .unwrap(); - assert_eq!(res, exp_res); - - // Test case 2 - println!("\nTEST 2:"); - total_bonded - .set(&mut storage, 17.into(), infraction_epoch - 2, 0) - .unwrap(); - total_unbonded - .at(&(current_epoch + params.pipeline_len)) - .insert(&mut storage, infraction_epoch - 2, 6.into()) - .unwrap(); - let res = slash_validator( - &storage, - ¶ms, - &bob, - slash_rate, - processing_epoch, - &Default::default(), - ) - .unwrap(); - exp_res.insert(Epoch(12), 17.into()); - exp_res.insert(Epoch(13), 17.into()); - assert_eq!(res, exp_res); - - // Test case 3 - println!("\nTEST 3:"); - total_redelegated_bonded - .at(&infraction_epoch.prev()) - .at(&alice) - .insert(&mut storage, Epoch(2), 5.into()) - .unwrap(); - total_redelegated_bonded - .at(&infraction_epoch.prev()) - .at(&alice) - .insert(&mut storage, Epoch(3), 1.into()) - .unwrap(); - - let res = slash_validator( - &storage, - ¶ms, - &bob, - slash_rate, - processing_epoch, - &Default::default(), - ) - .unwrap(); - assert_eq!(res, exp_res); - - // Test case 4 - println!("\nTEST 4:"); - total_unbonded_handle(&bob) - .at(&(current_epoch + params.pipeline_len)) - .remove(&mut storage, &(infraction_epoch - 2)) - .unwrap(); - total_unbonded_handle(&bob) - .at(&(current_epoch + params.pipeline_len)) - .insert(&mut storage, infraction_epoch - 1, 6.into()) - .unwrap(); - total_redelegated_unbonded - .at(&(current_epoch + params.pipeline_len)) - .at(&infraction_epoch.prev()) - .at(&alice) - .insert(&mut storage, Epoch(2), 5.into()) - .unwrap(); - total_redelegated_unbonded - .at(&(current_epoch + params.pipeline_len)) - .at(&infraction_epoch.prev()) - .at(&alice) - .insert(&mut storage, Epoch(3), 1.into()) - .unwrap(); - let res = slash_validator( - &storage, - ¶ms, - &bob, - slash_rate, - processing_epoch, - &Default::default(), - ) - .unwrap(); - assert_eq!(res, exp_res); - - // Test case 5 - println!("\nTEST 5:"); - total_bonded_handle(&bob) - .set(&mut storage, 19.into(), infraction_epoch - 2, 0) - .unwrap(); - total_unbonded_handle(&bob) - .at(&(current_epoch + params.pipeline_len)) - .insert(&mut storage, infraction_epoch - 1, 4.into()) - .unwrap(); - total_redelegated_bonded - .at(¤t_epoch) - .at(&alice) - .insert(&mut storage, Epoch(2), token::Amount::from(1)) - .unwrap(); - total_redelegated_unbonded - .at(&(current_epoch + params.pipeline_len)) - .at(&infraction_epoch.prev()) - .at(&alice) - .remove(&mut storage, &Epoch(3)) - .unwrap(); - total_redelegated_unbonded - .at(&(current_epoch + params.pipeline_len)) - .at(&infraction_epoch.prev()) - .at(&alice) - .insert(&mut storage, Epoch(2), 4.into()) - .unwrap(); - let res = slash_validator( - &storage, - ¶ms, - &bob, - slash_rate, - processing_epoch, - &Default::default(), - ) - .unwrap(); - exp_res.insert(Epoch(12), 19.into()); - exp_res.insert(Epoch(13), 19.into()); - assert_eq!(res, exp_res); - - // Test case 6 - println!("\nTEST 6:"); - total_unbonded_handle(&bob) - .remove_all(&mut storage, &(current_epoch + params.pipeline_len)) - .unwrap(); - total_redelegated_unbonded - .remove_all(&mut storage, &(current_epoch + params.pipeline_len)) - .unwrap(); - total_redelegated_bonded - .remove_all(&mut storage, ¤t_epoch) - .unwrap(); - total_bonded_handle(&bob) - .set(&mut storage, 23.into(), infraction_epoch - 2, 0) - .unwrap(); - total_bonded_handle(&bob) - .set(&mut storage, 6.into(), current_epoch, 0) - .unwrap(); - - let res = slash_validator( - &storage, - ¶ms, - &bob, - slash_rate, - processing_epoch, - &Default::default(), - ) - .unwrap(); - exp_res = initial_stakes; - assert_eq!(res, exp_res); - - // Test case 7 - println!("\nTEST 7:"); - total_bonded - .get_data_handler() - .remove(&mut storage, ¤t_epoch) - .unwrap(); - total_unbonded - .at(¤t_epoch.next()) - .insert(&mut storage, current_epoch, 6.into()) - .unwrap(); - let res = slash_validator( - &storage, - ¶ms, - &bob, - slash_rate, - processing_epoch, - &Default::default(), - ) - .unwrap(); - assert_eq!(res, exp_res); - - // Test case 8 - println!("\nTEST 8:"); - total_bonded - .get_data_handler() - .insert(&mut storage, current_epoch, 3.into()) - .unwrap(); - total_unbonded - .at(¤t_epoch.next()) - .insert(&mut storage, current_epoch, 3.into()) - .unwrap(); - let res = slash_validator( - &storage, - ¶ms, - &bob, - slash_rate, - processing_epoch, - &Default::default(), - ) - .unwrap(); - assert_eq!(res, exp_res); - - // Test case 9 - println!("\nTEST 9:"); - total_unbonded - .remove_all(&mut storage, ¤t_epoch.next()) - .unwrap(); - total_bonded - .set(&mut storage, 6.into(), current_epoch, 0) - .unwrap(); - total_redelegated_bonded - .at(¤t_epoch) - .at(&alice) - .insert(&mut storage, 2.into(), 5.into()) - .unwrap(); - total_redelegated_bonded - .at(¤t_epoch) - .at(&alice) - .insert(&mut storage, 3.into(), 1.into()) - .unwrap(); - let res = slash_validator( - &storage, - ¶ms, - &bob, - slash_rate, - processing_epoch, - &Default::default(), - ) - .unwrap(); - assert_eq!(res, exp_res); - - // Test case 10 - println!("\nTEST 10:"); - total_redelegated_bonded - .remove_all(&mut storage, ¤t_epoch) - .unwrap(); - total_bonded - .get_data_handler() - .remove(&mut storage, ¤t_epoch) - .unwrap(); - total_redelegated_unbonded - .at(¤t_epoch.next()) - .at(¤t_epoch) - .at(&alice) - .insert(&mut storage, 2.into(), 5.into()) - .unwrap(); - total_redelegated_unbonded - .at(¤t_epoch.next()) - .at(¤t_epoch) - .at(&alice) - .insert(&mut storage, 3.into(), 1.into()) - .unwrap(); - let res = slash_validator( - &storage, - ¶ms, - &bob, - slash_rate, - processing_epoch, - &Default::default(), - ) - .unwrap(); - assert_eq!(res, exp_res); - - // Test case 11 - println!("\nTEST 11:"); - total_bonded - .set(&mut storage, 2.into(), current_epoch, 0) - .unwrap(); - total_redelegated_unbonded - .at(¤t_epoch.next()) - .at(¤t_epoch) - .at(&alice) - .insert(&mut storage, 2.into(), 4.into()) - .unwrap(); - total_redelegated_unbonded - .at(¤t_epoch.next()) - .at(¤t_epoch) - .at(&alice) - .remove(&mut storage, &3.into()) - .unwrap(); - total_redelegated_bonded - .at(¤t_epoch) - .at(&alice) - .insert(&mut storage, 2.into(), 1.into()) - .unwrap(); - total_redelegated_bonded - .at(¤t_epoch) - .at(&alice) - .insert(&mut storage, 3.into(), 1.into()) - .unwrap(); - let res = slash_validator( - &storage, - ¶ms, - &bob, - slash_rate, - processing_epoch, - &Default::default(), - ) - .unwrap(); - assert_eq!(res, exp_res); - - // Test case 12 - println!("\nTEST 12:"); - total_bonded - .set(&mut storage, 6.into(), current_epoch, 0) - .unwrap(); - total_bonded - .set(&mut storage, 2.into(), current_epoch.next(), 0) - .unwrap(); - total_redelegated_bonded - .remove_all(&mut storage, ¤t_epoch) - .unwrap(); - total_redelegated_bonded - .at(¤t_epoch.next()) - .at(&alice) - .insert(&mut storage, 2.into(), 1.into()) - .unwrap(); - total_redelegated_bonded - .at(¤t_epoch.next()) - .at(&alice) - .insert(&mut storage, 3.into(), 1.into()) - .unwrap(); - let res = slash_validator( - &storage, - ¶ms, - &bob, - slash_rate, - processing_epoch, - &Default::default(), - ) - .unwrap(); - assert_eq!(res, exp_res); - - // Test case 13 - println!("\nTEST 13:"); - validator_slashes_handle(&bob) - .push( - &mut storage, - Slash { - epoch: infraction_epoch.prev(), - block_height: 0, - r#type: SlashType::DuplicateVote, - rate: Dec::one(), - }, - ) - .unwrap(); - total_redelegated_unbonded - .remove_all(&mut storage, ¤t_epoch.next()) - .unwrap(); - total_bonded - .get_data_handler() - .remove(&mut storage, ¤t_epoch.next()) - .unwrap(); - total_redelegated_bonded - .remove_all(&mut storage, ¤t_epoch.next()) - .unwrap(); - let res = slash_validator( - &storage, - ¶ms, - &bob, - slash_rate, - processing_epoch, - &Default::default(), - ) - .unwrap(); - exp_res.insert(Epoch(11), 0.into()); - exp_res.insert(Epoch(12), 0.into()); - exp_res.insert(Epoch(13), 0.into()); - assert_eq!(res, exp_res); -} - -/// `computeAmountAfterSlashingUnbondTest` -#[test] -fn compute_amount_after_slashing_unbond_test() { - let mut storage = TestWlStorage::default(); - let params = OwnedPosParams { - unbonding_len: 4, - ..Default::default() - }; - - // Test data - let alice = established_address_1(); - let bob = established_address_2(); - let unbonds: BTreeMap = BTreeMap::from_iter([ - ((Epoch(2)), token::Amount::from(5)), - ((Epoch(4)), token::Amount::from(6)), - ]); - let redelegated_unbonds: EagerRedelegatedUnbonds = BTreeMap::from_iter([( - Epoch(2), - BTreeMap::from_iter([( - alice.clone(), - BTreeMap::from_iter([(Epoch(1), token::Amount::from(1))]), - )]), - )]); - - // Test case 1 - let slashes = vec![]; - let result = compute_amount_after_slashing_unbond( - &storage, - ¶ms, - &unbonds, - &redelegated_unbonds, - slashes, - ) - .unwrap(); - assert_eq!(result.sum, 11.into()); - itertools::assert_equal( - result.epoch_map, - [(2.into(), 5.into()), (4.into(), 6.into())], - ); - - // Test case 2 - let bob_slash = Slash { - epoch: Epoch(5), - block_height: Default::default(), - r#type: SlashType::DuplicateVote, - rate: Dec::one(), - }; - let slashes = vec![bob_slash.clone()]; - validator_slashes_handle(&bob) - .push(&mut storage, bob_slash) - .unwrap(); - let result = compute_amount_after_slashing_unbond( - &storage, - ¶ms, - &unbonds, - &redelegated_unbonds, - slashes, - ) - .unwrap(); - assert_eq!(result.sum, 0.into()); - itertools::assert_equal( - result.epoch_map, - [(2.into(), 0.into()), (4.into(), 0.into())], - ); - - // Test case 3 - let alice_slash = Slash { - epoch: Epoch(0), - block_height: Default::default(), - r#type: SlashType::DuplicateVote, - rate: Dec::one(), - }; - let slashes = vec![alice_slash.clone()]; - validator_slashes_handle(&alice) - .push(&mut storage, alice_slash) - .unwrap(); - validator_slashes_handle(&bob).pop(&mut storage).unwrap(); - let result = compute_amount_after_slashing_unbond( - &storage, - ¶ms, - &unbonds, - &redelegated_unbonds, - slashes, - ) - .unwrap(); - assert_eq!(result.sum, 11.into()); - itertools::assert_equal( - result.epoch_map, - [(2.into(), 5.into()), (4.into(), 6.into())], - ); - - // Test case 4 - let alice_slash = Slash { - epoch: Epoch(1), - block_height: Default::default(), - r#type: SlashType::DuplicateVote, - rate: Dec::one(), - }; - let slashes = vec![alice_slash.clone()]; - validator_slashes_handle(&alice).pop(&mut storage).unwrap(); - validator_slashes_handle(&alice) - .push(&mut storage, alice_slash) - .unwrap(); - let result = compute_amount_after_slashing_unbond( - &storage, - ¶ms, - &unbonds, - &redelegated_unbonds, - slashes, - ) - .unwrap(); - assert_eq!(result.sum, 10.into()); - itertools::assert_equal( - result.epoch_map, - [(2.into(), 4.into()), (4.into(), 6.into())], - ); -} - -/// `computeAmountAfterSlashingWithdrawTest` -#[test] -fn compute_amount_after_slashing_withdraw_test() { - let mut storage = TestWlStorage::default(); - let params = OwnedPosParams { - unbonding_len: 4, - ..Default::default() - }; - - // Test data - let alice = established_address_1(); - let bob = established_address_2(); - let unbonds_and_redelegated_unbonds: BTreeMap< - (Epoch, Epoch), - (token::Amount, EagerRedelegatedBondsMap), - > = BTreeMap::from_iter([ - ( - (Epoch(2), Epoch(20)), - ( - // unbond - token::Amount::from(5), - // redelegations - BTreeMap::from_iter([( - alice.clone(), - BTreeMap::from_iter([(Epoch(1), token::Amount::from(1))]), - )]), - ), - ), - ( - (Epoch(4), Epoch(20)), - ( - // unbond - token::Amount::from(6), - // redelegations - BTreeMap::default(), - ), - ), - ]); - - // Test case 1 - let slashes = vec![]; - let result = compute_amount_after_slashing_withdraw( - &storage, - ¶ms, - &unbonds_and_redelegated_unbonds, - slashes, - ) - .unwrap(); - assert_eq!(result.sum, 11.into()); - itertools::assert_equal( - result.epoch_map, - [(2.into(), 5.into()), (4.into(), 6.into())], - ); - - // Test case 2 - let bob_slash = Slash { - epoch: Epoch(5), - block_height: Default::default(), - r#type: SlashType::DuplicateVote, - rate: Dec::one(), - }; - let slashes = vec![bob_slash.clone()]; - validator_slashes_handle(&bob) - .push(&mut storage, bob_slash) - .unwrap(); - let result = compute_amount_after_slashing_withdraw( - &storage, - ¶ms, - &unbonds_and_redelegated_unbonds, - slashes, - ) - .unwrap(); - assert_eq!(result.sum, 0.into()); - itertools::assert_equal( - result.epoch_map, - [(2.into(), 0.into()), (4.into(), 0.into())], - ); - - // Test case 3 - let alice_slash = Slash { - epoch: Epoch(0), - block_height: Default::default(), - r#type: SlashType::DuplicateVote, - rate: Dec::one(), - }; - let slashes = vec![alice_slash.clone()]; - validator_slashes_handle(&alice) - .push(&mut storage, alice_slash) - .unwrap(); - validator_slashes_handle(&bob).pop(&mut storage).unwrap(); - let result = compute_amount_after_slashing_withdraw( - &storage, - ¶ms, - &unbonds_and_redelegated_unbonds, - slashes, - ) - .unwrap(); - assert_eq!(result.sum, 11.into()); - itertools::assert_equal( - result.epoch_map, - [(2.into(), 5.into()), (4.into(), 6.into())], - ); - - // Test case 4 - let alice_slash = Slash { - epoch: Epoch(1), - block_height: Default::default(), - r#type: SlashType::DuplicateVote, - rate: Dec::one(), - }; - let slashes = vec![alice_slash.clone()]; - validator_slashes_handle(&alice).pop(&mut storage).unwrap(); - validator_slashes_handle(&alice) - .push(&mut storage, alice_slash) - .unwrap(); - let result = compute_amount_after_slashing_withdraw( - &storage, - ¶ms, - &unbonds_and_redelegated_unbonds, - slashes, - ) - .unwrap(); - assert_eq!(result.sum, 10.into()); - itertools::assert_equal( - result.epoch_map, - [(2.into(), 4.into()), (4.into(), 6.into())], - ); -} - -fn arb_redelegation_amounts( - max_delegation: u64, -) -> impl Strategy { - let arb_delegation = arb_amount_non_zero_ceiled(max_delegation); - let amounts = arb_delegation.prop_flat_map(move |amount_delegate| { - let amount_redelegate = arb_amount_non_zero_ceiled(max( - 1, - u64::try_from(amount_delegate.raw_amount()).unwrap() - 1, - )); - (Just(amount_delegate), amount_redelegate) - }); - amounts.prop_flat_map(move |(amount_delegate, amount_redelegate)| { - let amount_unbond = arb_amount_non_zero_ceiled(max( - 1, - u64::try_from(amount_redelegate.raw_amount()).unwrap() - 1, - )); - ( - Just(amount_delegate), - Just(amount_redelegate), - amount_unbond, - ) - }) -} - -fn test_simple_redelegation_aux( - mut validators: Vec, - amount_delegate: token::Amount, - amount_redelegate: token::Amount, - amount_unbond: token::Amount, -) { - validators.sort_by(|a, b| b.tokens.cmp(&a.tokens)); - - let src_validator = validators[0].address.clone(); - let dest_validator = validators[1].address.clone(); - - let mut storage = TestWlStorage::default(); - let params = OwnedPosParams { - unbonding_len: 4, - ..Default::default() - }; - - // Genesis - let mut current_epoch = storage.storage.block.epoch; - let params = test_init_genesis( - &mut storage, - params, - validators.clone().into_iter(), - current_epoch, - ) - .unwrap(); - storage.commit_block().unwrap(); - - // Get a delegator with some tokens - let staking_token = staking_token_address(&storage); - let delegator = address::testing::gen_implicit_address(); - let del_balance = token::Amount::from_uint(1_000_000, 0).unwrap(); - credit_tokens(&mut storage, &staking_token, &delegator, del_balance) - .unwrap(); - - // Ensure that we cannot redelegate with the same src and dest validator - let err = super::redelegate_tokens( - &mut storage, - &delegator, - &src_validator, - &src_validator, - current_epoch, - amount_redelegate, - ) - .unwrap_err(); - let err_str = err.to_string(); - assert_matches!( - err.downcast::().unwrap().deref(), - RedelegationError::RedelegationSrcEqDest, - "Redelegation with the same src and dest validator must be rejected, \ - got {err_str}", - ); - - for _ in 0..5 { - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - } - - let init_epoch = current_epoch; - - // Delegate in epoch 1 to src_validator - println!( - "\nBONDING {} TOKENS TO {}\n", - amount_delegate.to_string_native(), - &src_validator - ); - super::bond_tokens( - &mut storage, - Some(&delegator), - &src_validator, - amount_delegate, - current_epoch, - None, - ) - .unwrap(); - - println!("\nAFTER DELEGATION\n"); - let bonds = bond_handle(&delegator, &src_validator) - .get_data_handler() - .collect_map(&storage) - .unwrap(); - let bonds_dest = bond_handle(&delegator, &dest_validator) - .get_data_handler() - .collect_map(&storage) - .unwrap(); - let unbonds = unbond_handle(&delegator, &src_validator) - .collect_map(&storage) - .unwrap(); - let tot_bonds = total_bonded_handle(&src_validator) - .get_data_handler() - .collect_map(&storage) - .unwrap(); - let tot_unbonds = total_unbonded_handle(&src_validator) - .collect_map(&storage) - .unwrap(); - dbg!(&bonds, &bonds_dest, &unbonds, &tot_bonds, &tot_unbonds); - - // Advance three epochs - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - - // Redelegate in epoch 3 - println!( - "\nREDELEGATING {} TOKENS TO {}\n", - amount_redelegate.to_string_native(), - &dest_validator - ); - - super::redelegate_tokens( - &mut storage, - &delegator, - &src_validator, - &dest_validator, - current_epoch, - amount_redelegate, - ) - .unwrap(); - - println!("\nAFTER REDELEGATION\n"); - println!("\nDELEGATOR\n"); - let bonds_src = bond_handle(&delegator, &src_validator) - .get_data_handler() - .collect_map(&storage) - .unwrap(); - let bonds_dest = bond_handle(&delegator, &dest_validator) - .get_data_handler() - .collect_map(&storage) - .unwrap(); - let unbonds_src = unbond_handle(&delegator, &src_validator) - .collect_map(&storage) - .unwrap(); - let unbonds_dest = unbond_handle(&delegator, &dest_validator) - .collect_map(&storage) - .unwrap(); - let redel_bonds = delegator_redelegated_bonds_handle(&delegator) - .collect_map(&storage) - .unwrap(); - let redel_unbonds = delegator_redelegated_unbonds_handle(&delegator) - .collect_map(&storage) - .unwrap(); - - dbg!( - &bonds_src, - &bonds_dest, - &unbonds_src, - &unbonds_dest, - &redel_bonds, - &redel_unbonds - ); - - // Dest val - println!("\nDEST VALIDATOR\n"); - - let incoming_redels_dest = - validator_incoming_redelegations_handle(&dest_validator) - .collect_map(&storage) - .unwrap(); - let outgoing_redels_dest = - validator_outgoing_redelegations_handle(&dest_validator) - .collect_map(&storage) - .unwrap(); - let tot_bonds_dest = total_bonded_handle(&dest_validator) - .get_data_handler() - .collect_map(&storage) - .unwrap(); - let tot_unbonds_dest = total_unbonded_handle(&dest_validator) - .collect_map(&storage) - .unwrap(); - let tot_redel_bonds_dest = - validator_total_redelegated_bonded_handle(&dest_validator) - .collect_map(&storage) - .unwrap(); - let tot_redel_unbonds_dest = - validator_total_redelegated_unbonded_handle(&dest_validator) - .collect_map(&storage) - .unwrap(); - dbg!( - &incoming_redels_dest, - &outgoing_redels_dest, - &tot_bonds_dest, - &tot_unbonds_dest, - &tot_redel_bonds_dest, - &tot_redel_unbonds_dest - ); - - // Src val - println!("\nSRC VALIDATOR\n"); - - let incoming_redels_src = - validator_incoming_redelegations_handle(&src_validator) - .collect_map(&storage) - .unwrap(); - let outgoing_redels_src = - validator_outgoing_redelegations_handle(&src_validator) - .collect_map(&storage) - .unwrap(); - let tot_bonds_src = total_bonded_handle(&src_validator) - .get_data_handler() - .collect_map(&storage) - .unwrap(); - let tot_unbonds_src = total_unbonded_handle(&src_validator) - .collect_map(&storage) - .unwrap(); - let tot_redel_bonds_src = - validator_total_redelegated_bonded_handle(&src_validator) - .collect_map(&storage) - .unwrap(); - let tot_redel_unbonds_src = - validator_total_redelegated_unbonded_handle(&src_validator) - .collect_map(&storage) - .unwrap(); - dbg!( - &incoming_redels_src, - &outgoing_redels_src, - &tot_bonds_src, - &tot_unbonds_src, - &tot_redel_bonds_src, - &tot_redel_unbonds_src - ); - - // Checks - let redelegated = delegator_redelegated_bonds_handle(&delegator) - .at(&dest_validator) - .at(&(current_epoch + params.pipeline_len)) - .at(&src_validator) - .get(&storage, &(init_epoch + params.pipeline_len)) - .unwrap() - .unwrap(); - assert_eq!(redelegated, amount_redelegate); - - let redel_start_epoch = - validator_incoming_redelegations_handle(&dest_validator) - .get(&storage, &delegator) - .unwrap() - .unwrap(); - assert_eq!(redel_start_epoch, current_epoch + params.pipeline_len); - - let redelegated = validator_outgoing_redelegations_handle(&src_validator) - .at(&dest_validator) - .at(¤t_epoch.prev()) - .get(&storage, ¤t_epoch) - .unwrap() - .unwrap(); - assert_eq!(redelegated, amount_redelegate); - - // Advance three epochs - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - - // Unbond in epoch 5 from dest_validator - println!( - "\nUNBONDING {} TOKENS FROM {}\n", - amount_unbond.to_string_native(), - &dest_validator - ); - let _ = unbond_tokens( - &mut storage, - Some(&delegator), - &dest_validator, - amount_unbond, - current_epoch, - false, - ) - .unwrap(); - - println!("\nAFTER UNBONDING\n"); - println!("\nDELEGATOR\n"); - - let bonds_src = bond_handle(&delegator, &src_validator) - .get_data_handler() - .collect_map(&storage) - .unwrap(); - let bonds_dest = bond_handle(&delegator, &dest_validator) - .get_data_handler() - .collect_map(&storage) - .unwrap(); - let unbonds_src = unbond_handle(&delegator, &src_validator) - .collect_map(&storage) - .unwrap(); - let unbonds_dest = unbond_handle(&delegator, &dest_validator) - .collect_map(&storage) - .unwrap(); - let redel_bonds = delegator_redelegated_bonds_handle(&delegator) - .collect_map(&storage) - .unwrap(); - let redel_unbonds = delegator_redelegated_unbonds_handle(&delegator) - .collect_map(&storage) - .unwrap(); - - dbg!( - &bonds_src, - &bonds_dest, - &unbonds_src, - &unbonds_dest, - &redel_bonds, - &redel_unbonds - ); - - println!("\nDEST VALIDATOR\n"); - - let incoming_redels_dest = - validator_incoming_redelegations_handle(&dest_validator) - .collect_map(&storage) - .unwrap(); - let outgoing_redels_dest = - validator_outgoing_redelegations_handle(&dest_validator) - .collect_map(&storage) - .unwrap(); - let tot_bonds_dest = total_bonded_handle(&dest_validator) - .get_data_handler() - .collect_map(&storage) - .unwrap(); - let tot_unbonds_dest = total_unbonded_handle(&dest_validator) - .collect_map(&storage) - .unwrap(); - let tot_redel_bonds_dest = - validator_total_redelegated_bonded_handle(&dest_validator) - .collect_map(&storage) - .unwrap(); - let tot_redel_unbonds_dest = - validator_total_redelegated_unbonded_handle(&dest_validator) - .collect_map(&storage) - .unwrap(); - dbg!( - &incoming_redels_dest, - &outgoing_redels_dest, - &tot_bonds_dest, - &tot_unbonds_dest, - &tot_redel_bonds_dest, - &tot_redel_unbonds_dest - ); - - let bond_start = init_epoch + params.pipeline_len; - let redelegation_end = bond_start + params.pipeline_len + 1u64; - let unbond_end = - redelegation_end + params.withdrawable_epoch_offset() + 1u64; - let unbond_materialized = redelegation_end + params.pipeline_len + 1u64; - - // Checks - let redelegated_remaining = delegator_redelegated_bonds_handle(&delegator) - .at(&dest_validator) - .at(&redelegation_end) - .at(&src_validator) - .get(&storage, &bond_start) - .unwrap() - .unwrap_or_default(); - assert_eq!(redelegated_remaining, amount_redelegate - amount_unbond); - - let redel_unbonded = delegator_redelegated_unbonds_handle(&delegator) - .at(&dest_validator) - .at(&redelegation_end) - .at(&unbond_end) - .at(&src_validator) - .get(&storage, &bond_start) - .unwrap() - .unwrap(); - assert_eq!(redel_unbonded, amount_unbond); - - dbg!(unbond_materialized, redelegation_end, bond_start); - let total_redel_unbonded = - validator_total_redelegated_unbonded_handle(&dest_validator) - .at(&unbond_materialized) - .at(&redelegation_end) - .at(&src_validator) - .get(&storage, &bond_start) - .unwrap() - .unwrap(); - assert_eq!(total_redel_unbonded, amount_unbond); - - // Advance to withdrawal epoch - loop { - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - if current_epoch == unbond_end { - break; - } - } - - // Withdraw - withdraw_tokens( - &mut storage, - Some(&delegator), - &dest_validator, - current_epoch, - ) - .unwrap(); - - assert!( - delegator_redelegated_unbonds_handle(&delegator) - .at(&dest_validator) - .is_empty(&storage) - .unwrap() - ); - - let delegator_balance = storage - .read::(&token::balance_key(&staking_token, &delegator)) - .unwrap() - .unwrap_or_default(); - assert_eq!( - delegator_balance, - del_balance - amount_delegate + amount_unbond - ); -} - -fn test_redelegation_with_slashing_aux( - mut validators: Vec, - amount_delegate: token::Amount, - amount_redelegate: token::Amount, - amount_unbond: token::Amount, -) { - validators.sort_by(|a, b| b.tokens.cmp(&a.tokens)); - - let src_validator = validators[0].address.clone(); - let dest_validator = validators[1].address.clone(); - - let mut storage = TestWlStorage::default(); - let params = OwnedPosParams { - unbonding_len: 4, - // Avoid empty consensus set by removing the threshold - validator_stake_threshold: token::Amount::zero(), - ..Default::default() - }; - - // Genesis - let mut current_epoch = storage.storage.block.epoch; - let params = test_init_genesis( - &mut storage, - params, - validators.clone().into_iter(), - current_epoch, - ) - .unwrap(); - storage.commit_block().unwrap(); - - // Get a delegator with some tokens - let staking_token = staking_token_address(&storage); - let delegator = address::testing::gen_implicit_address(); - let del_balance = token::Amount::from_uint(1_000_000, 0).unwrap(); - credit_tokens(&mut storage, &staking_token, &delegator, del_balance) - .unwrap(); - - for _ in 0..5 { - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - } - - let init_epoch = current_epoch; - - // Delegate in epoch 5 to src_validator - println!( - "\nBONDING {} TOKENS TO {}\n", - amount_delegate.to_string_native(), - &src_validator - ); - super::bond_tokens( - &mut storage, - Some(&delegator), - &src_validator, - amount_delegate, - current_epoch, - None, - ) - .unwrap(); - - println!("\nAFTER DELEGATION\n"); - let bonds = bond_handle(&delegator, &src_validator) - .get_data_handler() - .collect_map(&storage) - .unwrap(); - let bonds_dest = bond_handle(&delegator, &dest_validator) - .get_data_handler() - .collect_map(&storage) - .unwrap(); - let unbonds = unbond_handle(&delegator, &src_validator) - .collect_map(&storage) - .unwrap(); - let tot_bonds = total_bonded_handle(&src_validator) - .get_data_handler() - .collect_map(&storage) - .unwrap(); - let tot_unbonds = total_unbonded_handle(&src_validator) - .collect_map(&storage) - .unwrap(); - dbg!(&bonds, &bonds_dest, &unbonds, &tot_bonds, &tot_unbonds); - - // Advance three epochs - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - - // Redelegate in epoch 8 - println!( - "\nREDELEGATING {} TOKENS TO {}\n", - amount_redelegate.to_string_native(), - &dest_validator - ); - - super::redelegate_tokens( - &mut storage, - &delegator, - &src_validator, - &dest_validator, - current_epoch, - amount_redelegate, - ) - .unwrap(); - - println!("\nAFTER REDELEGATION\n"); - println!("\nDELEGATOR\n"); - let bonds_src = bond_handle(&delegator, &src_validator) - .get_data_handler() - .collect_map(&storage) - .unwrap(); - let bonds_dest = bond_handle(&delegator, &dest_validator) - .get_data_handler() - .collect_map(&storage) - .unwrap(); - let unbonds_src = unbond_handle(&delegator, &src_validator) - .collect_map(&storage) - .unwrap(); - let unbonds_dest = unbond_handle(&delegator, &dest_validator) - .collect_map(&storage) - .unwrap(); - let redel_bonds = delegator_redelegated_bonds_handle(&delegator) - .collect_map(&storage) - .unwrap(); - let redel_unbonds = delegator_redelegated_unbonds_handle(&delegator) - .collect_map(&storage) - .unwrap(); - - dbg!( - &bonds_src, - &bonds_dest, - &unbonds_src, - &unbonds_dest, - &redel_bonds, - &redel_unbonds - ); - - // Dest val - println!("\nDEST VALIDATOR\n"); - - let incoming_redels_dest = - validator_incoming_redelegations_handle(&dest_validator) - .collect_map(&storage) - .unwrap(); - let outgoing_redels_dest = - validator_outgoing_redelegations_handle(&dest_validator) - .collect_map(&storage) - .unwrap(); - let tot_bonds_dest = total_bonded_handle(&dest_validator) - .get_data_handler() - .collect_map(&storage) - .unwrap(); - let tot_unbonds_dest = total_unbonded_handle(&dest_validator) - .collect_map(&storage) - .unwrap(); - let tot_redel_bonds_dest = - validator_total_redelegated_bonded_handle(&dest_validator) - .collect_map(&storage) - .unwrap(); - let tot_redel_unbonds_dest = - validator_total_redelegated_unbonded_handle(&dest_validator) - .collect_map(&storage) - .unwrap(); - dbg!( - &incoming_redels_dest, - &outgoing_redels_dest, - &tot_bonds_dest, - &tot_unbonds_dest, - &tot_redel_bonds_dest, - &tot_redel_unbonds_dest - ); - - // Src val - println!("\nSRC VALIDATOR\n"); - - let incoming_redels_src = - validator_incoming_redelegations_handle(&src_validator) - .collect_map(&storage) - .unwrap(); - let outgoing_redels_src = - validator_outgoing_redelegations_handle(&src_validator) - .collect_map(&storage) - .unwrap(); - let tot_bonds_src = total_bonded_handle(&src_validator) - .get_data_handler() - .collect_map(&storage) - .unwrap(); - let tot_unbonds_src = total_unbonded_handle(&src_validator) - .collect_map(&storage) - .unwrap(); - let tot_redel_bonds_src = - validator_total_redelegated_bonded_handle(&src_validator) - .collect_map(&storage) - .unwrap(); - let tot_redel_unbonds_src = - validator_total_redelegated_unbonded_handle(&src_validator) - .collect_map(&storage) - .unwrap(); - dbg!( - &incoming_redels_src, - &outgoing_redels_src, - &tot_bonds_src, - &tot_unbonds_src, - &tot_redel_bonds_src, - &tot_redel_unbonds_src - ); - - // Checks - let redelegated = delegator_redelegated_bonds_handle(&delegator) - .at(&dest_validator) - .at(&(current_epoch + params.pipeline_len)) - .at(&src_validator) - .get(&storage, &(init_epoch + params.pipeline_len)) - .unwrap() - .unwrap(); - assert_eq!(redelegated, amount_redelegate); - - let redel_start_epoch = - validator_incoming_redelegations_handle(&dest_validator) - .get(&storage, &delegator) - .unwrap() - .unwrap(); - assert_eq!(redel_start_epoch, current_epoch + params.pipeline_len); - - let redelegated = validator_outgoing_redelegations_handle(&src_validator) - .at(&dest_validator) - .at(¤t_epoch.prev()) - .get(&storage, ¤t_epoch) - .unwrap() - .unwrap(); - assert_eq!(redelegated, amount_redelegate); - - // Advance three epochs - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - - // Unbond in epoch 11 from dest_validator - println!( - "\nUNBONDING {} TOKENS FROM {}\n", - amount_unbond.to_string_native(), - &dest_validator - ); - let _ = unbond_tokens( - &mut storage, - Some(&delegator), - &dest_validator, - amount_unbond, - current_epoch, - false, - ) - .unwrap(); - - println!("\nAFTER UNBONDING\n"); - println!("\nDELEGATOR\n"); - - let bonds_src = bond_handle(&delegator, &src_validator) - .get_data_handler() - .collect_map(&storage) - .unwrap(); - let bonds_dest = bond_handle(&delegator, &dest_validator) - .get_data_handler() - .collect_map(&storage) - .unwrap(); - let unbonds_src = unbond_handle(&delegator, &src_validator) - .collect_map(&storage) - .unwrap(); - let unbonds_dest = unbond_handle(&delegator, &dest_validator) - .collect_map(&storage) - .unwrap(); - let redel_bonds = delegator_redelegated_bonds_handle(&delegator) - .collect_map(&storage) - .unwrap(); - let redel_unbonds = delegator_redelegated_unbonds_handle(&delegator) - .collect_map(&storage) - .unwrap(); - - dbg!( - &bonds_src, - &bonds_dest, - &unbonds_src, - &unbonds_dest, - &redel_bonds, - &redel_unbonds - ); - - println!("\nDEST VALIDATOR\n"); - - let incoming_redels_dest = - validator_incoming_redelegations_handle(&dest_validator) - .collect_map(&storage) - .unwrap(); - let outgoing_redels_dest = - validator_outgoing_redelegations_handle(&dest_validator) - .collect_map(&storage) - .unwrap(); - let tot_bonds_dest = total_bonded_handle(&dest_validator) - .get_data_handler() - .collect_map(&storage) - .unwrap(); - let tot_unbonds_dest = total_unbonded_handle(&dest_validator) - .collect_map(&storage) - .unwrap(); - let tot_redel_bonds_dest = - validator_total_redelegated_bonded_handle(&dest_validator) - .collect_map(&storage) - .unwrap(); - let tot_redel_unbonds_dest = - validator_total_redelegated_unbonded_handle(&dest_validator) - .collect_map(&storage) - .unwrap(); - dbg!( - &incoming_redels_dest, - &outgoing_redels_dest, - &tot_bonds_dest, - &tot_unbonds_dest, - &tot_redel_bonds_dest, - &tot_redel_unbonds_dest - ); - - // Advance one epoch - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - - // Discover evidence - slash( - &mut storage, - ¶ms, - current_epoch, - init_epoch + 2 * params.pipeline_len, - 0u64, - SlashType::DuplicateVote, - &src_validator, - current_epoch.next(), - ) - .unwrap(); - - let bond_start = init_epoch + params.pipeline_len; - let redelegation_end = bond_start + params.pipeline_len + 1u64; - let unbond_end = - redelegation_end + params.withdrawable_epoch_offset() + 1u64; - let unbond_materialized = redelegation_end + params.pipeline_len + 1u64; - - // Checks - let redelegated_remaining = delegator_redelegated_bonds_handle(&delegator) - .at(&dest_validator) - .at(&redelegation_end) - .at(&src_validator) - .get(&storage, &bond_start) - .unwrap() - .unwrap_or_default(); - assert_eq!(redelegated_remaining, amount_redelegate - amount_unbond); - - let redel_unbonded = delegator_redelegated_unbonds_handle(&delegator) - .at(&dest_validator) - .at(&redelegation_end) - .at(&unbond_end) - .at(&src_validator) - .get(&storage, &bond_start) - .unwrap() - .unwrap(); - assert_eq!(redel_unbonded, amount_unbond); - - dbg!(unbond_materialized, redelegation_end, bond_start); - let total_redel_unbonded = - validator_total_redelegated_unbonded_handle(&dest_validator) - .at(&unbond_materialized) - .at(&redelegation_end) - .at(&src_validator) - .get(&storage, &bond_start) - .unwrap() - .unwrap(); - assert_eq!(total_redel_unbonded, amount_unbond); - - // Advance to withdrawal epoch - loop { - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - if current_epoch == unbond_end { - break; - } - } - - // Withdraw - withdraw_tokens( - &mut storage, - Some(&delegator), - &dest_validator, - current_epoch, - ) - .unwrap(); - - assert!( - delegator_redelegated_unbonds_handle(&delegator) - .at(&dest_validator) - .is_empty(&storage) - .unwrap() - ); - - let delegator_balance = storage - .read::(&token::balance_key(&staking_token, &delegator)) - .unwrap() - .unwrap_or_default(); - assert_eq!(delegator_balance, del_balance - amount_delegate); -} - -fn test_chain_redelegations_aux(mut validators: Vec) { - validators.sort_by(|a, b| b.tokens.cmp(&a.tokens)); - - let src_validator = validators[0].address.clone(); - let _init_stake_src = validators[0].tokens; - let dest_validator = validators[1].address.clone(); - let _init_stake_dest = validators[1].tokens; - let dest_validator_2 = validators[2].address.clone(); - let _init_stake_dest_2 = validators[2].tokens; - - let mut storage = TestWlStorage::default(); - let params = OwnedPosParams { - unbonding_len: 4, - ..Default::default() - }; - - // Genesis - let mut current_epoch = storage.storage.block.epoch; - let params = test_init_genesis( - &mut storage, - params, - validators.clone().into_iter(), - current_epoch, - ) - .unwrap(); - storage.commit_block().unwrap(); - - // Get a delegator with some tokens - let staking_token = staking_token_address(&storage); - let delegator = address::testing::gen_implicit_address(); - let del_balance = token::Amount::from_uint(1_000_000, 0).unwrap(); - credit_tokens(&mut storage, &staking_token, &delegator, del_balance) - .unwrap(); - - // Delegate in epoch 0 to src_validator - let bond_amount: token::Amount = 100.into(); - super::bond_tokens( - &mut storage, - Some(&delegator), - &src_validator, - bond_amount, - current_epoch, - None, - ) - .unwrap(); - - let bond_start = current_epoch + params.pipeline_len; - - // Advance one epoch - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - - // Redelegate in epoch 1 to dest_validator - let redel_amount_1: token::Amount = 58.into(); - super::redelegate_tokens( - &mut storage, - &delegator, - &src_validator, - &dest_validator, - current_epoch, - redel_amount_1, - ) - .unwrap(); - - let redel_start = current_epoch; - let redel_end = current_epoch + params.pipeline_len; - - // Checks ---------------- - - // Dest validator should have an incoming redelegation - let incoming_redelegation = - validator_incoming_redelegations_handle(&dest_validator) - .get(&storage, &delegator) - .unwrap(); - assert_eq!(incoming_redelegation, Some(redel_end)); - - // Src validator should have an outoging redelegation - let outgoing_redelegation = - validator_outgoing_redelegations_handle(&src_validator) - .at(&dest_validator) - .at(&bond_start) - .get(&storage, &redel_start) - .unwrap(); - assert_eq!(outgoing_redelegation, Some(redel_amount_1)); - - // Delegator should have redelegated bonds - let del_total_redelegated_bonded = - delegator_redelegated_bonds_handle(&delegator) - .at(&dest_validator) - .at(&redel_end) - .at(&src_validator) - .get(&storage, &bond_start) - .unwrap() - .unwrap_or_default(); - assert_eq!(del_total_redelegated_bonded, redel_amount_1); - - // There should be delegator bonds for both src and dest validators - let bonded_src = bond_handle(&delegator, &src_validator); - let bonded_dest = bond_handle(&delegator, &dest_validator); - assert_eq!( - bonded_src - .get_delta_val(&storage, bond_start) - .unwrap() - .unwrap_or_default(), - bond_amount - redel_amount_1 - ); - assert_eq!( - bonded_dest - .get_delta_val(&storage, redel_end) - .unwrap() - .unwrap_or_default(), - redel_amount_1 - ); - - // The dest validator should have total redelegated bonded tokens - let dest_total_redelegated_bonded = - validator_total_redelegated_bonded_handle(&dest_validator) - .at(&redel_end) - .at(&src_validator) - .get(&storage, &bond_start) - .unwrap() - .unwrap_or_default(); - assert_eq!(dest_total_redelegated_bonded, redel_amount_1); - - // The dest validator's total bonded should have an entry for the genesis - // bond and the redelegation - let dest_total_bonded = total_bonded_handle(&dest_validator) - .get_data_handler() - .collect_map(&storage) - .unwrap(); - assert!( - dest_total_bonded.len() == 2 - && dest_total_bonded.contains_key(&Epoch::default()) - ); - assert_eq!( - dest_total_bonded - .get(&redel_end) - .cloned() - .unwrap_or_default(), - redel_amount_1 - ); - - // The src validator should have a total bonded entry for the original bond - // accounting for the redelegation - assert_eq!( - total_bonded_handle(&src_validator) - .get_delta_val(&storage, bond_start) - .unwrap() - .unwrap_or_default(), - bond_amount - redel_amount_1 - ); - - // The src validator should have a total unbonded entry due to the - // redelegation - let src_total_unbonded = total_unbonded_handle(&src_validator) - .at(&redel_end) - .get(&storage, &bond_start) - .unwrap() - .unwrap_or_default(); - assert_eq!(src_total_unbonded, redel_amount_1); - - // Attempt to redelegate in epoch 3 to dest_validator - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - - let redel_amount_2: token::Amount = 23.into(); - let redel_att = super::redelegate_tokens( - &mut storage, - &delegator, - &dest_validator, - &dest_validator_2, - current_epoch, - redel_amount_2, - ); - assert!(redel_att.is_err()); - - // Advance to right before the redelegation can be redelegated again - assert_eq!(redel_end, current_epoch); - let epoch_can_redel = - redel_end.prev() + params.slash_processing_epoch_offset(); - loop { - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - if current_epoch == epoch_can_redel.prev() { - break; - } - } - - // Attempt to redelegate in epoch before we actually are able to - let redel_att = super::redelegate_tokens( - &mut storage, - &delegator, - &dest_validator, - &dest_validator_2, - current_epoch, - redel_amount_2, - ); - assert!(redel_att.is_err()); - - // Advance one more epoch - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - - // Redelegate from dest_validator to dest_validator_2 now - super::redelegate_tokens( - &mut storage, - &delegator, - &dest_validator, - &dest_validator_2, - current_epoch, - redel_amount_2, - ) - .unwrap(); - - let redel_2_start = current_epoch; - let redel_2_end = current_epoch + params.pipeline_len; - - // Checks ----------------------------------- - - // Both the dest validator and dest validator 2 should have incoming - // redelegations - let incoming_redelegation_1 = - validator_incoming_redelegations_handle(&dest_validator) - .get(&storage, &delegator) - .unwrap(); - assert_eq!(incoming_redelegation_1, Some(redel_end)); - let incoming_redelegation_2 = - validator_incoming_redelegations_handle(&dest_validator_2) - .get(&storage, &delegator) - .unwrap(); - assert_eq!(incoming_redelegation_2, Some(redel_2_end)); - - // Both the src validator and dest validator should have outgoing - // redelegations - let outgoing_redelegation_1 = - validator_outgoing_redelegations_handle(&src_validator) - .at(&dest_validator) - .at(&bond_start) - .get(&storage, &redel_start) - .unwrap(); - assert_eq!(outgoing_redelegation_1, Some(redel_amount_1)); - - let outgoing_redelegation_2 = - validator_outgoing_redelegations_handle(&dest_validator) - .at(&dest_validator_2) - .at(&redel_end) - .get(&storage, &redel_2_start) - .unwrap(); - assert_eq!(outgoing_redelegation_2, Some(redel_amount_2)); - - // All three validators should have bonds - let bonded_dest2 = bond_handle(&delegator, &dest_validator_2); - assert_eq!( - bonded_src - .get_delta_val(&storage, bond_start) - .unwrap() - .unwrap_or_default(), - bond_amount - redel_amount_1 - ); - assert_eq!( - bonded_dest - .get_delta_val(&storage, redel_end) - .unwrap() - .unwrap_or_default(), - redel_amount_1 - redel_amount_2 - ); - assert_eq!( - bonded_dest2 - .get_delta_val(&storage, redel_2_end) - .unwrap() - .unwrap_or_default(), - redel_amount_2 - ); - - // There should be no unbond entries - let unbond_src = unbond_handle(&delegator, &src_validator); - let unbond_dest = unbond_handle(&delegator, &dest_validator); - assert!(unbond_src.is_empty(&storage).unwrap()); - assert!(unbond_dest.is_empty(&storage).unwrap()); - - // The dest validator should have some total unbonded due to the second - // redelegation - let dest_total_unbonded = total_unbonded_handle(&dest_validator) - .at(&redel_2_end) - .get(&storage, &redel_end) - .unwrap(); - assert_eq!(dest_total_unbonded, Some(redel_amount_2)); - - // Delegator should have redelegated bonds due to both redelegations - let del_redelegated_bonds = delegator_redelegated_bonds_handle(&delegator); - assert_eq!( - Some(redel_amount_1 - redel_amount_2), - del_redelegated_bonds - .at(&dest_validator) - .at(&redel_end) - .at(&src_validator) - .get(&storage, &bond_start) - .unwrap() - ); - assert_eq!( - Some(redel_amount_2), - del_redelegated_bonds - .at(&dest_validator_2) - .at(&redel_2_end) - .at(&dest_validator) - .get(&storage, &redel_end) - .unwrap() - ); - - // Delegator redelegated unbonds should be empty - assert!( - delegator_redelegated_unbonds_handle(&delegator) - .is_empty(&storage) - .unwrap() - ); - - // Both the dest validator and dest validator 2 should have total - // redelegated bonds - let dest_redelegated_bonded = - validator_total_redelegated_bonded_handle(&dest_validator) - .at(&redel_end) - .at(&src_validator) - .get(&storage, &bond_start) - .unwrap() - .unwrap_or_default(); - let dest2_redelegated_bonded = - validator_total_redelegated_bonded_handle(&dest_validator_2) - .at(&redel_2_end) - .at(&dest_validator) - .get(&storage, &redel_end) - .unwrap() - .unwrap_or_default(); - assert_eq!(dest_redelegated_bonded, redel_amount_1 - redel_amount_2); - assert_eq!(dest2_redelegated_bonded, redel_amount_2); - - // Total redelegated unbonded should be empty for src_validator and - // dest_validator_2 - assert!( - validator_total_redelegated_unbonded_handle(&dest_validator_2) - .is_empty(&storage) - .unwrap() - ); - assert!( - validator_total_redelegated_unbonded_handle(&src_validator) - .is_empty(&storage) - .unwrap() - ); - - // The dest_validator should have total_redelegated unbonded - let tot_redel_unbonded = - validator_total_redelegated_unbonded_handle(&dest_validator) - .at(&redel_2_end) - .at(&redel_end) - .at(&src_validator) - .get(&storage, &bond_start) - .unwrap() - .unwrap_or_default(); - assert_eq!(tot_redel_unbonded, redel_amount_2); -} - -/// SM test case 1 from Brent -#[test] -fn test_from_sm_case_1() { - use namada_core::types::address::testing::established_address_4; - - let mut storage = TestWlStorage::default(); - let gov_params = - namada_governance::parameters::GovernanceParameters::default(); - gov_params.init_storage(&mut storage).unwrap(); - write_pos_params(&mut storage, &OwnedPosParams::default()).unwrap(); - - let validator = established_address_1(); - let redeleg_src_1 = established_address_2(); - let redeleg_src_2 = established_address_3(); - let owner = established_address_4(); - let unbond_amount = token::Amount::from(3130688); - println!( - "Owner: {owner}\nValidator: {validator}\nRedeleg src 1: \ - {redeleg_src_1}\nRedeleg src 2: {redeleg_src_2}" - ); - - // Validator's incoming redelegations - let outer_epoch_1 = Epoch(27); - // from redeleg_src_1 - let epoch_1_redeleg_1 = token::Amount::from(8516); - // from redeleg_src_2 - let epoch_1_redeleg_2 = token::Amount::from(5704386); - let outer_epoch_2 = Epoch(30); - // from redeleg_src_2 - let epoch_2_redeleg_2 = token::Amount::from(1035191); - - // Insert the data - bonds and redelegated bonds - let bonds_handle = bond_handle(&owner, &validator); - bonds_handle - .add( - &mut storage, - epoch_1_redeleg_1 + epoch_1_redeleg_2, - outer_epoch_1, - 0, - ) - .unwrap(); - bonds_handle - .add(&mut storage, epoch_2_redeleg_2, outer_epoch_2, 0) - .unwrap(); - - let redelegated_bonds_map_1 = delegator_redelegated_bonds_handle(&owner) - .at(&validator) - .at(&outer_epoch_1); - redelegated_bonds_map_1 - .at(&redeleg_src_1) - .insert(&mut storage, Epoch(14), epoch_1_redeleg_1) - .unwrap(); - redelegated_bonds_map_1 - .at(&redeleg_src_2) - .insert(&mut storage, Epoch(18), epoch_1_redeleg_2) - .unwrap(); - let redelegated_bonds_map_1 = delegator_redelegated_bonds_handle(&owner) - .at(&validator) - .at(&outer_epoch_1); - - let redelegated_bonds_map_2 = delegator_redelegated_bonds_handle(&owner) - .at(&validator) - .at(&outer_epoch_2); - redelegated_bonds_map_2 - .at(&redeleg_src_2) - .insert(&mut storage, Epoch(18), epoch_2_redeleg_2) - .unwrap(); - - // Find the modified redelegation the same way as `unbond_tokens` - let bonds_to_unbond = find_bonds_to_remove( - &storage, - &bonds_handle.get_data_handler(), - unbond_amount, - ) - .unwrap(); - dbg!(&bonds_to_unbond); - - let (new_entry_epoch, new_bond_amount) = bonds_to_unbond.new_entry.unwrap(); - assert_eq!(outer_epoch_1, new_entry_epoch); - // The modified bond should be sum of all redelegations less the unbonded - // amouunt - assert_eq!( - epoch_1_redeleg_1 + epoch_1_redeleg_2 + epoch_2_redeleg_2 - - unbond_amount, - new_bond_amount - ); - // The current bond should be sum of redelegations fom the modified epoch - let cur_bond_amount = bonds_handle - .get_delta_val(&storage, new_entry_epoch) - .unwrap() - .unwrap_or_default(); - assert_eq!(epoch_1_redeleg_1 + epoch_1_redeleg_2, cur_bond_amount); - - let mr = compute_modified_redelegation( - &storage, - &redelegated_bonds_map_1, - new_entry_epoch, - cur_bond_amount - new_bond_amount, - ) - .unwrap(); - - let exp_mr = ModifiedRedelegation { - epoch: Some(Epoch(27)), - validators_to_remove: BTreeSet::from_iter([redeleg_src_2.clone()]), - validator_to_modify: Some(redeleg_src_2), - epochs_to_remove: BTreeSet::from_iter([Epoch(18)]), - epoch_to_modify: Some(Epoch(18)), - new_amount: Some(token::Amount::from(3608889)), - }; - - pretty_assertions::assert_eq!(mr, exp_mr); -} - -/// Test precisely that we are not overslashing, as originally discovered by Tomas in this issue: https://github.com/informalsystems/partnership-heliax/issues/74 -fn test_overslashing_aux(mut validators: Vec) { - assert_eq!(validators.len(), 4); - - let params = OwnedPosParams { - unbonding_len: 4, - ..Default::default() - }; - - let offending_stake = token::Amount::native_whole(110); - let other_stake = token::Amount::native_whole(100); - - // Set stakes so we know we will get a slashing rate between 0.5 -1.0 - validators[0].tokens = offending_stake; - validators[1].tokens = other_stake; - validators[2].tokens = other_stake; - validators[3].tokens = other_stake; - - // Get the offending validator - let validator = validators[0].address.clone(); - - println!("\nTest inputs: {params:?}, genesis validators: {validators:#?}"); - let mut storage = TestWlStorage::default(); - - // Genesis - let mut current_epoch = storage.storage.block.epoch; - let params = test_init_genesis( - &mut storage, - params, - validators.clone().into_iter(), - current_epoch, - ) - .unwrap(); - storage.commit_block().unwrap(); - - // Get a delegator with some tokens - let staking_token = storage.storage.native_token.clone(); - let delegator = address::testing::gen_implicit_address(); - let amount_del = token::Amount::native_whole(5); - credit_tokens(&mut storage, &staking_token, &delegator, amount_del) - .unwrap(); - - // Delegate tokens in epoch 0 to validator - bond_tokens( - &mut storage, - Some(&delegator), - &validator, - amount_del, - current_epoch, - None, - ) - .unwrap(); - - let self_bond_epoch = current_epoch; - let delegation_epoch = current_epoch + params.pipeline_len; - - // Advance to pipeline epoch - for _ in 0..params.pipeline_len { - current_epoch = advance_epoch(&mut storage, ¶ms); - } - assert_eq!(delegation_epoch, current_epoch); - - // Find a misbehavior committed in epoch 0 - slash( - &mut storage, - ¶ms, - current_epoch, - self_bond_epoch, - 0_u64, - SlashType::DuplicateVote, - &validator, - current_epoch.next(), - ) - .unwrap(); - - // Find a misbehavior committed in current epoch - slash( - &mut storage, - ¶ms, - current_epoch, - delegation_epoch, - 0_u64, - SlashType::DuplicateVote, - &validator, - current_epoch.next(), - ) - .unwrap(); - - let processing_epoch_1 = - self_bond_epoch + params.slash_processing_epoch_offset(); - let processing_epoch_2 = - delegation_epoch + params.slash_processing_epoch_offset(); - - // Advance to processing epoch 1 - loop { - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - if current_epoch == processing_epoch_1 { - break; - } - } - - let total_stake_1 = offending_stake + 3 * other_stake; - let stake_frac = Dec::from(offending_stake) / Dec::from(total_stake_1); - let slash_rate_1 = Dec::from_str("9.0").unwrap() * stake_frac * stake_frac; - dbg!(&slash_rate_1); - - let exp_slashed_1 = offending_stake.mul_ceil(slash_rate_1); - - // Check that the proper amount was slashed - let epoch = current_epoch.next(); - let validator_stake = - read_validator_stake(&storage, ¶ms, &validator, epoch).unwrap(); - let exp_validator_stake = offending_stake - exp_slashed_1 + amount_del; - assert_eq!(validator_stake, exp_validator_stake); - - let total_stake = read_total_stake(&storage, ¶ms, epoch).unwrap(); - let exp_total_stake = - offending_stake - exp_slashed_1 + amount_del + 3 * other_stake; - assert_eq!(total_stake, exp_total_stake); - - let self_bond_id = BondId { - source: validator.clone(), - validator: validator.clone(), - }; - let bond_amount = - crate::bond_amount(&storage, &self_bond_id, epoch).unwrap(); - let exp_bond_amount = offending_stake - exp_slashed_1; - assert_eq!(bond_amount, exp_bond_amount); - - // Advance to processing epoch 2 - loop { - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - if current_epoch == processing_epoch_2 { - break; - } - } - - let total_stake_2 = offending_stake + amount_del + 3 * other_stake; - let stake_frac = - Dec::from(offending_stake + amount_del) / Dec::from(total_stake_2); - let slash_rate_2 = Dec::from_str("9.0").unwrap() * stake_frac * stake_frac; - dbg!(&slash_rate_2); - - let exp_slashed_from_delegation = amount_del.mul_ceil(slash_rate_2); - - // Check that the proper amount was slashed. We expect that all of the - // validator self-bond has been slashed and some of the delegation has been - // slashed due to the second infraction. - let epoch = current_epoch.next(); - - let validator_stake = - read_validator_stake(&storage, ¶ms, &validator, epoch).unwrap(); - let exp_validator_stake = amount_del - exp_slashed_from_delegation; - assert_eq!(validator_stake, exp_validator_stake); - - let total_stake = read_total_stake(&storage, ¶ms, epoch).unwrap(); - let exp_total_stake = - amount_del - exp_slashed_from_delegation + 3 * other_stake; - assert_eq!(total_stake, exp_total_stake); - - let delegation_id = BondId { - source: delegator.clone(), - validator: validator.clone(), - }; - let delegation_amount = - crate::bond_amount(&storage, &delegation_id, epoch).unwrap(); - let exp_del_amount = amount_del - exp_slashed_from_delegation; - assert_eq!(delegation_amount, exp_del_amount); - - let self_bond_amount = - crate::bond_amount(&storage, &self_bond_id, epoch).unwrap(); - let exp_bond_amount = token::Amount::zero(); - assert_eq!(self_bond_amount, exp_bond_amount); -} - -fn test_unslashed_bond_amount_aux(validators: Vec) { - let mut storage = TestWlStorage::default(); - let params = OwnedPosParams { - unbonding_len: 4, - ..Default::default() - }; - - // Genesis - let mut current_epoch = storage.storage.block.epoch; - let params = test_init_genesis( - &mut storage, - params, - validators.clone().into_iter(), - current_epoch, - ) - .unwrap(); - storage.commit_block().unwrap(); - - let validator1 = validators[0].address.clone(); - let validator2 = validators[1].address.clone(); - - // Get a delegator with some tokens - let staking_token = staking_token_address(&storage); - let delegator = address::testing::gen_implicit_address(); - let del_balance = token::Amount::from_uint(1_000_000, 0).unwrap(); - credit_tokens(&mut storage, &staking_token, &delegator, del_balance) - .unwrap(); - - // Bond to validator 1 - super::bond_tokens( - &mut storage, - Some(&delegator), - &validator1, - 10_000.into(), - current_epoch, - None, - ) - .unwrap(); - - // Unbond some from validator 1 - super::unbond_tokens( - &mut storage, - Some(&delegator), - &validator1, - 1_342.into(), - current_epoch, - false, - ) - .unwrap(); - - // Redelegate some from validator 1 -> 2 - super::redelegate_tokens( - &mut storage, - &delegator, - &validator1, - &validator2, - current_epoch, - 1_875.into(), - ) - .unwrap(); - - // Unbond some from validator 2 - super::unbond_tokens( - &mut storage, - Some(&delegator), - &validator2, - 584.into(), - current_epoch, - false, - ) - .unwrap(); - - // Advance an epoch - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - - // Bond to validator 1 - super::bond_tokens( - &mut storage, - Some(&delegator), - &validator1, - 384.into(), - current_epoch, - None, - ) - .unwrap(); - - // Unbond some from validator 1 - super::unbond_tokens( - &mut storage, - Some(&delegator), - &validator1, - 144.into(), - current_epoch, - false, - ) - .unwrap(); - - // Redelegate some from validator 1 -> 2 - super::redelegate_tokens( - &mut storage, - &delegator, - &validator1, - &validator2, - current_epoch, - 3_448.into(), - ) - .unwrap(); - - // Unbond some from validator 2 - super::unbond_tokens( - &mut storage, - Some(&delegator), - &validator2, - 699.into(), - current_epoch, - false, - ) - .unwrap(); - - // Advance an epoch - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - - // Bond to validator 1 - super::bond_tokens( - &mut storage, - Some(&delegator), - &validator1, - 4_384.into(), - current_epoch, - None, - ) - .unwrap(); - - // Redelegate some from validator 1 -> 2 - super::redelegate_tokens( - &mut storage, - &delegator, - &validator1, - &validator2, - current_epoch, - 1_008.into(), - ) - .unwrap(); - - // Unbond some from validator 2 - super::unbond_tokens( - &mut storage, - Some(&delegator), - &validator2, - 3_500.into(), - current_epoch, - false, - ) - .unwrap(); - - // Checks - let val1_init_stake = validators[0].tokens; - - for epoch in Epoch::iter_bounds_inclusive( - Epoch(0), - current_epoch + params.pipeline_len, - ) { - let bond_amount = crate::bond_amount( - &storage, - &BondId { - source: delegator.clone(), - validator: validator1.clone(), - }, - epoch, - ) - .unwrap_or_default(); - - let val_stake = - crate::read_validator_stake(&storage, ¶ms, &validator1, epoch) - .unwrap(); - // dbg!(&bond_amount); - assert_eq!(val_stake - val1_init_stake, bond_amount); - } -} - -fn test_log_block_rewards_aux( - validators: Vec, - params: OwnedPosParams, -) { - tracing::info!( - "New case with {} validators: {:#?}", - validators.len(), - validators - .iter() - .map(|v| (&v.address, v.tokens.to_string_native())) - .collect::>() - ); - let mut s = TestWlStorage::default(); - // Init genesis - let current_epoch = s.storage.block.epoch; - let params = test_init_genesis( - &mut s, - params, - validators.clone().into_iter(), - current_epoch, - ) - .unwrap(); - s.commit_block().unwrap(); - let total_stake = - crate::get_total_consensus_stake(&s, current_epoch, ¶ms).unwrap(); - let consensus_set = - crate::read_consensus_validator_set_addresses(&s, current_epoch) - .unwrap(); - let proposer_address = consensus_set.iter().next().unwrap().clone(); - - tracing::info!( - ?params.block_proposer_reward, - ?params.block_vote_reward, - ); - tracing::info!(?proposer_address,); - - // Rewards accumulator should be empty at first - let rewards_handle = crate::rewards_accumulator_handle(); - assert!(rewards_handle.is_empty(&s).unwrap()); - - let mut last_rewards = BTreeMap::default(); - - let num_blocks = 100; - // Loop through `num_blocks`, log rewards & check results - for i in 0..num_blocks { - tracing::info!(""); - tracing::info!("Block {}", i + 1); - - // A helper closure to prepare minimum required votes - let prep_votes = |epoch| { - // Ceil of 2/3 of total stake - let min_required_votes = total_stake.mul_ceil(Dec::two() / 3); - - let mut total_votes = token::Amount::zero(); - let mut non_voters = HashSet::
::default(); - let mut prep_vote = |validator| { - // Add validator vote if it's in consensus set and if we don't - // yet have min required votes - if consensus_set.contains(validator) - && total_votes < min_required_votes - { - let stake = - read_validator_stake(&s, ¶ms, validator, epoch) - .unwrap(); - total_votes += stake; - let validator_vp = - into_tm_voting_power(params.tm_votes_per_token, stake) - as u64; - tracing::info!("Validator {validator} signed"); - Some(VoteInfo { - validator_address: validator.clone(), - validator_vp, - }) - } else { - non_voters.insert(validator.clone()); - None - } - }; - - let votes: Vec = validators - .iter() - .rev() - .filter_map(|validator| prep_vote(&validator.address)) - .collect(); - (votes, total_votes, non_voters) - }; - - let (votes, signing_stake, non_voters) = prep_votes(current_epoch); - crate::log_block_rewards( - &mut s, - current_epoch, - &proposer_address, - votes.clone(), - ) - .unwrap(); - - assert!(!rewards_handle.is_empty(&s).unwrap()); - - let rewards_calculator = PosRewardsCalculator { - proposer_reward: params.block_proposer_reward, - signer_reward: params.block_vote_reward, - signing_stake, - total_stake, - }; - let coeffs = rewards_calculator.get_reward_coeffs().unwrap(); - tracing::info!(?coeffs); - - // Check proposer reward - let stake = - read_validator_stake(&s, ¶ms, &proposer_address, current_epoch) - .unwrap(); - let proposer_signing_reward = votes.iter().find_map(|vote| { - if vote.validator_address == proposer_address { - let signing_fraction = - Dec::from(stake) / Dec::from(signing_stake); - Some(coeffs.signer_coeff * signing_fraction) - } else { - None - } - }); - let expected_proposer_rewards = last_rewards.get(&proposer_address).copied().unwrap_or_default() + - // Proposer reward - coeffs.proposer_coeff - // Consensus validator reward - + (coeffs.active_val_coeff - * (Dec::from(stake) / Dec::from(total_stake))) - // Signing reward (if proposer voted) - + proposer_signing_reward - .unwrap_or_default(); - tracing::info!( - "Expected proposer rewards: {expected_proposer_rewards}. Signed \ - block: {}", - proposer_signing_reward.is_some() - ); - assert_eq!( - rewards_handle.get(&s, &proposer_address).unwrap(), - Some(expected_proposer_rewards) - ); - - // Check voters rewards - for VoteInfo { - validator_address, .. - } in votes.iter() - { - // Skip proposer, in case voted - already checked - if validator_address == &proposer_address { - continue; - } - - let stake = read_validator_stake( - &s, - ¶ms, - validator_address, - current_epoch, - ) - .unwrap(); - let signing_fraction = Dec::from(stake) / Dec::from(signing_stake); - let expected_signer_rewards = last_rewards - .get(validator_address) - .copied() - .unwrap_or_default() - + coeffs.signer_coeff * signing_fraction - + (coeffs.active_val_coeff - * (Dec::from(stake) / Dec::from(total_stake))); - tracing::info!( - "Expected signer {validator_address} rewards: \ - {expected_signer_rewards}" - ); - assert_eq!( - rewards_handle.get(&s, validator_address).unwrap(), - Some(expected_signer_rewards) - ); - } - - // Check non-voters rewards, if any - for address in non_voters { - // Skip proposer, in case it didn't vote - already checked - if address == proposer_address { - continue; - } - - if consensus_set.contains(&address) { - let stake = - read_validator_stake(&s, ¶ms, &address, current_epoch) - .unwrap(); - let expected_non_signer_rewards = - last_rewards.get(&address).copied().unwrap_or_default() - + coeffs.active_val_coeff - * (Dec::from(stake) / Dec::from(total_stake)); - tracing::info!( - "Expected non-signer {address} rewards: \ - {expected_non_signer_rewards}" - ); - assert_eq!( - rewards_handle.get(&s, &address).unwrap(), - Some(expected_non_signer_rewards) - ); - } else { - let last_reward = last_rewards.get(&address).copied(); - assert_eq!( - rewards_handle.get(&s, &address).unwrap(), - last_reward - ); - } - } - s.commit_block().unwrap(); - - last_rewards = - crate::rewards_accumulator_handle().collect_map(&s).unwrap(); - - let rewards_sum: Dec = last_rewards.values().copied().sum(); - let expected_sum = Dec::one() * (i as u64 + 1); - let err_tolerance = Dec::new(1, 9).unwrap(); - let fail_msg = format!( - "Expected rewards sum at block {} to be {expected_sum}, got \ - {rewards_sum}. Error tolerance {err_tolerance}.", - i + 1 - ); - assert!(expected_sum <= rewards_sum + err_tolerance, "{fail_msg}"); - assert!(rewards_sum <= expected_sum, "{fail_msg}"); - } -} - -fn test_update_rewards_products_aux(validators: Vec) { - tracing::info!( - "New case with {} validators: {:#?}", - validators.len(), - validators - .iter() - .map(|v| (&v.address, v.tokens.to_string_native())) - .collect::>() - ); - let mut s = TestWlStorage::default(); - // Init genesis - let current_epoch = s.storage.block.epoch; - let params = OwnedPosParams::default(); - let params = test_init_genesis( - &mut s, - params, - validators.into_iter(), - current_epoch, - ) - .unwrap(); - s.commit_block().unwrap(); - - let staking_token = staking_token_address(&s); - let consensus_set = - crate::read_consensus_validator_set_addresses(&s, current_epoch) - .unwrap(); - - // Start a new epoch - let current_epoch = advance_epoch(&mut s, ¶ms); - - // Read some data before applying rewards - let pos_balance_pre = - read_balance(&s, &staking_token, &address::POS).unwrap(); - let gov_balance_pre = - read_balance(&s, &staking_token, &address::GOV).unwrap(); - - let num_consensus_validators = consensus_set.len() as u64; - let accum_val = Dec::one() / num_consensus_validators; - let num_blocks_in_last_epoch = 1000; - - // Assign some reward accumulator values to consensus validator - for validator in &consensus_set { - crate::rewards_accumulator_handle() - .insert( - &mut s, - validator.clone(), - accum_val * num_blocks_in_last_epoch, - ) - .unwrap(); - } - - // Distribute inflation into rewards - let last_epoch = current_epoch.prev(); - let inflation = token::Amount::native_whole(10_000_000); - crate::update_rewards_products_and_mint_inflation( - &mut s, - ¶ms, - last_epoch, - num_blocks_in_last_epoch, - inflation, - &staking_token, - ) - .unwrap(); - - let pos_balance_post = - read_balance(&s, &staking_token, &address::POS).unwrap(); - let gov_balance_post = - read_balance(&s, &staking_token, &address::GOV).unwrap(); - - assert_eq!( - pos_balance_pre + gov_balance_pre + inflation, - pos_balance_post + gov_balance_post, - "Expected inflation to be minted to PoS and left-over amount to Gov" - ); - - let pos_credit = pos_balance_post - pos_balance_pre; - let gov_credit = gov_balance_post - gov_balance_pre; - assert!( - pos_credit > gov_credit, - "PoS must receive more tokens than Gov, but got {} in PoS and {} in \ - Gov", - pos_credit.to_string_native(), - gov_credit.to_string_native() - ); - - // Rewards accumulator must be cleared out - let rewards_handle = crate::rewards_accumulator_handle(); - assert!(rewards_handle.is_empty(&s).unwrap()); -} - -fn test_slashed_bond_amount_aux(validators: Vec) { - let mut storage = TestWlStorage::default(); - let params = OwnedPosParams { - unbonding_len: 4, - ..Default::default() - }; - - let init_tot_stake = validators - .clone() - .into_iter() - .fold(token::Amount::zero(), |acc, v| acc + v.tokens); - let val1_init_stake = validators[0].tokens; - - let mut validators = validators; - validators[0].tokens = (init_tot_stake - val1_init_stake) / 30; - - // Genesis - let mut current_epoch = storage.storage.block.epoch; - let params = test_init_genesis( - &mut storage, - params, - validators.clone().into_iter(), - current_epoch, - ) - .unwrap(); - storage.commit_block().unwrap(); - - let validator1 = validators[0].address.clone(); - let validator2 = validators[1].address.clone(); - - // Get a delegator with some tokens - let staking_token = staking_token_address(&storage); - let delegator = address::testing::gen_implicit_address(); - let del_balance = token::Amount::from_uint(1_000_000, 0).unwrap(); - credit_tokens(&mut storage, &staking_token, &delegator, del_balance) - .unwrap(); - - // Bond to validator 1 - super::bond_tokens( - &mut storage, - Some(&delegator), - &validator1, - 10_000.into(), - current_epoch, - None, - ) - .unwrap(); - - // Unbond some from validator 1 - super::unbond_tokens( - &mut storage, - Some(&delegator), - &validator1, - 1_342.into(), - current_epoch, - false, - ) - .unwrap(); - - // Redelegate some from validator 1 -> 2 - super::redelegate_tokens( - &mut storage, - &delegator, - &validator1, - &validator2, - current_epoch, - 1_875.into(), - ) - .unwrap(); - - // Unbond some from validator 2 - super::unbond_tokens( - &mut storage, - Some(&delegator), - &validator2, - 584.into(), - current_epoch, - false, - ) - .unwrap(); - - // Advance an epoch to 1 - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - - // Bond to validator 1 - super::bond_tokens( - &mut storage, - Some(&delegator), - &validator1, - 384.into(), - current_epoch, - None, - ) - .unwrap(); - - // Unbond some from validator 1 - super::unbond_tokens( - &mut storage, - Some(&delegator), - &validator1, - 144.into(), - current_epoch, - false, - ) - .unwrap(); - - // Redelegate some from validator 1 -> 2 - super::redelegate_tokens( - &mut storage, - &delegator, - &validator1, - &validator2, - current_epoch, - 3_448.into(), - ) - .unwrap(); - - // Unbond some from validator 2 - super::unbond_tokens( - &mut storage, - Some(&delegator), - &validator2, - 699.into(), - current_epoch, - false, - ) - .unwrap(); - - // Advance an epoch to ep 2 - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - - // Bond to validator 1 - super::bond_tokens( - &mut storage, - Some(&delegator), - &validator1, - 4_384.into(), - current_epoch, - None, - ) - .unwrap(); - - // Redelegate some from validator 1 -> 2 - super::redelegate_tokens( - &mut storage, - &delegator, - &validator1, - &validator2, - current_epoch, - 1_008.into(), - ) - .unwrap(); - - // Unbond some from validator 2 - super::unbond_tokens( - &mut storage, - Some(&delegator), - &validator2, - 3_500.into(), - current_epoch, - false, - ) - .unwrap(); - - // Advance two epochs to ep 4 - for _ in 0..2 { - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - } - - // Find some slashes committed in various epochs - super::slash( - &mut storage, - ¶ms, - current_epoch, - Epoch(1), - 1_u64, - SlashType::DuplicateVote, - &validator1, - current_epoch, - ) - .unwrap(); - super::slash( - &mut storage, - ¶ms, - current_epoch, - Epoch(2), - 1_u64, - SlashType::DuplicateVote, - &validator1, - current_epoch, - ) - .unwrap(); - super::slash( - &mut storage, - ¶ms, - current_epoch, - Epoch(2), - 1_u64, - SlashType::DuplicateVote, - &validator1, - current_epoch, - ) - .unwrap(); - super::slash( - &mut storage, - ¶ms, - current_epoch, - Epoch(3), - 1_u64, - SlashType::DuplicateVote, - &validator1, - current_epoch, - ) - .unwrap(); - - // Advance such that these slashes are all processed - for _ in 0..params.slash_processing_epoch_offset() { - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - } - - let pipeline_epoch = current_epoch + params.pipeline_len; - - let del_bond_amount = crate::bond_amount( - &storage, - &BondId { - source: delegator.clone(), - validator: validator1.clone(), - }, - pipeline_epoch, - ) - .unwrap_or_default(); - - let self_bond_amount = crate::bond_amount( - &storage, - &BondId { - source: validator1.clone(), - validator: validator1.clone(), - }, - pipeline_epoch, - ) - .unwrap_or_default(); - - let val_stake = crate::read_validator_stake( - &storage, - ¶ms, - &validator1, - pipeline_epoch, - ) - .unwrap(); - // dbg!(&val_stake); - // dbg!(&del_bond_amount); - // dbg!(&self_bond_amount); - - let diff = val_stake - self_bond_amount - del_bond_amount; - assert!(diff <= 2.into()); -} - -fn test_consensus_key_change_aux(validators: Vec) { - assert_eq!(validators.len(), 1); - - let params = OwnedPosParams { - unbonding_len: 4, - ..Default::default() - }; - let validator = validators[0].address.clone(); - - println!("\nTest inputs: {params:?}, genesis validators: {validators:#?}"); - let mut storage = TestWlStorage::default(); - - // Genesis - let mut current_epoch = storage.storage.block.epoch; - let params = test_init_genesis( - &mut storage, - params, - validators.into_iter(), - current_epoch, - ) - .unwrap(); - storage.commit_block().unwrap(); - - // Check that there is one consensus key in the network - let consensus_keys = get_consensus_key_set(&storage).unwrap(); - assert_eq!(consensus_keys.len(), 1); - let ck = consensus_keys.first().cloned().unwrap(); - let og_ck = validator_consensus_key_handle(&validator) - .get(&storage, current_epoch, ¶ms) - .unwrap() - .unwrap(); - assert_eq!(ck, og_ck); - - // Attempt to change to a new secp256k1 consensus key (disallowed) - let secp_ck = gen_keypair::(); - let secp_ck = key::common::SecretKey::Secp256k1(secp_ck).ref_to(); - let res = - change_consensus_key(&mut storage, &validator, &secp_ck, current_epoch); - assert!(res.is_err()); - - // Change consensus keys - let ck_2 = common_sk_from_simple_seed(1).ref_to(); - change_consensus_key(&mut storage, &validator, &ck_2, current_epoch) - .unwrap(); - - // Check that there is a new consensus key - let consensus_keys = get_consensus_key_set(&storage).unwrap(); - assert_eq!(consensus_keys.len(), 2); - - for epoch in current_epoch.iter_range(params.pipeline_len) { - let ck = validator_consensus_key_handle(&validator) - .get(&storage, epoch, ¶ms) - .unwrap() - .unwrap(); - assert_eq!(ck, og_ck); - } - let pipeline_epoch = current_epoch + params.pipeline_len; - let ck = validator_consensus_key_handle(&validator) - .get(&storage, pipeline_epoch, ¶ms) - .unwrap() - .unwrap(); - assert_eq!(ck, ck_2); - - // Advance to the pipeline epoch - loop { - current_epoch = advance_epoch(&mut storage, ¶ms); - if current_epoch == pipeline_epoch { - break; - } - } - - // Check the consensus keys again - let consensus_keys = get_consensus_key_set(&storage).unwrap(); - assert_eq!(consensus_keys.len(), 2); - - for epoch in current_epoch.iter_range(params.pipeline_len + 1) { - let ck = validator_consensus_key_handle(&validator) - .get(&storage, epoch, ¶ms) - .unwrap() - .unwrap(); - assert_eq!(ck, ck_2); - } - - // Now change the consensus key again and bond in the same epoch - let ck_3 = common_sk_from_simple_seed(3).ref_to(); - change_consensus_key(&mut storage, &validator, &ck_3, current_epoch) - .unwrap(); - - let staking_token = storage.storage.native_token.clone(); - let amount_del = token::Amount::native_whole(5); - credit_tokens(&mut storage, &staking_token, &validator, amount_del) - .unwrap(); - bond_tokens( - &mut storage, - None, - &validator, - token::Amount::native_whole(1), - current_epoch, - None, - ) - .unwrap(); - - // Check consensus keys again - let consensus_keys = get_consensus_key_set(&storage).unwrap(); - assert_eq!(consensus_keys.len(), 3); - - for epoch in current_epoch.iter_range(params.pipeline_len) { - let ck = validator_consensus_key_handle(&validator) - .get(&storage, epoch, ¶ms) - .unwrap() - .unwrap(); - assert_eq!(ck, ck_2); - } - let pipeline_epoch = current_epoch + params.pipeline_len; - let ck = validator_consensus_key_handle(&validator) - .get(&storage, pipeline_epoch, ¶ms) - .unwrap() - .unwrap(); - assert_eq!(ck, ck_3); - - // Advance to the pipeline epoch to ensure that the validator set updates to - // tendermint will work - loop { - current_epoch = advance_epoch(&mut storage, ¶ms); - if current_epoch == pipeline_epoch { - break; - } - } - assert_eq!(current_epoch.0, 2 * params.pipeline_len); -} - -fn test_is_delegator_aux(mut validators: Vec) { - validators.sort_by(|a, b| b.tokens.cmp(&a.tokens)); - - let validator1 = validators[0].address.clone(); - let validator2 = validators[1].address.clone(); - - let mut storage = TestWlStorage::default(); - let params = OwnedPosParams { - unbonding_len: 4, - ..Default::default() - }; - - // Genesis - let mut current_epoch = storage.storage.block.epoch; - let params = test_init_genesis( - &mut storage, - params, - validators.clone().into_iter(), - current_epoch, - ) - .unwrap(); - storage.commit_block().unwrap(); - - // Get delegators with some tokens - let staking_token = staking_token_address(&storage); - let delegator1 = address::testing::gen_implicit_address(); - let delegator2 = address::testing::gen_implicit_address(); - let del_balance = token::Amount::native_whole(1000); - credit_tokens(&mut storage, &staking_token, &delegator1, del_balance) - .unwrap(); - credit_tokens(&mut storage, &staking_token, &delegator2, del_balance) - .unwrap(); - - // Advance to epoch 1 - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - - // Delegate in epoch 1 to validator1 - let del1_epoch = current_epoch; - super::bond_tokens( - &mut storage, - Some(&delegator1), - &validator1, - 1000.into(), - current_epoch, - None, - ) - .unwrap(); - - // Advance to epoch 2 - current_epoch = advance_epoch(&mut storage, ¶ms); - super::process_slashes(&mut storage, current_epoch).unwrap(); - - // Delegate in epoch 2 to validator2 - let del2_epoch = current_epoch; - super::bond_tokens( - &mut storage, - Some(&delegator2), - &validator2, - 1000.into(), - current_epoch, - None, - ) - .unwrap(); - - // Checks - assert!(super::is_validator(&storage, &validator1).unwrap()); - assert!(super::is_validator(&storage, &validator2).unwrap()); - assert!(!super::is_delegator(&storage, &validator1, None).unwrap()); - assert!(!super::is_delegator(&storage, &validator2, None).unwrap()); - - assert!(!super::is_validator(&storage, &delegator1).unwrap()); - assert!(!super::is_validator(&storage, &delegator2).unwrap()); - assert!(super::is_delegator(&storage, &delegator1, None).unwrap()); - assert!(super::is_delegator(&storage, &delegator2, None).unwrap()); - - for epoch in Epoch::default().iter_range(del1_epoch.0 + params.pipeline_len) - { - assert!( - !super::is_delegator(&storage, &delegator1, Some(epoch)).unwrap() - ); - } - assert!( - super::is_delegator( - &storage, - &delegator1, - Some(del1_epoch + params.pipeline_len) - ) - .unwrap() - ); - for epoch in Epoch::default().iter_range(del2_epoch.0 + params.pipeline_len) - { - assert!( - !super::is_delegator(&storage, &delegator2, Some(epoch)).unwrap() - ); - } - assert!( - super::is_delegator( - &storage, - &delegator2, - Some(del2_epoch + params.pipeline_len) - ) - .unwrap() - ); -} diff --git a/proof_of_stake/src/validator_set_update.rs b/proof_of_stake/src/validator_set_update.rs index 9ad53c20b1..c304ed1142 100644 --- a/proof_of_stake/src/validator_set_update.rs +++ b/proof_of_stake/src/validator_set_update.rs @@ -2,14 +2,12 @@ use std::collections::{HashMap, HashSet}; -use namada_core::ledger::storage_api::collections::lazy_map::{ - NestedSubKey, SubKey, -}; -use namada_core::ledger::storage_api::{self, StorageRead, StorageWrite}; use namada_core::types::address::Address; use namada_core::types::key::PublicKeyTmRawHash; use namada_core::types::storage::Epoch; use namada_core::types::token; +use namada_storage::collections::lazy_map::{NestedSubKey, SubKey}; +use namada_storage::{StorageRead, StorageWrite}; use once_cell::unsync::Lazy; use crate::storage::{ @@ -34,7 +32,7 @@ pub fn update_validator_set( token_change: token::Change, current_epoch: Epoch, offset: Option, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -362,7 +360,7 @@ pub fn insert_validator_into_validator_set( stake: token::Amount, current_epoch: Epoch, offset: u64, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -464,7 +462,7 @@ pub fn remove_consensus_validator( params: &PosParams, epoch: Epoch, validator: &Address, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -492,7 +490,7 @@ pub fn remove_below_capacity_validator( params: &PosParams, epoch: Epoch, validator: &Address, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -523,7 +521,7 @@ where pub fn promote_next_below_capacity_validator_to_consensus( storage: &mut S, epoch: Epoch, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -567,7 +565,7 @@ pub fn validator_set_update_tendermint( params: &PosParams, current_epoch: Epoch, f: impl FnMut(ValidatorSetUpdate) -> T, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -770,7 +768,7 @@ pub fn copy_validator_sets_and_positions( params: &PosParams, current_epoch: Epoch, target_epoch: Epoch, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -889,7 +887,7 @@ fn insert_into_consensus_and_demote_to_below_cap( offset: u64, consensus_set: &ConsensusValidatorSet, below_capacity_set: &BelowCapacityValidatorSet, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -939,7 +937,7 @@ where fn find_first_position( handle: &ValidatorPositionAddresses, storage: &S, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -955,7 +953,7 @@ where fn find_last_position( handle: &ValidatorPositionAddresses, storage: &S, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -971,7 +969,7 @@ where fn find_next_position( handle: &ValidatorPositionAddresses, storage: &S, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead, { @@ -987,7 +985,7 @@ where fn get_min_consensus_validator_amount( handle: &ConsensusValidatorSet, storage: &S, -) -> storage_api::Result +) -> namada_storage::Result where S: StorageRead, { @@ -1008,7 +1006,7 @@ where fn get_max_below_capacity_validator_amount( handle: &BelowCapacityValidatorSet, storage: &S, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { @@ -1032,7 +1030,7 @@ fn insert_validator_into_set( storage: &mut S, epoch: &Epoch, address: &Address, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where S: StorageRead + StorageWrite, { @@ -1060,7 +1058,7 @@ fn read_validator_set_position( validator: &Address, epoch: Epoch, _params: &PosParams, -) -> storage_api::Result> +) -> namada_storage::Result> where S: StorageRead, { diff --git a/storage/src/lib.rs b/storage/src/lib.rs index f4355a32e0..0b63a03f41 100644 --- a/storage/src/lib.rs +++ b/storage/src/lib.rs @@ -87,6 +87,19 @@ pub trait StorageRead { /// current transaction is being applied. fn get_block_epoch(&self) -> Result; + /// Get the height of the first block of the current epoch. + fn get_current_epoch_start_height(&self) -> Result; + + /// Get the height of the first block of the given epoch. + fn get_epoch_start_height( + &self, + epoch: Epoch, + ) -> Result>; + + /// Given the epoch at the given block height. + fn get_epoch_at_height(&self, height: BlockHeight) + -> Result>; + /// Get the transaction index. fn get_tx_index(&self) -> Result; From 2f23aad845fc9b82d048bdabcd8c199353812595 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 22 Dec 2023 13:02:57 +0000 Subject: [PATCH 059/118] storage: refactor new fns in StorageRead using pred `Epochs` --- storage/src/lib.rs | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/storage/src/lib.rs b/storage/src/lib.rs index 0b63a03f41..07a0288b1f 100644 --- a/storage/src/lib.rs +++ b/storage/src/lib.rs @@ -8,7 +8,7 @@ pub use error::{CustomError, Error, OptionExt, Result, ResultExt}; use namada_core::borsh::{BorshDeserialize, BorshSerialize, BorshSerializeExt}; use namada_core::types::address::Address; use namada_core::types::storage::{ - self, BlockHash, BlockHeight, Epoch, Header, TxIndex, + self, BlockHash, BlockHeight, Epoch, Epochs, Header, TxIndex, }; /// Common storage read interface @@ -90,21 +90,32 @@ pub trait StorageRead { /// Get the height of the first block of the current epoch. fn get_current_epoch_start_height(&self) -> Result; - /// Get the height of the first block of the given epoch. - fn get_epoch_start_height( - &self, - epoch: Epoch, - ) -> Result>; - - /// Given the epoch at the given block height. - fn get_epoch_at_height(&self, height: BlockHeight) - -> Result>; + /// Given the information about predecessor block epochs + fn get_pred_epochs(&self) -> Result; /// Get the transaction index. fn get_tx_index(&self) -> Result; /// Get the native token address fn get_native_token(&self) -> Result
; + + /// Get the height of the first block of the given epoch. + fn get_epoch_start_height( + &self, + epoch: Epoch, + ) -> Result> { + let epochs = self.get_pred_epochs()?; + Ok(epochs.get_start_height_of_epoch(epoch)) + } + + /// Given the epoch at the given block height. + fn get_epoch_at_height( + &self, + height: BlockHeight, + ) -> Result> { + let epochs = self.get_pred_epochs()?; + Ok(epochs.get_epoch(height)) + } } /// Common storage write interface From a7e806a4480592009606456171169108f1454201 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 27 Dec 2023 16:55:23 +0000 Subject: [PATCH 060/118] vote_ext: fix build --- Cargo.lock | 3 ++ vote_ext/Cargo.toml | 5 +++ vote_ext/src/bridge_pool_roots.rs | 37 +++++++++++++----- vote_ext/src/ethereum_events.rs | 41 ++++++++++++-------- vote_ext/src/lib.rs | 17 ++++++--- vote_ext/src/validator_set_update.rs | 57 +++++++++++++++++----------- 6 files changed, 107 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 852cb365a7..6fd6011909 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4778,8 +4778,11 @@ dependencies = [ name = "namada_vote_ext" version = "0.28.1" dependencies = [ + "borsh", + "data-encoding", "namada_core", "namada_tx", + "serde 1.0.193", ] [[package]] diff --git a/vote_ext/Cargo.toml b/vote_ext/Cargo.toml index d3e7b34900..ebdba3037b 100644 --- a/vote_ext/Cargo.toml +++ b/vote_ext/Cargo.toml @@ -16,3 +16,8 @@ version.workspace = true namada_core = { path = "../core", default-features = false } namada_tx = { path = "../tx" } +borsh.workspace = true +serde.workspace = true + +[dev-dependencies] +data-encoding.workspace = true diff --git a/vote_ext/src/bridge_pool_roots.rs b/vote_ext/src/bridge_pool_roots.rs index 22718521f3..1669202308 100644 --- a/vote_ext/src/bridge_pool_roots.rs +++ b/vote_ext/src/bridge_pool_roots.rs @@ -5,13 +5,12 @@ use std::collections::HashSet; use std::ops::{Deref, DerefMut}; -use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; - -use crate::proto::Signed; -use crate::types::address::Address; -use crate::types::key::common; -use crate::types::key::common::Signature; -use crate::types::storage::BlockHeight; +use namada_core::borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; +use namada_core::types::address::Address; +use namada_core::types::key::common; +use namada_core::types::key::common::Signature; +use namada_core::types::storage::BlockHeight; +use namada_tx::Signed; /// A vote extension containing a validator's signature /// of the current root and nonce of the @@ -53,13 +52,33 @@ pub type Vext = BridgePoolRootVext; /// /// Note that this is serialized with Ethereum's /// ABI encoding schema. -pub type SignedVext = Signed; +#[derive( + Clone, + Debug, + BorshSerialize, + BorshSchema, + BorshDeserialize, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, +)] +pub struct SignedVext(pub Signed); + +impl Deref for SignedVext { + type Target = Signed; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} impl Vext { /// Creates a new signed [`Vext`]. #[inline] pub fn sign(&self, sk: &common::SecretKey) -> SignedVext { - SignedVext::new(sk, self.clone()) + SignedVext(Signed::new(sk, self.clone())) } } diff --git a/vote_ext/src/ethereum_events.rs b/vote_ext/src/ethereum_events.rs index beca881fcb..3988caa4b3 100644 --- a/vote_ext/src/ethereum_events.rs +++ b/vote_ext/src/ethereum_events.rs @@ -2,21 +2,30 @@ //! in vote extensions. use std::collections::{BTreeSet, HashMap}; +use std::ops::Deref; -use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; - -use crate::proto::Signed; -use crate::types::address::Address; -use crate::types::ethereum_events::EthereumEvent; -use crate::types::key::common::{self, Signature}; -use crate::types::storage::BlockHeight; +use namada_core::borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; +use namada_core::types::address::Address; +use namada_core::types::ethereum_events::EthereumEvent; +use namada_core::types::key::common::{self, Signature}; +use namada_core::types::storage::BlockHeight; +use namada_tx::Signed; /// Type alias for an [`EthereumEventsVext`]. pub type Vext = EthereumEventsVext; /// Represents a [`Vext`] signed by some validator, with /// a Namada protocol key. -pub type SignedVext = Signed; +#[derive(Clone, Debug, BorshSerialize, BorshSchema, BorshDeserialize)] +pub struct SignedVext(pub Signed); + +impl Deref for SignedVext { + type Target = Signed; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} /// Represents a set of [`EthereumEvent`] instances seen by some validator. /// @@ -138,13 +147,13 @@ impl VextDigest { #[cfg(test)] mod tests { + use namada_core::types::address::{self, Address}; + use namada_core::types::ethereum_events::{EthereumEvent, Uint}; + use namada_core::types::hash::Hash; + use namada_core::types::key; + use namada_tx::proto::Signed; + use super::*; - use crate::proto::Signed; - use crate::types::address::{self, Address}; - use crate::types::ethereum_events::{EthereumEvent, Uint}; - use crate::types::hash::Hash; - use crate::types::key; - use crate::types::key::RefTo; /// Test the hashing of an Ethereum event #[test] @@ -262,9 +271,9 @@ mod tests { for vext in decompressed.into_iter() { assert!(ext.contains(&vext)); if vext.data.validator_addr == validator_1 { - assert!(vext.verify(&sk_1.ref_to()).is_ok()) + assert!(vext.verify(&sk_1.to_public()).is_ok()) } else { - assert!(vext.verify(&sk_2.ref_to()).is_ok()) + assert!(vext.verify(&sk_2.to_public()).is_ok()) } } } diff --git a/vote_ext/src/lib.rs b/vote_ext/src/lib.rs index cb72ba88af..ca49e96801 100644 --- a/vote_ext/src/lib.rs +++ b/vote_ext/src/lib.rs @@ -4,9 +4,14 @@ pub mod bridge_pool_roots; pub mod ethereum_events; pub mod validator_set_update; -use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; - -use crate::proto::Signed; +use namada_core::borsh::{ + BorshDeserialize, BorshSchema, BorshSerialize, BorshSerializeExt, +}; +use namada_core::types::chain::ChainId; +use namada_core::types::key::common; +use namada_tx::data::protocol::{ProtocolTx, ProtocolTxType}; +use namada_tx::data::TxType; +use namada_tx::{Signature, Signed, Tx, TxError}; /// This type represents the data we pass to the extension of /// a vote at the PreCommit phase of Tendermint. @@ -131,12 +136,12 @@ impl EthereumTxData { let (tx_data, tx_type) = self.serialize(); let mut outer_tx = Tx::from_type(TxType::Protocol(Box::new(ProtocolTx { - pk: signing_key.ref_to(), + pk: signing_key.to_public(), tx: tx_type, }))); outer_tx.header.chain_id = chain_id; - outer_tx.set_data(Data::new(tx_data)); - outer_tx.add_section(Section::Signature(Signature::new( + outer_tx.set_data(namada_tx::Data::new(tx_data)); + outer_tx.add_section(namada_tx::Section::Signature(Signature::new( outer_tx.sechashes(), [(0, signing_key.clone())].into_iter().collect(), None, diff --git a/vote_ext/src/validator_set_update.rs b/vote_ext/src/validator_set_update.rs index bf3d749ff5..37d7cfec87 100644 --- a/vote_ext/src/validator_set_update.rs +++ b/vote_ext/src/validator_set_update.rs @@ -2,18 +2,20 @@ //! in vote extensions. use std::cmp::Ordering; use std::collections::HashMap; - -use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; - -use crate::proto::Signed; -use crate::types::address::Address; -use crate::types::eth_abi::{AbiEncode, Encode, Token}; -use crate::types::ethereum_events::EthAddress; -use crate::types::keccak::KeccakHash; -use crate::types::key::common::{self, Signature}; -use crate::types::storage::Epoch; -use crate::types::token; -use crate::types::voting_power::{EthBridgeVotingPower, FractionalVotingPower}; +use std::ops::Deref; + +use namada_core::borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; +use namada_core::types::address::Address; +use namada_core::types::eth_abi::{AbiEncode, Encode, Token}; +use namada_core::types::ethereum_events::EthAddress; +use namada_core::types::keccak::KeccakHash; +use namada_core::types::key::common::{self, Signature}; +use namada_core::types::storage::Epoch; +use namada_core::types::voting_power::{ + EthBridgeVotingPower, FractionalVotingPower, +}; +use namada_core::types::{ethereum_structs, token}; +use namada_tx::Signed; // the contract versions and namespaces plugged into validator set hashes // TODO: ideally, these values should not be hardcoded @@ -41,7 +43,7 @@ pub struct ValidatorSetUpdateVextDigest { impl VextDigest { /// Build a singleton [`VextDigest`], from the provided [`Vext`]. #[inline] - pub fn singleton(ext: SignedVext) -> VextDigest { + pub fn singleton(SignedVext(ext): SignedVext) -> VextDigest { VextDigest { signatures: HashMap::from([( ext.data.validator_addr.clone(), @@ -67,7 +69,7 @@ impl VextDigest { voting_powers, signing_epoch, }; - extensions.push(SignedVext::new_from(data, signature)); + extensions.push(SignedVext(Signed::new_from(data, signature))); } extensions } @@ -75,7 +77,18 @@ impl VextDigest { /// Represents a [`Vext`] signed by some validator, with /// an Ethereum key. -pub type SignedVext = Signed; +#[derive( + Clone, Debug, BorshSerialize, BorshDeserialize, BorshSchema, PartialEq, Eq, +)] +pub struct SignedVext(pub Signed); + +impl Deref for SignedVext { + type Target = Signed; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} /// Type alias for a [`ValidatorSetUpdateVext`]. pub type Vext = ValidatorSetUpdateVext; @@ -120,7 +133,7 @@ impl Vext { /// For more information, read the docs of [`SignedVext::new`]. #[inline] pub fn sign(&self, sk: &common::SecretKey) -> SignedVext { - SignedVext::new(sk, self.clone()) + SignedVext(Signed::new(sk, self.clone())) } } @@ -334,14 +347,14 @@ pub struct ValidatorSetArgs { pub epoch: Epoch, } -impl From for ethbridge_structs::ValidatorSetArgs { +impl From for ethereum_structs::ValidatorSetArgs { fn from(valset: ValidatorSetArgs) -> Self { let ValidatorSetArgs { validators, voting_powers, epoch, } = valset; - ethbridge_structs::ValidatorSetArgs { + ethereum_structs::ValidatorSetArgs { validator_set: validators .into_iter() .zip(voting_powers.into_iter()) @@ -371,15 +384,15 @@ impl Encode<1> for ValidatorSetArgs { // this is only here so we don't pollute the // outer namespace with serde traits mod tag { + use namada_core::types::eth_abi::{AbiEncode, Encode, Token}; + use namada_core::types::hash::KeccakHasher; + use namada_core::types::keccak::KeccakHash; + use namada_core::types::key::Signable; use serde::{Deserialize, Serialize}; use super::{ epoch_to_token, Vext, VotingPowersMapExt, GOVERNANCE_CONTRACT_VERSION, }; - use crate::ledger::storage::KeccakHasher; - use crate::proto::Signable; - use crate::types::eth_abi::{AbiEncode, Encode, Token}; - use crate::types::keccak::KeccakHash; /// Tag type that indicates we should use [`AbiEncode`] /// to sign data in a [`crate::proto::Signed`] wrapper. From 9b2cd95153769dbdee43bf3b00e07b344f42a1c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Thu, 28 Dec 2023 12:50:21 +0000 Subject: [PATCH 061/118] merkle_tree: fix build --- Cargo.lock | 2 +- core/src/types/eth_bridge_pool.rs | 27 + core/src/types/hash.rs | 29 + core/src/types/mod.rs | 28 + core/src/types/storage.rs | 39 ++ macros/src/lib.rs | 4 +- merkle_tree/Cargo.toml | 2 +- merkle_tree/src/eth_bridge_pool.rs | 944 +++++++++++++++++++++++++++++ merkle_tree/src/lib.rs | 219 ++++++- 9 files changed, 1270 insertions(+), 24 deletions(-) create mode 100644 merkle_tree/src/eth_bridge_pool.rs diff --git a/Cargo.lock b/Cargo.lock index 6fd6011909..4070a3e1c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4532,9 +4532,9 @@ name = "namada_merkle_tree" version = "0.28.1" dependencies = [ "borsh", + "eyre", "ics23", "namada_core", - "namada_ethereum_bridge", "prost 0.12.3", "sparse-merkle-tree", "thiserror", diff --git a/core/src/types/eth_bridge_pool.rs b/core/src/types/eth_bridge_pool.rs index a7f9d2e9f6..d157ed5525 100644 --- a/core/src/types/eth_bridge_pool.rs +++ b/core/src/types/eth_bridge_pool.rs @@ -6,9 +6,12 @@ use std::borrow::Cow; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use borsh_ext::BorshSerializeExt; use ethabi::token::Token; +use namada_macros::StorageKeys; use serde::{Deserialize, Serialize}; use super::address::InternalAddress; +use super::storage; +use crate as namada_core; // This is needed for `StorageKeys` macro use crate::types::address::Address; use crate::types::eth_abi::Encode; use crate::types::ethereum_events::{ @@ -18,6 +21,30 @@ use crate::types::hash::Hash as HashDigest; use crate::types::storage::{DbKeySeg, Key}; use crate::types::token::Amount; +/// The main address of the Ethereum bridge pool +pub const BRIDGE_POOL_ADDRESS: Address = + Address::Internal(InternalAddress::EthBridgePool); + +/// Bridge pool key segments. +#[derive(StorageKeys)] +pub struct Segments { + signed_root: &'static str, + bridge_pool_nonce: &'static str, +} + +/// Check if a key is for a pending transfer +pub fn is_pending_transfer_key(key: &storage::Key) -> bool { + let segment = match &key.segments[..] { + [DbKeySeg::AddressSeg(addr), DbKeySeg::StringSeg(segment)] + if addr == &BRIDGE_POOL_ADDRESS => + { + segment.as_str() + } + _ => return false, + }; + !Segments::ALL.iter().any(|s| s == &segment) +} + /// A version used in our Ethereuem smart contracts const VERSION: u8 = 1; diff --git a/core/src/types/hash.rs b/core/src/types/hash.rs index 932e4595af..59c6dd279f 100644 --- a/core/src/types/hash.rs +++ b/core/src/types/hash.rs @@ -50,6 +50,16 @@ pub type HashResult = std::result::Result; /// A hash, typically a sha-2 hash of a tx pub struct Hash(pub [u8; HASH_LENGTH]); +impl arse_merkle_tree::traits::Value for Hash { + fn as_slice(&self) -> &[u8] { + self.0.as_slice() + } + + fn zero() -> Self { + Hash([0u8; 32]) + } +} + impl Display for Hash { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", HEXUPPER.encode(&self.0)) @@ -148,6 +158,25 @@ impl From for crate::tendermint::Hash { } } +impl From for H256 { + fn from(hash: Hash) -> Self { + hash.0.into() + } +} + +impl From for Hash { + fn from(hash: H256) -> Self { + Self(hash.into()) + } +} + +impl From<&H256> for Hash { + fn from(hash: &H256) -> Self { + let hash = hash.to_owned(); + Self(hash.into()) + } +} + /// The storage hasher used for the merkle tree. pub trait StorageHasher: Hasher + fmt::Debug + Default { /// Hash the value to store diff --git a/core/src/types/mod.rs b/core/src/types/mod.rs index 5c2c99454c..d297b084d8 100644 --- a/core/src/types/mod.rs +++ b/core/src/types/mod.rs @@ -21,3 +21,31 @@ pub mod token; pub mod uint; pub mod validity_predicate; pub mod voting_power; + +use borsh_ext::BorshSerializeExt; +use thiserror::Error; + +use crate::borsh::{BorshDeserialize, BorshSerialize}; + +#[allow(missing_docs)] +#[derive(Error, Debug)] +pub enum DecodeError { + #[error("Deserialization error: {0}")] + DeserializationError(std::io::Error), +} + +/// Encode a value with borsh +pub fn encode(value: &T) -> Vec +where + T: BorshSerialize, +{ + value.serialize_to_vec() +} + +/// Decode a value with borsh +pub fn decode(bytes: impl AsRef<[u8]>) -> Result +where + T: BorshDeserialize, +{ + T::try_from_slice(bytes.as_ref()).map_err(DecodeError::DeserializationError) +} diff --git a/core/src/types/storage.rs b/core/src/types/storage.rs index 1b633cf086..d370d7ade2 100644 --- a/core/src/types/storage.rs +++ b/core/src/types/storage.rs @@ -467,11 +467,50 @@ impl BorshDeserialize for StringKey { } } +impl arse_merkle_tree::Key for StringKey { + type Error = TreeKeyError; + + fn as_slice(&self) -> &[u8] { + &self.original.as_slice()[..self.length] + } + + fn try_from_bytes(bytes: &[u8]) -> std::result::Result { + let mut tree_key = [0u8; IBC_KEY_LIMIT]; + let mut original = [0u8; IBC_KEY_LIMIT]; + let mut length = 0; + for (i, byte) in bytes.iter().enumerate() { + if i >= IBC_KEY_LIMIT { + return Err(TreeKeyError::InvalidMerkleKey( + "Input IBC key is too large".into(), + )); + } + original[i] = *byte; + tree_key[i] = byte.wrapping_add(1); + length += 1; + } + Ok(Self { + original, + tree_key: tree_key.into(), + length, + }) + } +} + /// A wrapper around raw bytes to be stored as values /// in a merkle tree #[derive(Clone, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize)] pub struct TreeBytes(pub Vec); +impl arse_merkle_tree::traits::Value for TreeBytes { + fn as_slice(&self) -> &[u8] { + self.0.as_slice() + } + + fn zero() -> Self { + TreeBytes::zero() + } +} + impl TreeBytes { /// The value indicating that a leaf should be deleted pub fn zero() -> Self { diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 594a9ae991..02976a22b8 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -269,7 +269,7 @@ fn derive_storage_keys_inner(struct_def: TokenStream2) -> TokenStream2 { let id = syn::Ident::new(&id, ident.span()); quote! { #[allow(missing_docs)] - pub(crate) fn #id(key: &namada_core::types::storage::Key, address: &Address) -> bool { + pub fn #id(key: &namada_core::types::storage::Key, address: &Address) -> bool { matches!(&key.segments[..], [ namada_core::types::storage::DbKeySeg::AddressSeg(a), namada_core::types::storage::DbKeySeg::StringSeg(#ident), @@ -282,7 +282,7 @@ fn derive_storage_keys_inner(struct_def: TokenStream2) -> TokenStream2 { let id = syn::Ident::new(&id, ident.span()); quote! { #[allow(missing_docs)] - pub(crate) fn #id(address: Address) -> namada_core::types::storage::Key { + pub fn #id(address: Address) -> namada_core::types::storage::Key { namada_core::types::storage::Key { segments: vec![ namada_core::types::storage::DbKeySeg::AddressSeg(address), diff --git a/merkle_tree/Cargo.toml b/merkle_tree/Cargo.toml index be842fb452..6fc7e1bcc8 100644 --- a/merkle_tree/Cargo.toml +++ b/merkle_tree/Cargo.toml @@ -14,10 +14,10 @@ version.workspace = true [dependencies] namada_core = { path = "../core", default-features = false } -namada_ethereum_bridge = { path = "../ethereum_bridge" } arse-merkle-tree.workspace = true borsh.workspace = true +eyre.workspace = true ics23.workspace = true prost.workspace = true thiserror.workspace = true diff --git a/merkle_tree/src/eth_bridge_pool.rs b/merkle_tree/src/eth_bridge_pool.rs new file mode 100644 index 0000000000..5981e9b14c --- /dev/null +++ b/merkle_tree/src/eth_bridge_pool.rs @@ -0,0 +1,944 @@ +//! Ethereum bridge pool merkle tree + +use std::collections::{BTreeMap, BTreeSet}; + +use eyre::eyre; +use namada_core::borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; +use namada_core::types::eth_abi::{Encode, Token}; +use namada_core::types::eth_bridge_pool::PendingTransfer; +use namada_core::types::hash::Hash; +use namada_core::types::keccak::{keccak_hash, KeccakHash}; +use namada_core::types::storage; +use namada_core::types::storage::{BlockHeight, DbKeySeg}; + +#[derive(thiserror::Error, Debug)] +#[error(transparent)] +/// Generic error that may be returned by the validity predicate +pub struct Error(#[from] eyre::Error); + +/// Prefix to be used in Bridge pool tree root computations. +/// This value corresponds to leaf nodes. +const POOL_ROOT_PREFIX_LEAF: u8 = 0x00; + +/// Prefix to be used in Bridge pool tree root computations. +/// This value corresponds to non-leaf nodes. +const POOL_ROOT_PREFIX_NON_LEAF: u8 = 0xff; + +/// A simple Merkle tree for the Ethereum bridge pool +/// +/// Note that an empty tree has root [0u8; 20] by definition. +#[derive( + Debug, Default, Clone, BorshSerialize, BorshDeserialize, BorshSchema, +)] +pub struct BridgePoolTree { + /// Root of the tree + root: KeccakHash, + /// The underlying storage, containing hashes of [`PendingTransfer`]s. + leaves: BTreeMap, +} + +impl BridgePoolTree { + /// Create a new merkle tree for the Ethereum bridge pool + pub fn new( + root: KeccakHash, + store: BTreeMap, + ) -> Self { + Self { + root, + leaves: store, + } + } + + /// Parse the key to ensure it is of the correct type. + /// + /// If it is, it can be converted to a hash. + /// Checks if the hash is in the tree. + pub fn contains_key(&self, key: &storage::Key) -> Result { + Ok(self.leaves.contains_key(&Self::parse_key(key)?)) + } + + /// Get the height at which the key was inserted + /// + /// Returns the height if successful. Will + /// return an error if the key is malformed. + pub fn get(&self, key: &storage::Key) -> Result { + let hash = Self::parse_key(key)?; + self.leaves.get(&hash).cloned().ok_or_else(|| { + eyre!("Key not present in Bridge Pool merkle tree.").into() + }) + } + + /// Update the tree with a new value. + /// + /// Returns the new root if successful. Will + /// return an error if the key is malformed. + pub fn insert_key( + &mut self, + key: &storage::Key, + value: BlockHeight, + ) -> Result { + let hash = Self::parse_key(key)?; + _ = self.leaves.insert(hash, value); + self.root = self.compute_root(); + Ok(self.root().into()) + } + + /// Delete a key from storage and update the root + pub fn delete_key(&mut self, key: &storage::Key) -> Result<(), Error> { + let hash = Self::parse_key(key)?; + _ = self.leaves.remove(&hash); + self.root = self.compute_root(); + Ok(()) + } + + /// Compute the root of the merkle tree + fn compute_root(&self) -> KeccakHash { + let mut hashes: Vec = self.leaves.keys().cloned().collect(); + let mut prefix = POOL_ROOT_PREFIX_LEAF; + while hashes.len() > 1 { + let mut next_hashes = vec![]; + for pair in hashes.chunks(2) { + let left = pair[0].clone(); + let right = pair.get(1).cloned().unwrap_or_default(); + next_hashes.push(hash_pair(left, right, prefix)); + } + hashes = next_hashes; + prefix = POOL_ROOT_PREFIX_NON_LEAF; + } + + if hashes.is_empty() { + Default::default() + } else { + hashes.remove(0) + } + } + + /// Return the root as a [`struct@Hash`] type. + pub fn root(&self) -> KeccakHash { + self.root.clone() + } + + /// Get a reference to the backing store + pub fn store(&self) -> &BTreeMap { + &self.leaves + } + + /// Create a batched membership proof for the provided keys + pub fn get_membership_proof( + &self, + mut values: Vec, + ) -> Result { + // sort the values according to their hash values + values.sort_by_key(|transfer| transfer.keccak256()); + + // get the leaf hashes + let leaves: BTreeSet = + values.iter().map(|v| v.keccak256()).collect(); + if !leaves.iter().all(|h| self.leaves.contains_key(h)) { + return Err(eyre!( + "Cannot generate proof for values that aren't in the tree" + ) + .into()); + } + let mut proof_hashes = vec![]; + let mut flags = vec![]; + let mut hashes: Vec<_> = self + .leaves + .keys() + .cloned() + .map(|hash| { + if leaves.contains(&hash) { + Node::OnPath(hash) + } else { + Node::OffPath(hash) + } + }) + .collect(); + + let mut prefix = POOL_ROOT_PREFIX_LEAF; + + while hashes.len() > 1 { + let mut next_hashes = vec![]; + + for pair in hashes.chunks(2) { + let left = pair[0].clone(); + let right = pair.get(1).cloned().unwrap_or_default(); + match (left, right) { + (Node::OnPath(left), Node::OnPath(right)) => { + flags.push(true); + next_hashes.push(Node::OnPath(hash_pair( + left.clone(), + right, + prefix, + ))); + } + (Node::OnPath(hash), Node::OffPath(sib)) => { + flags.push(false); + proof_hashes.push(sib.clone()); + next_hashes.push(Node::OnPath(hash_pair( + hash.clone(), + sib, + prefix, + ))); + } + (Node::OffPath(sib), Node::OnPath(hash)) => { + flags.push(false); + proof_hashes.push(sib.clone()); + next_hashes.push(Node::OnPath(hash_pair( + hash, + sib.clone(), + prefix, + ))); + } + (Node::OffPath(left), Node::OffPath(right)) => { + next_hashes.push(Node::OffPath(hash_pair( + left.clone(), + right, + prefix, + ))); + } + } + } + hashes = next_hashes; + prefix = POOL_ROOT_PREFIX_NON_LEAF; + } + // add the root to the proof + if flags.is_empty() && proof_hashes.is_empty() && leaves.is_empty() { + proof_hashes.push(self.root.clone()); + } + + Ok(BridgePoolProof { + proof: proof_hashes, + leaves: values, + flags, + }) + } + + /// Parse a db key to see if it is valid for the + /// bridge pool. + /// + /// It should have one string segment which should + /// parse into a [`struct@Hash`]. + pub fn parse_key(key: &storage::Key) -> Result { + if key.segments.len() == 1 { + match &key.segments[0] { + DbKeySeg::StringSeg(str) => { + str.as_str().try_into().map_err(|_| { + eyre!("Could not parse key segment as a hash").into() + }) + } + _ => Err(eyre!("Bridge pool keys should be strings.").into()), + } + } else { + Err(eyre!( + "Key for the bridge pool should have exactly one segment." + ) + .into()) + } + } +} + +/// Concatenate a byte prefix and two keccak hashes, +/// then compute the keccak hash of the resulting +/// byte array. +#[inline] +fn hash_pair(left: KeccakHash, right: KeccakHash, prefix: u8) -> KeccakHash { + let mut buf = [0u8; 32 + 32 + 1]; + buf[0] = prefix; + if left.0 < right.0 { + buf[1..33].copy_from_slice(&left.0); + buf[33..].copy_from_slice(&right.0); + } else { + buf[1..33].copy_from_slice(&right.0); + buf[33..].copy_from_slice(&left.0); + } + keccak_hash(buf) +} + +/// Keeps track if a node is on a path from the +/// root of the merkle tree to one of the leaves +/// being included in a multi-proof. +#[derive(Debug, Clone)] +enum Node { + /// Node is on a path from root to leaf in proof + OnPath(KeccakHash), + /// Node is not on a path from root to leaf in proof + OffPath(KeccakHash), +} + +impl Default for Node { + fn default() -> Self { + Self::OffPath(Default::default()) + } +} + +/// A multi-leaf membership proof +pub struct BridgePoolProof { + /// The hashes other than the provided leaves + pub proof: Vec, + /// The leaves; must be sorted + pub leaves: Vec, + /// Flags are used to indicate which consecutive + /// pairs of leaves in `leaves` are siblings. + pub flags: Vec, +} + +impl BridgePoolProof { + /// Verify a membership proof matches the provided root + pub fn verify(&self, root: KeccakHash) -> bool { + if self.proof.len() + self.leaves.len() != self.flags.len() + 1 { + return false; + } + if self.flags.is_empty() { + return if let Some(leaf) = self.leaves.last() { + root == leaf.keccak256() + } else { + match self.proof.last() { + Some(proof_root) => &root == proof_root, + None => false, + } + }; + } + let total_hashes = self.flags.len(); + let leaf_len = self.leaves.len(); + + let mut hashes = vec![KeccakHash::default(); self.flags.len()]; + let mut hash_pos = 0usize; + let mut leaf_pos = 0usize; + let mut proof_pos = 0usize; + + for i in 0..total_hashes { + let (left, prefix) = if leaf_pos < leaf_len { + let next = self.leaves[leaf_pos].keccak256(); + leaf_pos += 1; + (next, POOL_ROOT_PREFIX_LEAF) + } else { + let next = hashes[hash_pos].clone(); + hash_pos += 1; + (next, POOL_ROOT_PREFIX_NON_LEAF) + }; + let right = if self.flags[i] { + if leaf_pos < leaf_len { + let next = self.leaves[leaf_pos].keccak256(); + leaf_pos += 1; + next + } else { + let next = hashes[hash_pos].clone(); + hash_pos += 1; + next + } + } else { + let next = self.proof[proof_pos].clone(); + proof_pos += 1; + next + }; + hashes[i] = hash_pair(left, right, prefix); + } + + if let Some(computed) = hashes.last() { + *computed == root + } else { + false + } + } +} + +impl Encode<3> for BridgePoolProof { + fn tokenize(&self) -> [Token; 3] { + let BridgePoolProof { + proof, + leaves, + flags, + } = self; + let proof = Token::Array( + proof + .iter() + .map(|hash| Token::FixedBytes(hash.0.to_vec())) + .collect(), + ); + let transfers = Token::Array( + leaves + .iter() + .map(|t| Token::FixedArray(t.tokenize().to_vec())) + .collect(), + ); + let flags = + Token::Array(flags.iter().map(|flag| Token::Bool(*flag)).collect()); + [proof, transfers, flags] + } +} + +#[cfg(test)] +mod test_bridge_pool_tree { + + use itertools::Itertools; + use proptest::prelude::*; + + use super::*; + use crate::types::address::nam; + use crate::types::eth_bridge_pool::{ + GasFee, TransferToEthereum, TransferToEthereumKind, + }; + use crate::types::ethereum_events::EthAddress; + + /// An established user address for testing & development + fn bertha_address() -> Address { + Address::decode("tnam1qyctxtpnkhwaygye0sftkq28zedf774xc5a2m7st") + .expect("The token address decoding shouldn't fail") + } + + /// Test that if tree has a single leaf, its root is the hash + /// of that leaf + #[test] + fn test_update_single_key() { + let mut tree = BridgePoolTree::default(); + assert_eq!(tree.root().0, [0; 32]); + let transfer = PendingTransfer { + transfer: TransferToEthereum { + kind: TransferToEthereumKind::Erc20, + asset: EthAddress([1; 20]), + sender: bertha_address(), + recipient: EthAddress([2; 20]), + amount: 1.into(), + }, + gas_fee: GasFee { + token: nam(), + amount: 0.into(), + payer: bertha_address(), + }, + }; + let key = Key::from(&transfer); + let root = KeccakHash::from( + tree.insert_key(&key, BlockHeight(1)).expect("Test failed"), + ); + assert_eq!(root, transfer.keccak256()); + let height = tree.get(&key).expect("Test failed"); + assert_eq!(BlockHeight(1), height); + } + + #[test] + fn test_two_keys() { + let mut tree = BridgePoolTree::default(); + let mut transfers = vec![]; + for i in 0..2 { + let transfer = PendingTransfer { + transfer: TransferToEthereum { + kind: TransferToEthereumKind::Erc20, + asset: EthAddress([i; 20]), + sender: bertha_address(), + recipient: EthAddress([i + 1; 20]), + amount: (i as u64).into(), + }, + gas_fee: GasFee { + token: nam(), + amount: 0.into(), + payer: bertha_address(), + }, + }; + let key = Key::from(&transfer); + transfers.push(transfer); + let _ = tree.insert_key(&key, BlockHeight(1)).expect("Test failed"); + } + let expected = hash_pair( + transfers[0].keccak256(), + transfers[1].keccak256(), + POOL_ROOT_PREFIX_LEAF, + ); + assert_eq!(tree.root(), expected); + } + + /// This is the first number of keys to use dummy leaves + #[test] + fn test_three_leaves() { + let mut tree = BridgePoolTree::default(); + let mut transfers = vec![]; + for i in 0..3 { + let transfer = PendingTransfer { + transfer: TransferToEthereum { + kind: TransferToEthereumKind::Erc20, + asset: EthAddress([i; 20]), + sender: bertha_address(), + recipient: EthAddress([i + 1; 20]), + amount: (i as u64).into(), + }, + gas_fee: GasFee { + token: nam(), + amount: 0.into(), + payer: bertha_address(), + }, + }; + let key = Key::from(&transfer); + transfers.push(transfer); + let _ = tree.insert_key(&key, BlockHeight(1)).expect("Test failed"); + } + transfers.sort_by_key(|t| t.keccak256()); + let hashes: BTreeSet = + transfers.iter().map(|t| t.keccak256()).collect(); + assert_eq!( + hashes, + tree.leaves.keys().cloned().collect::>() + ); + + let left_hash = hash_pair( + transfers[0].keccak256(), + transfers[1].keccak256(), + POOL_ROOT_PREFIX_LEAF, + ); + let right_hash = hash_pair( + transfers[2].keccak256(), + Default::default(), + POOL_ROOT_PREFIX_LEAF, + ); + let expected = + hash_pair(left_hash, right_hash, POOL_ROOT_PREFIX_NON_LEAF); + assert_eq!(tree.root(), expected); + } + + /// Test removing all keys + #[test] + fn test_delete_all_keys() { + let mut tree = BridgePoolTree::default(); + + let transfer = PendingTransfer { + transfer: TransferToEthereum { + kind: TransferToEthereumKind::Erc20, + asset: EthAddress([1; 20]), + sender: bertha_address(), + recipient: EthAddress([2; 20]), + amount: 1.into(), + }, + gas_fee: GasFee { + token: nam(), + amount: 0.into(), + payer: bertha_address(), + }, + }; + let key = Key::from(&transfer); + let root = KeccakHash::from( + tree.insert_key(&key, BlockHeight(1)).expect("Test failed"), + ); + assert_eq!(root, transfer.keccak256()); + tree.delete_key(&key).expect("Test failed"); + assert_eq!(tree.root().0, [0; 32]); + } + + /// Test deleting a key + #[test] + fn test_delete_key() { + let mut tree = BridgePoolTree::default(); + let mut transfers = vec![]; + for i in 0..3 { + let transfer = PendingTransfer { + transfer: TransferToEthereum { + kind: TransferToEthereumKind::Erc20, + asset: EthAddress([i; 20]), + sender: bertha_address(), + recipient: EthAddress([i + 1; 20]), + amount: (i as u64).into(), + }, + gas_fee: GasFee { + token: nam(), + amount: 0.into(), + payer: bertha_address(), + }, + }; + + let key = Key::from(&transfer); + transfers.push(transfer); + let _ = tree.insert_key(&key, BlockHeight(1)).expect("Test failed"); + } + transfers.sort_by_key(|t| t.keccak256()); + let deleted_key = Key::from(&transfers[1]); + tree.delete_key(&deleted_key).expect("Test failed"); + + let expected = hash_pair( + transfers[0].keccak256(), + transfers[2].keccak256(), + POOL_ROOT_PREFIX_LEAF, + ); + assert_eq!(tree.root(), expected); + assert_matches!(tree.get(&deleted_key), Err(_)); + } + + /// Test that parse key works correctly + #[test] + fn test_parse_key() { + let transfer = PendingTransfer { + transfer: TransferToEthereum { + kind: TransferToEthereumKind::Erc20, + asset: EthAddress([1; 20]), + sender: bertha_address(), + recipient: EthAddress([2; 20]), + amount: 1u64.into(), + }, + gas_fee: GasFee { + token: nam(), + amount: 0.into(), + payer: bertha_address(), + }, + }; + let expected = transfer.keccak256(); + let key = Key::from(&transfer); + assert_eq!( + BridgePoolTree::parse_key(&key).expect("Test failed"), + expected + ); + } + + /// Test that parsing a key with multiple segments fails + #[test] + fn test_key_multiple_segments() { + let transfer = PendingTransfer { + transfer: TransferToEthereum { + kind: TransferToEthereumKind::Erc20, + asset: EthAddress([1; 20]), + sender: bertha_address(), + recipient: EthAddress([2; 20]), + amount: 1u64.into(), + }, + gas_fee: GasFee { + token: nam(), + amount: 0.into(), + payer: bertha_address(), + }, + }; + let hash = transfer.keccak256().to_string(); + let key = Key { + segments: vec![ + DbKeySeg::AddressSeg(bertha_address()), + DbKeySeg::StringSeg(hash), + ], + }; + assert!(BridgePoolTree::parse_key(&key).is_err()); + } + + /// Test that parsing a key that is not a hash fails + #[test] + fn test_key_not_hash() { + let key = Key { + segments: vec![DbKeySeg::StringSeg("bloop".into())], + }; + assert!(BridgePoolTree::parse_key(&key).is_err()); + } + + /// Test that [`contains_key`] works correctly + #[test] + fn test_contains_key() { + let mut tree = BridgePoolTree::default(); + let transfer = PendingTransfer { + transfer: TransferToEthereum { + kind: TransferToEthereumKind::Erc20, + asset: EthAddress([1; 20]), + sender: bertha_address(), + recipient: EthAddress([2; 20]), + amount: 1.into(), + }, + gas_fee: GasFee { + token: nam(), + amount: 0.into(), + payer: bertha_address(), + }, + }; + tree.insert_key(&Key::from(&transfer), BlockHeight(1)) + .expect("Test failed"); + assert!( + tree.contains_key(&Key::from(&transfer)) + .expect("Test failed") + ); + let transfer = PendingTransfer { + transfer: TransferToEthereum { + kind: TransferToEthereumKind::Erc20, + asset: EthAddress([1; 20]), + sender: bertha_address(), + recipient: EthAddress([0; 20]), + amount: 1u64.into(), + }, + gas_fee: GasFee { + token: nam(), + amount: 0.into(), + payer: bertha_address(), + }, + }; + assert!( + !tree + .contains_key(&Key::from(&transfer)) + .expect("Test failed") + ); + } + + /// Test that the empty proof works. + #[test] + fn test_empty_proof() { + let tree = BridgePoolTree::default(); + let values = vec![]; + let proof = tree.get_membership_proof(values).expect("Test failed"); + assert!(proof.verify(Default::default())); + } + + /// Test that the proof works for proving the only leaf in the tree + #[test] + fn test_single_leaf() { + let transfer = PendingTransfer { + transfer: TransferToEthereum { + kind: TransferToEthereumKind::Erc20, + asset: EthAddress([0; 20]), + sender: bertha_address(), + recipient: EthAddress([0; 20]), + amount: 0.into(), + }, + gas_fee: GasFee { + token: nam(), + amount: 0.into(), + payer: bertha_address(), + }, + }; + let mut tree = BridgePoolTree::default(); + let key = Key::from(&transfer); + let _ = tree.insert_key(&key, BlockHeight(1)).expect("Test failed"); + let proof = tree + .get_membership_proof(vec![transfer]) + .expect("Test failed"); + assert!(proof.verify(tree.root())); + } + + /// Check proofs for membership of single transfer + /// in a tree with two leaves. + #[test] + fn test_one_leaf_of_two_proof() { + let mut tree = BridgePoolTree::default(); + let mut transfers = vec![]; + for i in 0..2 { + let transfer = PendingTransfer { + transfer: TransferToEthereum { + kind: TransferToEthereumKind::Erc20, + asset: EthAddress([i; 20]), + sender: bertha_address(), + recipient: EthAddress([i + 1; 20]), + amount: (i as u64).into(), + }, + gas_fee: GasFee { + token: nam(), + amount: 0.into(), + payer: bertha_address(), + }, + }; + + let key = Key::from(&transfer); + transfers.push(transfer); + let _ = tree.insert_key(&key, BlockHeight(1)).expect("Test failed"); + } + let proof = tree + .get_membership_proof(vec![transfers.remove(0)]) + .expect("Test failed"); + assert!(proof.verify(tree.root())); + } + + /// Test that a multiproof works for leaves who are siblings + #[test] + fn test_proof_two_out_of_three_leaves() { + let mut tree = BridgePoolTree::default(); + let mut transfers = vec![]; + for i in 0..3 { + let transfer = PendingTransfer { + transfer: TransferToEthereum { + kind: TransferToEthereumKind::Erc20, + asset: EthAddress([i; 20]), + sender: bertha_address(), + recipient: EthAddress([i + 1; 20]), + amount: (i as u64).into(), + }, + gas_fee: GasFee { + token: nam(), + amount: 0.into(), + payer: bertha_address(), + }, + }; + + let key = Key::from(&transfer); + transfers.push(transfer); + let _ = tree.insert_key(&key, BlockHeight(1)).expect("Test failed"); + } + transfers.sort_by_key(|t| t.keccak256()); + let values = vec![transfers[0].clone(), transfers[1].clone()]; + let proof = tree.get_membership_proof(values).expect("Test failed"); + assert!(proof.verify(tree.root())); + } + + /// Test that proving an empty subset of leaves always works + #[test] + fn test_proof_no_leaves() { + let mut tree = BridgePoolTree::default(); + let mut transfers = vec![]; + for i in 0..3 { + let transfer = PendingTransfer { + transfer: TransferToEthereum { + kind: TransferToEthereumKind::Erc20, + asset: EthAddress([i; 20]), + sender: bertha_address(), + recipient: EthAddress([i + 1; 20]), + amount: (i as u64).into(), + }, + gas_fee: GasFee { + token: nam(), + amount: 0.into(), + payer: bertha_address(), + }, + }; + let key = Key::from(&transfer); + transfers.push(transfer); + let _ = tree.insert_key(&key, BlockHeight(1)).expect("Test failed"); + } + let values = vec![]; + let proof = tree.get_membership_proof(values).expect("Test failed"); + assert!(proof.verify(tree.root())) + } + + /// Test a proof for all the leaves + #[test] + fn test_proof_all_leaves() { + let mut tree = BridgePoolTree::default(); + let mut transfers = vec![]; + for i in 0..2 { + let transfer = PendingTransfer { + transfer: TransferToEthereum { + kind: TransferToEthereumKind::Erc20, + asset: EthAddress([i; 20]), + sender: bertha_address(), + recipient: EthAddress([i + 1; 20]), + amount: (i as u64).into(), + }, + gas_fee: GasFee { + token: nam(), + amount: 0.into(), + payer: bertha_address(), + }, + }; + let key = Key::from(&transfer); + transfers.push(transfer); + let _ = tree.insert_key(&key, BlockHeight(1)).expect("Test failed"); + } + transfers.sort_by_key(|t| t.keccak256()); + let proof = tree.get_membership_proof(transfers).expect("Test failed"); + assert!(proof.verify(tree.root())); + } + + /// Test a proof for all the leaves when the number of leaves is odd + #[test] + fn test_proof_all_leaves_odd() { + let mut tree = BridgePoolTree::default(); + let mut transfers = vec![]; + for i in 0..3 { + let transfer = PendingTransfer { + transfer: TransferToEthereum { + kind: TransferToEthereumKind::Erc20, + asset: EthAddress([i; 20]), + sender: bertha_address(), + recipient: EthAddress([i + 1; 20]), + amount: (i as u64).into(), + }, + gas_fee: GasFee { + token: nam(), + amount: 0.into(), + payer: bertha_address(), + }, + }; + let key = Key::from(&transfer); + transfers.push(transfer); + let _ = tree.insert_key(&key, BlockHeight(1)).expect("Test failed"); + } + transfers.sort_by_key(|t| t.keccak256()); + let proof = tree.get_membership_proof(transfers).expect("Test failed"); + assert!(proof.verify(tree.root())); + } + + /// Test proofs of large trees + #[test] + fn test_large_proof() { + let mut tree = BridgePoolTree::default(); + let mut transfers = vec![]; + for i in 0..5 { + let transfer = PendingTransfer { + transfer: TransferToEthereum { + kind: TransferToEthereumKind::Erc20, + asset: EthAddress([i; 20]), + sender: bertha_address(), + recipient: EthAddress([i + 1; 20]), + amount: (i as u64).into(), + }, + gas_fee: GasFee { + token: nam(), + amount: 0.into(), + payer: bertha_address(), + }, + }; + let key = Key::from(&transfer); + transfers.push(transfer); + let _ = tree.insert_key(&key, BlockHeight(1)).expect("Test failed"); + } + transfers.sort_by_key(|t| t.keccak256()); + let values: Vec<_> = transfers.iter().step_by(2).cloned().collect(); + let proof = tree.get_membership_proof(values).expect("Test failed"); + assert!(proof.verify(tree.root())); + } + + /// Create a random set of transfers. + fn random_transfers( + number: usize, + ) -> impl Strategy> { + prop::collection::vec(prop::array::uniform20(0u8..), 0..=number) + .prop_flat_map(|addrs| { + Just( + addrs + .into_iter() + .map(|addr| PendingTransfer { + transfer: TransferToEthereum { + kind: TransferToEthereumKind::Erc20, + asset: EthAddress(addr), + sender: bertha_address(), + recipient: EthAddress(addr), + amount: Default::default(), + }, + gas_fee: GasFee { + token: nam(), + amount: Default::default(), + payer: bertha_address(), + }, + }) + .dedup() + .collect::>(), + ) + }) + } + + prop_compose! { + /// Creates a random set of transfers and + /// then returns them along with a chosen subset. + fn arb_transfers_and_subset() + (transfers in random_transfers(50)) + ( + transfers in Just(transfers.clone()), + to_prove in proptest::sample::subsequence(transfers.clone(), 0..=transfers.len()), + ) + -> (Vec, Vec) { + (transfers, to_prove) + } + } + + proptest! { + /// Given a random tree and a subset of leaves, + /// verify that the constructed multi-proof correctly + /// verifies. + #[test] + fn test_verify_proof((transfers, mut to_prove) in arb_transfers_and_subset()) { + let mut tree = BridgePoolTree::default(); + for transfer in &transfers { + let key = Key::from(transfer); + let _ = tree.insert_key(&key, BlockHeight(1)).expect("Test failed"); + } + + to_prove.sort_by_key(|t| t.keccak256()); + let proof = tree.get_membership_proof(to_prove).expect("Test failed"); + assert!(proof.verify(tree.root())); + } + } +} diff --git a/merkle_tree/src/lib.rs b/merkle_tree/src/lib.rs index d13898a247..fd4a301896 100644 --- a/merkle_tree/src/lib.rs +++ b/merkle_tree/src/lib.rs @@ -1,5 +1,6 @@ //! The merkle tree in the storage +pub mod eth_bridge_pool; pub mod ics23_specs; use std::fmt; @@ -10,21 +11,23 @@ use arse_merkle_tree::error::Error as MtError; use arse_merkle_tree::{ Hash as SmtHash, Key as TreeKey, SparseMerkleTree as ArseMerkleTree, H256, }; +use eth_bridge_pool::{BridgePoolProof, BridgePoolTree}; use ics23::commitment_proof::Proof as Ics23Proof; use ics23::{CommitmentProof, ExistenceProof, NonExistenceProof}; use ics23_specs::ibc_leaf_spec; use namada_core::borsh::{BorshDeserialize, BorshSerialize, BorshSerializeExt}; use namada_core::bytes::ByteBuf; use namada_core::types::address::{Address, InternalAddress}; +use namada_core::types::eth_bridge_pool::{ + is_pending_transfer_key, PendingTransfer, +}; use namada_core::types::hash::{Hash, StorageHasher}; use namada_core::types::keccak::KeccakHash; use namada_core::types::storage::{ self, BlockHeight, DbKeySeg, Epoch, Error as StorageError, Key, KeySeg, StringKey, TreeBytes, TreeKeyError, IBC_KEY_LIMIT, }; -use namada_ethereum_bridge::storage::bridge_pool::{ - is_pending_transfer_key, BridgePoolProof, BridgePoolTree, -}; +use namada_core::types::{self, DecodeError}; use thiserror::Error; /// Trait for reading from a merkle tree that is a sub-tree @@ -298,24 +301,13 @@ impl StoreType { pub fn decode_store>( &self, bytes: T, - ) -> std::result::Result { - use super::Error; + ) -> std::result::Result { match self { - Self::Base => Ok(Store::Base( - types::decode(bytes).map_err(Error::CodingError)?, - )), - Self::Account => Ok(Store::Account( - types::decode(bytes).map_err(Error::CodingError)?, - )), - Self::Ibc => Ok(Store::Ibc( - types::decode(bytes).map_err(Error::CodingError)?, - )), - Self::PoS => Ok(Store::PoS( - types::decode(bytes).map_err(Error::CodingError)?, - )), - Self::BridgePool => Ok(Store::BridgePool( - types::decode(bytes).map_err(Error::CodingError)?, - )), + Self::Base => Ok(Store::Base(types::decode(bytes)?)), + Self::Account => Ok(Store::Account(types::decode(bytes)?)), + Self::Ibc => Ok(Store::Ibc(types::decode(bytes)?)), + Self::PoS => Ok(Store::PoS(types::decode(bytes)?)), + Self::BridgePool => Ok(Store::BridgePool(types::decode(bytes)?)), } } } @@ -822,6 +814,193 @@ impl From for namada_core::tendermint::merkle::proof::ProofOps { } } +impl<'a, H: StorageHasher + Default> SubTreeRead for &'a Smt { + fn root(&self) -> MerkleRoot { + Smt::::root(self).into() + } + + fn subtree_has_key(&self, key: &Key) -> Result { + match self.get(&H::hash(key.to_string()).into()) { + Ok(hash) => Ok(!hash.is_zero()), + Err(e) => Err(Error::MerkleTree(e.to_string())), + } + } + + fn subtree_get(&self, key: &Key) -> Result> { + match self.get(&H::hash(key.to_string()).into()) { + Ok(hash) => Ok(hash.0.to_vec()), + Err(err) => Err(Error::MerkleTree(err.to_string())), + } + } + + fn subtree_membership_proof( + &self, + keys: &[Key], + mut values: Vec, + ) -> Result { + if keys.len() != 1 || values.len() != 1 { + return Err(Error::Ics23MultiLeaf); + } + let key: &Key = &keys[0]; + let value = values.remove(0); + let cp = self.membership_proof(&H::hash(key.to_string()).into())?; + // Replace the values and the leaf op for the verification + match cp.proof.expect("The proof should exist") { + Ics23Proof::Exist(ep) => Ok(CommitmentProof { + proof: Some(Ics23Proof::Exist(ExistenceProof { + key: key.to_string().as_bytes().to_vec(), + value: value.to_vec(), + leaf: Some(ics23_specs::leaf_spec::()), + ..ep + })), + } + .into()), + // the proof should have an ExistenceProof + _ => unreachable!(), + } + } +} + +impl<'a, H: StorageHasher + Default> SubTreeWrite for &'a mut Smt { + fn subtree_update( + &mut self, + key: &Key, + value: StorageBytes, + ) -> Result { + let value = H::hash(value); + self.update(H::hash(key.to_string()).into(), value.into()) + .map(Hash::from) + .map_err(|err| Error::MerkleTree(err.to_string())) + } + + fn subtree_delete(&mut self, key: &Key) -> Result { + let value = Hash::zero(); + self.update(H::hash(key.to_string()).into(), value) + .map(Hash::from) + .map_err(|err| Error::MerkleTree(err.to_string())) + } +} + +impl<'a, H: StorageHasher + Default> SubTreeRead for &'a Amt { + fn root(&self) -> MerkleRoot { + Amt::::root(self).into() + } + + fn subtree_has_key(&self, key: &Key) -> Result { + let key = StringKey::try_from_bytes(key.to_string().as_bytes())?; + match self.get(&key) { + Ok(hash) => Ok(!hash.is_zero()), + Err(e) => Err(Error::MerkleTree(e.to_string())), + } + } + + fn subtree_get(&self, key: &Key) -> Result> { + let key = StringKey::try_from_bytes(key.to_string().as_bytes())?; + match self.get(&key) { + Ok(tree_bytes) => Ok(tree_bytes.into()), + Err(err) => Err(Error::MerkleTree(err.to_string())), + } + } + + fn subtree_membership_proof( + &self, + keys: &[Key], + _: Vec, + ) -> Result { + if keys.len() != 1 { + return Err(Error::Ics23MultiLeaf); + } + + let key = StringKey::try_from_bytes(keys[0].to_string().as_bytes())?; + let cp = self.membership_proof(&key)?; + // Replace the values and the leaf op for the verification + match cp.proof.expect("The proof should exist") { + Ics23Proof::Exist(ep) => Ok(CommitmentProof { + proof: Some(Ics23Proof::Exist(ExistenceProof { + leaf: Some(ics23_specs::ibc_leaf_spec::()), + ..ep + })), + } + .into()), + // the proof should have an ExistenceProof + _ => unreachable!(), + } + } +} + +impl<'a, H: StorageHasher + Default> SubTreeWrite for &'a mut Amt { + fn subtree_update( + &mut self, + key: &Key, + value: StorageBytes, + ) -> Result { + let key = StringKey::try_from_bytes(key.to_string().as_bytes())?; + let value = TreeBytes::from(value.to_vec()); + self.update(key, value) + .map(Into::into) + .map_err(|err| Error::MerkleTree(err.to_string())) + } + + fn subtree_delete(&mut self, key: &Key) -> Result { + let key = StringKey::try_from_bytes(key.to_string().as_bytes())?; + let value = TreeBytes::zero(); + self.update(key, value) + .map(Hash::from) + .map_err(|err| Error::MerkleTree(format!("{:?}", err))) + } +} + +impl<'a> SubTreeRead for &'a BridgePoolTree { + fn root(&self) -> MerkleRoot { + BridgePoolTree::root(self).into() + } + + fn subtree_has_key(&self, key: &Key) -> Result { + self.contains_key(key) + .map_err(|err| Error::MerkleTree(err.to_string())) + } + + fn subtree_get(&self, key: &Key) -> Result> { + match self.get(key) { + Ok(height) => Ok(height.serialize_to_vec()), + Err(err) => Err(Error::MerkleTree(err.to_string())), + } + } + + fn subtree_membership_proof( + &self, + _: &[Key], + values: Vec, + ) -> Result { + let values = values + .iter() + .filter_map(|val| PendingTransfer::try_from_slice(val).ok()) + .collect(); + self.get_membership_proof(values) + .map(Into::into) + .map_err(|err| Error::MerkleTree(err.to_string())) + } +} + +impl<'a> SubTreeWrite for &'a mut BridgePoolTree { + fn subtree_update( + &mut self, + key: &Key, + value: StorageBytes, + ) -> Result { + let height = BlockHeight::try_from_slice(value) + .map_err(|err| Error::MerkleTree(err.to_string()))?; + self.insert_key(key, height) + .map_err(|err| Error::MerkleTree(err.to_string())) + } + + fn subtree_delete(&mut self, key: &Key) -> Result { + self.delete_key(key) + .map_err(|err| Error::MerkleTree(err.to_string()))?; + Ok(self.root().into()) + } +} + #[cfg(test)] mod test { use ics23::HostFunctionsManager; From 518112260fef337f5958ba520b39b8182c8f7f28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Thu, 28 Dec 2023 19:29:15 +0000 Subject: [PATCH 062/118] state: fix build --- Cargo.lock | 10 ++ state/Cargo.toml | 10 ++ state/src/lib.rs | 81 +++++----- state/src/traits.rs | 262 +-------------------------------- state/src/tx_queue.rs | 32 ++-- state/src/types.rs | 32 ---- state/src/wl_storage.rs | 317 ++++++++++++++++++++-------------------- state/src/write_log.rs | 60 ++++---- storage/src/lib.rs | 13 +- 9 files changed, 277 insertions(+), 540 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4070a3e1c2..686abf0172 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4637,11 +4637,21 @@ version = "0.1.0" name = "namada_state" version = "0.28.1" dependencies = [ + "borsh", "ics23", + "itertools 0.10.5", "namada_core", "namada_gas", + "namada_merkle_tree", + "namada_parameters", + "namada_storage", + "namada_trans_token", "namada_tx", + "sha2 0.9.9", "sparse-merkle-tree", + "thiserror", + "tiny-keccak", + "tracing", ] [[package]] diff --git a/state/Cargo.toml b/state/Cargo.toml index eecfa7a528..54620d7f81 100644 --- a/state/Cargo.toml +++ b/state/Cargo.toml @@ -15,7 +15,17 @@ version.workspace = true [dependencies] namada_core = { path = "../core", default-features = false } namada_gas = { path = "../gas" } +namada_merkle_tree = { path = "../merkle_tree" } +namada_parameters = { path = "../parameters" } +namada_storage = { path = "../storage" } +namada_trans_token = { path = "../trans_token" } namada_tx = { path = "../tx" } arse-merkle-tree.workspace = true +borsh.workspace = true ics23.workspace = true +itertools.workspace = true +sha2.workspace = true +thiserror.workspace = true +tiny-keccak = {version = "2.0.2", features = ["keccak"]} +tracing.workspace = true diff --git a/state/src/lib.rs b/state/src/lib.rs index 6d3108da41..9e30ee6d36 100644 --- a/state/src/lib.rs +++ b/state/src/lib.rs @@ -2,7 +2,6 @@ #[cfg(any(test, feature = "testing"))] pub mod mockdb; -pub mod traits; pub mod tx_queue; pub mod types; pub mod wl_storage; @@ -12,41 +11,40 @@ use core::fmt::Debug; use std::cmp::Ordering; use std::format; -use borsh::{BorshDeserialize, BorshSerialize}; -use borsh_ext::BorshSerializeExt; -pub use merkle_tree::{ +use namada_core::borsh::{BorshDeserialize, BorshSerialize, BorshSerializeExt}; +use namada_core::tendermint::merkle::proof::ProofOps; +use namada_core::types::address::{ + Address, EstablishedAddressGen, InternalAddress, +}; +use namada_core::types::chain::{ChainId, CHAIN_ID_LENGTH}; +use namada_core::types::eth_bridge_pool::is_pending_transfer_key; +use namada_core::types::ethereum_events::Uint; +use namada_core::types::hash::{Error as HashError, Hash, StorageHasher}; +use namada_core::types::storage::{ + BlockHash, BlockHeight, BlockResults, Epoch, Epochs, EthEventsQueue, + Header, Key, KeySeg, TxIndex, BLOCK_HASH_LENGTH, BLOCK_HEIGHT_LENGTH, + EPOCH_TYPE_LENGTH, +}; +use namada_core::types::time::DateTimeUtc; +pub use namada_core::types::token::ConversionState; +use namada_core::types::{encode, ethereum_structs}; +use namada_gas::{ + MEMORY_ACCESS_GAS_PER_BYTE, STORAGE_ACCESS_GAS_PER_BYTE, + STORAGE_WRITE_GAS_PER_BYTE, +}; +use namada_merkle_tree::{ + Error as MerkleTreeError, MembershipProof, MerkleRoot, +}; +pub use namada_merkle_tree::{ MerkleTree, MerkleTreeStoresRead, MerkleTreeStoresWrite, StoreType, }; +use namada_parameters::{self, EpochDuration, Parameters}; use thiserror::Error; -pub use traits::{DummyHasher, KeccakHasher, Sha256Hasher, StorageHasher}; +use tx_queue::{ExpiredTxsQueue, TxQueue}; pub use wl_storage::{ iter_prefix_post, iter_prefix_pre, PrefixIter, TempWlStorage, WlStorage, }; -use super::gas::MEMORY_ACCESS_GAS_PER_BYTE; -use crate::ledger::eth_bridge::storage::bridge_pool::is_pending_transfer_key; -use crate::ledger::gas::{ - STORAGE_ACCESS_GAS_PER_BYTE, STORAGE_WRITE_GAS_PER_BYTE, -}; -use crate::ledger::parameters::{self, EpochDuration, Parameters}; -use crate::ledger::storage::merkle_tree::{ - Error as MerkleTreeError, MerkleRoot, -}; -use crate::tendermint::merkle::proof::ProofOps; -use crate::types::address::{Address, EstablishedAddressGen, InternalAddress}; -use crate::types::chain::{ChainId, CHAIN_ID_LENGTH}; -use crate::types::ethereum_events::Uint; -use crate::types::ethereum_structs; -use crate::types::hash::{Error as HashError, Hash}; -use crate::types::internal::{ExpiredTxsQueue, TxQueue}; -use crate::types::storage::{ - BlockHash, BlockHeight, BlockResults, Epoch, Epochs, EthEventsQueue, - Header, Key, KeySeg, MembershipProof, TxIndex, BLOCK_HASH_LENGTH, - BLOCK_HEIGHT_LENGTH, EPOCH_TYPE_LENGTH, -}; -use crate::types::time::DateTimeUtc; -pub use crate::types::token::ConversionState; - /// A result of a function that may fail pub type Result = std::result::Result; @@ -156,9 +154,9 @@ pub enum Error { #[error("Found an unknown key: {key}")] UnknownKey { key: String }, #[error("Storage key error {0}")] - KeyError(crate::types::storage::Error), + KeyError(namada_core::types::storage::Error), #[error("Coding error: {0}")] - CodingError(types::Error), + CodingError(namada_core::types::DecodeError), #[error("Merkle tree error: {0}")] MerkleTreeError(MerkleTreeError), #[error("DB error: {0}")] @@ -726,7 +724,7 @@ where addr: &Address, ) -> Result<(Option, u64)> { let key = if let Address::Implicit(_) = addr { - parameters::storage::get_implicit_vp_key() + namada_parameters::storage::get_implicit_vp_key() } else { Key::validity_predicate(addr) }; @@ -916,7 +914,7 @@ where pub fn get_existence_proof( &self, key: &Key, - value: merkle_tree::StorageBytes, + value: namada_merkle_tree::StorageBytes, height: BlockHeight, ) -> Result { use std::array; @@ -1077,21 +1075,19 @@ where .map_err(Error::KeyError)?; self.block .tree - .update(&key, types::encode(&self.next_epoch_min_start_height))?; + .update(&key, encode(&self.next_epoch_min_start_height))?; let key = key_prefix .push(&"epoch_start_time".to_string()) .map_err(Error::KeyError)?; self.block .tree - .update(&key, types::encode(&self.next_epoch_min_start_time))?; + .update(&key, encode(&self.next_epoch_min_start_time))?; let key = key_prefix .push(&"current_epoch".to_string()) .map_err(Error::KeyError)?; - self.block - .tree - .update(&key, types::encode(&self.block.epoch))?; + self.block.tree.update(&key, encode(&self.block.epoch))?; Ok(()) } @@ -1296,10 +1292,11 @@ impl From for Error { /// Helpers for testing components that depend on storage #[cfg(any(test, feature = "testing"))] pub mod testing { + use namada_core::types::address; + use super::mockdb::MockDB; use super::*; use crate::ledger::storage::traits::Sha256Hasher; - use crate::types::address; /// `WlStorage` with a mock DB for testing pub type TestWlStorage = WlStorage; @@ -1364,15 +1361,15 @@ mod tests { use std::collections::BTreeMap; use chrono::{TimeZone, Utc}; + use namada_core::types::dec::Dec; + use namada_core::types::time::{self, Duration}; + use namada_core::types::token; use proptest::prelude::*; use proptest::test_runner::Config; use super::testing::*; use super::*; use crate::ledger::parameters::{self, Parameters}; - use crate::types::dec::Dec; - use crate::types::time::{self, Duration}; - use crate::types::token; prop_compose! { /// Setup test input data with arbitrary epoch duration, epoch start diff --git a/state/src/traits.rs b/state/src/traits.rs index d276816288..27296b7aa6 100644 --- a/state/src/traits.rs +++ b/state/src/traits.rs @@ -4,11 +4,12 @@ use std::convert::TryInto; use std::fmt; use arse_merkle_tree::traits::{Hasher, Value}; -use arse_merkle_tree::{Key as TreeKey, H256}; -use borsh::BorshDeserialize; -use borsh_ext::BorshSerializeExt; +use arse_merkle_tree::H256; use ics23::commitment_proof::Proof as Ics23Proof; use ics23::{CommitmentProof, ExistenceProof}; +use namada_core::borsh::{BorshDeserialize, BorshSerializeExt}; +use namada_core::types::hash::StorageHasher; +use namada_core::types::storage::Key; use sha2::{Digest, Sha256}; use tiny_keccak::Hasher as KHasher; @@ -21,258 +22,3 @@ use crate::types::hash::Hash; use crate::types::storage::{ BlockHeight, Key, MembershipProof, StringKey, TreeBytes, IBC_KEY_LIMIT, }; - -impl<'a, H: StorageHasher + Default> SubTreeRead for &'a Smt { - fn root(&self) -> MerkleRoot { - Smt::::root(self).into() - } - - fn subtree_has_key(&self, key: &Key) -> Result { - match self.get(&H::hash(key.to_string()).into()) { - Ok(hash) => Ok(!hash.is_zero()), - Err(e) => Err(Error::MerkleTree(e.to_string())), - } - } - - fn subtree_get(&self, key: &Key) -> Result, Error> { - match self.get(&H::hash(key.to_string()).into()) { - Ok(hash) => Ok(hash.0.to_vec()), - Err(err) => Err(Error::MerkleTree(err.to_string())), - } - } - - fn subtree_membership_proof( - &self, - keys: &[Key], - mut values: Vec, - ) -> Result { - if keys.len() != 1 || values.len() != 1 { - return Err(Error::Ics23MultiLeaf); - } - let key: &Key = &keys[0]; - let value = values.remove(0); - let cp = self.membership_proof(&H::hash(key.to_string()).into())?; - // Replace the values and the leaf op for the verification - match cp.proof.expect("The proof should exist") { - Ics23Proof::Exist(ep) => Ok(CommitmentProof { - proof: Some(Ics23Proof::Exist(ExistenceProof { - key: key.to_string().as_bytes().to_vec(), - value: value.to_vec(), - leaf: Some(ics23_specs::leaf_spec::()), - ..ep - })), - } - .into()), - // the proof should have an ExistenceProof - _ => unreachable!(), - } - } -} - -impl<'a, H: StorageHasher + Default> SubTreeWrite for &'a mut Smt { - fn subtree_update( - &mut self, - key: &Key, - value: StorageBytes, - ) -> Result { - let value = H::hash(value); - self.update(H::hash(key.to_string()).into(), value.into()) - .map(Hash::from) - .map_err(|err| Error::MerkleTree(err.to_string())) - } - - fn subtree_delete(&mut self, key: &Key) -> Result { - let value = Hash::zero(); - self.update(H::hash(key.to_string()).into(), value) - .map(Hash::from) - .map_err(|err| Error::MerkleTree(err.to_string())) - } -} - -impl<'a, H: StorageHasher + Default> SubTreeRead for &'a Amt { - fn root(&self) -> MerkleRoot { - Amt::::root(self).into() - } - - fn subtree_has_key(&self, key: &Key) -> Result { - let key = StringKey::try_from_bytes(key.to_string().as_bytes())?; - match self.get(&key) { - Ok(hash) => Ok(!hash.is_zero()), - Err(e) => Err(Error::MerkleTree(e.to_string())), - } - } - - fn subtree_get(&self, key: &Key) -> Result, Error> { - let key = StringKey::try_from_bytes(key.to_string().as_bytes())?; - match self.get(&key) { - Ok(tree_bytes) => Ok(tree_bytes.into()), - Err(err) => Err(Error::MerkleTree(err.to_string())), - } - } - - fn subtree_membership_proof( - &self, - keys: &[Key], - _: Vec, - ) -> Result { - if keys.len() != 1 { - return Err(Error::Ics23MultiLeaf); - } - - let key = StringKey::try_from_bytes(keys[0].to_string().as_bytes())?; - let cp = self.membership_proof(&key)?; - // Replace the values and the leaf op for the verification - match cp.proof.expect("The proof should exist") { - Ics23Proof::Exist(ep) => Ok(CommitmentProof { - proof: Some(Ics23Proof::Exist(ExistenceProof { - leaf: Some(ics23_specs::ibc_leaf_spec::()), - ..ep - })), - } - .into()), - // the proof should have an ExistenceProof - _ => unreachable!(), - } - } -} - -impl<'a, H: StorageHasher + Default> SubTreeWrite for &'a mut Amt { - fn subtree_update( - &mut self, - key: &Key, - value: StorageBytes, - ) -> Result { - let key = StringKey::try_from_bytes(key.to_string().as_bytes())?; - let value = TreeBytes::from(value.to_vec()); - self.update(key, value) - .map(Into::into) - .map_err(|err| Error::MerkleTree(err.to_string())) - } - - fn subtree_delete(&mut self, key: &Key) -> Result { - let key = StringKey::try_from_bytes(key.to_string().as_bytes())?; - let value = TreeBytes::zero(); - self.update(key, value) - .map(Hash::from) - .map_err(|err| Error::MerkleTree(format!("{:?}", err))) - } -} - -impl<'a> SubTreeRead for &'a BridgePoolTree { - fn root(&self) -> MerkleRoot { - BridgePoolTree::root(self).into() - } - - fn subtree_has_key(&self, key: &Key) -> Result { - self.contains_key(key) - .map_err(|err| Error::MerkleTree(err.to_string())) - } - - fn subtree_get(&self, key: &Key) -> Result, Error> { - match self.get(key) { - Ok(height) => Ok(height.serialize_to_vec()), - Err(err) => Err(Error::MerkleTree(err.to_string())), - } - } - - fn subtree_membership_proof( - &self, - _: &[Key], - values: Vec, - ) -> Result { - let values = values - .iter() - .filter_map(|val| PendingTransfer::try_from_slice(val).ok()) - .collect(); - self.get_membership_proof(values) - .map(Into::into) - .map_err(|err| Error::MerkleTree(err.to_string())) - } -} - -impl<'a> SubTreeWrite for &'a mut BridgePoolTree { - fn subtree_update( - &mut self, - key: &Key, - value: StorageBytes, - ) -> Result { - let height = BlockHeight::try_from_slice(value) - .map_err(|err| Error::MerkleTree(err.to_string()))?; - self.insert_key(key, height) - .map_err(|err| Error::MerkleTree(err.to_string())) - } - - fn subtree_delete(&mut self, key: &Key) -> Result { - self.delete_key(key) - .map_err(|err| Error::MerkleTree(err.to_string()))?; - Ok(self.root().into()) - } -} - -impl TreeKey for StringKey { - type Error = Error; - - fn as_slice(&self) -> &[u8] { - &self.original.as_slice()[..self.length] - } - - fn try_from_bytes(bytes: &[u8]) -> Result { - let mut tree_key = [0u8; IBC_KEY_LIMIT]; - let mut original = [0u8; IBC_KEY_LIMIT]; - let mut length = 0; - for (i, byte) in bytes.iter().enumerate() { - if i >= IBC_KEY_LIMIT { - return Err(Error::InvalidMerkleKey( - "Input IBC key is too large".into(), - )); - } - original[i] = *byte; - tree_key[i] = byte.wrapping_add(1); - length += 1; - } - Ok(Self { - original, - tree_key: tree_key.into(), - length, - }) - } -} - -impl Value for Hash { - fn as_slice(&self) -> &[u8] { - self.0.as_slice() - } - - fn zero() -> Self { - Hash([0u8; 32]) - } -} - -impl From for H256 { - fn from(hash: Hash) -> Self { - hash.0.into() - } -} - -impl From for Hash { - fn from(hash: H256) -> Self { - Self(hash.into()) - } -} - -impl From<&H256> for Hash { - fn from(hash: &H256) -> Self { - let hash = hash.to_owned(); - Self(hash.into()) - } -} - -impl Value for TreeBytes { - fn as_slice(&self) -> &[u8] { - self.0.as_slice() - } - - fn zero() -> Self { - TreeBytes::zero() - } -} diff --git a/state/src/tx_queue.rs b/state/src/tx_queue.rs index 6369e056f5..dded7cb8ff 100644 --- a/state/src/tx_queue.rs +++ b/state/src/tx_queue.rs @@ -1,7 +1,7 @@ use borsh::{BorshDeserialize, BorshSerialize}; - +use namada_core::types::ethereum_events::EthereumEvent; use namada_gas::Gas; -use namada_tx::proto::Tx; +use namada_tx::Tx; /// A wrapper for `crate::types::transaction::WrapperTx` to conditionally /// add `has_valid_pow` flag for only used in testnets. @@ -47,32 +47,30 @@ impl TxQueue { self.0.get(index) } } -} - -pub use tx_queue::{TxInQueue, TxQueue}; /// Expired transaction kinds. #[derive(Clone, Debug, BorshSerialize, BorshDeserialize)] pub enum ExpiredTx { -/// Broadcast the given Ethereum event. -EthereumEvent(EthereumEvent), + /// Broadcast the given Ethereum event. + EthereumEvent(EthereumEvent), } /// Queue of expired transactions that need to be retransmitted. #[derive(Default, Clone, Debug, BorshSerialize, BorshDeserialize)] pub struct ExpiredTxsQueue { -inner: Vec, + inner: Vec, } impl ExpiredTxsQueue { -/// Push a new transaction to the back of the queue. -#[inline] -pub fn push(&mut self, tx: ExpiredTx) { - self.inner.push(tx); -} + /// Push a new transaction to the back of the queue. + #[inline] + pub fn push(&mut self, tx: ExpiredTx) { + self.inner.push(tx); + } -/// Consume all the transactions in the queue. -#[inline] -pub fn drain(&mut self) -> impl Iterator + '_ { - self.inner.drain(..) + /// Consume all the transactions in the queue. + #[inline] + pub fn drain(&mut self) -> impl Iterator + '_ { + self.inner.drain(..) + } } diff --git a/state/src/types.rs b/state/src/types.rs index ffe06e4063..52fc61c4eb 100644 --- a/state/src/types.rs +++ b/state/src/types.rs @@ -1,37 +1,5 @@ //! The key and values that may be persisted in a DB. -use borsh::{BorshDeserialize, BorshSerialize}; -use thiserror::Error; - -#[allow(missing_docs)] -#[derive(Error, Debug)] -pub enum Error { - #[error("Deserialization error: {0}")] - DeserializationError(std::io::Error), -} - -/// Result for functions that may fail -type Result = std::result::Result; - -/// Encode a value with borsh -pub fn encode(value: &T) -> Vec -where - T: BorshSerialize, -{ - let size = std::mem::size_of::(); - let mut result = Vec::with_capacity(size); - value.serialize(&mut result).expect("serialization failed"); - result -} - -/// Decode a value with borsh -pub fn decode(bytes: impl AsRef<[u8]>) -> Result -where - T: BorshDeserialize, -{ - T::try_from_slice(bytes.as_ref()).map_err(Error::DeserializationError) -} - /// A key-value pair as raw bytes pub type KVBytes = (Box<[u8]>, Box<[u8]>); diff --git a/state/src/wl_storage.rs b/state/src/wl_storage.rs index 8b4f1a5750..5aed144914 100644 --- a/state/src/wl_storage.rs +++ b/state/src/wl_storage.rs @@ -2,16 +2,16 @@ use std::iter::Peekable; +use namada_core::types::address::Address; +use namada_core::types::hash::{Hash, StorageHasher}; +use namada_core::types::storage::{self, BlockHeight, Epochs}; +use namada_core::types::time::DateTimeUtc; +use namada_parameters::EpochDuration; +use namada_storage::{ResultExt, StorageRead, StorageWrite}; + use super::EPOCH_SWITCH_BLOCKS_DELAY; -use crate::ledger::parameters::EpochDuration; -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, parameters, storage_api}; -use crate::types::address::Address; -use crate::types::hash::Hash; -use crate::types::storage::{self, BlockHeight}; -use crate::types::time::DateTimeUtc; +use crate::write_log::{self, WriteLog}; +use crate::{DBIter, Storage, DB}; /// Storage with write log that allows to implement prefix iterator that works /// with changes not yet committed to the DB. @@ -80,7 +80,7 @@ where } /// Common trait for [`WlStorage`] and [`TempWlStorage`], used to implement -/// storage_api traits. +/// namada_storage traits. pub trait WriteLogAndStorage { /// DB type type D: DB + for<'iter> DBIter<'iter>; @@ -102,10 +102,7 @@ pub trait WriteLogAndStorage { fn split_borrow(&mut self) -> (&mut WriteLog, &Storage); /// Write the provided tx hash to storage. - fn write_tx_hash( - &mut self, - hash: Hash, - ) -> crate::ledger::storage::write_log::Result<()>; + fn write_tx_hash(&mut self, hash: Hash) -> write_log::Result<()>; } impl WriteLogAndStorage for WlStorage @@ -132,10 +129,7 @@ where (&mut self.write_log, &self.storage) } - fn write_tx_hash( - &mut self, - hash: Hash, - ) -> crate::ledger::storage::write_log::Result<()> { + fn write_tx_hash(&mut self, hash: Hash) -> write_log::Result<()> { self.write_log.write_tx_hash(hash) } } @@ -164,10 +158,7 @@ where (&mut self.write_log, (self.storage)) } - fn write_tx_hash( - &mut self, - hash: Hash, - ) -> crate::ledger::storage::write_log::Result<()> { + fn write_tx_hash(&mut self, hash: Hash) -> write_log::Result<()> { self.write_log.write_tx_hash(hash) } } @@ -197,7 +188,7 @@ where /// 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<()> { + pub fn commit_block(&mut self) -> namada_storage::Result<()> { if self.storage.last_epoch != self.storage.block.epoch { self.storage .update_epoch_in_merkle_tree() @@ -217,9 +208,9 @@ where &mut self, height: BlockHeight, time: DateTimeUtc, - ) -> crate::ledger::storage::Result { - let parameters = - parameters::read(self).expect("Couldn't read protocol parameters"); + ) -> storage::Result { + let parameters = namada_parameters::read(self) + .expect("Couldn't read protocol parameters"); match self.storage.update_epoch_blocks_delay.as_mut() { None => { @@ -265,10 +256,7 @@ where } /// Delete the provided transaction's hash from storage. - pub fn delete_tx_hash( - &mut self, - hash: Hash, - ) -> crate::ledger::storage::write_log::Result<()> { + pub fn delete_tx_hash(&mut self, hash: Hash) -> write_log::Result<()> { self.write_log.delete_tx_hash(hash) } } @@ -306,7 +294,7 @@ where storage_iter, write_log_iter, }, - prefix.len() as u64 * gas::STORAGE_ACCESS_GAS_PER_BYTE, + prefix.len() as u64 * namada_gas::STORAGE_ACCESS_GAS_PER_BYTE, ) } @@ -331,7 +319,7 @@ where storage_iter, write_log_iter, }, - prefix.len() as u64 * gas::STORAGE_ACCESS_GAS_PER_BYTE, + prefix.len() as u64 * namada_gas::STORAGE_ACCESS_GAS_PER_BYTE, ) } @@ -409,146 +397,157 @@ where } } -impl StorageRead for T -where - T: WriteLogAndStorage, - D: 'static + DB + for<'iter> DBIter<'iter>, - H: 'static + 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_code_hash, - }) => Ok(Some(vp_code_hash.to_vec())), - 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() +macro_rules! impl_storage_traits { + ($($type:ty)*) => { + impl StorageRead for $($type)* + where + D: 'static + DB + for<'iter> DBIter<'iter>, + H: 'static + StorageHasher, + { + type PrefixIter<'iter> = PrefixIter<'iter, D> where Self: 'iter; + + fn read_bytes( + &self, + key: &storage::Key, + ) -> namada_storage::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_code_hash, + }) => Ok(Some(vp_code_hash.to_vec())), + 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) + fn has_key(&self, key: &storage::Key) -> namada_storage::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 + Ok(self.storage().has_key(key).into_storage_result()?.0) + } + } } - None => { - // when not found in write log, try to check the storage - Ok(self.storage().has_key(key).into_storage_result()?.0) + + fn iter_prefix<'iter>( + &'iter self, + prefix: &storage::Key, + ) -> namada_storage::Result> { + let (iter, _gas) = + iter_prefix_post(self.write_log(), self.storage(), prefix); + Ok(iter) } - } - } - 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>, + ) -> namada_storage::Result)>> { + Ok(iter.next().map(|(key, val, _gas)| (key, val))) + } - 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_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_height( - &self, - ) -> std::result::Result { - Ok(self.storage().block.height) - } + fn get_block_header( + &self, + height: storage::BlockHeight, + ) -> std::result::Result, namada_storage::Error> + { + self.storage() + .db + .read_block_header(height) + .into_storage_result() + } - fn get_block_header( - &self, - height: storage::BlockHeight, - ) -> std::result::Result, storage_api::Error> { - self.storage() - .db - .read_block_header(height) - .into_storage_result() - } + fn get_block_hash( + &self, + ) -> std::result::Result { + Ok(self.storage().block.hash.clone()) + } - 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_block_epoch( - &self, - ) -> std::result::Result { - Ok(self.storage().block.epoch) - } + fn get_pred_epochs(&self) -> namada_storage::Result { + Ok(self.storage().block.pred_epochs.clone()) + } - fn get_tx_index( - &self, - ) -> std::result::Result { - Ok(self.storage().tx_index) - } + 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()) - } -} + fn get_native_token(&self) -> namada_storage::Result
{ + Ok(self.storage().native_token.clone()) + } + } -impl StorageWrite for T -where - T: WriteLogAndStorage, - D: DB + for<'iter> DBIter<'iter>, - H: StorageHasher, -{ - // N.B. Calling this when testing pre- and post- reads in - // regards to testing native vps is incorrect. - fn write_bytes( - &mut self, - key: &storage::Key, - val: impl AsRef<[u8]>, - ) -> storage_api::Result<()> { - let _ = self - .write_log_mut() - .protocol_write(key, val.as_ref().to_vec()) - .into_storage_result(); - Ok(()) - } + impl StorageWrite for $($type)* + where + D: DB + for<'iter> DBIter<'iter>, + H: StorageHasher, + { + // N.B. Calling this when testing pre- and post- reads in + // regards to testing native vps is incorrect. + fn write_bytes( + &mut self, + key: &storage::Key, + val: impl AsRef<[u8]>, + ) -> namada_storage::Result<()> { + let _ = self + .write_log_mut() + .protocol_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_mut() - .protocol_delete(key) - .into_storage_result(); - Ok(()) - } + fn delete(&mut self, key: &storage::Key) -> namada_storage::Result<()> { + let _ = self + .write_log_mut() + .protocol_delete(key) + .into_storage_result(); + Ok(()) + } + } + }; } +impl_storage_traits!(WlStorage); +impl_storage_traits!(TempWlStorage<'_, D, H>); #[cfg(test)] mod tests { @@ -556,6 +555,8 @@ mod tests { use borsh::BorshDeserialize; use borsh_ext::BorshSerializeExt; + use namada_core::types::address::InternalAddress; + use namada_core::types::storage::DbKeySeg; use proptest::prelude::*; use proptest::test_runner::Config; // Use `RUST_LOG=info` (or another tracing level) and `--nocapture` to @@ -564,8 +565,6 @@ mod tests { use super::*; use crate::ledger::storage::testing::TestWlStorage; - use crate::types::address::InternalAddress; - use crate::types::storage::DbKeySeg; proptest! { // Generate arb valid input for `test_prefix_iters_aux` @@ -713,7 +712,7 @@ mod tests { } Level::TxWriteLog(WlMod::DeletePrefix) => { // Find keys matching the prefix - let keys = storage_api::iter_prefix_bytes(s, key) + let keys = namada_storage::iter_prefix_bytes(s, key) .unwrap() .map(|res| { let (key, _val) = res.unwrap(); diff --git a/state/src/write_log.rs b/state/src/write_log.rs index e24fcc54a2..e2b91c16bd 100644 --- a/state/src/write_log.rs +++ b/state/src/write_log.rs @@ -4,28 +4,26 @@ use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use itertools::Itertools; -use thiserror::Error; - -use crate::ledger; -use crate::ledger::gas::{ - MEMORY_ACCESS_GAS_PER_BYTE, STORAGE_WRITE_GAS_PER_BYTE, +use namada_core::ledger::replay_protection; +use namada_core::types::address::{ + Address, EstablishedAddressGen, InternalAddress, }; -use crate::ledger::replay_protection::{all_key, last_key}; -use crate::ledger::storage::traits::StorageHasher; -use crate::ledger::storage::Storage; -use crate::types::address::{Address, EstablishedAddressGen, InternalAddress}; -use crate::types::hash::Hash; -use crate::types::ibc::IbcEvent; -use crate::types::storage; -use crate::types::token::{ +use namada_core::types::hash::{Hash, StorageHasher}; +use namada_core::types::ibc::IbcEvent; +use namada_core::types::storage; +use namada_gas::{MEMORY_ACCESS_GAS_PER_BYTE, STORAGE_WRITE_GAS_PER_BYTE}; +use namada_trans_token::storage_key::{ is_any_minted_balance_key, is_any_minter_key, is_any_token_balance_key, }; +use thiserror::Error; + +use crate::{DBIter, Storage, DB}; #[allow(missing_docs)] #[derive(Error, Debug)] pub enum Error { #[error("Storage error applying a write log: {0}")] - StorageError(ledger::storage::Error), + StorageError(crate::Error), #[error("Trying to update a temporary value")] UpdateTemporaryValue, #[error( @@ -496,15 +494,13 @@ impl WriteLog { /// Commit the current block's write log to the storage. Starts a new block /// write log. - pub fn commit_block( + pub fn commit_block( &mut self, - storage: &mut Storage, - batch: &mut DB::WriteBatch, + storage: &mut Storage, + batch: &mut D::WriteBatch, ) -> Result<()> where - DB: 'static - + ledger::storage::DB - + for<'iter> ledger::storage::DBIter<'iter>, + D: 'static + DB + for<'iter> DBIter<'iter>, H: StorageHasher, { for (key, entry) in self.block_write_log.iter() { @@ -536,7 +532,7 @@ impl WriteLog { batch, // Can only write tx hashes to the previous block, no // further - &last_key(hash), + &replay_protection::last_key(hash), ) .map_err(Error::StorageError)?, ReProtStorageModification::Delete => storage @@ -544,15 +540,21 @@ impl WriteLog { batch, // Can only delete tx hashes from the previous block, // no further - &last_key(hash), + &replay_protection::last_key(hash), ) .map_err(Error::StorageError)?, ReProtStorageModification::Finalize => { storage - .write_replay_protection_entry(batch, &all_key(hash)) + .write_replay_protection_entry( + batch, + &replay_protection::all_key(hash), + ) .map_err(Error::StorageError)?; storage - .delete_replay_protection_entry(batch, &last_key(hash)) + .delete_replay_protection_entry( + batch, + &replay_protection::last_key(hash), + ) .map_err(Error::StorageError)? } } @@ -725,12 +727,12 @@ impl WriteLog { #[cfg(test)] mod tests { + use namada_core::types::hash::Hash; + use namada_core::types::{address, storage}; use pretty_assertions::assert_eq; use proptest::prelude::*; use super::*; - use crate::types::hash::Hash; - use crate::types::{address, storage}; #[test] fn test_crud_value() { @@ -1104,13 +1106,13 @@ 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::hash::HASH_LENGTH; + 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::hash::HASH_LENGTH; - use crate::types::storage::testing::arb_key; /// Generate an arbitrary tx write log of [`HashMap`]. diff --git a/storage/src/lib.rs b/storage/src/lib.rs index 07a0288b1f..fa17755908 100644 --- a/storage/src/lib.rs +++ b/storage/src/lib.rs @@ -87,9 +87,6 @@ pub trait StorageRead { /// current transaction is being applied. fn get_block_epoch(&self) -> Result; - /// Get the height of the first block of the current epoch. - fn get_current_epoch_start_height(&self) -> Result; - /// Given the information about predecessor block epochs fn get_pred_epochs(&self) -> Result; @@ -99,6 +96,16 @@ pub trait StorageRead { /// Get the native token address fn get_native_token(&self) -> Result
; + /// Get the height of the first block of the current epoch. + fn get_current_epoch_start_height(&self) -> Result { + Ok(self + .get_pred_epochs()? + .first_block_heights() + .last() + .copied() + .expect("The block height of the current epoch should be known")) + } + /// Get the height of the first block of the given epoch. fn get_epoch_start_height( &self, From ac516d9b0ebbc22d29a1864213ba72e6c759c5cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Thu, 28 Dec 2023 19:40:36 +0000 Subject: [PATCH 063/118] ethereum_bridge: use the original name for wrapped erc20s --- .../storage/{wrapped_erc20.rs => wrapped_erc20s.rs} | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) rename ethereum_bridge/src/storage/{wrapped_erc20.rs => wrapped_erc20s.rs} (96%) diff --git a/ethereum_bridge/src/storage/wrapped_erc20.rs b/ethereum_bridge/src/storage/wrapped_erc20s.rs similarity index 96% rename from ethereum_bridge/src/storage/wrapped_erc20.rs rename to ethereum_bridge/src/storage/wrapped_erc20s.rs index 0a4a576038..c1f14e49e7 100644 --- a/ethereum_bridge/src/storage/wrapped_erc20.rs +++ b/ethereum_bridge/src/storage/wrapped_erc20s.rs @@ -1,11 +1,11 @@ //! Functionality for accessing the multitoken subspace use eyre::eyre; - -use crate::types::address::{Address, InternalAddress}; -use crate::types::ethereum_events::EthAddress; -use crate::types::storage::{self, DbKeySeg}; -use crate::types::token::{ +use namada_core::types::address::{Address, InternalAddress}; +use namada_core::types::eth_bridge_pool::erc20_token_address; +use namada_core::types::ethereum_events::EthAddress; +use namada_core::types::storage::{self, DbKeySeg}; +use namada_trans_token::storage_key::{ balance_key, minted_balance_key, MINTED_STORAGE_KEY, }; @@ -32,7 +32,7 @@ pub struct Key { impl From<&Key> for storage::Key { fn from(mt_key: &Key) -> Self { - let token = token(&mt_key.asset); + let token = erc20_token_address(&mt_key.asset); match &mt_key.suffix { KeyType::Balance { owner } => balance_key(&token, owner), KeyType::Supply => minted_balance_key(&token), From 6b629ef4152c13966341c8d6bf379fd7aed24fee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 2 Jan 2024 16:56:44 +0000 Subject: [PATCH 064/118] ethereum_bridge: fix build --- Cargo.lock | 7 +- core/src/types/eth_bridge_pool.rs | 6 +- ethereum_bridge/Cargo.toml | 13 +- .../transactions/bridge_pool_roots.rs | 25 +- .../transactions/ethereum_events/eth_msgs.rs | 2 +- .../transactions/ethereum_events/events.rs | 38 +- .../transactions/ethereum_events/mod.rs | 13 +- .../src/protocol/transactions/read.rs | 7 +- .../src/protocol/transactions/update.rs | 7 +- .../src/protocol/transactions/utils.rs | 2 +- .../transactions/validator_set_update/mod.rs | 8 +- .../src/protocol/transactions/votes.rs | 2 +- .../protocol/transactions/votes/storage.rs | 9 +- .../src/protocol/transactions/votes/update.rs | 2 +- ethereum_bridge/src/storage/bridge_pool.rs | 976 +----------------- .../src/storage/eth_bridge_queries.rs | 34 +- ethereum_bridge/src/storage/mod.rs | 11 +- ethereum_bridge/src/storage/parameters.rs | 30 +- ethereum_bridge/src/storage/proof.rs | 4 +- ethereum_bridge/src/storage/vote_tallies.rs | 2 +- ethereum_bridge/src/storage/vp/bridge_pool.rs | 12 +- .../src/storage/vp/ethereum_bridge.rs | 20 +- ethereum_bridge/src/storage/whitelist.rs | 8 +- ethereum_bridge/src/test_utils.rs | 8 +- macros/src/lib.rs | 7 +- state/src/lib.rs | 3 +- trans_token/src/storage_key.rs | 8 +- 27 files changed, 152 insertions(+), 1112 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 686abf0172..5774973c4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4439,7 +4439,6 @@ version = "0.29.0" dependencies = [ "assert_matches", "borsh", - "borsh-ext", "data-encoding", "ethabi", "ethers", @@ -4447,14 +4446,20 @@ dependencies = [ "itertools 0.10.5", "namada_core", "namada_macros", + "namada_parameters", "namada_proof_of_stake", + "namada_state", + "namada_storage", "namada_trans_token", + "namada_tx", + "namada_vote_ext", "rand 0.8.5", "serde 1.0.193", "serde_json", "tendermint", "tendermint-proto", "tendermint-rpc", + "thiserror", "toml 0.5.11", "tracing", ] diff --git a/core/src/types/eth_bridge_pool.rs b/core/src/types/eth_bridge_pool.rs index d157ed5525..d3fa9c563b 100644 --- a/core/src/types/eth_bridge_pool.rs +++ b/core/src/types/eth_bridge_pool.rs @@ -28,8 +28,10 @@ pub const BRIDGE_POOL_ADDRESS: Address = /// Bridge pool key segments. #[derive(StorageKeys)] pub struct Segments { - signed_root: &'static str, - bridge_pool_nonce: &'static str, + /// Signed root storage key + pub signed_root: &'static str, + /// Bridge pool nonce storage key + pub bridge_pool_nonce: &'static str, } /// Check if a key is for a pending transfer diff --git a/ethereum_bridge/Cargo.toml b/ethereum_bridge/Cargo.toml index 0a2e005d2d..ca85d3ac72 100644 --- a/ethereum_bridge/Cargo.toml +++ b/ethereum_bridge/Cargo.toml @@ -20,11 +20,17 @@ testing = [ [dependencies] namada_core = {path = "../core", default-features = false, features = ["ethers-derive"]} -namada_trans_token = {path = "../trans_token"} namada_macros = {path = "../macros"} +namada_parameters = {path = "../parameters"} namada_proof_of_stake = {path = "../proof_of_stake", default-features = false} +namada_state = {path = "../state"} +namada_storage = {path = "../storage"} +namada_trans_token = {path = "../trans_token"} +namada_tx = {path = "../tx"} +namada_vote_ext = {path = "../vote_ext"} + borsh.workspace = true -borsh-ext.workspace = true +ethabi.workspace = true ethers.workspace = true eyre.workspace = true itertools.workspace = true @@ -32,8 +38,9 @@ serde.workspace = true serde_json.workspace = true rand.workspace = true tendermint = {workspace = true} -tendermint-rpc = {workspace = true} tendermint-proto = {workspace = true} +tendermint-rpc = {workspace = true} +thiserror.workspace = true tracing = "0.1.30" [dev-dependencies] diff --git a/ethereum_bridge/src/protocol/transactions/bridge_pool_roots.rs b/ethereum_bridge/src/protocol/transactions/bridge_pool_roots.rs index bffcf013e1..9ac83fae05 100644 --- a/ethereum_bridge/src/protocol/transactions/bridge_pool_roots.rs +++ b/ethereum_bridge/src/protocol/transactions/bridge_pool_roots.rs @@ -1,20 +1,20 @@ use std::collections::{HashMap, HashSet}; use eyre::Result; -use namada_core::ledger::eth_bridge::storage::bridge_pool::get_signed_root_key; -use namada_core::ledger::storage::{DBIter, StorageHasher, WlStorage, DB}; -use namada_core::ledger::storage_api::{StorageRead, StorageWrite}; use namada_core::types::address::Address; use namada_core::types::storage::BlockHeight; use namada_core::types::token::Amount; -use namada_core::types::transaction::TxResult; -use namada_core::types::vote_extensions::bridge_pool_roots::MultiSignedVext; use namada_proof_of_stake::pos_queries::PosQueries; +use namada_state::{DBIter, StorageHasher, WlStorage, DB}; +use namada_storage::{StorageRead, StorageWrite}; +use namada_tx::data::TxResult; +use namada_vote_ext::bridge_pool_roots::{MultiSignedVext, SignedVext}; use crate::protocol::transactions::utils::GetVoters; use crate::protocol::transactions::votes::update::NewVotes; use crate::protocol::transactions::votes::{calculate_new, Votes}; use crate::protocol::transactions::{utils, votes, ChangedKeys}; +use crate::storage::bridge_pool::get_signed_root_key; use crate::storage::eth_bridge_queries::EthBridgeQueries; use crate::storage::proof::BridgePoolRootProof; use crate::storage::vote_tallies::{self, BridgePoolRoot}; @@ -145,7 +145,7 @@ where .get_bridge_pool_nonce_at_height(height); let mut partial_proof = BridgePoolRootProof::new((root, nonce)); partial_proof.attach_signature_batch(multisigned.clone().into_iter().map( - |signed| { + |SignedVext(signed)| { ( wl_storage .ethbridge_queries() @@ -157,8 +157,11 @@ where )); let seen_by: Votes = multisigned + .0 .into_iter() - .map(|signed| (signed.data.validator_addr, signed.data.block_height)) + .map(|SignedVext(signed)| { + (signed.data.validator_addr, signed.data.block_height) + }) .collect(); (BridgePoolRoot(partial_proof), seen_by) } @@ -222,25 +225,23 @@ mod test_apply_bp_roots_to_storage { use assert_matches::assert_matches; use borsh::BorshDeserialize; - use namada_core::ledger::eth_bridge::storage::bridge_pool::{ - get_key_from_hash, get_nonce_key, - }; use namada_core::ledger::storage::testing::TestWlStorage; - use namada_core::ledger::storage_api::StorageRead; use namada_core::proto::{SignableEthMessage, Signed}; use namada_core::types::address; use namada_core::types::ethereum_events::Uint; use namada_core::types::keccak::{keccak_hash, KeccakHash}; use namada_core::types::storage::Key; - use namada_core::types::vote_extensions::bridge_pool_roots; use namada_core::types::voting_power::FractionalVotingPower; use namada_proof_of_stake::parameters::OwnedPosParams; use namada_proof_of_stake::storage::write_pos_params; + use namada_storage::StorageRead; + use namada_vote_ext::bridge_pool_roots; use super::*; use crate::protocol::transactions::votes::{ EpochedVotingPower, EpochedVotingPowerExt, }; + use crate::storage::bridge_pool::{get_key_from_hash, get_nonce_key}; use crate::storage::vp; use crate::test_utils; diff --git a/ethereum_bridge/src/protocol/transactions/ethereum_events/eth_msgs.rs b/ethereum_bridge/src/protocol/transactions/ethereum_events/eth_msgs.rs index df9b9740ee..515c793a65 100644 --- a/ethereum_bridge/src/protocol/transactions/ethereum_events/eth_msgs.rs +++ b/ethereum_bridge/src/protocol/transactions/ethereum_events/eth_msgs.rs @@ -1,6 +1,6 @@ use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use namada_core::types::ethereum_events::EthereumEvent; -use namada_core::types::vote_extensions::ethereum_events::MultiSignedEthEvent; +use namada_vote_ext::ethereum_events::MultiSignedEthEvent; use crate::protocol::transactions::votes::{dedupe, Tally, Votes}; diff --git a/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs b/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs index af4c96e904..e06f47d657 100644 --- a/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs +++ b/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs @@ -6,21 +6,12 @@ use std::str::FromStr; use borsh::BorshDeserialize; use eyre::{Result, WrapErr}; use namada_core::hints; -use namada_core::ledger::eth_bridge::storage::bridge_pool::{ - get_nonce_key, is_pending_transfer_key, BRIDGE_POOL_ADDRESS, -}; -use namada_core::ledger::eth_bridge::storage::{ - self as bridge_storage, wrapped_erc20s, -}; use namada_core::ledger::eth_bridge::ADDRESS as BRIDGE_ADDRESS; -use namada_core::ledger::parameters::read_epoch_duration_parameter; -use namada_core::ledger::storage::traits::StorageHasher; -use namada_core::ledger::storage::{DBIter, WlStorage, DB}; -use namada_core::ledger::storage_api::{StorageRead, StorageWrite}; use namada_core::types::address::Address; use namada_core::types::eth_abi::Encode; use namada_core::types::eth_bridge_pool::{ - PendingTransfer, TransferToEthereumKind, + erc20_nut_address, erc20_token_address, PendingTransfer, + TransferToEthereumKind, }; use namada_core::types::ethereum_events::{ EthAddress, EthereumEvent, TransferToEthereum, TransferToNamada, @@ -29,11 +20,18 @@ use namada_core::types::ethereum_events::{ use namada_core::types::ethereum_structs::EthBridgeEvent; use namada_core::types::storage::{BlockHeight, Key, KeySeg}; use namada_core::types::token; -use namada_core::types::token::{balance_key, minted_balance_key}; +use namada_parameters::read_epoch_duration_parameter; +use namada_state::{DBIter, StorageHasher, WlStorage, DB}; +use namada_storage::{StorageRead, StorageWrite}; +use namada_trans_token::storage_key::{balance_key, minted_balance_key}; use crate::protocol::transactions::update; +use crate::storage::bridge_pool::{ + get_nonce_key, is_pending_transfer_key, BRIDGE_POOL_ADDRESS, +}; use crate::storage::eth_bridge_queries::{EthAssetMint, EthBridgeQueries}; use crate::storage::parameters::read_native_erc20_address; +use crate::storage::{self as bridge_storage}; /// Updates storage based on the given confirmed `event`. For example, for a /// confirmed [`EthereumEvent::TransfersToNamada`], mint the corresponding @@ -163,11 +161,11 @@ where H: 'static + StorageHasher + Sync, { let eth_bridge_native_token_balance_key = - token::balance_key(&wl_storage.storage.native_token, &BRIDGE_ADDRESS); + balance_key(&wl_storage.storage.native_token, &BRIDGE_ADDRESS); let receiver_native_token_balance_key = - token::balance_key(&wl_storage.storage.native_token, receiver); + balance_key(&wl_storage.storage.native_token, receiver); let native_werc20_supply_key = - minted_balance_key(&wrapped_erc20s::token(native_erc20)); + minted_balance_key(&erc20_token_address(native_erc20)); update::amount( wl_storage, @@ -255,11 +253,11 @@ where // check if we should mint nuts asset_count .should_mint_nuts() - .then(|| (wrapped_erc20s::nut(asset), asset_count.nut_amount)), + .then(|| (erc20_nut_address(asset), asset_count.nut_amount)), // check if we should mint erc20s asset_count .should_mint_erc20s() - .then(|| (wrapped_erc20s::token(asset), asset_count.erc20_amount)), + .then(|| (erc20_token_address(asset), asset_count.erc20_amount)), ] .into_iter() // remove assets that do not need to be @@ -584,10 +582,6 @@ mod tests { use assert_matches::assert_matches; use borsh_ext::BorshSerializeExt; use eyre::Result; - use namada_core::ledger::eth_bridge::storage::bridge_pool::get_pending_key; - use namada_core::ledger::parameters::{ - update_epoch_parameter, EpochDuration, - }; use namada_core::ledger::storage::mockdb::MockDBWriteBatch; use namada_core::ledger::storage::testing::TestWlStorage; use namada_core::ledger::storage::types::encode; @@ -600,8 +594,10 @@ mod tests { use namada_core::types::time::DurationSecs; use namada_core::types::token::Amount; use namada_core::types::{address, eth_bridge_pool}; + use namada_parameters::{update_epoch_parameter, EpochDuration}; use super::*; + use crate::storage::bridge_pool::get_pending_key; use crate::test_utils::{self, stored_keys_count}; fn init_storage(wl_storage: &mut TestWlStorage) { diff --git a/ethereum_bridge/src/protocol/transactions/ethereum_events/mod.rs b/ethereum_bridge/src/protocol/transactions/ethereum_events/mod.rs index 00f0bb2084..02bd26a95e 100644 --- a/ethereum_bridge/src/protocol/transactions/ethereum_events/mod.rs +++ b/ethereum_bridge/src/protocol/transactions/ethereum_events/mod.rs @@ -8,17 +8,16 @@ use std::collections::{BTreeSet, HashMap, HashSet}; use borsh::BorshDeserialize; use eth_msgs::EthMsgUpdate; use eyre::Result; -use namada_core::ledger::storage::traits::StorageHasher; -use namada_core::ledger::storage::{DBIter, WlStorage, DB}; use namada_core::types::address::Address; use namada_core::types::ethereum_events::EthereumEvent; use namada_core::types::ethereum_structs::EthBridgeEvent; -use namada_core::types::internal::ExpiredTx; use namada_core::types::storage::{BlockHeight, Epoch, Key}; use namada_core::types::token::Amount; -use namada_core::types::transaction::TxResult; -use namada_core::types::vote_extensions::ethereum_events::MultiSignedEthEvent; use namada_proof_of_stake::pos_queries::PosQueries; +use namada_state::tx_queue::ExpiredTx; +use namada_state::{DBIter, StorageHasher, WlStorage, DB}; +use namada_tx::data::TxResult; +use namada_vote_ext::ethereum_events::MultiSignedEthEvent; use super::ChangedKeys; use crate::protocol::transactions::utils; @@ -301,10 +300,8 @@ mod tests { use std::collections::{BTreeSet, HashMap, HashSet}; use borsh::BorshDeserialize; - use namada_core::ledger::eth_bridge::storage::wrapped_erc20s; use namada_core::ledger::storage::mockdb::MockDBWriteBatch; use namada_core::ledger::storage::testing::TestWlStorage; - use namada_core::ledger::storage_api::StorageRead; use namada_core::types::address; use namada_core::types::ethereum_events::testing::{ arbitrary_amount, arbitrary_eth_address, arbitrary_nonce, @@ -315,12 +312,14 @@ mod tests { }; use namada_core::types::token::{balance_key, minted_balance_key}; use namada_core::types::voting_power::FractionalVotingPower; + use namada_storage::StorageRead; use super::*; use crate::protocol::transactions::utils::GetVoters; use crate::protocol::transactions::votes::{ EpochedVotingPower, EpochedVotingPowerExt, Votes, }; + use crate::storage::wrapped_erc20s; use crate::test_utils; /// All kinds of [`Keys`]. diff --git a/ethereum_bridge/src/protocol/transactions/read.rs b/ethereum_bridge/src/protocol/transactions/read.rs index 257c045e33..9aae435d0f 100644 --- a/ethereum_bridge/src/protocol/transactions/read.rs +++ b/ethereum_bridge/src/protocol/transactions/read.rs @@ -1,11 +1,10 @@ //! Helpers for reading from storage use borsh::BorshDeserialize; use eyre::{eyre, Result}; -use namada_core::ledger::storage::traits::StorageHasher; -use namada_core::ledger::storage::{DBIter, WlStorage, DB}; -use namada_core::ledger::storage_api::StorageRead; use namada_core::types::storage; use namada_core::types::token::Amount; +use namada_state::{DBIter, StorageHasher, WlStorage, DB}; +use namada_storage::StorageRead; /// Returns the stored Amount, or 0 if not stored pub(super) fn amount_or_default( @@ -57,9 +56,9 @@ mod tests { use assert_matches::assert_matches; use borsh_ext::BorshSerializeExt; use namada_core::ledger::storage::testing::TestWlStorage; - use namada_core::ledger::storage_api::StorageWrite; use namada_core::types::storage; use namada_core::types::token::Amount; + use namada_storage::StorageWrite; use crate::protocol::transactions::read; diff --git a/ethereum_bridge/src/protocol/transactions/update.rs b/ethereum_bridge/src/protocol/transactions/update.rs index d14c20ec35..2be80c6cea 100644 --- a/ethereum_bridge/src/protocol/transactions/update.rs +++ b/ethereum_bridge/src/protocol/transactions/update.rs @@ -1,10 +1,11 @@ //! Helpers for writing to storage use borsh::{BorshDeserialize, BorshSerialize}; use eyre::Result; -use namada_core::ledger::storage::{DBIter, StorageHasher, WlStorage, DB}; -use namada_core::ledger::storage_api::StorageWrite; +use namada_core::types::hash::StorageHasher; use namada_core::types::storage; use namada_core::types::token::Amount; +use namada_state::{DBIter, WlStorage, DB}; +use namada_storage::StorageWrite; /// Reads the `Amount` from key, applies update then writes it back pub fn amount( @@ -45,8 +46,8 @@ mod tests { use borsh_ext::BorshSerializeExt; use eyre::{eyre, Result}; use namada_core::ledger::storage::testing::TestWlStorage; - use namada_core::ledger::storage_api::{StorageRead, StorageWrite}; use namada_core::types::storage; + use namada_storage::{StorageRead, StorageWrite}; #[test] /// Test updating a value diff --git a/ethereum_bridge/src/protocol/transactions/utils.rs b/ethereum_bridge/src/protocol/transactions/utils.rs index beee69a0de..63eaf8530e 100644 --- a/ethereum_bridge/src/protocol/transactions/utils.rs +++ b/ethereum_bridge/src/protocol/transactions/utils.rs @@ -2,12 +2,12 @@ use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use eyre::eyre; use itertools::Itertools; -use namada_core::ledger::storage::{DBIter, StorageHasher, WlStorage, DB}; use namada_core::types::address::Address; use namada_core::types::storage::BlockHeight; use namada_core::types::token; use namada_proof_of_stake::pos_queries::PosQueries; use namada_proof_of_stake::types::WeightedValidator; +use namada_state::{DBIter, StorageHasher, WlStorage, DB}; /// Proof of some arbitrary tally whose voters can be queried. pub(super) trait GetVoters { diff --git a/ethereum_bridge/src/protocol/transactions/validator_set_update/mod.rs b/ethereum_bridge/src/protocol/transactions/validator_set_update/mod.rs index e4b562e0ac..db20459bb8 100644 --- a/ethereum_bridge/src/protocol/transactions/validator_set_update/mod.rs +++ b/ethereum_bridge/src/protocol/transactions/validator_set_update/mod.rs @@ -3,12 +3,12 @@ use std::collections::{HashMap, HashSet}; use eyre::Result; -use namada_core::ledger::storage::{DBIter, StorageHasher, WlStorage, DB}; use namada_core::types::address::Address; use namada_core::types::storage::{BlockHeight, Epoch}; use namada_core::types::token::Amount; -use namada_core::types::transaction::TxResult; -use namada_core::types::vote_extensions::validator_set_update; +use namada_state::{DBIter, StorageHasher, WlStorage, DB}; +use namada_tx::data::TxResult; +use namada_vote_ext::validator_set_update; use super::ChangedKeys; use crate::protocol::transactions::utils; @@ -197,9 +197,9 @@ where #[cfg(test)] mod test_valset_upd_state_changes { use namada_core::types::address; - use namada_core::types::vote_extensions::validator_set_update::VotingPowersMap; use namada_core::types::voting_power::FractionalVotingPower; use namada_proof_of_stake::pos_queries::PosQueries; + use namada_vote_ext::validator_set_update::VotingPowersMap; use super::*; use crate::test_utils; diff --git a/ethereum_bridge/src/protocol/transactions/votes.rs b/ethereum_bridge/src/protocol/transactions/votes.rs index cdf7461047..7accb41f66 100644 --- a/ethereum_bridge/src/protocol/transactions/votes.rs +++ b/ethereum_bridge/src/protocol/transactions/votes.rs @@ -5,12 +5,12 @@ use std::collections::{BTreeMap, BTreeSet, HashMap}; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use eyre::{eyre, Result}; -use namada_core::ledger::storage::{DBIter, StorageHasher, WlStorage, DB}; use namada_core::types::address::Address; use namada_core::types::storage::{BlockHeight, Epoch}; use namada_core::types::token; use namada_core::types::voting_power::FractionalVotingPower; use namada_proof_of_stake::pos_queries::PosQueries; +use namada_state::{DBIter, StorageHasher, WlStorage, DB}; use super::{read, ChangedKeys}; diff --git a/ethereum_bridge/src/protocol/transactions/votes/storage.rs b/ethereum_bridge/src/protocol/transactions/votes/storage.rs index fe43c37c80..5f4c9e6423 100644 --- a/ethereum_bridge/src/protocol/transactions/votes/storage.rs +++ b/ethereum_bridge/src/protocol/transactions/votes/storage.rs @@ -1,13 +1,10 @@ -use borsh::{BorshDeserialize, BorshSerialize}; -use borsh_ext::BorshSerializeExt; use eyre::{Result, WrapErr}; +use namada_core::borsh::{BorshDeserialize, BorshSerialize, BorshSerializeExt}; use namada_core::hints; -use namada_core::ledger::storage::{ - DBIter, PrefixIter, StorageHasher, WlStorage, DB, -}; -use namada_core::ledger::storage_api::{StorageRead, StorageWrite}; use namada_core::types::storage::Key; use namada_core::types::voting_power::FractionalVotingPower; +use namada_state::{DBIter, PrefixIter, StorageHasher, WlStorage, DB}; +use namada_storage::{StorageRead, StorageWrite}; use super::{EpochedVotingPower, EpochedVotingPowerExt, Tally, Votes}; use crate::storage::vote_tallies; diff --git a/ethereum_bridge/src/protocol/transactions/votes/update.rs b/ethereum_bridge/src/protocol/transactions/votes/update.rs index b6ac1d102d..4335d864af 100644 --- a/ethereum_bridge/src/protocol/transactions/votes/update.rs +++ b/ethereum_bridge/src/protocol/transactions/votes/update.rs @@ -2,11 +2,11 @@ use std::collections::{BTreeSet, HashMap, HashSet}; use borsh::BorshDeserialize; use eyre::{eyre, Result}; -use namada_core::ledger::storage::{DBIter, StorageHasher, WlStorage, DB}; use namada_core::types::address::Address; use namada_core::types::storage::BlockHeight; use namada_core::types::token; use namada_proof_of_stake::pos_queries::PosQueries; +use namada_state::{DBIter, StorageHasher, WlStorage, DB}; use super::{ChangedKeys, EpochedVotingPowerExt, Tally, Votes}; use crate::storage::vote_tallies; diff --git a/ethereum_bridge/src/storage/bridge_pool.rs b/ethereum_bridge/src/storage/bridge_pool.rs index abe8ca9061..c65205f5dd 100644 --- a/ethereum_bridge/src/storage/bridge_pool.rs +++ b/ethereum_bridge/src/storage/bridge_pool.rs @@ -1,43 +1,13 @@ //! Tools for accessing the storage subspaces of the Ethereum //! bridge pool -use std::collections::{BTreeMap, BTreeSet}; -use std::convert::TryInto; -use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; -use ethabi::Token; -use eyre::eyre; -use namada_macros::StorageKeys; - -use crate::types::address::{Address, InternalAddress}; -use crate::types::eth_abi::Encode; -use crate::types::eth_bridge_pool::PendingTransfer; -use crate::types::hash::Hash; -use crate::types::keccak::{keccak_hash, KeccakHash}; -use crate::types::storage::{BlockHeight, DbKeySeg, Key, KeySeg}; - -/// Prefix to be used in Bridge pool tree root computations. -/// This value corresponds to leaf nodes. -const POOL_ROOT_PREFIX_LEAF: u8 = 0x00; - -/// Prefix to be used in Bridge pool tree root computations. -/// This value corresponds to non-leaf nodes. -const POOL_ROOT_PREFIX_NON_LEAF: u8 = 0xff; - -/// The main address of the Ethereum bridge pool -pub const BRIDGE_POOL_ADDRESS: Address = - Address::Internal(InternalAddress::EthBridgePool); - -/// Bridge pool key segments. -#[derive(StorageKeys)] -struct Segments { - signed_root: &'static str, - bridge_pool_nonce: &'static str, -} - -#[derive(thiserror::Error, Debug)] -#[error(transparent)] -/// Generic error that may be returned by the validity predicate -pub struct Error(#[from] eyre::Error); +use namada_core::types::eth_abi::Encode; +pub use namada_core::types::eth_bridge_pool::{ + is_pending_transfer_key, BRIDGE_POOL_ADDRESS, +}; +use namada_core::types::eth_bridge_pool::{PendingTransfer, Segments}; +use namada_core::types::keccak::KeccakHash; +use namada_core::types::storage::{DbKeySeg, Key, KeySeg}; /// Get the storage key for the transfers in the pool pub fn get_pending_key(transfer: &PendingTransfer) -> Key { @@ -80,935 +50,3 @@ pub fn get_nonce_key() -> Key { pub fn is_bridge_pool_key(key: &Key) -> bool { matches!(&key.segments[0], DbKeySeg::AddressSeg(addr) if addr == &BRIDGE_POOL_ADDRESS) } - -/// Check if a key is for a pending transfer -pub fn is_pending_transfer_key(key: &Key) -> bool { - let segment = match &key.segments[..] { - [DbKeySeg::AddressSeg(addr), DbKeySeg::StringSeg(segment)] - if addr == &BRIDGE_POOL_ADDRESS => - { - segment.as_str() - } - _ => return false, - }; - !Segments::ALL.iter().any(|s| s == &segment) -} - -/// A simple Merkle tree for the Ethereum bridge pool -/// -/// Note that an empty tree has root [0u8; 20] by definition. -#[derive( - Debug, Default, Clone, BorshSerialize, BorshDeserialize, BorshSchema, -)] -pub struct BridgePoolTree { - /// Root of the tree - root: KeccakHash, - /// The underlying storage, containing hashes of [`PendingTransfer`]s. - leaves: BTreeMap, -} - -impl BridgePoolTree { - /// Create a new merkle tree for the Ethereum bridge pool - pub fn new( - root: KeccakHash, - store: BTreeMap, - ) -> Self { - Self { - root, - leaves: store, - } - } - - /// Parse the key to ensure it is of the correct type. - /// - /// If it is, it can be converted to a hash. - /// Checks if the hash is in the tree. - pub fn contains_key(&self, key: &Key) -> Result { - Ok(self.leaves.contains_key(&Self::parse_key(key)?)) - } - - /// Get the height at which the key was inserted - /// - /// Returns the height if successful. Will - /// return an error if the key is malformed. - pub fn get(&self, key: &Key) -> Result { - let hash = Self::parse_key(key)?; - self.leaves.get(&hash).cloned().ok_or_else(|| { - eyre!("Key not present in Bridge Pool merkle tree.").into() - }) - } - - /// Update the tree with a new value. - /// - /// Returns the new root if successful. Will - /// return an error if the key is malformed. - pub fn insert_key( - &mut self, - key: &Key, - value: BlockHeight, - ) -> Result { - let hash = Self::parse_key(key)?; - _ = self.leaves.insert(hash, value); - self.root = self.compute_root(); - Ok(self.root().into()) - } - - /// Delete a key from storage and update the root - pub fn delete_key(&mut self, key: &Key) -> Result<(), Error> { - let hash = Self::parse_key(key)?; - _ = self.leaves.remove(&hash); - self.root = self.compute_root(); - Ok(()) - } - - /// Compute the root of the merkle tree - fn compute_root(&self) -> KeccakHash { - let mut hashes: Vec = self.leaves.keys().cloned().collect(); - let mut prefix = POOL_ROOT_PREFIX_LEAF; - while hashes.len() > 1 { - let mut next_hashes = vec![]; - for pair in hashes.chunks(2) { - let left = pair[0].clone(); - let right = pair.get(1).cloned().unwrap_or_default(); - next_hashes.push(hash_pair(left, right, prefix)); - } - hashes = next_hashes; - prefix = POOL_ROOT_PREFIX_NON_LEAF; - } - - if hashes.is_empty() { - Default::default() - } else { - hashes.remove(0) - } - } - - /// Return the root as a [`struct@Hash`] type. - pub fn root(&self) -> KeccakHash { - self.root.clone() - } - - /// Get a reference to the backing store - pub fn store(&self) -> &BTreeMap { - &self.leaves - } - - /// Create a batched membership proof for the provided keys - pub fn get_membership_proof( - &self, - mut values: Vec, - ) -> Result { - // sort the values according to their hash values - values.sort_by_key(|transfer| transfer.keccak256()); - - // get the leaf hashes - let leaves: BTreeSet = - values.iter().map(|v| v.keccak256()).collect(); - if !leaves.iter().all(|h| self.leaves.contains_key(h)) { - return Err(eyre!( - "Cannot generate proof for values that aren't in the tree" - ) - .into()); - } - let mut proof_hashes = vec![]; - let mut flags = vec![]; - let mut hashes: Vec<_> = self - .leaves - .keys() - .cloned() - .map(|hash| { - if leaves.contains(&hash) { - Node::OnPath(hash) - } else { - Node::OffPath(hash) - } - }) - .collect(); - - let mut prefix = POOL_ROOT_PREFIX_LEAF; - - while hashes.len() > 1 { - let mut next_hashes = vec![]; - - for pair in hashes.chunks(2) { - let left = pair[0].clone(); - let right = pair.get(1).cloned().unwrap_or_default(); - match (left, right) { - (Node::OnPath(left), Node::OnPath(right)) => { - flags.push(true); - next_hashes.push(Node::OnPath(hash_pair( - left.clone(), - right, - prefix, - ))); - } - (Node::OnPath(hash), Node::OffPath(sib)) => { - flags.push(false); - proof_hashes.push(sib.clone()); - next_hashes.push(Node::OnPath(hash_pair( - hash.clone(), - sib, - prefix, - ))); - } - (Node::OffPath(sib), Node::OnPath(hash)) => { - flags.push(false); - proof_hashes.push(sib.clone()); - next_hashes.push(Node::OnPath(hash_pair( - hash, - sib.clone(), - prefix, - ))); - } - (Node::OffPath(left), Node::OffPath(right)) => { - next_hashes.push(Node::OffPath(hash_pair( - left.clone(), - right, - prefix, - ))); - } - } - } - hashes = next_hashes; - prefix = POOL_ROOT_PREFIX_NON_LEAF; - } - // add the root to the proof - if flags.is_empty() && proof_hashes.is_empty() && leaves.is_empty() { - proof_hashes.push(self.root.clone()); - } - - Ok(BridgePoolProof { - proof: proof_hashes, - leaves: values, - flags, - }) - } - - /// Parse a db key to see if it is valid for the - /// bridge pool. - /// - /// It should have one string segment which should - /// parse into a [`struct@Hash`]. - pub fn parse_key(key: &Key) -> Result { - if key.segments.len() == 1 { - match &key.segments[0] { - DbKeySeg::StringSeg(str) => { - str.as_str().try_into().map_err(|_| { - eyre!("Could not parse key segment as a hash").into() - }) - } - _ => Err(eyre!("Bridge pool keys should be strings.").into()), - } - } else { - Err(eyre!( - "Key for the bridge pool should have exactly one segment." - ) - .into()) - } - } -} - -/// Concatenate a byte prefix and two keccak hashes, -/// then compute the keccak hash of the resulting -/// byte array. -#[inline] -fn hash_pair(left: KeccakHash, right: KeccakHash, prefix: u8) -> KeccakHash { - let mut buf = [0u8; 32 + 32 + 1]; - buf[0] = prefix; - if left.0 < right.0 { - buf[1..33].copy_from_slice(&left.0); - buf[33..].copy_from_slice(&right.0); - } else { - buf[1..33].copy_from_slice(&right.0); - buf[33..].copy_from_slice(&left.0); - } - keccak_hash(buf) -} - -/// Keeps track if a node is on a path from the -/// root of the merkle tree to one of the leaves -/// being included in a multi-proof. -#[derive(Debug, Clone)] -enum Node { - /// Node is on a path from root to leaf in proof - OnPath(KeccakHash), - /// Node is not on a path from root to leaf in proof - OffPath(KeccakHash), -} - -impl Default for Node { - fn default() -> Self { - Self::OffPath(Default::default()) - } -} - -/// A multi-leaf membership proof -pub struct BridgePoolProof { - /// The hashes other than the provided leaves - pub proof: Vec, - /// The leaves; must be sorted - pub leaves: Vec, - /// Flags are used to indicate which consecutive - /// pairs of leaves in `leaves` are siblings. - pub flags: Vec, -} - -impl BridgePoolProof { - /// Verify a membership proof matches the provided root - pub fn verify(&self, root: KeccakHash) -> bool { - if self.proof.len() + self.leaves.len() != self.flags.len() + 1 { - return false; - } - if self.flags.is_empty() { - return if let Some(leaf) = self.leaves.last() { - root == leaf.keccak256() - } else { - match self.proof.last() { - Some(proof_root) => &root == proof_root, - None => false, - } - }; - } - let total_hashes = self.flags.len(); - let leaf_len = self.leaves.len(); - - let mut hashes = vec![KeccakHash::default(); self.flags.len()]; - let mut hash_pos = 0usize; - let mut leaf_pos = 0usize; - let mut proof_pos = 0usize; - - for i in 0..total_hashes { - let (left, prefix) = if leaf_pos < leaf_len { - let next = self.leaves[leaf_pos].keccak256(); - leaf_pos += 1; - (next, POOL_ROOT_PREFIX_LEAF) - } else { - let next = hashes[hash_pos].clone(); - hash_pos += 1; - (next, POOL_ROOT_PREFIX_NON_LEAF) - }; - let right = if self.flags[i] { - if leaf_pos < leaf_len { - let next = self.leaves[leaf_pos].keccak256(); - leaf_pos += 1; - next - } else { - let next = hashes[hash_pos].clone(); - hash_pos += 1; - next - } - } else { - let next = self.proof[proof_pos].clone(); - proof_pos += 1; - next - }; - hashes[i] = hash_pair(left, right, prefix); - } - - if let Some(computed) = hashes.last() { - *computed == root - } else { - false - } - } -} - -impl Encode<3> for BridgePoolProof { - fn tokenize(&self) -> [Token; 3] { - let BridgePoolProof { - proof, - leaves, - flags, - } = self; - let proof = Token::Array( - proof - .iter() - .map(|hash| Token::FixedBytes(hash.0.to_vec())) - .collect(), - ); - let transfers = Token::Array( - leaves - .iter() - .map(|t| Token::FixedArray(t.tokenize().to_vec())) - .collect(), - ); - let flags = - Token::Array(flags.iter().map(|flag| Token::Bool(*flag)).collect()); - [proof, transfers, flags] - } -} - -#[cfg(test)] -mod test_bridge_pool_tree { - - use itertools::Itertools; - use proptest::prelude::*; - - use super::*; - use crate::types::address::nam; - use crate::types::eth_bridge_pool::{ - GasFee, TransferToEthereum, TransferToEthereumKind, - }; - use crate::types::ethereum_events::EthAddress; - - /// An established user address for testing & development - fn bertha_address() -> Address { - Address::decode("tnam1qyctxtpnkhwaygye0sftkq28zedf774xc5a2m7st") - .expect("The token address decoding shouldn't fail") - } - - /// Test that if tree has a single leaf, its root is the hash - /// of that leaf - #[test] - fn test_update_single_key() { - let mut tree = BridgePoolTree::default(); - assert_eq!(tree.root().0, [0; 32]); - let transfer = PendingTransfer { - transfer: TransferToEthereum { - kind: TransferToEthereumKind::Erc20, - asset: EthAddress([1; 20]), - sender: bertha_address(), - recipient: EthAddress([2; 20]), - amount: 1.into(), - }, - gas_fee: GasFee { - token: nam(), - amount: 0.into(), - payer: bertha_address(), - }, - }; - let key = Key::from(&transfer); - let root = KeccakHash::from( - tree.insert_key(&key, BlockHeight(1)).expect("Test failed"), - ); - assert_eq!(root, transfer.keccak256()); - let height = tree.get(&key).expect("Test failed"); - assert_eq!(BlockHeight(1), height); - } - - #[test] - fn test_two_keys() { - let mut tree = BridgePoolTree::default(); - let mut transfers = vec![]; - for i in 0..2 { - let transfer = PendingTransfer { - transfer: TransferToEthereum { - kind: TransferToEthereumKind::Erc20, - asset: EthAddress([i; 20]), - sender: bertha_address(), - recipient: EthAddress([i + 1; 20]), - amount: (i as u64).into(), - }, - gas_fee: GasFee { - token: nam(), - amount: 0.into(), - payer: bertha_address(), - }, - }; - let key = Key::from(&transfer); - transfers.push(transfer); - let _ = tree.insert_key(&key, BlockHeight(1)).expect("Test failed"); - } - let expected = hash_pair( - transfers[0].keccak256(), - transfers[1].keccak256(), - POOL_ROOT_PREFIX_LEAF, - ); - assert_eq!(tree.root(), expected); - } - - /// This is the first number of keys to use dummy leaves - #[test] - fn test_three_leaves() { - let mut tree = BridgePoolTree::default(); - let mut transfers = vec![]; - for i in 0..3 { - let transfer = PendingTransfer { - transfer: TransferToEthereum { - kind: TransferToEthereumKind::Erc20, - asset: EthAddress([i; 20]), - sender: bertha_address(), - recipient: EthAddress([i + 1; 20]), - amount: (i as u64).into(), - }, - gas_fee: GasFee { - token: nam(), - amount: 0.into(), - payer: bertha_address(), - }, - }; - let key = Key::from(&transfer); - transfers.push(transfer); - let _ = tree.insert_key(&key, BlockHeight(1)).expect("Test failed"); - } - transfers.sort_by_key(|t| t.keccak256()); - let hashes: BTreeSet = - transfers.iter().map(|t| t.keccak256()).collect(); - assert_eq!( - hashes, - tree.leaves.keys().cloned().collect::>() - ); - - let left_hash = hash_pair( - transfers[0].keccak256(), - transfers[1].keccak256(), - POOL_ROOT_PREFIX_LEAF, - ); - let right_hash = hash_pair( - transfers[2].keccak256(), - Default::default(), - POOL_ROOT_PREFIX_LEAF, - ); - let expected = - hash_pair(left_hash, right_hash, POOL_ROOT_PREFIX_NON_LEAF); - assert_eq!(tree.root(), expected); - } - - /// Test removing all keys - #[test] - fn test_delete_all_keys() { - let mut tree = BridgePoolTree::default(); - - let transfer = PendingTransfer { - transfer: TransferToEthereum { - kind: TransferToEthereumKind::Erc20, - asset: EthAddress([1; 20]), - sender: bertha_address(), - recipient: EthAddress([2; 20]), - amount: 1.into(), - }, - gas_fee: GasFee { - token: nam(), - amount: 0.into(), - payer: bertha_address(), - }, - }; - let key = Key::from(&transfer); - let root = KeccakHash::from( - tree.insert_key(&key, BlockHeight(1)).expect("Test failed"), - ); - assert_eq!(root, transfer.keccak256()); - tree.delete_key(&key).expect("Test failed"); - assert_eq!(tree.root().0, [0; 32]); - } - - /// Test deleting a key - #[test] - fn test_delete_key() { - let mut tree = BridgePoolTree::default(); - let mut transfers = vec![]; - for i in 0..3 { - let transfer = PendingTransfer { - transfer: TransferToEthereum { - kind: TransferToEthereumKind::Erc20, - asset: EthAddress([i; 20]), - sender: bertha_address(), - recipient: EthAddress([i + 1; 20]), - amount: (i as u64).into(), - }, - gas_fee: GasFee { - token: nam(), - amount: 0.into(), - payer: bertha_address(), - }, - }; - - let key = Key::from(&transfer); - transfers.push(transfer); - let _ = tree.insert_key(&key, BlockHeight(1)).expect("Test failed"); - } - transfers.sort_by_key(|t| t.keccak256()); - let deleted_key = Key::from(&transfers[1]); - tree.delete_key(&deleted_key).expect("Test failed"); - - let expected = hash_pair( - transfers[0].keccak256(), - transfers[2].keccak256(), - POOL_ROOT_PREFIX_LEAF, - ); - assert_eq!(tree.root(), expected); - assert_matches!(tree.get(&deleted_key), Err(_)); - } - - /// Test that parse key works correctly - #[test] - fn test_parse_key() { - let transfer = PendingTransfer { - transfer: TransferToEthereum { - kind: TransferToEthereumKind::Erc20, - asset: EthAddress([1; 20]), - sender: bertha_address(), - recipient: EthAddress([2; 20]), - amount: 1u64.into(), - }, - gas_fee: GasFee { - token: nam(), - amount: 0.into(), - payer: bertha_address(), - }, - }; - let expected = transfer.keccak256(); - let key = Key::from(&transfer); - assert_eq!( - BridgePoolTree::parse_key(&key).expect("Test failed"), - expected - ); - } - - /// Test that parsing a key with multiple segments fails - #[test] - fn test_key_multiple_segments() { - let transfer = PendingTransfer { - transfer: TransferToEthereum { - kind: TransferToEthereumKind::Erc20, - asset: EthAddress([1; 20]), - sender: bertha_address(), - recipient: EthAddress([2; 20]), - amount: 1u64.into(), - }, - gas_fee: GasFee { - token: nam(), - amount: 0.into(), - payer: bertha_address(), - }, - }; - let hash = transfer.keccak256().to_string(); - let key = Key { - segments: vec![ - DbKeySeg::AddressSeg(bertha_address()), - DbKeySeg::StringSeg(hash), - ], - }; - assert!(BridgePoolTree::parse_key(&key).is_err()); - } - - /// Test that parsing a key that is not a hash fails - #[test] - fn test_key_not_hash() { - let key = Key { - segments: vec![DbKeySeg::StringSeg("bloop".into())], - }; - assert!(BridgePoolTree::parse_key(&key).is_err()); - } - - /// Test that [`contains_key`] works correctly - #[test] - fn test_contains_key() { - let mut tree = BridgePoolTree::default(); - let transfer = PendingTransfer { - transfer: TransferToEthereum { - kind: TransferToEthereumKind::Erc20, - asset: EthAddress([1; 20]), - sender: bertha_address(), - recipient: EthAddress([2; 20]), - amount: 1.into(), - }, - gas_fee: GasFee { - token: nam(), - amount: 0.into(), - payer: bertha_address(), - }, - }; - tree.insert_key(&Key::from(&transfer), BlockHeight(1)) - .expect("Test failed"); - assert!( - tree.contains_key(&Key::from(&transfer)) - .expect("Test failed") - ); - let transfer = PendingTransfer { - transfer: TransferToEthereum { - kind: TransferToEthereumKind::Erc20, - asset: EthAddress([1; 20]), - sender: bertha_address(), - recipient: EthAddress([0; 20]), - amount: 1u64.into(), - }, - gas_fee: GasFee { - token: nam(), - amount: 0.into(), - payer: bertha_address(), - }, - }; - assert!( - !tree - .contains_key(&Key::from(&transfer)) - .expect("Test failed") - ); - } - - /// Test that the empty proof works. - #[test] - fn test_empty_proof() { - let tree = BridgePoolTree::default(); - let values = vec![]; - let proof = tree.get_membership_proof(values).expect("Test failed"); - assert!(proof.verify(Default::default())); - } - - /// Test that the proof works for proving the only leaf in the tree - #[test] - fn test_single_leaf() { - let transfer = PendingTransfer { - transfer: TransferToEthereum { - kind: TransferToEthereumKind::Erc20, - asset: EthAddress([0; 20]), - sender: bertha_address(), - recipient: EthAddress([0; 20]), - amount: 0.into(), - }, - gas_fee: GasFee { - token: nam(), - amount: 0.into(), - payer: bertha_address(), - }, - }; - let mut tree = BridgePoolTree::default(); - let key = Key::from(&transfer); - let _ = tree.insert_key(&key, BlockHeight(1)).expect("Test failed"); - let proof = tree - .get_membership_proof(vec![transfer]) - .expect("Test failed"); - assert!(proof.verify(tree.root())); - } - - /// Check proofs for membership of single transfer - /// in a tree with two leaves. - #[test] - fn test_one_leaf_of_two_proof() { - let mut tree = BridgePoolTree::default(); - let mut transfers = vec![]; - for i in 0..2 { - let transfer = PendingTransfer { - transfer: TransferToEthereum { - kind: TransferToEthereumKind::Erc20, - asset: EthAddress([i; 20]), - sender: bertha_address(), - recipient: EthAddress([i + 1; 20]), - amount: (i as u64).into(), - }, - gas_fee: GasFee { - token: nam(), - amount: 0.into(), - payer: bertha_address(), - }, - }; - - let key = Key::from(&transfer); - transfers.push(transfer); - let _ = tree.insert_key(&key, BlockHeight(1)).expect("Test failed"); - } - let proof = tree - .get_membership_proof(vec![transfers.remove(0)]) - .expect("Test failed"); - assert!(proof.verify(tree.root())); - } - - /// Test that a multiproof works for leaves who are siblings - #[test] - fn test_proof_two_out_of_three_leaves() { - let mut tree = BridgePoolTree::default(); - let mut transfers = vec![]; - for i in 0..3 { - let transfer = PendingTransfer { - transfer: TransferToEthereum { - kind: TransferToEthereumKind::Erc20, - asset: EthAddress([i; 20]), - sender: bertha_address(), - recipient: EthAddress([i + 1; 20]), - amount: (i as u64).into(), - }, - gas_fee: GasFee { - token: nam(), - amount: 0.into(), - payer: bertha_address(), - }, - }; - - let key = Key::from(&transfer); - transfers.push(transfer); - let _ = tree.insert_key(&key, BlockHeight(1)).expect("Test failed"); - } - transfers.sort_by_key(|t| t.keccak256()); - let values = vec![transfers[0].clone(), transfers[1].clone()]; - let proof = tree.get_membership_proof(values).expect("Test failed"); - assert!(proof.verify(tree.root())); - } - - /// Test that proving an empty subset of leaves always works - #[test] - fn test_proof_no_leaves() { - let mut tree = BridgePoolTree::default(); - let mut transfers = vec![]; - for i in 0..3 { - let transfer = PendingTransfer { - transfer: TransferToEthereum { - kind: TransferToEthereumKind::Erc20, - asset: EthAddress([i; 20]), - sender: bertha_address(), - recipient: EthAddress([i + 1; 20]), - amount: (i as u64).into(), - }, - gas_fee: GasFee { - token: nam(), - amount: 0.into(), - payer: bertha_address(), - }, - }; - let key = Key::from(&transfer); - transfers.push(transfer); - let _ = tree.insert_key(&key, BlockHeight(1)).expect("Test failed"); - } - let values = vec![]; - let proof = tree.get_membership_proof(values).expect("Test failed"); - assert!(proof.verify(tree.root())) - } - - /// Test a proof for all the leaves - #[test] - fn test_proof_all_leaves() { - let mut tree = BridgePoolTree::default(); - let mut transfers = vec![]; - for i in 0..2 { - let transfer = PendingTransfer { - transfer: TransferToEthereum { - kind: TransferToEthereumKind::Erc20, - asset: EthAddress([i; 20]), - sender: bertha_address(), - recipient: EthAddress([i + 1; 20]), - amount: (i as u64).into(), - }, - gas_fee: GasFee { - token: nam(), - amount: 0.into(), - payer: bertha_address(), - }, - }; - let key = Key::from(&transfer); - transfers.push(transfer); - let _ = tree.insert_key(&key, BlockHeight(1)).expect("Test failed"); - } - transfers.sort_by_key(|t| t.keccak256()); - let proof = tree.get_membership_proof(transfers).expect("Test failed"); - assert!(proof.verify(tree.root())); - } - - /// Test a proof for all the leaves when the number of leaves is odd - #[test] - fn test_proof_all_leaves_odd() { - let mut tree = BridgePoolTree::default(); - let mut transfers = vec![]; - for i in 0..3 { - let transfer = PendingTransfer { - transfer: TransferToEthereum { - kind: TransferToEthereumKind::Erc20, - asset: EthAddress([i; 20]), - sender: bertha_address(), - recipient: EthAddress([i + 1; 20]), - amount: (i as u64).into(), - }, - gas_fee: GasFee { - token: nam(), - amount: 0.into(), - payer: bertha_address(), - }, - }; - let key = Key::from(&transfer); - transfers.push(transfer); - let _ = tree.insert_key(&key, BlockHeight(1)).expect("Test failed"); - } - transfers.sort_by_key(|t| t.keccak256()); - let proof = tree.get_membership_proof(transfers).expect("Test failed"); - assert!(proof.verify(tree.root())); - } - - /// Test proofs of large trees - #[test] - fn test_large_proof() { - let mut tree = BridgePoolTree::default(); - let mut transfers = vec![]; - for i in 0..5 { - let transfer = PendingTransfer { - transfer: TransferToEthereum { - kind: TransferToEthereumKind::Erc20, - asset: EthAddress([i; 20]), - sender: bertha_address(), - recipient: EthAddress([i + 1; 20]), - amount: (i as u64).into(), - }, - gas_fee: GasFee { - token: nam(), - amount: 0.into(), - payer: bertha_address(), - }, - }; - let key = Key::from(&transfer); - transfers.push(transfer); - let _ = tree.insert_key(&key, BlockHeight(1)).expect("Test failed"); - } - transfers.sort_by_key(|t| t.keccak256()); - let values: Vec<_> = transfers.iter().step_by(2).cloned().collect(); - let proof = tree.get_membership_proof(values).expect("Test failed"); - assert!(proof.verify(tree.root())); - } - - /// Create a random set of transfers. - fn random_transfers( - number: usize, - ) -> impl Strategy> { - prop::collection::vec(prop::array::uniform20(0u8..), 0..=number) - .prop_flat_map(|addrs| { - Just( - addrs - .into_iter() - .map(|addr| PendingTransfer { - transfer: TransferToEthereum { - kind: TransferToEthereumKind::Erc20, - asset: EthAddress(addr), - sender: bertha_address(), - recipient: EthAddress(addr), - amount: Default::default(), - }, - gas_fee: GasFee { - token: nam(), - amount: Default::default(), - payer: bertha_address(), - }, - }) - .dedup() - .collect::>(), - ) - }) - } - - prop_compose! { - /// Creates a random set of transfers and - /// then returns them along with a chosen subset. - fn arb_transfers_and_subset() - (transfers in random_transfers(50)) - ( - transfers in Just(transfers.clone()), - to_prove in proptest::sample::subsequence(transfers.clone(), 0..=transfers.len()), - ) - -> (Vec, Vec) { - (transfers, to_prove) - } - } - - proptest! { - /// Given a random tree and a subset of leaves, - /// verify that the constructed multi-proof correctly - /// verifies. - #[test] - fn test_verify_proof((transfers, mut to_prove) in arb_transfers_and_subset()) { - let mut tree = BridgePoolTree::default(); - for transfer in &transfers { - let key = Key::from(transfer); - let _ = tree.insert_key(&key, BlockHeight(1)).expect("Test failed"); - } - - to_prove.sort_by_key(|t| t.keccak256()); - let proof = tree.get_membership_proof(to_prove).expect("Test failed"); - assert!(proof.verify(tree.root())); - } - } -} diff --git a/ethereum_bridge/src/storage/eth_bridge_queries.rs b/ethereum_bridge/src/storage/eth_bridge_queries.rs index 5e132592de..759ef22567 100644 --- a/ethereum_bridge/src/storage/eth_bridge_queries.rs +++ b/ethereum_bridge/src/storage/eth_bridge_queries.rs @@ -1,11 +1,5 @@ use borsh::{BorshDeserialize, BorshSerialize}; use namada_core::hints; -use namada_core::ledger::eth_bridge::storage::{ - active_key, bridge_pool, whitelist, -}; -use namada_core::ledger::storage; -use namada_core::ledger::storage::{StoreType, WlStorage}; -use namada_core::ledger::storage_api::StorageRead; use namada_core::types::address::Address; use namada_core::types::eth_abi::Encode; use namada_core::types::eth_bridge_pool::PendingTransfer; @@ -15,9 +9,6 @@ use namada_core::types::ethereum_events::{ use namada_core::types::keccak::KeccakHash; use namada_core::types::storage::{BlockHeight, Epoch, Key as StorageKey}; use namada_core::types::token; -use namada_core::types::vote_extensions::validator_set_update::{ - EthAddrBook, ValidatorSetArgs, VotingPowersMap, VotingPowersMapExt, -}; use namada_core::types::voting_power::{ EthBridgeVotingPower, FractionalVotingPower, }; @@ -25,9 +16,14 @@ use namada_proof_of_stake::pos_queries::{ConsensusValidators, PosQueries}; use namada_proof_of_stake::storage::{ validator_eth_cold_key_handle, validator_eth_hot_key_handle, }; +use namada_state::{DBIter, StorageHasher, StoreType, WlStorage, DB}; +use namada_storage::StorageRead; +use namada_vote_ext::validator_set_update::{ + EthAddrBook, ValidatorSetArgs, VotingPowersMap, VotingPowersMapExt, +}; use crate::storage::proof::BridgePoolRootProof; -use crate::storage::vote_tallies; +use crate::storage::{active_key, bridge_pool, vote_tallies, whitelist}; /// This enum is used as a parameter to /// [`EthBridgeQueriesHook::must_send_valset_upd`]. @@ -91,8 +87,8 @@ pub trait EthBridgeQueries { impl EthBridgeQueries for WlStorage where - D: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, - H: 'static + storage::StorageHasher, + D: 'static + DB + for<'iter> DBIter<'iter>, + H: 'static + StorageHasher, { type Storage = Self; @@ -124,8 +120,8 @@ impl<'db, DB> Copy for EthBridgeQueriesHook<'db, DB> {} impl<'db, D, H> EthBridgeQueriesHook<'db, WlStorage> where - D: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, - H: 'static + storage::StorageHasher, + D: 'static + DB + for<'iter> DBIter<'iter>, + H: 'static + StorageHasher, { /// Return a handle to the inner [`WlStorage`]. #[inline] @@ -617,18 +613,18 @@ impl EthAssetMint { /// validators in Namada, at some given epoch. pub struct ConsensusEthAddresses<'db, D, H> where - D: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, - H: 'static + storage::StorageHasher, + D: 'static + DB + for<'iter> DBIter<'iter>, + H: 'static + StorageHasher, { epoch: Epoch, wl_storage: &'db WlStorage, - consensus_validators: ConsensusValidators<'db, D, H>, + consensus_validators: ConsensusValidators<'db, WlStorage>, } impl<'db, D, H> ConsensusEthAddresses<'db, D, H> where - D: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, - H: 'static + storage::StorageHasher, + D: 'static + DB + for<'iter> DBIter<'iter>, + H: 'static + StorageHasher, { /// Iterate over the Ethereum addresses of the set of consensus validators /// in Namada, at some given epoch. diff --git a/ethereum_bridge/src/storage/mod.rs b/ethereum_bridge/src/storage/mod.rs index 7554d7f93c..df16dd3103 100644 --- a/ethereum_bridge/src/storage/mod.rs +++ b/ethereum_bridge/src/storage/mod.rs @@ -1,20 +1,19 @@ //! Functionality for accessing the storage subspace + +pub mod bridge_pool; pub mod eth_bridge_queries; pub mod parameters; pub mod proof; pub mod vote_tallies; -pub mod bridge_pool; pub mod vp; pub mod whitelist; -pub mod wrapped_erc20; - -//! Functionality for accessing the storage subspace +pub mod wrapped_erc20s; use namada_core::ledger::eth_bridge::ADDRESS; -use namada_core::ledger::parameters::storage::*; -use namada_core::ledger::parameters::ADDRESS as PARAM_ADDRESS; use namada_core::types::address::Address; use namada_core::types::storage::{DbKeySeg, Key, KeySeg}; +use namada_parameters::storage::*; +use namada_parameters::ADDRESS as PARAM_ADDRESS; use namada_trans_token::storage_key::balance_key; /// Key prefix for the storage subspace diff --git a/ethereum_bridge/src/storage/parameters.rs b/ethereum_bridge/src/storage/parameters.rs index ada16e87d4..9f1357388b 100644 --- a/ethereum_bridge/src/storage/parameters.rs +++ b/ethereum_bridge/src/storage/parameters.rs @@ -3,17 +3,15 @@ use std::num::NonZeroU64; use borsh::{BorshDeserialize, BorshSerialize}; use eyre::{eyre, Result}; -use namada_core::ledger::eth_bridge::storage::whitelist; -use namada_core::ledger::storage; -use namada_core::ledger::storage::types::encode; -use namada_core::ledger::storage::WlStorage; -use namada_core::ledger::storage_api::{StorageRead, StorageWrite}; use namada_core::types::ethereum_events::EthAddress; -use namada_core::types::ethereum_structs; use namada_core::types::storage::Key; use namada_core::types::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; +use namada_core::types::{encode, ethereum_structs}; +use namada_state::{DBIter, StorageHasher, WlStorage, DB}; +use namada_storage::{StorageRead, StorageWrite}; use serde::{Deserialize, Serialize}; +use super::whitelist; use crate::storage as bridge_storage; use crate::storage::eth_bridge_queries::{ EthBridgeEnabled, EthBridgeQueries, EthBridgeStatus, @@ -169,10 +167,10 @@ impl EthereumBridgeParams { /// /// If these parameters are initialized, the storage subspaces /// for the Ethereum bridge VPs are also initialized. - pub fn init_storage(&self, wl_storage: &mut WlStorage) + pub fn init_storage(&self, wl_storage: &mut WlStorage) where - DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, - H: 'static + storage::traits::StorageHasher, + D: 'static + DB + for<'iter> DBIter<'iter>, + H: 'static + StorageHasher, { let Self { erc20_whitelist, @@ -285,10 +283,10 @@ impl EthereumOracleConfig { /// present, `None` will be returned - this could be the case if the bridge /// has not been bootstrapped yet. Panics if the storage appears to be /// corrupt. - pub fn read(wl_storage: &WlStorage) -> Option + pub fn read(wl_storage: &WlStorage) -> Option where - DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, - H: 'static + storage::traits::StorageHasher, + D: 'static + DB + for<'iter> DBIter<'iter>, + H: 'static + StorageHasher, { // TODO(namada#1720): remove present key check; `is_bridge_active` // should not panic, when the active status key has not been @@ -346,13 +344,13 @@ where /// Reads the value of `key` from `storage` and deserializes it, or panics /// otherwise. -fn must_read_key( - wl_storage: &WlStorage, +fn must_read_key( + wl_storage: &WlStorage, key: &Key, ) -> T where - DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, - H: 'static + storage::traits::StorageHasher, + D: 'static + DB + for<'iter> DBIter<'iter>, + H: 'static + StorageHasher, { StorageRead::read::(wl_storage, key).map_or_else( |err| panic!("Could not read {key}: {err:?}"), diff --git a/ethereum_bridge/src/storage/proof.rs b/ethereum_bridge/src/storage/proof.rs index a028725883..5c3d28386a 100644 --- a/ethereum_bridge/src/storage/proof.rs +++ b/ethereum_bridge/src/storage/proof.rs @@ -9,10 +9,10 @@ use namada_core::types::ethereum_events::Uint; use namada_core::types::keccak::KeccakHash; use namada_core::types::key::{common, secp256k1}; use namada_core::types::storage::Epoch; -use namada_core::types::vote_extensions::validator_set_update::{ +use namada_core::types::{eth_abi, ethereum_structs}; +use namada_vote_ext::validator_set_update::{ valset_upd_toks_to_hashes, EthAddrBook, VotingPowersMap, VotingPowersMapExt, }; -use namada_core::types::{eth_abi, ethereum_structs}; /// Ethereum proofs contain the [`secp256k1`] signatures of validators /// over some data to be signed. diff --git a/ethereum_bridge/src/storage/vote_tallies.rs b/ethereum_bridge/src/storage/vote_tallies.rs index edb60114e8..354edb9958 100644 --- a/ethereum_bridge/src/storage/vote_tallies.rs +++ b/ethereum_bridge/src/storage/vote_tallies.rs @@ -10,8 +10,8 @@ use namada_core::types::ethereum_events::{EthereumEvent, Uint}; use namada_core::types::hash::Hash; use namada_core::types::keccak::{keccak_hash, KeccakHash}; use namada_core::types::storage::{BlockHeight, DbKeySeg, Epoch, Key}; -use namada_core::types::vote_extensions::validator_set_update::VotingPowersMap; use namada_macros::StorageKeys; +use namada_vote_ext::validator_set_update::VotingPowersMap; use crate::storage::proof::{BridgePoolRootProof, EthereumProof}; diff --git a/ethereum_bridge/src/storage/vp/bridge_pool.rs b/ethereum_bridge/src/storage/vp/bridge_pool.rs index 68f97c5e03..ec52cf37db 100644 --- a/ethereum_bridge/src/storage/vp/bridge_pool.rs +++ b/ethereum_bridge/src/storage/vp/bridge_pool.rs @@ -1,10 +1,10 @@ -use namada_core::ledger::eth_bridge::storage::bridge_pool::{ - get_nonce_key, BRIDGE_POOL_ADDRESS, -}; -use namada_core::ledger::storage::{DBIter, StorageHasher, WlStorage, DB}; -use namada_core::ledger::storage_api::StorageWrite; use namada_core::types::ethereum_events::Uint; -use namada_core::types::token::{balance_key, Amount}; +use namada_state::{DBIter, StorageHasher, WlStorage, DB}; +use namada_storage::StorageWrite; +use namada_trans_token::storage_key::balance_key; +use namada_trans_token::Amount; + +use crate::storage::bridge_pool::{get_nonce_key, BRIDGE_POOL_ADDRESS}; /// Initialize the storage owned by the Bridge Pool VP. /// diff --git a/ethereum_bridge/src/storage/vp/ethereum_bridge.rs b/ethereum_bridge/src/storage/vp/ethereum_bridge.rs index ed678ff03b..4f84b855ab 100644 --- a/ethereum_bridge/src/storage/vp/ethereum_bridge.rs +++ b/ethereum_bridge/src/storage/vp/ethereum_bridge.rs @@ -1,21 +1,21 @@ -use borsh_ext::BorshSerializeExt; -use namada_core::ledger::storage::{self as ledger_storage, StorageHasher}; -use namada_core::ledger::storage_api::StorageWrite; -use namada_core::types::token::{balance_key, Amount}; +use namada_core::borsh::BorshSerializeExt; +use namada_core::ledger::eth_bridge::ADDRESS; +use namada_core::types::hash::StorageHasher; +use namada_state::{DBIter, WlStorage, DB}; +use namada_storage::StorageWrite; +use namada_trans_token::storage_key::balance_key; +use namada_trans_token::Amount; /// Initialize the storage owned by the Ethereum Bridge VP. /// /// This means that the amount of escrowed Nam is /// initialized to 0. -pub fn init_storage(wl_storage: &mut ledger_storage::WlStorage) +pub fn init_storage(wl_storage: &mut WlStorage) where - D: ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, + D: DB + for<'iter> DBIter<'iter>, H: StorageHasher, { - let escrow_key = balance_key( - &wl_storage.storage.native_token, - &namada_core::ledger::eth_bridge::ADDRESS, - ); + let escrow_key = balance_key(&wl_storage.storage.native_token, &ADDRESS); wl_storage .write_bytes(&escrow_key, Amount::default().serialize_to_vec()) .expect( diff --git a/ethereum_bridge/src/storage/whitelist.rs b/ethereum_bridge/src/storage/whitelist.rs index 3113bfad8a..6abc873dfb 100644 --- a/ethereum_bridge/src/storage/whitelist.rs +++ b/ethereum_bridge/src/storage/whitelist.rs @@ -6,17 +6,17 @@ use std::str::FromStr; use namada_core::ledger::eth_bridge::ADDRESS as BRIDGE_ADDRESS; +use namada_core::types::eth_bridge_pool::erc20_token_address; use namada_core::types::ethereum_events::EthAddress; use namada_core::types::storage; use namada_core::types::storage::DbKeySeg; use namada_trans_token::storage_key::{denom_key, minted_balance_key}; -use super::{prefix as ethbridge_key_prefix, wrapped_erc20s}; +use super::prefix as ethbridge_key_prefix; mod segments { //! Storage key segments under the token whitelist. use namada_core::types::address::Address; - use namada_core::types::storage::{DbKeySeg, Key}; use namada_macros::StorageKeys; /// The name of the main storage segment. @@ -86,11 +86,11 @@ impl From<&Key> for storage::Key { .push(&segments::VALUES.cap.to_owned()) .expect("Should be able to push a storage key segment"), KeyType::WrappedSupply => { - let token = wrapped_erc20s::token(&key.asset); + let token = erc20_token_address(&key.asset); minted_balance_key(&token) } KeyType::Denomination => { - let token = wrapped_erc20s::token(&key.asset); + let token = erc20_token_address(&key.asset); denom_key(&token) } } diff --git a/ethereum_bridge/src/test_utils.rs b/ethereum_bridge/src/test_utils.rs index feeb76fb75..c56ebfa083 100644 --- a/ethereum_bridge/src/test_utils.rs +++ b/ethereum_bridge/src/test_utils.rs @@ -3,11 +3,7 @@ use std::collections::HashMap; use std::num::NonZeroU64; -use namada_core::ledger::eth_bridge::storage::bridge_pool::get_key_from_hash; -use namada_core::ledger::eth_bridge::storage::whitelist; use namada_core::ledger::storage::testing::TestWlStorage; -use namada_core::ledger::storage_api::token::credit_tokens; -use namada_core::ledger::storage_api::{StorageRead, StorageWrite}; use namada_core::types::address::{self, wnam, Address}; use namada_core::types::dec::Dec; use namada_core::types::ethereum_events::EthAddress; @@ -22,11 +18,15 @@ use namada_proof_of_stake::{ become_validator, bond_tokens, compute_and_store_total_consensus_stake, staking_token_address, BecomeValidator, }; +use namada_storage::token::credit_tokens; +use namada_storage::{StorageRead, StorageWrite}; +use crate::storage::bridge_pool::get_key_from_hash; use crate::storage::parameters::{ ContractVersion, Contracts, EthereumBridgeParams, MinimumConfirmations, UpgradeableContract, }; +use crate::storage::whitelist; /// Validator keys used for testing purposes. pub struct TestValidatorKeys { diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 02976a22b8..25fed1f121 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -299,8 +299,8 @@ fn derive_storage_keys_inner(struct_def: TokenStream2) -> TokenStream2 { quote! { impl #struct_def_ident { - #[allow(dead_code)] - const ALL: &[&'static str] = { + /// A list of all storage keys + pub const ALL: &[&'static str] = { let #struct_def_ident { #ident_list } = Self::VALUES; @@ -308,7 +308,8 @@ fn derive_storage_keys_inner(struct_def: TokenStream2) -> TokenStream2 { &[ #ident_list ] }; - const VALUES: #struct_def_ident = Self { + /// Storage keys values + pub const VALUES: #struct_def_ident = Self { #values_list }; } diff --git a/state/src/lib.rs b/state/src/lib.rs index 9e30ee6d36..94d195b4bf 100644 --- a/state/src/lib.rs +++ b/state/src/lib.rs @@ -19,7 +19,8 @@ use namada_core::types::address::{ use namada_core::types::chain::{ChainId, CHAIN_ID_LENGTH}; use namada_core::types::eth_bridge_pool::is_pending_transfer_key; use namada_core::types::ethereum_events::Uint; -use namada_core::types::hash::{Error as HashError, Hash, StorageHasher}; +pub use namada_core::types::hash::StorageHasher; +use namada_core::types::hash::{Error as HashError, Hash}; use namada_core::types::storage::{ BlockHash, BlockHeight, BlockResults, Epoch, Epochs, EthEventsQueue, Header, Key, KeySeg, TxIndex, BLOCK_HASH_LENGTH, BLOCK_HEIGHT_LENGTH, diff --git a/trans_token/src/storage_key.rs b/trans_token/src/storage_key.rs index 94c5d633e6..136886b35a 100644 --- a/trans_token/src/storage_key.rs +++ b/trans_token/src/storage_key.rs @@ -4,13 +4,13 @@ use namada_core::types::address::{self, Address, InternalAddress}; use namada_core::types::storage::{self, DbKeySeg, KeySeg}; /// Key segment for a balance key -const BALANCE_STORAGE_KEY: &str = "balance"; +pub const BALANCE_STORAGE_KEY: &str = "balance"; /// Key segment for a denomination key -const DENOM_STORAGE_KEY: &str = "denomination"; +pub const DENOM_STORAGE_KEY: &str = "denomination"; /// Key segment for multitoken minter -const MINTER_STORAGE_KEY: &str = "minter"; +pub const MINTER_STORAGE_KEY: &str = "minter"; /// Key segment for minted balance -const MINTED_STORAGE_KEY: &str = "minted"; +pub const MINTED_STORAGE_KEY: &str = "minted"; // TODO: move to shielded /// Key segment for head shielded transaction pointer keys From 996881fc1615feb6af092a6462685b3ca03aba12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 3 Jan 2024 10:42:53 +0000 Subject: [PATCH 065/118] ibc: fix build --- Cargo.lock | 17 +++++++- core/src/types/address.rs | 4 +- ibc/Cargo.toml | 31 +++++++++++++-- ibc/src/context/client.rs | 27 +++++++------ ibc/src/context/common.rs | 65 +++++++++++++------------------ ibc/src/context/execution.rs | 33 ++++++++-------- ibc/src/context/mod.rs | 4 +- ibc/src/context/router.rs | 9 +++-- ibc/src/context/storage.rs | 9 ++--- ibc/src/context/token_transfer.rs | 25 ++++++------ ibc/src/context/transfer_mod.rs | 36 +++++++++-------- ibc/src/context/validation.rs | 44 ++++++++++----------- ibc/src/lib.rs | 40 +++++++++---------- ibc/src/storage.rs | 21 +++++----- storage/src/error.rs | 14 +++++++ 15 files changed, 213 insertions(+), 166 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5774973c4a..4de279c6f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4506,7 +4506,22 @@ dependencies = [ [[package]] name = "namada_ibc" -version = "0.1.0" +version = "0.28.1" +dependencies = [ + "borsh", + "ibc", + "ibc-derive", + "ics23", + "namada_core", + "namada_parameters", + "namada_storage", + "namada_trans_token", + "primitive-types", + "prost 0.12.3", + "sha2 0.9.9", + "thiserror", + "tracing", +] [[package]] name = "namada_light_sdk" diff --git a/core/src/types/address.rs b/core/src/types/address.rs index 1d3cd165d2..53a736e1d5 100644 --- a/core/src/types/address.rs +++ b/core/src/types/address.rs @@ -33,10 +33,10 @@ pub const ADDRESS_LEN: usize = string_encoding::hrp_len::
() + 1 + HASH_HEX_LEN; /// Length of a hash of an address as a hexadecimal string -pub(crate) const HASH_HEX_LEN: usize = 40; +pub const HASH_HEX_LEN: usize = 40; /// Length of a trimmed hash of an address. -pub(crate) const HASH_LEN: usize = 20; +pub const HASH_LEN: usize = 20; /// SHA-256 hash len /// diff --git a/ibc/Cargo.toml b/ibc/Cargo.toml index d8e1feb686..92a25f8d59 100644 --- a/ibc/Cargo.toml +++ b/ibc/Cargo.toml @@ -1,8 +1,33 @@ [package] name = "namada_ibc" -version = "0.1.0" -edition = "2021" +description = "Namada IBC" +resolver = "2" +authors.workspace = true +edition.workspace = true +documentation.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +default = [] +testing = ["namada_core/testing"] [dependencies] +namada_core = { path = "../core", default-features = false } +namada_parameters = { path = "../parameters" } +namada_storage = { path = "../storage" } +namada_trans_token = { path = "../trans_token" } + +borsh.workspace = true +ibc-derive.workspace = true +ibc.workspace = true +ics23.workspace = true +primitive-types.workspace = true +prost.workspace = true +sha2.workspace = true +thiserror.workspace = true +tracing.workspace = true diff --git a/ibc/src/context/client.rs b/ibc/src/context/client.rs index 0000432c76..c17414be50 100644 --- a/ibc/src/context/client.rs +++ b/ibc/src/context/client.rs @@ -7,29 +7,28 @@ use ibc_testkit::testapp::ibc::clients::mock::client_state::MockClientContext; use ibc_testkit::testapp::ibc::clients::mock::client_state::MockClientState; #[cfg(feature = "testing")] use ibc_testkit::testapp::ibc::clients::mock::consensus_state::MockConsensusState; -use prost::Message; - -use crate::ibc::clients::tendermint::client_state::ClientState as TmClientState; -use crate::ibc::clients::tendermint::consensus_state::ConsensusState as TmConsensusState; -use crate::ibc::clients::tendermint::context::{ +use namada_core::ibc::clients::tendermint::client_state::ClientState as TmClientState; +use namada_core::ibc::clients::tendermint::consensus_state::ConsensusState as TmConsensusState; +use namada_core::ibc::clients::tendermint::context::{ CommonContext, ExecutionContext as TmExecutionContext, ValidationContext as TmValidationContext, }; -use crate::ibc::core::client::context::client_state::{ +use namada_core::ibc::core::client::context::client_state::{ ClientStateCommon, ClientStateExecution, ClientStateValidation, }; -use crate::ibc::core::client::context::{ +use namada_core::ibc::core::client::context::{ ClientExecutionContext, ClientValidationContext, }; -use crate::ibc::core::client::types::error::ClientError; -use crate::ibc::core::client::types::{Height, Status, UpdateKind}; -use crate::ibc::core::commitment_types::commitment::{ +use namada_core::ibc::core::client::types::error::ClientError; +use namada_core::ibc::core::client::types::{Height, Status, UpdateKind}; +use namada_core::ibc::core::commitment_types::commitment::{ CommitmentPrefix, CommitmentProofBytes, CommitmentRoot, }; -use crate::ibc::core::host::types::identifiers::{ClientId, ClientType}; -use crate::ibc::core::host::types::path::Path; -use crate::ibc::core::host::ExecutionContext; -use crate::ibc::primitives::proto::Any; +use namada_core::ibc::core::host::types::identifiers::{ClientId, ClientType}; +use namada_core::ibc::core::host::types::path::Path; +use namada_core::ibc::core::host::ExecutionContext; +use namada_core::ibc::primitives::proto::Any; +use prost::Message; // TODO: #[derive(ClientState)] doesn't support contexts with contexts generic // for now diff --git a/ibc/src/context/common.rs b/ibc/src/context/common.rs index e8245babdd..b5b761bd86 100644 --- a/ibc/src/context/common.rs +++ b/ibc/src/context/common.rs @@ -2,37 +2,38 @@ use core::time::Duration; +use namada_core::ibc::clients::tendermint::consensus_state::ConsensusState as TmConsensusState; +use namada_core::ibc::clients::tendermint::types::ConsensusState as TmConsensusStateType; +use namada_core::ibc::core::channel::types::channel::ChannelEnd; +use namada_core::ibc::core::channel::types::commitment::{ + AcknowledgementCommitment, PacketCommitment, +}; +use namada_core::ibc::core::channel::types::error::{ + ChannelError, PacketError, +}; +use namada_core::ibc::core::channel::types::packet::Receipt; +use namada_core::ibc::core::channel::types::timeout::TimeoutHeight; +use namada_core::ibc::core::client::context::consensus_state::ConsensusState; +use namada_core::ibc::core::client::types::error::ClientError; +use namada_core::ibc::core::client::types::Height; +use namada_core::ibc::core::connection::types::error::ConnectionError; +use namada_core::ibc::core::connection::types::ConnectionEnd; +use namada_core::ibc::core::handler::types::error::ContextError; +use namada_core::ibc::core::host::types::identifiers::{ + ChannelId, ClientId, ConnectionId, PortId, Sequence, +}; +use namada_core::ibc::primitives::proto::{Any, Protobuf}; +use namada_core::ibc::primitives::Timestamp; +use namada_core::tendermint::Time as TmTime; +use namada_core::types::storage::{BlockHeight, Key}; +use namada_core::types::time::DurationSecs; +use namada_parameters::storage::get_max_expected_time_per_block_key; use prost::Message; use sha2::Digest; use super::client::{AnyClientState, AnyConsensusState}; use super::storage::IbcStorageContext; -use crate::ibc::clients::tendermint::consensus_state::ConsensusState as TmConsensusState; -use crate::ibc::clients::tendermint::types::ConsensusState as TmConsensusStateType; -use crate::ibc::core::channel::types::channel::ChannelEnd; -use crate::ibc::core::channel::types::commitment::{ - AcknowledgementCommitment, PacketCommitment, -}; -use crate::ibc::core::channel::types::error::{ChannelError, PacketError}; -use crate::ibc::core::channel::types::packet::Receipt; -use crate::ibc::core::channel::types::timeout::TimeoutHeight; -use crate::ibc::core::client::context::consensus_state::ConsensusState; -use crate::ibc::core::client::types::error::ClientError; -use crate::ibc::core::client::types::Height; -use crate::ibc::core::connection::types::error::ConnectionError; -use crate::ibc::core::connection::types::ConnectionEnd; -use crate::ibc::core::handler::types::error::ContextError; -use crate::ibc::core::host::types::identifiers::{ - ChannelId, ClientId, ConnectionId, PortId, Sequence, -}; -use crate::ibc::primitives::proto::{Any, Protobuf}; -use crate::ibc::primitives::Timestamp; -use crate::ledger::ibc::storage; -use crate::ledger::parameters::storage::get_max_expected_time_per_block_key; -use crate::ledger::storage_api; -use crate::tendermint::Time as TmTime; -use crate::types::storage::{BlockHeight, Key}; -use crate::types::time::DurationSecs; +use crate::storage; /// Result of IBC common function call pub type Result = std::result::Result; @@ -668,15 +669,3 @@ pub trait IbcCommonContext: IbcStorageContext { Ok(()) } } - -/// Convert `storage_api::Error` into `ContextError`. -/// It always returns `ClientError::Other` though the storage error could happen -/// in any storage access. -impl From for ContextError { - fn from(error: storage_api::Error) -> Self { - ClientError::Other { - description: format!("Storage error: {error}"), - } - .into() - } -} diff --git a/ibc/src/context/execution.rs b/ibc/src/context/execution.rs index bdebb242ef..8b38d66761 100644 --- a/ibc/src/context/execution.rs +++ b/ibc/src/context/execution.rs @@ -1,29 +1,30 @@ //! ExecutionContext implementation for IBC -use super::client::{AnyClientState, AnyConsensusState}; -use super::common::IbcCommonContext; -use super::IbcContext; -use crate::ibc::core::channel::types::channel::ChannelEnd; -use crate::ibc::core::channel::types::commitment::{ +use namada_core::ibc::core::channel::types::channel::ChannelEnd; +use namada_core::ibc::core::channel::types::commitment::{ AcknowledgementCommitment, PacketCommitment, }; -use crate::ibc::core::channel::types::packet::Receipt; -use crate::ibc::core::client::context::ClientExecutionContext; -use crate::ibc::core::client::types::Height; -use crate::ibc::core::connection::types::ConnectionEnd; -use crate::ibc::core::handler::types::error::ContextError; -use crate::ibc::core::handler::types::events::IbcEvent; -use crate::ibc::core::host::types::identifiers::{ +use namada_core::ibc::core::channel::types::packet::Receipt; +use namada_core::ibc::core::client::context::ClientExecutionContext; +use namada_core::ibc::core::client::types::Height; +use namada_core::ibc::core::connection::types::ConnectionEnd; +use namada_core::ibc::core::handler::types::error::ContextError; +use namada_core::ibc::core::handler::types::events::IbcEvent; +use namada_core::ibc::core::host::types::identifiers::{ ClientId, ConnectionId, Sequence, }; -use crate::ibc::core::host::types::path::{ +use namada_core::ibc::core::host::types::path::{ AckPath, ChannelEndPath, ClientConnectionPath, ClientConsensusStatePath, ClientStatePath, CommitmentPath, ConnectionPath, ReceiptPath, SeqAckPath, SeqRecvPath, SeqSendPath, }; -use crate::ibc::core::host::ExecutionContext; -use crate::ibc::primitives::Timestamp; -use crate::ledger::ibc::storage; +use namada_core::ibc::core::host::ExecutionContext; +use namada_core::ibc::primitives::Timestamp; + +use super::client::{AnyClientState, AnyConsensusState}; +use super::common::IbcCommonContext; +use super::IbcContext; +use crate::storage; impl ClientExecutionContext for IbcContext where diff --git a/ibc/src/context/mod.rs b/ibc/src/context/mod.rs index b661bf2f09..60053a73db 100644 --- a/ibc/src/context/mod.rs +++ b/ibc/src/context/mod.rs @@ -14,8 +14,8 @@ use std::fmt::Debug; use std::rc::Rc; use std::time::Duration; -use crate::ibc::core::commitment_types::specs::ProofSpecs; -use crate::ibc::core::host::types::identifiers::ChainId as IbcChainId; +use namada_core::ibc::core::commitment_types::specs::ProofSpecs; +use namada_core::ibc::core::host::types::identifiers::ChainId as IbcChainId; /// IBC context to handle IBC-related data #[derive(Debug)] diff --git a/ibc/src/context/router.rs b/ibc/src/context/router.rs index cc9abca1fc..da7b90c31d 100644 --- a/ibc/src/context/router.rs +++ b/ibc/src/context/router.rs @@ -3,11 +3,12 @@ use std::collections::HashMap; use std::rc::Rc; +use namada_core::ibc::core::host::types::identifiers::PortId; +use namada_core::ibc::core::router::module::Module; +use namada_core::ibc::core::router::router::Router; +use namada_core::ibc::core::router::types::module::ModuleId; + use super::super::ModuleWrapper; -use crate::ibc::core::host::types::identifiers::PortId; -use crate::ibc::core::router::module::Module; -use crate::ibc::core::router::router::Router; -use crate::ibc::core::router::types::module::ModuleId; /// IBC router #[derive(Debug, Default)] diff --git a/ibc/src/context/storage.rs b/ibc/src/context/storage.rs index facbb2a5c2..88f3fdf80f 100644 --- a/ibc/src/context/storage.rs +++ b/ibc/src/context/storage.rs @@ -1,11 +1,10 @@ //! IBC storage context pub use ics23::ProofSpec; - -use crate::ledger::storage_api::{Error, StorageRead, StorageWrite}; -use crate::types::address::Address; -use crate::types::ibc::{IbcEvent, IbcShieldedTransfer}; -use crate::types::token::DenominatedAmount; +use namada_core::types::address::Address; +use namada_core::types::ibc::{IbcEvent, IbcShieldedTransfer}; +use namada_core::types::token::DenominatedAmount; +use namada_storage::{Error, StorageRead, StorageWrite}; /// IBC context trait to be implemented in integration that can read and write pub trait IbcStorageContext: StorageRead + StorageWrite { diff --git a/ibc/src/context/token_transfer.rs b/ibc/src/context/token_transfer.rs index 3e0cf5bc23..4b3e333ec4 100644 --- a/ibc/src/context/token_transfer.rs +++ b/ibc/src/context/token_transfer.rs @@ -3,20 +3,21 @@ use std::cell::RefCell; use std::rc::Rc; -use super::common::IbcCommonContext; -use crate::ibc::apps::transfer::context::{ +use namada_core::ibc::apps::transfer::context::{ TokenTransferExecutionContext, TokenTransferValidationContext, }; -use crate::ibc::apps::transfer::types::error::TokenTransferError; -use crate::ibc::apps::transfer::types::{PrefixedCoin, PrefixedDenom}; -use crate::ibc::core::channel::types::error::ChannelError; -use crate::ibc::core::handler::types::error::ContextError; -use crate::ibc::core::host::types::identifiers::{ChannelId, PortId}; -use crate::ledger::ibc::storage; -use crate::ledger::storage_api::token::read_denom; -use crate::types::address::{Address, InternalAddress}; -use crate::types::token; -use crate::types::uint::Uint; +use namada_core::ibc::apps::transfer::types::error::TokenTransferError; +use namada_core::ibc::apps::transfer::types::{PrefixedCoin, PrefixedDenom}; +use namada_core::ibc::core::channel::types::error::ChannelError; +use namada_core::ibc::core::handler::types::error::ContextError; +use namada_core::ibc::core::host::types::identifiers::{ChannelId, PortId}; +use namada_core::types::address::{Address, InternalAddress}; +use namada_core::types::token; +use namada_core::types::uint::Uint; +use namada_trans_token::read_denom; + +use super::common::IbcCommonContext; +use crate::storage; /// Token transfer context to handle tokens #[derive(Debug)] diff --git a/ibc/src/context/transfer_mod.rs b/ibc/src/context/transfer_mod.rs index 5bc18b35c3..35c0a1d872 100644 --- a/ibc/src/context/transfer_mod.rs +++ b/ibc/src/context/transfer_mod.rs @@ -4,9 +4,7 @@ use std::cell::RefCell; use std::fmt::Debug; use std::rc::Rc; -use super::common::IbcCommonContext; -use super::token_transfer::TokenTransferContext; -use crate::ibc::apps::transfer::module::{ +use namada_core::ibc::apps::transfer::module::{ on_acknowledgement_packet_execute, on_acknowledgement_packet_validate, on_chan_close_confirm_execute, on_chan_close_confirm_validate, on_chan_close_init_execute, on_chan_close_init_validate, @@ -17,19 +15,24 @@ use crate::ibc::apps::transfer::module::{ on_recv_packet_execute, on_timeout_packet_execute, on_timeout_packet_validate, }; -use crate::ibc::apps::transfer::types::error::TokenTransferError; -use crate::ibc::apps::transfer::types::MODULE_ID_STR; -use crate::ibc::core::channel::types::acknowledgement::Acknowledgement; -use crate::ibc::core::channel::types::channel::{Counterparty, Order}; -use crate::ibc::core::channel::types::error::{ChannelError, PacketError}; -use crate::ibc::core::channel::types::packet::Packet; -use crate::ibc::core::channel::types::Version; -use crate::ibc::core::host::types::identifiers::{ +use namada_core::ibc::apps::transfer::types::error::TokenTransferError; +use namada_core::ibc::apps::transfer::types::MODULE_ID_STR; +use namada_core::ibc::core::channel::types::acknowledgement::Acknowledgement; +use namada_core::ibc::core::channel::types::channel::{Counterparty, Order}; +use namada_core::ibc::core::channel::types::error::{ + ChannelError, PacketError, +}; +use namada_core::ibc::core::channel::types::packet::Packet; +use namada_core::ibc::core::channel::types::Version; +use namada_core::ibc::core::host::types::identifiers::{ ChannelId, ConnectionId, PortId, }; -use crate::ibc::core::router::module::Module; -use crate::ibc::core::router::types::module::{ModuleExtras, ModuleId}; -use crate::ibc::primitives::Signer; +use namada_core::ibc::core::router::module::Module; +use namada_core::ibc::core::router::types::module::{ModuleExtras, ModuleId}; +use namada_core::ibc::primitives::Signer; + +use super::common::IbcCommonContext; +use super::token_transfer::TokenTransferContext; /// IBC module wrapper for getting the reference of the module pub trait ModuleWrapper: Module { @@ -331,9 +334,10 @@ fn into_packet_error(error: TokenTransferError) -> PacketError { /// Helpers for testing #[cfg(any(test, feature = "testing"))] pub mod testing { + use namada_core::ibc::apps::transfer::types::ack_success_b64; + use namada_core::ibc::core::channel::types::acknowledgement::AcknowledgementStatus; + use super::*; - use crate::ibc::apps::transfer::types::ack_success_b64; - use crate::ibc::core::channel::types::acknowledgement::AcknowledgementStatus; /// Dummy IBC module for token transfer #[derive(Debug)] diff --git a/ibc/src/context/validation.rs b/ibc/src/context/validation.rs index 8ecd55fe0b..b65b8c640f 100644 --- a/ibc/src/context/validation.rs +++ b/ibc/src/context/validation.rs @@ -2,37 +2,37 @@ #[cfg(feature = "testing")] use ibc_testkit::testapp::ibc::clients::mock::client_state::MockClientState; - -use super::client::{AnyClientState, AnyConsensusState}; -use super::common::IbcCommonContext; -use super::IbcContext; -use crate::ibc::clients::tendermint::context::{ +use namada_core::ibc::clients::tendermint::context::{ CommonContext as TmCommonContext, ValidationContext as TmValidationContext, }; -use crate::ibc::core::channel::types::channel::ChannelEnd; -use crate::ibc::core::channel::types::commitment::{ +use namada_core::ibc::core::channel::types::channel::ChannelEnd; +use namada_core::ibc::core::channel::types::commitment::{ AcknowledgementCommitment, PacketCommitment, }; -use crate::ibc::core::channel::types::packet::Receipt; -use crate::ibc::core::client::context::ClientValidationContext; -use crate::ibc::core::client::types::error::ClientError; -use crate::ibc::core::client::types::Height; -use crate::ibc::core::commitment_types::commitment::CommitmentPrefix; -use crate::ibc::core::commitment_types::specs::ProofSpecs; -use crate::ibc::core::connection::types::ConnectionEnd; -use crate::ibc::core::handler::types::error::ContextError; -use crate::ibc::core::host::types::identifiers::{ +use namada_core::ibc::core::channel::types::packet::Receipt; +use namada_core::ibc::core::client::context::ClientValidationContext; +use namada_core::ibc::core::client::types::error::ClientError; +use namada_core::ibc::core::client::types::Height; +use namada_core::ibc::core::commitment_types::commitment::CommitmentPrefix; +use namada_core::ibc::core::commitment_types::specs::ProofSpecs; +use namada_core::ibc::core::connection::types::ConnectionEnd; +use namada_core::ibc::core::handler::types::error::ContextError; +use namada_core::ibc::core::host::types::identifiers::{ ChainId, ClientId, ConnectionId, Sequence, }; -use crate::ibc::core::host::types::path::{ +use namada_core::ibc::core::host::types::path::{ AckPath, ChannelEndPath, ClientConsensusStatePath, CommitmentPath, ReceiptPath, SeqAckPath, SeqRecvPath, SeqSendPath, }; -use crate::ibc::core::host::ValidationContext; -use crate::ibc::cosmos_host::ValidateSelfClientContext; -use crate::ibc::primitives::proto::Any; -use crate::ibc::primitives::{Signer, Timestamp}; -use crate::ledger::ibc::storage; +use namada_core::ibc::core::host::ValidationContext; +use namada_core::ibc::cosmos_host::ValidateSelfClientContext; +use namada_core::ibc::primitives::proto::Any; +use namada_core::ibc::primitives::{Signer, Timestamp}; + +use super::client::{AnyClientState, AnyConsensusState}; +use super::common::IbcCommonContext; +use super::IbcContext; +use crate::storage; const COMMITMENT_PREFIX: &[u8] = b"ibc"; diff --git a/ibc/src/lib.rs b/ibc/src/lib.rs index b695aef259..cb48bc70e3 100644 --- a/ibc/src/lib.rs +++ b/ibc/src/lib.rs @@ -16,32 +16,31 @@ pub use context::token_transfer::TokenTransferContext; pub use context::transfer_mod::{ModuleWrapper, TransferModule}; use context::IbcContext; pub use context::ValidationParams; -use prost::Message; -use thiserror::Error; - -use crate::ibc::apps::transfer::handler::{ +use namada_core::ibc::apps::transfer::handler::{ send_transfer_execute, send_transfer_validate, }; -use crate::ibc::apps::transfer::types::error::TokenTransferError; -use crate::ibc::apps::transfer::types::msgs::transfer::MsgTransfer; -use crate::ibc::apps::transfer::types::{ +use namada_core::ibc::apps::transfer::types::error::TokenTransferError; +use namada_core::ibc::apps::transfer::types::msgs::transfer::MsgTransfer; +use namada_core::ibc::apps::transfer::types::{ is_receiver_chain_source, PrefixedDenom, TracePrefix, }; -use crate::ibc::core::channel::types::msgs::PacketMsg; -use crate::ibc::core::entrypoint::{execute, validate}; -use crate::ibc::core::handler::types::error::ContextError; -use crate::ibc::core::handler::types::msgs::MsgEnvelope; -use crate::ibc::core::host::types::error::IdentifierError; -use crate::ibc::core::host::types::identifiers::{ChannelId, PortId}; -use crate::ibc::core::router::types::error::RouterError; -use crate::ibc::core::router::types::module::ModuleId; -use crate::ibc::primitives::proto::Any; -use crate::types::address::{Address, MASP}; -use crate::types::ibc::{ +use namada_core::ibc::core::channel::types::msgs::PacketMsg; +use namada_core::ibc::core::entrypoint::{execute, validate}; +use namada_core::ibc::core::handler::types::error::ContextError; +use namada_core::ibc::core::handler::types::msgs::MsgEnvelope; +use namada_core::ibc::core::host::types::error::IdentifierError; +use namada_core::ibc::core::host::types::identifiers::{ChannelId, PortId}; +use namada_core::ibc::core::router::types::error::RouterError; +use namada_core::ibc::core::router::types::module::ModuleId; +use namada_core::ibc::primitives::proto::Any; +use namada_core::types::address::{Address, MASP}; +use namada_core::types::ibc::{ get_shielded_transfer, is_ibc_denom, MsgShieldedTransfer, EVENT_TYPE_DENOM_TRACE, EVENT_TYPE_PACKET, }; -use crate::types::masp::PaymentAddress; +use namada_core::types::masp::PaymentAddress; +use prost::Message; +use thiserror::Error; #[allow(missing_docs)] #[derive(Error, Debug)] @@ -357,11 +356,10 @@ pub mod testing { use ibc::core::primitives::Signer; use ibc::primitives::proto::Any; use ibc::primitives::{Msg, Timestamp}; + use namada_core::ibc::apps::transfer::types::msgs::transfer::MsgTransfer; use proptest::prelude::{Just, Strategy}; use proptest::{collection, prop_compose}; - use crate::ibc::apps::transfer::types::msgs::transfer::MsgTransfer; - prop_compose! { /// Generate an arbitrary port ID pub fn arb_ibc_port_id()(id in "[a-zA-Z0-9_+.\\-\\[\\]#<>]{2,128}") -> PortId { diff --git a/ibc/src/storage.rs b/ibc/src/storage.rs index eeaecd2efa..2defc63ef9 100644 --- a/ibc/src/storage.rs +++ b/ibc/src/storage.rs @@ -2,21 +2,22 @@ use std::str::FromStr; -use sha2::{Digest, Sha256}; -use thiserror::Error; - -use crate::ibc::core::client::types::Height; -use crate::ibc::core::host::types::identifiers::{ +use namada_core::ibc::core::client::types::Height; +use namada_core::ibc::core::host::types::identifiers::{ ChannelId, ClientId, ConnectionId, PortId, Sequence, }; -use crate::ibc::core::host::types::path::{ +use namada_core::ibc::core::host::types::path::{ AckPath, ChannelEndPath, ClientConnectionPath, ClientConsensusStatePath, ClientStatePath, CommitmentPath, ConnectionPath, Path, PortPath, ReceiptPath, SeqAckPath, SeqRecvPath, SeqSendPath, }; -use crate::types::address::{Address, InternalAddress, HASH_LEN, SHA_HASH_LEN}; -use crate::types::ibc::IbcTokenHash; -use crate::types::storage::{self, DbKeySeg, Key, KeySeg}; +use namada_core::types::address::{ + Address, InternalAddress, HASH_LEN, SHA_HASH_LEN, +}; +use namada_core::types::ibc::IbcTokenHash; +use namada_core::types::storage::{DbKeySeg, Key, KeySeg}; +use sha2::{Digest, Sha256}; +use thiserror::Error; const CLIENTS_COUNTER: &str = "clients/counter"; const CONNECTIONS_COUNTER: &str = "connections/counter"; @@ -27,7 +28,7 @@ const DENOM: &str = "ibc_denom"; #[derive(Error, Debug)] pub enum Error { #[error("Storage key error: {0}")] - StorageKey(storage::Error), + StorageKey(namada_core::types::storage::Error), #[error("Invalid Key: {0}")] InvalidKey(String), #[error("Port capability error: {0}")] diff --git a/storage/src/error.rs b/storage/src/error.rs index 5644bc0a1a..77138b66fd 100644 --- a/storage/src/error.rs +++ b/storage/src/error.rs @@ -116,3 +116,17 @@ impl OptionExt for Option { self.ok_or_else(|| Error::new_const(msg)) } } + +/// Convert `namada_storage::Error` into IBC `ContextError`. +/// It always returns `ClientError::Other` though the storage error could happen +/// in any storage access. +impl From + for namada_core::ibc::core::handler::types::error::ContextError +{ + fn from(error: Error) -> Self { + namada_core::ibc::core::client::types::error::ClientError::Other { + description: format!("Storage error: {error}"), + } + .into() + } +} From 6f1aedb7a306529d0f33be27a8480affecb634fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 3 Jan 2024 10:57:17 +0000 Subject: [PATCH 066/118] tx_env: fix build --- tx_env/src/lib.rs | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/tx_env/src/lib.rs b/tx_env/src/lib.rs index f986b5c79e..bc482d5004 100644 --- a/tx_env/src/lib.rs +++ b/tx_env/src/lib.rs @@ -1,11 +1,11 @@ //! Transaction environment contains functions that can be called from //! inside a tx. -use borsh::BorshSerialize; +use namada_core::borsh::BorshSerialize; use namada_core::types::address::Address; use namada_core::types::ibc::IbcEvent; use namada_core::types::storage; -use namada_storage::{self, StorageRead, StorageWrite}; +use namada_storage::{Result, StorageRead, StorageWrite}; /// Transaction host functions pub trait TxEnv: StorageRead + StorageWrite { @@ -15,14 +15,14 @@ pub trait TxEnv: StorageRead + StorageWrite { &mut self, key: &storage::Key, val: T, - ) -> Result<(), storage_api::Error>; + ) -> Result<()>; /// Write a temporary value as bytes at the given key to storage. fn write_bytes_temp( &mut self, key: &storage::Key, val: impl AsRef<[u8]>, - ) -> Result<(), storage_api::Error>; + ) -> Result<()>; /// Insert a verifier address. This address must exist on chain, otherwise /// the transaction will be rejected. @@ -30,10 +30,7 @@ pub trait TxEnv: StorageRead + StorageWrite { /// Validity predicates of each verifier addresses inserted in the /// transaction will validate the transaction and will receive all the /// changed storage keys and initialized accounts in their inputs. - fn insert_verifier( - &mut self, - addr: &Address, - ) -> Result<(), storage_api::Error>; + fn insert_verifier(&mut self, addr: &Address) -> Result<()>; /// Initialize a new account generates a new established address and /// writes the given code as its validity predicate into the storage. @@ -41,7 +38,7 @@ pub trait TxEnv: StorageRead + StorageWrite { &mut self, code_hash: impl AsRef<[u8]>, code_tag: &Option, - ) -> Result; + ) -> Result
; /// Update a validity predicate fn update_validity_predicate( @@ -49,22 +46,19 @@ pub trait TxEnv: StorageRead + StorageWrite { addr: &Address, code: impl AsRef<[u8]>, code_tag: &Option, - ) -> Result<(), storage_api::Error>; + ) -> Result<()>; /// Emit an IBC event. On multiple calls, these emitted event will be added. - fn emit_ibc_event( - &mut self, - event: &IbcEvent, - ) -> Result<(), storage_api::Error>; + fn emit_ibc_event(&mut self, event: &IbcEvent) -> Result<()>; /// Request to charge the provided amount of gas for the current transaction - fn charge_gas(&mut self, used_gas: u64) -> Result<(), storage_api::Error>; + fn charge_gas(&mut self, used_gas: u64) -> Result<()>; /// Get IBC events with a event type fn get_ibc_events( &self, event_type: impl AsRef, - ) -> Result, storage_api::Error>; + ) -> Result>; /// Set the sentinel for an invalid section commitment fn set_commitment_sentinel(&mut self); From 6ed7ff7f5a2c82695fbb8a474dab02ede31d404d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 3 Jan 2024 11:43:52 +0000 Subject: [PATCH 067/118] shielded_token: fix build --- Cargo.lock | 11 ++- shielded_token/Cargo.toml | 25 +++++- shielded_token/src/conversion.rs | 121 ++++++++++++++---------------- shielded_token/src/lib.rs | 2 + shielded_token/src/storage.rs | 32 ++++++++ shielded_token/src/storage_key.rs | 99 ++++++++++++++++++++++++ shielded_token/src/utils.rs | 14 ++-- trans_token/src/storage.rs | 15 +--- trans_token/src/storage_key.rs | 97 +----------------------- 9 files changed, 231 insertions(+), 185 deletions(-) create mode 100644 shielded_token/src/storage.rs create mode 100644 shielded_token/src/storage_key.rs diff --git a/Cargo.lock b/Cargo.lock index 4de279c6f7..8560191a9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4651,7 +4651,16 @@ dependencies = [ [[package]] name = "namada_shielded_token" -version = "0.1.0" +version = "0.28.1" +dependencies = [ + "masp_primitives", + "namada_core", + "namada_parameters", + "namada_state", + "namada_storage", + "namada_trans_token", + "tracing", +] [[package]] name = "namada_state" diff --git a/shielded_token/Cargo.toml b/shielded_token/Cargo.toml index 43fd32be90..9fcfea99e6 100644 --- a/shielded_token/Cargo.toml +++ b/shielded_token/Cargo.toml @@ -1,8 +1,27 @@ [package] name = "namada_shielded_token" -version = "0.1.0" -edition = "2021" +description = "Namada shielded token" +resolver = "2" +authors.workspace = true +edition.workspace = true +documentation.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +default = [] +testing = ["namada_core/testing"] [dependencies] +namada_core = { path = "../core", default-features = false } +namada_parameters = { path = "../parameters" } +namada_state = { path = "../state" } +namada_storage = { path = "../storage" } +namada_trans_token = { path = "../trans_token" } + +masp_primitives.workspace = true +tracing.workspace = true diff --git a/shielded_token/src/conversion.rs b/shielded_token/src/conversion.rs index cb2a167a4c..3c932d10d3 100644 --- a/shielded_token/src/conversion.rs +++ b/shielded_token/src/conversion.rs @@ -1,31 +1,30 @@ //! MASP rewards conversions -use std::collections::BTreeMap; - -use borsh::{BorshDeserialize, BorshSerialize}; -use borsh_ext::BorshSerializeExt; use masp_primitives::asset_type::AssetType; -use masp_primitives::convert::AllowedConversion; -use masp_primitives::merkle_tree::FrozenCommitmentTree; -use masp_primitives::sapling::Node; - -use crate::ledger::inflation::{RewardsController, ValsToUpdate}; -use crate::ledger::parameters; -use crate::ledger::storage::{DBIter, StorageHasher, WlStorage, DB}; -use crate::ledger::storage_api::token::read_denom; -use crate::ledger::storage_api::{StorageRead, StorageWrite}; -use crate::types::address::{Address, MASP}; -use crate::types::dec::Dec; -use crate::types::storage::Epoch; -use crate::types::token::{self, DenominatedAmount, MaspDenom}; -use crate::types::uint::Uint; +use namada_core::borsh::BorshSerializeExt; +use namada_core::ledger::inflation::{RewardsController, ValsToUpdate}; +use namada_core::types::address::{Address, MASP}; +use namada_core::types::dec::Dec; +use namada_core::types::storage::Epoch; +use namada_core::types::uint::Uint; +use namada_parameters as parameters; +use namada_state::{DBIter, StorageHasher, WlStorage, DB}; +use namada_storage::{StorageRead, StorageWrite}; +use namada_trans_token::storage_key::{balance_key, minted_balance_key}; +use namada_trans_token::{read_denom, Amount, DenominatedAmount, MaspDenom}; + +use crate::storage_key::{ + masp_kd_gain_key, masp_kp_gain_key, masp_last_inflation_key, + masp_last_locked_ratio_key, masp_locked_ratio_target_key, + masp_max_reward_rate_key, +}; /// Compute the MASP rewards by applying the PD-controller to the genesis /// parameters and the last inflation and last locked rewards ratio values. pub fn calculate_masp_rewards( wl_storage: &mut WlStorage, addr: &Address, -) -> crate::ledger::storage_api::Result<(u128, u128)> +) -> namada_storage::Result<(u128, u128)> where D: 'static + DB + for<'iter> DBIter<'iter>, H: 'static + StorageHasher, @@ -45,18 +44,18 @@ where // Query the storage for information //// information about the amount of tokens on the chain - let total_tokens: token::Amount = wl_storage - .read(&token::minted_balance_key(addr))? + let total_tokens: Amount = wl_storage + .read(&minted_balance_key(addr))? .expect("the total supply key should be here"); //// information about the amount of native tokens on the chain - let total_native_tokens: token::Amount = wl_storage - .read(&token::minted_balance_key(&wl_storage.storage.native_token))? + let total_native_tokens: Amount = wl_storage + .read(&minted_balance_key(&wl_storage.storage.native_token))? .expect("the total supply key should be here"); // total staked amount in the Shielded pool - let total_token_in_masp: token::Amount = wl_storage - .read(&token::balance_key(addr, &masp_addr))? + let total_token_in_masp: Amount = wl_storage + .read(&balance_key(addr, &masp_addr))? .unwrap_or_default(); let epochs_per_year: u64 = wl_storage @@ -64,29 +63,29 @@ where .expect("epochs per year should properly decode"); //// Values from the last epoch - let last_inflation: token::Amount = wl_storage - .read(&token::masp_last_inflation_key(addr))? + let last_inflation: Amount = wl_storage + .read(&masp_last_inflation_key(addr))? .expect("failure to read last inflation"); let last_locked_ratio: Dec = wl_storage - .read(&token::masp_last_locked_ratio_key(addr))? + .read(&masp_last_locked_ratio_key(addr))? .expect("failure to read last inflation"); //// Parameters for each token let max_reward_rate: Dec = wl_storage - .read(&token::masp_max_reward_rate_key(addr))? + .read(&masp_max_reward_rate_key(addr))? .expect("max reward should properly decode"); let kp_gain_nom: Dec = wl_storage - .read(&token::masp_kp_gain_key(addr))? + .read(&masp_kp_gain_key(addr))? .expect("kp_gain_nom reward should properly decode"); let kd_gain_nom: Dec = wl_storage - .read(&token::masp_kd_gain_key(addr))? + .read(&masp_kd_gain_key(addr))? .expect("kd_gain_nom reward should properly decode"); let locked_target_ratio: Dec = wl_storage - .read(&token::masp_locked_ratio_target_key(addr))? + .read(&masp_locked_ratio_target_key(addr))? .expect("locked ratio target should properly decode"); // Creating the PD controller for handing out tokens @@ -131,7 +130,7 @@ where 0u128 }) }; - let inflation_amount = token::Amount::from_uint( + let inflation_amount = Amount::from_uint( (total_token_in_masp.raw_amount() / precision) * Uint::from(noterized_inflation), 0, @@ -167,10 +166,9 @@ where // but we should make sure the return value's ratio matches // this new inflation rate in 'update_allowed_conversions', // otherwise we will have an inaccurate view of inflation - wl_storage - .write(&token::masp_last_inflation_key(addr), inflation_amount)?; + wl_storage.write(&masp_last_inflation_key(addr), inflation_amount)?; - wl_storage.write(&token::masp_last_locked_ratio_key(addr), locked_ratio)?; + wl_storage.write(&masp_last_locked_ratio_key(addr), locked_ratio)?; Ok((noterized_inflation, precision)) } @@ -180,7 +178,7 @@ where /// Update the MASP's allowed conversions pub fn update_allowed_conversions( wl_storage: &mut WlStorage, -) -> crate::ledger::storage_api::Result<()> +) -> namada_storage::Result<()> where D: 'static + DB + for<'iter> DBIter<'iter>, H: 'static + StorageHasher, @@ -190,14 +188,13 @@ where use masp_primitives::bls12_381; use masp_primitives::ff::PrimeField; use masp_primitives::transaction::components::I128Sum as MaspAmount; + use namada_core::types::storage::{Key, KeySeg}; + use namada_core::types::{address, MASP_CONVERT_ANCHOR_KEY}; use rayon::iter::{ IndexedParallelIterator, IntoParallelIterator, ParallelIterator, }; use rayon::prelude::ParallelSlice; - use crate::types::storage::{Key, KeySeg}; - use crate::types::token::MASP_CONVERT_ANCHOR_KEY; - // The derived conversions will be placed in MASP address space let masp_addr = MASP; @@ -221,7 +218,7 @@ where } }); // The total transparent value of the rewards being distributed - let mut total_reward = token::Amount::native_whole(0); + let mut total_reward = Amount::native_whole(0); // Construct MASP asset type for rewards. Always deflate and timestamp // reward tokens with the zeroth epoch to minimize the number of convert @@ -247,10 +244,10 @@ where ref_inflation = reward.1; } // Dispense a transparent reward in parallel to the shielded rewards - let addr_bal: token::Amount = wl_storage - .read(&token::balance_key(addr, &masp_addr))? + let addr_bal: Amount = wl_storage + .read(&balance_key(addr, &masp_addr))? .unwrap_or_default(); - for denom in token::MaspDenom::iter() { + for denom in MaspDenom::iter() { // Provide an allowed conversion from previous timestamp. The // negative sign allows each instance of the old asset to be // cancelled out/replaced with the new asset @@ -407,9 +404,8 @@ where // Update the MASP's transparent reward token balance to ensure that it // is sufficiently backed to redeem rewards - let reward_key = token::balance_key(&native_token, &masp_addr); - let addr_bal: token::Amount = - wl_storage.read(&reward_key)?.unwrap_or_default(); + let reward_key = balance_key(&native_token, &masp_addr); + let addr_bal: Amount = wl_storage.read(&reward_key)?.unwrap_or_default(); let new_bal = addr_bal + total_reward; wl_storage.write(&reward_key, new_bal)?; // Try to distribute Merkle tree construction as evenly as possible @@ -435,7 +431,7 @@ where .expect("Cannot obtain a storage key"); wl_storage.write( &anchor_key, - crate::types::hash::Hash( + namada_core::types::hash::Hash( bls12_381::Scalar::from( wl_storage.storage.conversion_state.tree.root(), ) @@ -446,7 +442,7 @@ where // Add purely decoding entries to the assets map. These will be // overwritten before the creation of the next commitment tree for addr in masp_reward_keys { - for denom in token::MaspDenom::iter() { + for denom in MaspDenom::iter() { // Add the decoding entry for the new asset type. An uncommitted // node position is used since this is not a conversion. let new_asset = encode_asset_type( @@ -485,18 +481,18 @@ mod tests { use std::collections::HashMap; use std::str::FromStr; + use namada_core::types::address; + use namada_core::types::dec::testing::arb_non_negative_dec; + use namada_core::types::testing::arb_amount; + use namada_core::types::time::DurationSecs; + use namada_parameters::{EpochDuration, Parameters}; + use namada_storage::write_denom; use proptest::prelude::*; use proptest::test_runner::Config; use test_log::test; use super::*; - use crate::ledger::parameters::{EpochDuration, Parameters}; use crate::ledger::storage::testing::TestWlStorage; - use crate::ledger::storage_api::token::write_denom; - use crate::types::address; - use crate::types::dec::testing::arb_non_negative_dec; - use crate::types::time::DurationSecs; - use crate::types::token::testing::arb_amount; proptest! { #![proptest_config(Config { @@ -513,7 +509,7 @@ mod tests { } fn test_updated_allowed_conversions_aux( - initial_balance: token::Amount, + initial_balance: Amount, masp_locked_ratio: Dec, ) { const ROUNDS: usize = 10; @@ -546,7 +542,7 @@ mod tests { params.init_storage(&mut s).unwrap(); // Tokens - let token_params = token::Parameters { + let token_params = Parameters { max_reward_rate: Dec::from_str("0.1").unwrap(), kp_gain_nom: Dec::from_str("0.1").unwrap(), kd_gain_nom: Dec::from_str("0.1").unwrap(), @@ -560,15 +556,12 @@ mod tests { // Write a minted token balance let total_token_balance = initial_balance; - s.write( - &token::minted_balance_key(&token_addr), - total_token_balance, - ) - .unwrap(); + s.write(&minted_balance_key(&token_addr), total_token_balance) + .unwrap(); // Put the locked ratio into MASP s.write( - &token::balance_key(&token_addr, &address::MASP), + &balance_key(&token_addr, &address::MASP), masp_locked_ratio * total_token_balance, ) .unwrap(); @@ -589,7 +582,7 @@ mod tests { } } - pub fn tokens() -> HashMap { + pub fn tokens() -> HashMap { vec![ (address::nam(), ("nam", 6.into())), (address::btc(), ("btc", 8.into())), diff --git a/shielded_token/src/lib.rs b/shielded_token/src/lib.rs index e8f97918f1..7fe8d56292 100644 --- a/shielded_token/src/lib.rs +++ b/shielded_token/src/lib.rs @@ -1,4 +1,6 @@ //! Namada shielded token. pub mod conversion; +pub mod storage; +pub mod storage_key; pub mod utils; diff --git a/shielded_token/src/storage.rs b/shielded_token/src/storage.rs new file mode 100644 index 0000000000..17ebfdf927 --- /dev/null +++ b/shielded_token/src/storage.rs @@ -0,0 +1,32 @@ +use namada_core::types::address::Address; +use namada_core::types::dec::Dec; +use namada_core::types::token; +use namada_core::types::token::Amount; +use namada_storage as storage; +use namada_storage::{StorageRead, StorageWrite}; + +use crate::storage_key::*; + +/// Initialize parameters for the token in storage during the genesis block. +pub fn write_params( + params: &token::Parameters, + storage: &mut S, + address: &Address, +) -> storage::Result<()> +where + S: StorageRead + StorageWrite, +{ + let token::Parameters { + max_reward_rate: max_rate, + kd_gain_nom, + kp_gain_nom, + locked_ratio_target: locked_target, + } = params; + storage.write(&masp_last_inflation_key(address), Amount::zero())?; + storage.write(&masp_last_locked_ratio_key(address), Dec::zero())?; + storage.write(&masp_max_reward_rate_key(address), max_rate)?; + storage.write(&masp_locked_ratio_target_key(address), locked_target)?; + storage.write(&masp_kp_gain_key(address), kp_gain_nom)?; + storage.write(&masp_kd_gain_key(address), kd_gain_nom)?; + Ok(()) +} diff --git a/shielded_token/src/storage_key.rs b/shielded_token/src/storage_key.rs new file mode 100644 index 0000000000..2e241638ec --- /dev/null +++ b/shielded_token/src/storage_key.rs @@ -0,0 +1,99 @@ +//! Shielded token storage keys + +use namada_core::types::address::{self, Address}; +use namada_core::types::storage::{self, DbKeySeg}; +use namada_trans_token::storage_key::key_of_token; + +/// Key segment for head shielded transaction pointer keys +pub const HEAD_TX_KEY: &str = "head-tx"; +/// Key segment prefix for shielded transaction key +pub const TX_KEY_PREFIX: &str = "tx-"; +/// Key segment prefix for pinned shielded transactions +pub const PIN_KEY_PREFIX: &str = "pin-"; +/// Key segment prefix for the nullifiers +pub const MASP_NULLIFIERS_KEY: &str = "nullifiers"; +/// Key segment prefix for the note commitment merkle tree +pub const MASP_NOTE_COMMITMENT_TREE_KEY: &str = "commitment_tree"; +/// Key segment prefix for the note commitment anchor +pub const MASP_NOTE_COMMITMENT_ANCHOR_PREFIX: &str = "note_commitment_anchor"; +/// Key segment prefix for the convert anchor +pub const MASP_CONVERT_ANCHOR_KEY: &str = "convert_anchor"; +/// Last calculated inflation value handed out +pub const MASP_LAST_INFLATION_KEY: &str = "last_inflation"; +/// The last locked ratio +pub const MASP_LAST_LOCKED_RATIO_KEY: &str = "last_locked_ratio"; +/// The key for the nominal proportional gain of a shielded pool for a given +/// asset +pub const MASP_KP_GAIN_KEY: &str = "proportional_gain"; +/// The key for the nominal derivative gain of a shielded pool for a given asset +pub const MASP_KD_GAIN_KEY: &str = "derivative_gain"; +/// The key for the locked ratio target for a given asset +pub const MASP_LOCKED_RATIO_TARGET_KEY: &str = "locked_ratio_target"; +/// The key for the max reward rate for a given asset +pub const MASP_MAX_REWARD_RATE_KEY: &str = "max_reward_rate"; + +/// Obtain the nominal proportional key for the given token +pub fn masp_kp_gain_key(token_addr: &Address) -> storage::Key { + key_of_token(token_addr, MASP_KP_GAIN_KEY, "nominal proproitonal gains") +} + +/// Obtain the nominal derivative key for the given token +pub fn masp_kd_gain_key(token_addr: &Address) -> storage::Key { + key_of_token(token_addr, MASP_KD_GAIN_KEY, "nominal proproitonal gains") +} + +/// The max reward rate key for the given token +pub fn masp_max_reward_rate_key(token_addr: &Address) -> storage::Key { + key_of_token(token_addr, MASP_MAX_REWARD_RATE_KEY, "max reward rate") +} + +/// Obtain the locked target ratio key for the given token +pub fn masp_locked_ratio_target_key(token_addr: &Address) -> storage::Key { + key_of_token( + token_addr, + MASP_LOCKED_RATIO_TARGET_KEY, + "nominal proproitonal gains", + ) +} + +/// Check if the given storage key is a masp key +pub fn is_masp_key(key: &storage::Key) -> bool { + if key.segments.len() >= 2 { + matches!(&key.segments[..2], + [DbKeySeg::AddressSeg(addr), DbKeySeg::StringSeg(key)] + if *addr == address::MASP + && (key == HEAD_TX_KEY + || key.starts_with(TX_KEY_PREFIX) + || key.starts_with(PIN_KEY_PREFIX) + || key.starts_with(MASP_NULLIFIERS_KEY))) + } else { + false + } +} + +/// Check if the given storage key is a masp nullifier key +pub fn is_masp_nullifier_key(key: &storage::Key) -> bool { + matches!(&key.segments[..], + [DbKeySeg::AddressSeg(addr), + DbKeySeg::StringSeg(prefix), + .. + ] if *addr == address::MASP && prefix == MASP_NULLIFIERS_KEY) +} + +/// Obtain the storage key for the last locked ratio of a token +pub fn masp_last_locked_ratio_key(token_address: &Address) -> storage::Key { + key_of_token( + token_address, + MASP_LAST_LOCKED_RATIO_KEY, + "cannot obtain storage key for the last locked ratio", + ) +} + +/// Obtain the storage key for the last inflation of a token +pub fn masp_last_inflation_key(token_address: &Address) -> storage::Key { + key_of_token( + token_address, + MASP_LAST_INFLATION_KEY, + "cannot obtain storage key for the last inflation rate", + ) +} diff --git a/shielded_token/src/utils.rs b/shielded_token/src/utils.rs index f37b725531..0d1870a0f2 100644 --- a/shielded_token/src/utils.rs +++ b/shielded_token/src/utils.rs @@ -3,14 +3,14 @@ use masp_primitives::merkle_tree::CommitmentTree; use masp_primitives::sapling::Node; use masp_primitives::transaction::Transaction; +use namada_core::types::address::MASP; +use namada_core::types::hash::Hash; +use namada_core::types::storage::{BlockHeight, Epoch, Key, KeySeg, TxIndex}; +use namada_core::types::token::Transfer; +use namada_storage::{Error, Result, StorageRead, StorageWrite}; -use super::storage_api::{StorageRead, StorageWrite}; -use crate::ledger::storage_api::{Error, Result}; -use crate::types::address::MASP; -use crate::types::hash::Hash; -use crate::types::storage::{BlockHeight, Epoch, Key, KeySeg, TxIndex}; -use crate::types::token::{ - Transfer, HEAD_TX_KEY, MASP_NOTE_COMMITMENT_TREE_KEY, MASP_NULLIFIERS_KEY, +use crate::storage_key::{ + HEAD_TX_KEY, MASP_NOTE_COMMITMENT_TREE_KEY, MASP_NULLIFIERS_KEY, PIN_KEY_PREFIX, TX_KEY_PREFIX, }; diff --git a/trans_token/src/storage.rs b/trans_token/src/storage.rs index 9f5c0072c5..429ec94154 100644 --- a/trans_token/src/storage.rs +++ b/trans_token/src/storage.rs @@ -1,6 +1,5 @@ use namada_core::hints; use namada_core::types::address::{Address, InternalAddress}; -use namada_core::types::dec::Dec; use namada_core::types::token::{self, Amount, DenominatedAmount}; use namada_storage as storage; use namada_storage::{StorageRead, StorageWrite}; @@ -9,25 +8,13 @@ use crate::storage_key::*; /// Initialize parameters for the token in storage during the genesis block. pub fn write_params( - params: &token::Parameters, + _params: &token::Parameters, storage: &mut S, address: &Address, ) -> storage::Result<()> where S: StorageRead + StorageWrite, { - let token::Parameters { - max_reward_rate: max_rate, - kd_gain_nom, - kp_gain_nom, - locked_ratio_target: locked_target, - } = params; - storage.write(&masp_last_inflation_key(address), Amount::zero())?; - storage.write(&masp_last_locked_ratio_key(address), Dec::zero())?; - storage.write(&masp_max_reward_rate_key(address), max_rate)?; - storage.write(&masp_locked_ratio_target_key(address), locked_target)?; - storage.write(&masp_kp_gain_key(address), kp_gain_nom)?; - storage.write(&masp_kd_gain_key(address), kd_gain_nom)?; storage.write(&minted_balance_key(address), Amount::zero())?; Ok(()) } diff --git a/trans_token/src/storage_key.rs b/trans_token/src/storage_key.rs index 136886b35a..2bc6aa91e1 100644 --- a/trans_token/src/storage_key.rs +++ b/trans_token/src/storage_key.rs @@ -1,6 +1,6 @@ //! Transparent token storage keys -use namada_core::types::address::{self, Address, InternalAddress}; +use namada_core::types::address::{Address, InternalAddress}; use namada_core::types::storage::{self, DbKeySeg, KeySeg}; /// Key segment for a balance key @@ -12,35 +12,6 @@ pub const MINTER_STORAGE_KEY: &str = "minter"; /// Key segment for minted balance pub const MINTED_STORAGE_KEY: &str = "minted"; -// TODO: move to shielded -/// Key segment for head shielded transaction pointer keys -pub const HEAD_TX_KEY: &str = "head-tx"; -/// Key segment prefix for shielded transaction key -pub const TX_KEY_PREFIX: &str = "tx-"; -/// Key segment prefix for pinned shielded transactions -pub const PIN_KEY_PREFIX: &str = "pin-"; -/// Key segment prefix for the nullifiers -pub const MASP_NULLIFIERS_KEY: &str = "nullifiers"; -/// Key segment prefix for the note commitment merkle tree -pub const MASP_NOTE_COMMITMENT_TREE_KEY: &str = "commitment_tree"; -/// Key segment prefix for the note commitment anchor -pub const MASP_NOTE_COMMITMENT_ANCHOR_PREFIX: &str = "note_commitment_anchor"; -/// Key segment prefix for the convert anchor -pub const MASP_CONVERT_ANCHOR_KEY: &str = "convert_anchor"; -/// Last calculated inflation value handed out -pub const MASP_LAST_INFLATION_KEY: &str = "last_inflation"; -/// The last locked ratio -pub const MASP_LAST_LOCKED_RATIO_KEY: &str = "last_locked_ratio"; -/// The key for the nominal proportional gain of a shielded pool for a given -/// asset -pub const MASP_KP_GAIN_KEY: &str = "proportional_gain"; -/// The key for the nominal derivative gain of a shielded pool for a given asset -pub const MASP_KD_GAIN_KEY: &str = "derivative_gain"; -/// The key for the locked ratio target for a given asset -pub const MASP_LOCKED_RATIO_TARGET_KEY: &str = "locked_ratio_target"; -/// The key for the max reward rate for a given asset -pub const MASP_MAX_REWARD_RATE_KEY: &str = "max_reward_rate"; - /// Gets the key for the given token address, error with the given /// message to expect if the key is not in the address pub fn key_of_token( @@ -89,30 +60,6 @@ pub fn minted_balance_key(token_addr: &Address) -> storage::Key { .expect("Cannot obtain a storage key") } -/// Obtain the nominal proportional key for the given token -pub fn masp_kp_gain_key(token_addr: &Address) -> storage::Key { - key_of_token(token_addr, MASP_KP_GAIN_KEY, "nominal proproitonal gains") -} - -/// Obtain the nominal derivative key for the given token -pub fn masp_kd_gain_key(token_addr: &Address) -> storage::Key { - key_of_token(token_addr, MASP_KD_GAIN_KEY, "nominal proproitonal gains") -} - -/// The max reward rate key for the given token -pub fn masp_max_reward_rate_key(token_addr: &Address) -> storage::Key { - key_of_token(token_addr, MASP_MAX_REWARD_RATE_KEY, "max reward rate") -} - -/// Obtain the locked target ratio key for the given token -pub fn masp_locked_ratio_target_key(token_addr: &Address) -> storage::Key { - key_of_token( - token_addr, - MASP_LOCKED_RATIO_TARGET_KEY, - "nominal proproitonal gains", - ) -} - /// Check if the given storage key is balance key for the given token. If it is, /// returns the owner. For minted balances, use [`is_any_minted_balance_key()`]. pub fn is_balance_key<'a>( @@ -170,48 +117,6 @@ pub fn is_denom_key(token_addr: &Address, key: &storage::Key) -> bool { ] if key == DENOM_STORAGE_KEY && addr == token_addr) } -/// Check if the given storage key is a masp key -pub fn is_masp_key(key: &storage::Key) -> bool { - if key.segments.len() >= 2 { - matches!(&key.segments[..2], - [DbKeySeg::AddressSeg(addr), DbKeySeg::StringSeg(key)] - if *addr == address::MASP - && (key == HEAD_TX_KEY - || key.starts_with(TX_KEY_PREFIX) - || key.starts_with(PIN_KEY_PREFIX) - || key.starts_with(MASP_NULLIFIERS_KEY))) - } else { - false - } -} - -/// Check if the given storage key is a masp nullifier key -pub fn is_masp_nullifier_key(key: &storage::Key) -> bool { - matches!(&key.segments[..], - [DbKeySeg::AddressSeg(addr), - DbKeySeg::StringSeg(prefix), - .. - ] if *addr == address::MASP && prefix == MASP_NULLIFIERS_KEY) -} - -/// Obtain the storage key for the last locked ratio of a token -pub fn masp_last_locked_ratio_key(token_address: &Address) -> storage::Key { - key_of_token( - token_address, - MASP_LAST_LOCKED_RATIO_KEY, - "cannot obtain storage key for the last locked ratio", - ) -} - -/// Obtain the storage key for the last inflation of a token -pub fn masp_last_inflation_key(token_address: &Address) -> storage::Key { - key_of_token( - token_address, - MASP_LAST_INFLATION_KEY, - "cannot obtain storage key for the last inflation rate", - ) -} - /// Check if the given storage key is for a minter of a unspecified token. /// If it is, returns the token. pub fn is_any_minter_key(key: &storage::Key) -> Option<&Address> { From cdb2e5ee176b830ca093baadd453a2e8d5045a95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 3 Jan 2024 11:55:00 +0000 Subject: [PATCH 068/118] tx_prelude: fix build --- Cargo.lock | 9 ++++++- tx_prelude/Cargo.toml | 18 +++++++++---- tx_prelude/src/account.rs | 4 +-- tx_prelude/src/ibc.rs | 21 +++++++++------- tx_prelude/src/key.rs | 2 +- tx_prelude/src/lib.rs | 43 +++++++++++++++++--------------- tx_prelude/src/pgf.rs | 10 +++++--- tx_prelude/src/proof_of_stake.rs | 2 +- tx_prelude/src/token.rs | 29 ++++++++++----------- 9 files changed, 82 insertions(+), 56 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8560191a9c..324010be83 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4794,11 +4794,18 @@ name = "namada_tx_prelude" version = "0.29.0" dependencies = [ "borsh", - "borsh-ext", "masp_primitives", + "namada_account", "namada_core", + "namada_governance", + "namada_ibc", "namada_macros", + "namada_parameters", "namada_proof_of_stake", + "namada_storage", + "namada_token", + "namada_tx", + "namada_tx_env", "namada_vm_env", "sha2 0.9.9", "thiserror", diff --git a/tx_prelude/Cargo.toml b/tx_prelude/Cargo.toml index c23d66530e..f561dfdd3e 100644 --- a/tx_prelude/Cargo.toml +++ b/tx_prelude/Cargo.toml @@ -16,12 +16,20 @@ version.workspace = true default = [] [dependencies] -namada_core = {path = "../core", default-features = false} -namada_macros = {path = "../macros"} -namada_proof_of_stake = {path = "../proof_of_stake", default-features = false} -namada_vm_env = {path = "../vm_env", default-features = false} +namada_account = { path = "../account" } +namada_core = { path = "../core", default-features = false } +namada_governance = { path = "../governance" } +namada_ibc = { path = "../ibc" } +namada_macros = { path = "../macros" } +namada_parameters = { path = "../parameters" } +namada_proof_of_stake = { path = "../proof_of_stake", default-features = false } +namada_storage = { path = "../storage" } +namada_token = { path = "../token" } +namada_tx = { path = "../tx" } +namada_tx_env = { path = "../tx_env" } +namada_vm_env = { path = "../vm_env", default-features = false } + borsh.workspace = true -borsh-ext.workspace = true masp_primitives.workspace = true sha2.workspace = true thiserror.workspace = true diff --git a/tx_prelude/src/account.rs b/tx_prelude/src/account.rs index da6c213601..5054411a90 100644 --- a/tx_prelude/src/account.rs +++ b/tx_prelude/src/account.rs @@ -1,4 +1,4 @@ -use namada_core::types::transaction::account::InitAccount; +use namada_tx::data::account::InitAccount; use super::*; @@ -7,7 +7,7 @@ pub fn init_account( owner: &Address, data: InitAccount, ) -> EnvResult
{ - storage_api::account::init_account_storage( + namada_account::init_account_storage( ctx, owner, &data.public_keys, diff --git a/tx_prelude/src/ibc.rs b/tx_prelude/src/ibc.rs index ebf97e7859..45a6c6e448 100644 --- a/tx_prelude/src/ibc.rs +++ b/tx_prelude/src/ibc.rs @@ -3,14 +3,14 @@ use std::cell::RefCell; use std::rc::Rc; -pub use namada_core::ledger::ibc::{ - IbcActions, IbcCommonContext, IbcStorageContext, ProofSpec, TransferModule, -}; -use namada_core::ledger::masp_utils; -use namada_core::ledger::tx_env::TxEnv; use namada_core::types::address::{Address, InternalAddress}; pub use namada_core::types::ibc::{IbcEvent, IbcShieldedTransfer}; use namada_core::types::token::DenominatedAmount; +pub use namada_ibc::{ + IbcActions, IbcCommonContext, IbcStorageContext, ProofSpec, TransferModule, +}; +use namada_token::denom_to_amount; +use namada_tx_env::TxEnv; use crate::token::{burn, mint, transfer}; use crate::{Ctx, Error}; @@ -53,12 +53,15 @@ impl IbcStorageContext for Ctx { &mut self, shielded: &IbcShieldedTransfer, ) -> Result<(), Error> { - masp_utils::handle_masp_tx( + namada_token::utils::handle_masp_tx( self, &shielded.transfer, &shielded.masp_tx, )?; - masp_utils::update_note_commitment_tree(self, &shielded.masp_tx) + namada_token::utils::update_note_commitment_tree( + self, + &shielded.masp_tx, + ) } fn mint_token( @@ -72,7 +75,7 @@ impl IbcStorageContext for Ctx { &Address::Internal(InternalAddress::Ibc), target, token, - amount.to_amount(token, self)?, + denom_to_amount(amount, token, self)?, ) } @@ -82,7 +85,7 @@ impl IbcStorageContext for Ctx { token: &Address, amount: DenominatedAmount, ) -> Result<(), Error> { - burn(self, target, token, amount.to_amount(token, self)?) + burn(self, target, token, denom_to_amount(amount, token, self)?) } fn log_string(&self, message: String) { diff --git a/tx_prelude/src/key.rs b/tx_prelude/src/key.rs index 32a6c2089d..a273d25cc7 100644 --- a/tx_prelude/src/key.rs +++ b/tx_prelude/src/key.rs @@ -7,5 +7,5 @@ use super::*; /// Reveal a PK of an implicit account - the PK is written into the storage /// of the address derived from the PK. pub fn reveal_pk(ctx: &mut Ctx, pk: &common::PublicKey) -> EnvResult<()> { - storage_api::key::reveal_pk(ctx, pk) + namada_account::reveal_pk(ctx, pk) } diff --git a/tx_prelude/src/lib.rs b/tx_prelude/src/lib.rs index fbb6e03a78..e3a6648744 100644 --- a/tx_prelude/src/lib.rs +++ b/tx_prelude/src/lib.rs @@ -16,34 +16,33 @@ pub mod token; use core::slice; use std::marker::PhantomData; -pub use borsh::{BorshDeserialize, BorshSerialize}; -pub use borsh_ext; -use borsh_ext::BorshSerializeExt; use masp_primitives::transaction::Transaction; -pub use namada_core::ledger::governance::storage as gov_storage; -pub use namada_core::ledger::parameters::storage as parameters_storage; -pub use namada_core::ledger::storage::types::encode; -pub use namada_core::ledger::storage_api::{ - self, governance, iter_prefix, iter_prefix_bytes, Error, OptionExt, - ResultExt, StorageRead, StorageWrite, +pub use namada_core::borsh::{ + BorshDeserialize, BorshSerialize, BorshSerializeExt, }; -pub use namada_core::ledger::tx_env::TxEnv; -pub use namada_core::ledger::{eth_bridge, parameters}; -pub use namada_core::proto::{Section, Tx}; -use namada_core::types::account::AccountPublicKeysMap; +pub use namada_core::ledger::eth_bridge; pub use namada_core::types::address::Address; use namada_core::types::chain::CHAIN_ID_LENGTH; pub use namada_core::types::ethereum_events::EthAddress; use namada_core::types::internal::HostEnvResult; -use namada_core::types::key::common; +use namada_core::types::key::{common, AccountPublicKeysMap}; use namada_core::types::storage::TxIndex; pub use namada_core::types::storage::{ self, BlockHash, BlockHeight, Epoch, Header, BLOCK_HASH_LENGTH, }; -pub use namada_core::types::{eth_bridge_pool, *}; +pub use namada_core::types::{encode, eth_bridge_pool, *}; +pub use namada_governance::storage as gov_storage; pub use namada_macros::transaction; +pub use namada_parameters::storage as parameters_storage; +pub use namada_storage::{ + iter_prefix, iter_prefix_bytes, Error, OptionExt, ResultExt, StorageRead, + StorageWrite, +}; +pub use namada_tx::{Section, Tx}; +pub use namada_tx_env::TxEnv; use namada_vm_env::tx::*; use namada_vm_env::{read_from_buffer, read_key_val_bytes_from_buffer}; +pub use {namada_governance as governance, namada_parameters as parameters}; /// Log a string. The message will be printed at the `tracing::Level::Info`. pub fn log_string>(msg: T) { @@ -105,8 +104,8 @@ impl Ctx { } } -/// Result of `TxEnv`, `storage_api::StorageRead` or `storage_api::StorageWrite` -/// method call +/// Result of `TxEnv`, `namada_storage::StorageRead` or +/// `namada_storage::StorageWrite` method call pub type EnvResult = Result; /// Transaction result @@ -216,10 +215,14 @@ impl StorageRead for Ctx { )) } - fn get_tx_index(&self) -> Result { + fn get_tx_index(&self) -> Result { let tx_index = unsafe { namada_tx_get_tx_index() }; Ok(TxIndex(tx_index)) } + + fn get_pred_epochs(&self) -> namada_storage::Result { + todo!() + } } impl StorageWrite for Ctx { @@ -227,7 +230,7 @@ impl StorageWrite for Ctx { &mut self, key: &storage::Key, val: impl AsRef<[u8]>, - ) -> storage_api::Result<()> { + ) -> namada_storage::Result<()> { let key = key.to_string(); unsafe { namada_tx_write( @@ -240,7 +243,7 @@ impl StorageWrite for Ctx { Ok(()) } - fn delete(&mut self, key: &storage::Key) -> storage_api::Result<()> { + fn delete(&mut self, key: &storage::Key) -> namada_storage::Result<()> { let key = key.to_string(); unsafe { namada_tx_delete(key.as_ptr() as _, key.len() as _) }; Ok(()) diff --git a/tx_prelude/src/pgf.rs b/tx_prelude/src/pgf.rs index 43c19d352f..ad154add2e 100644 --- a/tx_prelude/src/pgf.rs +++ b/tx_prelude/src/pgf.rs @@ -1,4 +1,4 @@ -use namada_core::types::transaction::pgf::UpdateStewardCommission; +use namada_tx::data::pgf::UpdateStewardCommission; use super::*; @@ -6,13 +6,17 @@ pub fn update_steward_commission( ctx: &mut Ctx, data: UpdateStewardCommission, ) -> EnvResult<()> { - storage_api::pgf::update_commission(ctx, data.steward, data.commission)?; + namada_governance::pgf::storage::update_commission( + ctx, + data.steward, + data.commission, + )?; Ok(()) } pub fn remove_steward(ctx: &mut Ctx, data: &Address) -> EnvResult<()> { - storage_api::pgf::remove_steward(ctx, data)?; + namada_governance::pgf::storage::remove_steward(ctx, data)?; Ok(()) } diff --git a/tx_prelude/src/proof_of_stake.rs b/tx_prelude/src/proof_of_stake.rs index 3b7883361d..9a598079f5 100644 --- a/tx_prelude/src/proof_of_stake.rs +++ b/tx_prelude/src/proof_of_stake.rs @@ -2,7 +2,6 @@ use namada_core::types::dec::Dec; use namada_core::types::key::common; -use namada_core::types::transaction::pos::BecomeValidator; use namada_core::types::{key, token}; pub use namada_proof_of_stake::parameters::PosParams; use namada_proof_of_stake::storage::read_pos_params; @@ -14,6 +13,7 @@ use namada_proof_of_stake::{ redelegate_tokens, unbond_tokens, unjail_validator, withdraw_tokens, }; pub use namada_proof_of_stake::{parameters, types}; +use namada_tx::data::pos::BecomeValidator; use super::*; diff --git a/tx_prelude/src/token.rs b/tx_prelude/src/token.rs index 50cdef6c4e..331b402306 100644 --- a/tx_prelude/src/token.rs +++ b/tx_prelude/src/token.rs @@ -1,9 +1,10 @@ -pub use namada_core::ledger::masp_utils; use namada_core::types::address::Address; -use namada_core::types::token; -pub use namada_core::types::token::*; +use namada_proof_of_stake::token::storage_key::{ + balance_key, minted_balance_key, minter_key, +}; +pub use namada_token::*; -use super::*; +use crate::{log_string, Ctx, StorageRead, StorageWrite, TxResult}; #[allow(clippy::too_many_arguments)] /// A token transfer that can be used in a transaction. @@ -14,10 +15,10 @@ pub fn transfer( token: &Address, amount: DenominatedAmount, ) -> TxResult { - let amount = amount.to_amount(token, ctx)?; + let amount = denom_to_amount(amount, token, ctx)?; if amount != Amount::default() && src != dest { - let src_key = token::balance_key(token, src); - let dest_key = token::balance_key(token, dest); + let src_key = balance_key(token, src); + let dest_key = balance_key(token, dest); let src_bal: Option = ctx.read(&src_key)?; let mut src_bal = src_bal.unwrap_or_else(|| { log_string(format!("src {} has no balance", src_key)); @@ -41,8 +42,8 @@ pub fn undenominated_transfer( amount: Amount, ) -> TxResult { if amount != Amount::default() && src != dest { - let src_key = token::balance_key(token, src); - let dest_key = token::balance_key(token, dest); + let src_key = balance_key(token, src); + let dest_key = balance_key(token, dest); let src_bal: Option = ctx.read(&src_key)?; let mut src_bal = src_bal.unwrap_or_else(|| { log_string(format!("src {} has no balance", src_key)); @@ -65,18 +66,18 @@ pub fn mint( token: &Address, amount: Amount, ) -> TxResult { - let target_key = token::balance_key(token, target); + let target_key = balance_key(token, target); let mut target_bal: Amount = ctx.read(&target_key)?.unwrap_or_default(); target_bal.receive(&amount); - let minted_key = token::minted_balance_key(token); + let minted_key = minted_balance_key(token); let mut minted_bal: Amount = ctx.read(&minted_key)?.unwrap_or_default(); minted_bal.receive(&amount); ctx.write(&target_key, target_bal)?; ctx.write(&minted_key, minted_bal)?; - let minter_key = token::minter_key(token); + let minter_key = minter_key(token); ctx.write(&minter_key, minter)?; Ok(()) @@ -89,12 +90,12 @@ pub fn burn( token: &Address, amount: Amount, ) -> TxResult { - let target_key = token::balance_key(token, target); + let target_key = balance_key(token, target); let mut target_bal: Amount = ctx.read(&target_key)?.unwrap_or_default(); target_bal.spend(&amount); // burn the minted amount - let minted_key = token::minted_balance_key(token); + let minted_key = minted_balance_key(token); let mut minted_bal: Amount = ctx.read(&minted_key)?.unwrap_or_default(); minted_bal.spend(&amount); From b80571f0d595764d289e84f426d61b0c5977b9e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 3 Jan 2024 12:08:23 +0000 Subject: [PATCH 069/118] vp_prelude: fix build --- Cargo.lock | 8 +++++++- vp_prelude/Cargo.toml | 17 +++++++++++----- vp_prelude/src/lib.rs | 45 ++++++++++++++++++++++++++----------------- 3 files changed, 46 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 324010be83..4b3315d90f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4848,11 +4848,17 @@ name = "namada_vp_prelude" version = "0.29.0" dependencies = [ "borsh", - "borsh-ext", + "namada_account", "namada_core", + "namada_governance", "namada_macros", + "namada_parameters", "namada_proof_of_stake", + "namada_storage", + "namada_token", + "namada_tx", "namada_vm_env", + "namada_vp_env", "sha2 0.9.9", "thiserror", ] diff --git a/vp_prelude/Cargo.toml b/vp_prelude/Cargo.toml index 0766b39794..6de70ae749 100644 --- a/vp_prelude/Cargo.toml +++ b/vp_prelude/Cargo.toml @@ -16,11 +16,18 @@ version.workspace = true default = [] [dependencies] -namada_core = {path = "../core", default-features = false} -namada_macros = {path = "../macros"} -namada_proof_of_stake = {path = "../proof_of_stake", default-features = false} -namada_vm_env = {path = "../vm_env", default-features = false} +namada_account = { path = "../account" } +namada_core = { path = "../core", default-features = false } +namada_governance = { path = "../governance" } +namada_macros = { path = "../macros" } +namada_parameters = { path = "../parameters" } +namada_proof_of_stake = { path = "../proof_of_stake", default-features = false } +namada_storage = { path = "../storage" } +namada_token = { path = "../token" } +namada_tx = { path = "../tx" } +namada_vm_env = { path = "../vm_env", default-features = false } +namada_vp_env = { path = "../vp_env" } + borsh.workspace = true -borsh-ext.workspace = true sha2.workspace = true thiserror.workspace = true diff --git a/vp_prelude/src/lib.rs b/vp_prelude/src/lib.rs index cb0ff1cc27..190a64c16c 100644 --- a/vp_prelude/src/lib.rs +++ b/vp_prelude/src/lib.rs @@ -13,17 +13,9 @@ pub use std::collections::{BTreeSet, HashSet}; use std::convert::TryFrom; use std::marker::PhantomData; -pub use borsh::{BorshDeserialize, BorshSerialize}; -use borsh_ext::BorshSerializeExt; -pub use namada_core::ledger::governance::storage as gov_storage; -pub use namada_core::ledger::parameters; -pub use namada_core::ledger::pgf::storage as pgf_storage; -pub use namada_core::ledger::storage_api::{ - self, iter_prefix, iter_prefix_bytes, Error, OptionExt, ResultExt, - StorageRead, +pub use namada_core::borsh::{ + BorshDeserialize, BorshSerialize, BorshSerializeExt, }; -pub use namada_core::ledger::vp_env::VpEnv; -pub use namada_core::proto::{Section, Tx}; pub use namada_core::types::address::Address; use namada_core::types::chain::CHAIN_ID_LENGTH; use namada_core::types::hash::{Hash, HASH_LENGTH}; @@ -32,11 +24,21 @@ use namada_core::types::storage::{ BlockHash, BlockHeight, Epoch, Header, TxIndex, BLOCK_HASH_LENGTH, }; pub use namada_core::types::*; +pub use namada_governance::pgf::storage as pgf_storage; +pub use namada_governance::storage as gov_storage; pub use namada_macros::validity_predicate; +pub use namada_storage::{ + iter_prefix, iter_prefix_bytes, Error, OptionExt, ResultExt, StorageRead, +}; +pub use namada_tx::{Section, Tx}; use namada_vm_env::vp::*; use namada_vm_env::{read_from_buffer, read_key_val_bytes_from_buffer}; +pub use namada_vp_env::VpEnv; pub use sha2::{Digest, Sha256, Sha384, Sha512}; -pub use {borsh_ext, namada_proof_of_stake as proof_of_stake}; +pub use { + namada_account as account, namada_parameters as parameters, + namada_proof_of_stake as proof_of_stake, namada_token as token, +}; pub fn sha256(bytes: &[u8]) -> Hash { let digest = Sha256::digest(bytes); @@ -85,9 +87,8 @@ pub fn verify_signatures(ctx: &Ctx, tx: &Tx, owner: &Address) -> VpResult { parameters::max_signatures_per_transaction(&ctx.pre())?; let public_keys_index_map = - storage_api::account::public_keys_index_map(&ctx.pre(), owner)?; - let threshold = - storage_api::account::threshold(&ctx.pre(), owner)?.unwrap_or(1); + account::public_keys_index_map(&ctx.pre(), owner)?; + let threshold = account::threshold(&ctx.pre(), owner)?.unwrap_or(1); // Serialize parameters let max_signatures = max_signatures_per_transaction.serialize_to_vec(); @@ -206,7 +207,7 @@ pub struct CtxPostStorageRead<'a> { _ctx: &'a Ctx, } -/// Result of `VpEnv` or `storage_api::StorageRead` method call +/// Result of `VpEnv` or `namada_storage::StorageRead` method call pub type EnvResult = Result; /// Validity predicate result @@ -417,13 +418,17 @@ impl StorageRead for CtxPreStorageRead<'_> { get_block_epoch() } - fn get_tx_index(&self) -> Result { + fn get_tx_index(&self) -> Result { get_tx_index() } fn get_native_token(&self) -> Result { get_native_token() } + + fn get_pred_epochs(&self) -> namada_storage::Result { + todo!() + } } impl StorageRead for CtxPostStorageRead<'_> { @@ -487,13 +492,17 @@ impl StorageRead for CtxPostStorageRead<'_> { get_block_epoch() } - fn get_tx_index(&self) -> Result { + fn get_tx_index(&self) -> Result { get_tx_index() } fn get_native_token(&self) -> Result { get_native_token() } + + fn get_pred_epochs(&self) -> namada_storage::Result { + todo!() + } } fn iter_prefix_pre_impl( @@ -558,7 +567,7 @@ fn get_block_epoch() -> Result { Ok(Epoch(unsafe { namada_vp_get_block_epoch() })) } -fn get_tx_index() -> Result { +fn get_tx_index() -> Result { Ok(TxIndex(unsafe { namada_vp_get_tx_index() })) } From c5664863cd37976ef8ceb4a4205203296476e178 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 3 Jan 2024 12:14:53 +0000 Subject: [PATCH 070/118] add namada_token crate --- Cargo.lock | 10 ++++++++++ Cargo.toml | 1 + Makefile | 1 + token/Cargo.toml | 19 +++++++++++++++++++ token/src/lib.rs | 10 ++++++++++ 5 files changed, 41 insertions(+) create mode 100644 token/Cargo.toml create mode 100644 token/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 4b3315d90f..ba88fa2f51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4752,6 +4752,16 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "namada_token" +version = "0.28.1" +dependencies = [ + "namada_core", + "namada_shielded_token", + "namada_storage", + "namada_trans_token", +] + [[package]] name = "namada_trans_token" version = "0.29.0" diff --git a/Cargo.toml b/Cargo.toml index 49f0bab02d..3ade8d2f65 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ members = [ "storage", "test_utils", "tests", + "token", "trans_token", "tx", "tx_env", diff --git a/Makefile b/Makefile index ea4884a373..8a71cb679e 100644 --- a/Makefile +++ b/Makefile @@ -53,6 +53,7 @@ crates += namada_state crates += namada_storage crates += namada_test_utils crates += namada_tests +crates += namada_token crates += namada_trans_token crates += namada_tx crates += namada_tx_env diff --git a/token/Cargo.toml b/token/Cargo.toml new file mode 100644 index 0000000000..5b5138e014 --- /dev/null +++ b/token/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "namada_token" +description = "Namada transparent and shielded token" +resolver = "2" +authors.workspace = true +edition.workspace = true +documentation.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +namada_core = { path = "../core", default-features = false } +namada_shielded_token = { path = "../shielded_token" } +namada_storage = { path = "../storage" } +namada_trans_token = { path = "../trans_token" } diff --git a/token/src/lib.rs b/token/src/lib.rs new file mode 100644 index 0000000000..6bbaeb07fc --- /dev/null +++ b/token/src/lib.rs @@ -0,0 +1,10 @@ +//! Namada transparent and shielded token types, storage keys and storage +//! fns. + +pub use namada_shielded_token::*; +pub use namada_trans_token::*; + +pub mod storage_key { + pub use namada_shielded_token::storage_key::*; + pub use namada_trans_token::storage_key::*; +} From 38c842d8ae7a351db20dad646641eed7aa4dfe49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 3 Jan 2024 12:54:11 +0000 Subject: [PATCH 071/118] 0.29 rebase fixes --- Cargo.lock | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ba88fa2f51..42692fb29e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4250,7 +4250,7 @@ dependencies = [ [[package]] name = "namada_account" -version = "0.28.1" +version = "0.29.0" dependencies = [ "borsh", "namada_core", @@ -4477,7 +4477,7 @@ dependencies = [ [[package]] name = "namada_gas" -version = "0.28.1" +version = "0.29.0" dependencies = [ "borsh", "namada_core", @@ -4506,11 +4506,11 @@ dependencies = [ [[package]] name = "namada_ibc" -version = "0.28.1" +version = "0.29.0" dependencies = [ "borsh", - "ibc", - "ibc-derive", + "ibc 0.48.1", + "ibc-derive 0.4.0", "ics23", "namada_core", "namada_parameters", @@ -4549,7 +4549,7 @@ dependencies = [ [[package]] name = "namada_merkle_tree" -version = "0.28.1" +version = "0.29.0" dependencies = [ "borsh", "eyre", @@ -4562,7 +4562,7 @@ dependencies = [ [[package]] name = "namada_parameters" -version = "0.28.1" +version = "0.29.0" dependencies = [ "borsh", "namada_core", @@ -4651,7 +4651,7 @@ dependencies = [ [[package]] name = "namada_shielded_token" -version = "0.28.1" +version = "0.29.0" dependencies = [ "masp_primitives", "namada_core", @@ -4664,7 +4664,7 @@ dependencies = [ [[package]] name = "namada_state" -version = "0.28.1" +version = "0.29.0" dependencies = [ "borsh", "ics23", @@ -4685,7 +4685,7 @@ dependencies = [ [[package]] name = "namada_storage" -version = "0.28.1" +version = "0.29.0" dependencies = [ "itertools 0.10.5", "namada_core", @@ -4754,7 +4754,7 @@ dependencies = [ [[package]] name = "namada_token" -version = "0.28.1" +version = "0.29.0" dependencies = [ "namada_core", "namada_shielded_token", @@ -4772,7 +4772,7 @@ dependencies = [ [[package]] name = "namada_tx" -version = "0.28.1" +version = "0.29.0" dependencies = [ "ark-bls12-381", "borsh", @@ -4793,7 +4793,7 @@ dependencies = [ [[package]] name = "namada_tx_env" -version = "0.28.1" +version = "0.29.0" dependencies = [ "namada_core", "namada_storage", @@ -4832,7 +4832,7 @@ dependencies = [ [[package]] name = "namada_vote_ext" -version = "0.28.1" +version = "0.29.0" dependencies = [ "borsh", "data-encoding", @@ -4843,7 +4843,7 @@ dependencies = [ [[package]] name = "namada_vp_env" -version = "0.28.1" +version = "0.29.0" dependencies = [ "derivative", "masp_primitives", From ed3736ff353ca6fe912a0efaf779cbe5e276889b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 3 Jan 2024 13:56:46 +0000 Subject: [PATCH 072/118] token: add common `write_params` fn --- shielded_token/src/lib.rs | 4 +++- token/src/lib.rs | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/shielded_token/src/lib.rs b/shielded_token/src/lib.rs index 7fe8d56292..6e747b14da 100644 --- a/shielded_token/src/lib.rs +++ b/shielded_token/src/lib.rs @@ -1,6 +1,8 @@ //! Namada shielded token. pub mod conversion; -pub mod storage; +mod storage; pub mod storage_key; pub mod utils; + +pub use storage::*; diff --git a/token/src/lib.rs b/token/src/lib.rs index 6bbaeb07fc..4c0e444a66 100644 --- a/token/src/lib.rs +++ b/token/src/lib.rs @@ -8,3 +8,20 @@ pub mod storage_key { pub use namada_shielded_token::storage_key::*; pub use namada_trans_token::storage_key::*; } + +use namada_core::types::address::Address; +use namada_storage::{Result, StorageRead, StorageWrite}; +use namada_trans_token::Parameters; + +/// Initialize parameters for the token in storage during the genesis block. +pub fn write_params( + params: &Parameters, + storage: &mut S, + address: &Address, +) -> Result<()> +where + S: StorageRead + StorageWrite, +{ + namada_trans_token::write_params(params, storage, address)?; + namada_shielded_token::write_params(params, storage, address) +} From ee4c99265116470b1fa46764272bfeca6dd47ed5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 3 Jan 2024 16:00:57 +0000 Subject: [PATCH 073/118] sdk: fix build --- Cargo.lock | 9 ++++ account/src/lib.rs | 3 +- sdk/Cargo.toml | 61 +++++++++++++---------- sdk/src/args.rs | 52 +++++++++---------- sdk/src/error.rs | 2 +- sdk/src/eth_bridge/bridge_pool.rs | 17 ++++--- sdk/src/eth_bridge/mod.rs | 2 +- sdk/src/eth_bridge/validator_set.rs | 4 +- sdk/src/events/mod.rs | 4 +- sdk/src/lib.rs | 7 +-- sdk/src/masp.rs | 11 ++--- sdk/src/queries/mod.rs | 22 ++++----- sdk/src/queries/router.rs | 26 +++++----- sdk/src/queries/shell.rs | 73 +++++++++++++-------------- sdk/src/queries/shell/eth_bridge.rs | 77 ++++++++++++++--------------- sdk/src/queries/types.rs | 7 ++- sdk/src/queries/vp/governance.rs | 21 ++++---- sdk/src/queries/vp/pgf.rs | 25 +++++----- sdk/src/queries/vp/pos.rs | 65 ++++++++++++------------ sdk/src/queries/vp/token.rs | 10 ++-- sdk/src/rpc.rs | 23 +++++---- sdk/src/signing.rs | 29 +++++------ sdk/src/tx.rs | 38 +++++++------- state/src/lib.rs | 7 ++- 24 files changed, 297 insertions(+), 298 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 42692fb29e..6fff41381c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4619,10 +4619,19 @@ dependencies = [ "lazy_static", "masp_primitives", "masp_proofs", + "namada_account", "namada_core", "namada_ethereum_bridge", + "namada_governance", + "namada_ibc", + "namada_parameters", "namada_proof_of_stake", + "namada_state", + "namada_storage", "namada_test_utils", + "namada_token", + "namada_tx", + "namada_vote_ext", "num256", "orion", "owo-colors", diff --git a/account/src/lib.rs b/account/src/lib.rs index be393d9288..faff8c98ff 100644 --- a/account/src/lib.rs +++ b/account/src/lib.rs @@ -7,7 +7,8 @@ mod storage_key; use borsh::{BorshDeserialize, BorshSerialize}; use namada_core::types::address::Address; -use namada_core::types::key::{common, AccountPublicKeysMap}; +use namada_core::types::key::common; +pub use namada_core::types::key::AccountPublicKeysMap; use serde::{Deserialize, Serialize}; pub use storage::*; pub use storage_key::*; diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index f4d23d57b9..843a44b295 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -19,27 +19,17 @@ default = ["tendermint-rpc", "download-params"] multicore = ["masp_proofs/multicore"] -namada-sdk = [ - "tendermint-rpc", - "masp_primitives/transparent-inputs" -] +namada-sdk = ["tendermint-rpc", "masp_primitives/transparent-inputs"] std = ["fd-lock"] # tendermint-rpc support -tendermint-rpc = [ - "async-client", - "dep:tendermint-rpc", -] +tendermint-rpc = ["async-client", "dep:tendermint-rpc"] -wasm-runtime = [ - "namada_core/wasm-runtime", -] +wasm-runtime = ["namada_core/wasm-runtime"] # Enable queries support for an async client -async-client = [ - "async-trait", -] +async-client = ["async-trait"] async-send = [] @@ -56,7 +46,20 @@ testing = [ download-params = ["masp_proofs/download-params"] [dependencies] -async-trait = {version = "0.1.51", optional = true} +namada_account = { path = "../account" } +namada_core = { path = "../core" } +namada_ethereum_bridge = { path = "../ethereum_bridge", default-features = false } +namada_governance = { path = "../governance" } +namada_ibc = { path = "../ibc" } +namada_parameters = { path = "../parameters" } +namada_proof_of_stake = { path = "../proof_of_stake", default-features = false } +namada_state = { path = "../state" } +namada_storage = { path = "../storage" } +namada_token = { path = "../token" } +namada_tx = { path = "../tx" } +namada_vote_ext = { path = "../vote_ext" } + +async-trait = { version = "0.1.51", optional = true } bimap.workspace = true borsh.workspace = true borsh-ext.workspace = true @@ -68,18 +71,15 @@ ethers.workspace = true fd-lock = { workspace = true, optional = true } futures.workspace = true itertools.workspace = true -lazy_static.workspace= true +lazy_static.workspace = true masp_primitives.workspace = true masp_proofs.workspace = true -namada_core = {path = "../core", default-features = false, features = ["rand"]} -namada_ethereum_bridge = {path = "../ethereum_bridge", default-features = false} -namada_proof_of_stake = {path = "../proof_of_stake", default-features = false} num256.workspace = true orion.workspace = true owo-colors = "3.5.0" parse_duration = "2.1.1" paste.workspace = true -proptest = {workspace = true, optional = true} +proptest = { workspace = true, optional = true } prost.workspace = true rand.workspace = true rand_core.workspace = true @@ -88,7 +88,7 @@ serde.workspace = true serde_json.workspace = true sha2.workspace = true slip10_ed25519.workspace = true -tendermint-rpc = {workspace = true, optional = true} +tendermint-rpc = { workspace = true, optional = true } thiserror.workspace = true tiny-bip39.workspace = true tiny-hderive.workspace = true @@ -97,18 +97,25 @@ tracing.workspace = true zeroize.workspace = true [target.'cfg(not(target_family = "wasm"))'.dependencies] -tokio = {workspace = true, features = ["full"]} +tokio = { workspace = true, features = ["full"] } [target.'cfg(target_family = "wasm")'.dependencies] -tokio = {workspace = true, default-features = false, features = ["sync"]} +tokio = { workspace = true, default-features = false, features = ["sync"] } wasmtimer = "0.2.0" [dev-dependencies] assert_matches.workspace = true base58.workspace = true -namada_core = {path = "../core", default-features = false, features = ["rand", "testing"]} -namada_ethereum_bridge = {path = "../ethereum_bridge", default-features = false, features = ["testing"]} -namada_proof_of_stake = {path = "../proof_of_stake", default-features = false, features = ["testing"]} -namada_test_utils = {path = "../test_utils"} +namada_core = { path = "../core", default-features = false, features = [ + "rand", + "testing", +] } +namada_ethereum_bridge = { path = "../ethereum_bridge", default-features = false, features = [ + "testing", +] } +namada_proof_of_stake = { path = "../proof_of_stake", default-features = false, features = [ + "testing", +] } +namada_test_utils = { path = "../test_utils" } proptest.workspace = true tempfile.workspace = true diff --git a/sdk/src/args.rs b/sdk/src/args.rs index c12ecdf537..1fa2a7d323 100644 --- a/sdk/src/args.rs +++ b/sdk/src/args.rs @@ -4,9 +4,6 @@ use std::collections::HashMap; use std::path::PathBuf; use std::time::Duration as StdDuration; -use namada_core::ledger::governance::cli::onchain::{ - DefaultProposal, PgfFundingProposal, PgfStewardProposal, -}; use namada_core::types::address::Address; use namada_core::types::chain::ChainId; use namada_core::types::dec::Dec; @@ -16,8 +13,11 @@ use namada_core::types::key::{common, SchemeType}; use namada_core::types::masp::PaymentAddress; use namada_core::types::storage::Epoch; use namada_core::types::time::DateTimeUtc; -use namada_core::types::transaction::GasLimit; use namada_core::types::{storage, token}; +use namada_governance::cli::onchain::{ + DefaultProposal, PgfFundingProposal, PgfStewardProposal, +}; +use namada_tx::data::GasLimit; use serde::{Deserialize, Serialize}; use zeroize::Zeroizing; @@ -178,7 +178,7 @@ impl TxCustom { pub async fn build( &self, context: &impl Namada, - ) -> crate::error::Result<(crate::proto::Tx, SigningTxData)> { + ) -> crate::error::Result<(namada_tx::Tx, SigningTxData)> { tx::build_custom(context, self).await } } @@ -283,7 +283,7 @@ impl TxTransfer { pub async fn build( &mut self, context: &impl Namada, - ) -> crate::error::Result<(crate::proto::Tx, SigningTxData, Option)> + ) -> crate::error::Result<(namada_tx::Tx, SigningTxData, Option)> { tx::build_transfer(context, self).await } @@ -397,7 +397,7 @@ impl TxIbcTransfer { pub async fn build( &self, context: &impl Namada, - ) -> crate::error::Result<(crate::proto::Tx, SigningTxData, Option)> + ) -> crate::error::Result<(namada_tx::Tx, SigningTxData, Option)> { tx::build_ibc_transfer(context, self).await } @@ -486,7 +486,7 @@ impl InitProposal { pub async fn build( &self, context: &impl Namada, - ) -> crate::error::Result<(crate::proto::Tx, SigningTxData)> { + ) -> crate::error::Result<(namada_tx::Tx, SigningTxData)> { let current_epoch = rpc::query_epoch(context.client()).await?; let governance_parameters = rpc::query_governance_parameters(context.client()).await; @@ -642,7 +642,7 @@ impl VoteProposal { pub async fn build( &self, context: &impl Namada, - ) -> crate::error::Result<(crate::proto::Tx, SigningTxData)> { + ) -> crate::error::Result<(namada_tx::Tx, SigningTxData)> { let current_epoch = rpc::query_epoch(context.client()).await?; tx::build_vote_proposal(context, self, current_epoch).await } @@ -714,7 +714,7 @@ impl TxInitAccount { pub async fn build( &self, context: &impl Namada, - ) -> crate::error::Result<(crate::proto::Tx, SigningTxData)> { + ) -> crate::error::Result<(namada_tx::Tx, SigningTxData)> { tx::build_init_account(context, self).await } } @@ -868,7 +868,7 @@ impl TxUpdateAccount { pub async fn build( &self, context: &impl Namada, - ) -> crate::error::Result<(crate::proto::Tx, SigningTxData)> { + ) -> crate::error::Result<(namada_tx::Tx, SigningTxData)> { tx::build_update_account(context, self).await } } @@ -945,7 +945,7 @@ impl Bond { pub async fn build( &self, context: &impl Namada, - ) -> crate::error::Result<(crate::proto::Tx, SigningTxData)> { + ) -> crate::error::Result<(namada_tx::Tx, SigningTxData)> { tx::build_bond(context, self).await } } @@ -972,7 +972,7 @@ impl Unbond { &self, context: &impl Namada, ) -> crate::error::Result<( - crate::proto::Tx, + namada_tx::Tx, SigningTxData, Option<(Epoch, token::Amount)>, )> { @@ -1043,7 +1043,7 @@ impl Redelegate { pub async fn build( &self, context: &impl Namada, - ) -> crate::error::Result<(crate::proto::Tx, SigningTxData)> { + ) -> crate::error::Result<(namada_tx::Tx, SigningTxData)> { tx::build_redelegation(context, self).await } } @@ -1124,7 +1124,7 @@ impl RevealPk { pub async fn build( &self, context: &impl Namada, - ) -> crate::error::Result<(crate::proto::Tx, SigningTxData)> { + ) -> crate::error::Result<(namada_tx::Tx, SigningTxData)> { tx::build_reveal_pk(context, &self.tx, &self.public_key).await } } @@ -1207,7 +1207,7 @@ impl Withdraw { pub async fn build( &self, context: &impl Namada, - ) -> crate::error::Result<(crate::proto::Tx, SigningTxData)> { + ) -> crate::error::Result<(namada_tx::Tx, SigningTxData)> { tx::build_withdraw(context, self).await } } @@ -1243,7 +1243,7 @@ impl ClaimRewards { pub async fn build( &self, context: &impl Namada, - ) -> crate::error::Result<(crate::proto::Tx, SigningTxData)> { + ) -> crate::error::Result<(namada_tx::Tx, SigningTxData)> { tx::build_claim_rewards(context, self).await } } @@ -1375,7 +1375,7 @@ impl CommissionRateChange { pub async fn build( &self, context: &impl Namada, - ) -> crate::error::Result<(crate::proto::Tx, SigningTxData)> { + ) -> crate::error::Result<(namada_tx::Tx, SigningTxData)> { tx::build_validator_commission_change(context, self).await } } @@ -1435,7 +1435,7 @@ pub struct ConsensusKeyChange { // pub async fn build( // &self, // context: &impl Namada, -// ) -> crate::error::Result<(crate::proto::Tx, SigningTxData, +// ) -> crate::error::Result<(namada_tx::Tx, SigningTxData, // Option)> { // tx::build_change_consensus_key(context, self).await // } @@ -1494,7 +1494,7 @@ impl MetaDataChange { pub async fn build( &self, context: &impl Namada, - ) -> crate::error::Result<(crate::proto::Tx, SigningTxData)> { + ) -> crate::error::Result<(namada_tx::Tx, SigningTxData)> { tx::build_validator_metadata_change(context, self).await } } @@ -1549,7 +1549,7 @@ impl UpdateStewardCommission { pub async fn build( &self, context: &impl Namada, - ) -> crate::error::Result<(crate::proto::Tx, SigningTxData)> { + ) -> crate::error::Result<(namada_tx::Tx, SigningTxData)> { tx::build_update_steward_commission(context, self).await } } @@ -1597,7 +1597,7 @@ impl ResignSteward { pub async fn build( &self, context: &impl Namada, - ) -> crate::error::Result<(crate::proto::Tx, SigningTxData)> { + ) -> crate::error::Result<(namada_tx::Tx, SigningTxData)> { tx::build_resign_steward(context, self).await } } @@ -1645,7 +1645,7 @@ impl TxUnjailValidator { pub async fn build( &self, context: &impl Namada, - ) -> crate::error::Result<(crate::proto::Tx, SigningTxData)> { + ) -> crate::error::Result<(namada_tx::Tx, SigningTxData)> { tx::build_unjail_validator(context, self).await } } @@ -1693,7 +1693,7 @@ impl TxDeactivateValidator { pub async fn build( &self, context: &impl Namada, - ) -> crate::error::Result<(crate::proto::Tx, SigningTxData)> { + ) -> crate::error::Result<(namada_tx::Tx, SigningTxData)> { tx::build_deactivate_validator(context, self).await } } @@ -1741,7 +1741,7 @@ impl TxReactivateValidator { pub async fn build( &self, context: &impl Namada, - ) -> crate::error::Result<(crate::proto::Tx, SigningTxData)> { + ) -> crate::error::Result<(namada_tx::Tx, SigningTxData)> { tx::build_reactivate_validator(context, self).await } } @@ -2273,7 +2273,7 @@ impl EthereumBridgePool { pub async fn build( self, context: &impl Namada, - ) -> crate::error::Result<(crate::proto::Tx, SigningTxData)> { + ) -> crate::error::Result<(namada_tx::Tx, SigningTxData)> { bridge_pool::build_bridge_pool_tx(context, self).await } } diff --git a/sdk/src/error.rs b/sdk/src/error.rs index 18be902d10..0216e3a059 100644 --- a/sdk/src/error.rs +++ b/sdk/src/error.rs @@ -1,11 +1,11 @@ //! Generic Error Type for all of the Shared Crate -use namada_core::proto::Tx; use namada_core::types::address::Address; use namada_core::types::dec::Dec; use namada_core::types::ethereum_events::EthAddress; use namada_core::types::storage; use namada_core::types::storage::Epoch; +use namada_tx::Tx; use prost::EncodeError; use tendermint_rpc::Error as RpcError; use thiserror::Error; diff --git a/sdk/src/eth_bridge/bridge_pool.rs b/sdk/src/eth_bridge/bridge_pool.rs index fa6eba1d02..98c394019d 100644 --- a/sdk/src/eth_bridge/bridge_pool.rs +++ b/sdk/src/eth_bridge/bridge_pool.rs @@ -9,17 +9,19 @@ use borsh_ext::BorshSerializeExt; use ethbridge_bridge_contract::Bridge; use ethers::providers::Middleware; use futures::future::FutureExt; -use namada_core::ledger::eth_bridge::storage::bridge_pool::get_pending_key; -use namada_core::ledger::eth_bridge::storage::wrapped_erc20s; use namada_core::types::address::{Address, InternalAddress}; use namada_core::types::eth_abi::Encode; use namada_core::types::eth_bridge_pool::{ - GasFee, PendingTransfer, TransferToEthereum, TransferToEthereumKind, + erc20_token_address, GasFee, PendingTransfer, TransferToEthereum, + TransferToEthereumKind, }; use namada_core::types::ethereum_events::EthAddress; use namada_core::types::keccak::KeccakHash; -use namada_core::types::token::{balance_key, Amount}; use namada_core::types::voting_power::FractionalVotingPower; +use namada_ethereum_bridge::storage::bridge_pool::get_pending_key; +use namada_token::storage_key::balance_key; +use namada_token::Amount; +use namada_tx::Tx; use owo_colors::OwoColorize; use serde::Serialize; @@ -32,7 +34,6 @@ use crate::error::{ use crate::eth_bridge::ethers::abi::AbiDecode; use crate::internal_macros::echo_error; use crate::io::Io; -use crate::proto::Tx; use crate::queries::{ Client, GenBridgePoolProofReq, GenBridgePoolProofRsp, TransferToErcArgs, TransferToEthereumStatus, RPC, @@ -124,7 +125,7 @@ async fn validate_bridge_pool_tx( fee_payer: Option
, fee_token: Address, ) -> Result { - let token_addr = wrapped_erc20s::token(&asset); + let token_addr = erc20_token_address(&asset); let validate_token_amount = validate_amount(context, amount, &token_addr, force).map(|result| { result.map_err(|e| { @@ -207,7 +208,7 @@ async fn validate_bridge_pool_tx( EthereumBridgeError::InvalidFeeToken(transfer.gas_fee.token), )); } - fee_token if fee_token == &wrapped_erc20s::token(&wnam_addr) => { + fee_token if fee_token == &erc20_token_address(&wnam_addr) => { return Err(Error::EthereumBridge( EthereumBridgeError::InvalidFeeToken(transfer.gas_fee.token), )); @@ -733,7 +734,7 @@ mod recommendations { use namada_core::types::ethereum_events::Uint as EthUint; use namada_core::types::storage::BlockHeight; use namada_core::types::uint::{self, Uint, I256}; - use namada_core::types::vote_extensions::validator_set_update::{ + use namada_vote_ext::validator_set_update::{ EthAddrBook, VotingPowersMap, VotingPowersMapExt, }; diff --git a/sdk/src/eth_bridge/mod.rs b/sdk/src/eth_bridge/mod.rs index cfcc750444..9107798938 100644 --- a/sdk/src/eth_bridge/mod.rs +++ b/sdk/src/eth_bridge/mod.rs @@ -8,11 +8,11 @@ use std::ops::ControlFlow; pub use ethers; use ethers::providers::Middleware; use itertools::Either; -pub use namada_core::ledger::eth_bridge::storage::wrapped_erc20s; pub use namada_core::ledger::eth_bridge::{ADDRESS, INTERNAL_ADDRESS}; pub use namada_core::types::ethereum_structs as structs; pub use namada_ethereum_bridge::storage::eth_bridge_queries::*; pub use namada_ethereum_bridge::storage::parameters::*; +pub use namada_ethereum_bridge::storage::wrapped_erc20s; pub use namada_ethereum_bridge::*; use num256::Uint256; diff --git a/sdk/src/eth_bridge/validator_set.rs b/sdk/src/eth_bridge/validator_set.rs index e92e5c545e..c11b9b62c6 100644 --- a/sdk/src/eth_bridge/validator_set.rs +++ b/sdk/src/eth_bridge/validator_set.rs @@ -14,10 +14,10 @@ use namada_core::hints; use namada_core::types::eth_abi::EncodeCell; use namada_core::types::ethereum_events::EthAddress; use namada_core::types::storage::Epoch; -use namada_core::types::vote_extensions::validator_set_update::{ +use namada_ethereum_bridge::storage::proof::EthereumProof; +use namada_vote_ext::validator_set_update::{ ValidatorSetArgs, VotingPowersMap, }; -use namada_ethereum_bridge::storage::proof::EthereumProof; use super::{block_on_eth_sync, eth_sync_or, eth_sync_or_exit, BlockOnEthSync}; use crate::control_flow::install_shutdown_signal; diff --git a/sdk/src/events/mod.rs b/sdk/src/events/mod.rs index 8c5dc8573f..646fd1b8d3 100644 --- a/sdk/src/events/mod.rs +++ b/sdk/src/events/mod.rs @@ -10,7 +10,7 @@ use std::str::FromStr; use borsh::{BorshDeserialize, BorshSerialize}; use namada_core::types::ethereum_structs::{BpTransferStatus, EthBridgeEvent}; use namada_core::types::ibc::IbcEvent; -use namada_core::types::transaction::TxType; +use namada_tx::data::TxType; use serde_json::Value; // use crate::ledger::governance::utils::ProposalEvent; @@ -126,7 +126,7 @@ impl FromStr for EventType { impl Event { /// Creates a new event with the hash and height of the transaction /// already filled in - pub fn new_tx_event(tx: &crate::proto::Tx, height: u64) -> Self { + pub fn new_tx_event(tx: &namada_tx::Tx, height: u64) -> Self { let mut event = match tx.header().tx_type { TxType::Wrapper(_) => { let mut event = Event { diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index 65b4a45cf4..88a7b9edf4 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -1,6 +1,8 @@ extern crate alloc; -pub use namada_core::{ibc, proto, tendermint, tendermint_proto}; +pub use namada_core::{ibc, tendermint, tendermint_proto}; +pub use namada_tx::proto; +use namada_tx::Tx; #[cfg(feature = "tendermint-rpc")] pub use tendermint_rpc; pub use { @@ -42,12 +44,11 @@ use namada_core::types::ethereum_events::EthAddress; use namada_core::types::key::*; use namada_core::types::masp::{TransferSource, TransferTarget}; use namada_core::types::token; -use namada_core::types::transaction::GasLimit; +use namada_tx::data::wrapper::GasLimit; use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use crate::io::Io; use crate::masp::{ShieldedContext, ShieldedUtils}; -use crate::proto::Tx; use crate::rpc::{ denominate_amount, format_denominated_amount, query_native_token, }; diff --git a/sdk/src/masp.rs b/sdk/src/masp.rs index 576f311cb1..714fa13e66 100644 --- a/sdk/src/masp.rs +++ b/sdk/src/masp.rs @@ -57,10 +57,10 @@ use namada_core::types::masp::{ use namada_core::types::storage::{BlockHeight, Epoch, Key, KeySeg, TxIndex}; use namada_core::types::time::{DateTimeUtc, DurationSecs}; use namada_core::types::token; -use namada_core::types::token::{ - Change, MaspDenom, Transfer, HEAD_TX_KEY, PIN_KEY_PREFIX, TX_KEY_PREFIX, -}; -use namada_core::types::transaction::WrapperTx; +use namada_token::storage_key::{HEAD_TX_KEY, PIN_KEY_PREFIX, TX_KEY_PREFIX}; +use namada_token::{Change, MaspDenom, Transfer}; +use namada_tx::data::WrapperTx; +use namada_tx::Tx; use rand_core::{CryptoRng, OsRng, RngCore}; use ripemd::Digest as RipemdDigest; use sha2::Digest; @@ -70,7 +70,6 @@ use thiserror::Error; use crate::error::EncodingError; use crate::error::{Error, PinnedBalanceError, QueryError}; use crate::io::Io; -use crate::proto::Tx; use crate::queries::Client; use crate::rpc::{query_conversion, query_storage_value}; use crate::tendermint_rpc::query::Query; @@ -1604,7 +1603,7 @@ impl ShieldedContext { expiration.0.signed_duration_since(current_time.0); let max_expected_time_per_block_key = - namada_core::ledger::parameters::storage::get_max_expected_time_per_block_key(); + namada_parameters::storage::get_max_expected_time_per_block_key(); let max_block_time = crate::rpc::query_storage_value::<_, DurationSecs>( context.client(), diff --git a/sdk/src/queries/mod.rs b/sdk/src/queries/mod.rs index 4dbc5173b8..1813226039 100644 --- a/sdk/src/queries/mod.rs +++ b/sdk/src/queries/mod.rs @@ -2,10 +2,8 @@ //! defined via `router!` macro. // Re-export to show in rustdoc! -use namada_core::ledger::storage::traits::StorageHasher; -use namada_core::ledger::storage::{DBIter, DB}; -use namada_core::ledger::storage_api; use namada_core::types::storage::BlockHeight; +use namada_state::{DBIter, StorageHasher, DB}; pub use shell::Shell; use shell::SHELL; pub use types::{ @@ -18,7 +16,7 @@ pub use self::shell::eth_bridge::{ Erc20FlowControl, GenBridgePoolProofReq, GenBridgePoolProofRsp, TransferToErcArgs, TransferToEthereumStatus, }; -use crate::{MaybeSend, MaybeSync}; +use crate::MaybeSend; #[macro_use] mod router; @@ -40,7 +38,7 @@ router! {RPC, pub fn handle_path( ctx: RequestCtx<'_, D, H, V, T>, request: &RequestQuery, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -55,7 +53,7 @@ where pub fn require_latest_height( ctx: &RequestCtx<'_, D, H, V, T>, request: &RequestQuery, -) -> storage_api::Result<()> +) -> namada_storage::Result<()> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -64,7 +62,7 @@ where && request.height.value() != ctx.wl_storage.storage.get_last_block_height().0 { - return Err(storage_api::Error::new_const( + return Err(namada_storage::Error::new_const( "This query doesn't support arbitrary block heights, only the \ latest committed block height ('0' can be used as a special \ value that means the latest block height)", @@ -75,9 +73,9 @@ where /// For queries that do not support proofs, check that proof is not requested, /// otherwise return an error. -pub fn require_no_proof(request: &RequestQuery) -> storage_api::Result<()> { +pub fn require_no_proof(request: &RequestQuery) -> namada_storage::Result<()> { if request.prove { - return Err(storage_api::Error::new_const( + return Err(namada_storage::Error::new_const( "This query doesn't support proofs", )); } @@ -86,9 +84,9 @@ pub fn require_no_proof(request: &RequestQuery) -> storage_api::Result<()> { /// For queries that don't use request data, require that there are no data /// attached. -pub fn require_no_data(request: &RequestQuery) -> storage_api::Result<()> { +pub fn require_no_data(request: &RequestQuery) -> namada_storage::Result<()> { if !request.data.is_empty() { - return Err(storage_api::Error::new_const( + return Err(namada_storage::Error::new_const( "This query doesn't accept request data", )); } @@ -158,7 +156,7 @@ mod testing { #[cfg_attr(not(feature = "async-send"), async_trait::async_trait(?Send))] impl Client for TestClient where - RPC: Router + MaybeSync, + RPC: Router + crate::MaybeSync, { type Error = std::io::Error; diff --git a/sdk/src/queries/router.rs b/sdk/src/queries/router.rs index d27e612d66..bf55143a2c 100644 --- a/sdk/src/queries/router.rs +++ b/sdk/src/queries/router.rs @@ -759,7 +759,7 @@ macro_rules! router_type { /// Handler functions used in the patterns should have the expected signature: /// ```rust,ignore /// fn handler(ctx: RequestCtx<'_, D, H>, args ...) -/// -> storage_api::Result +/// -> namada_storage::Result /// where /// D: 'static + DB + for<'iter> DBIter<'iter> + Sync, /// H: 'static + StorageHasher + Sync; @@ -769,7 +769,7 @@ macro_rules! router_type { /// `(with_options $handler)` and then the expected signature is: /// ```rust,ignore /// fn handler(ctx: RequestCtx<'_, D, H>, request: &RequestQuery, args -/// ...) -> storage_api::Result> +/// ...) -> namada_storage::Result> /// where /// D: 'static + DB + for<'iter> DBIter<'iter> + Sync, /// H: 'static + StorageHasher + Sync; @@ -791,14 +791,14 @@ macro_rules! router { ctx: $crate::queries::RequestCtx<'_, D, H, V, T>, request: &$crate::queries::RequestQuery, start: usize - ) -> namada_core::ledger::storage_api::Result<$crate::queries::EncodedResponseQuery> + ) -> namada_storage::Result<$crate::queries::EncodedResponseQuery> where - D: 'static + namada_core::ledger::storage::DB + for<'iter> namada_core::ledger::storage::DBIter<'iter> + Sync, - H: 'static + namada_core::ledger::storage::StorageHasher + Sync, + D: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter> + Sync, + H: 'static + namada_state::StorageHasher + Sync, { // Import for `.into_storage_result()` - use namada_core::ledger::storage_api::ResultExt; + use namada_storage::ResultExt; // Import helper from this crate used inside the macros use $crate::queries::router::find_next_slash_index; @@ -835,10 +835,9 @@ macro_rules! router { #[cfg(test)] mod test_rpc_handlers { use borsh_ext::BorshSerializeExt; - use namada_core::ledger::storage::{DBIter, StorageHasher, DB}; - use namada_core::ledger::storage_api; use namada_core::types::storage::Epoch; use namada_core::types::token; + use namada_state::{DBIter, StorageHasher, DB}; use crate::queries::{ EncodedResponseQuery, RequestCtx, RequestQuery, ResponseQuery, @@ -857,7 +856,7 @@ mod test_rpc_handlers { pub fn $name( _ctx: RequestCtx<'_, D, H, V, T>, $( $( $param: $param_ty ),* )? - ) -> storage_api::Result + ) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -906,7 +905,7 @@ mod test_rpc_handlers { a1: token::DenominatedAmount, a2: token::DenominatedAmount, a3: Option, - ) -> storage_api::Result + ) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -926,7 +925,7 @@ mod test_rpc_handlers { a2: token::DenominatedAmount, a3: Option, a4: Option, - ) -> storage_api::Result + ) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -944,7 +943,7 @@ mod test_rpc_handlers { pub fn c( _ctx: RequestCtx<'_, D, H, V, T>, _request: &RequestQuery, - ) -> storage_api::Result + ) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -1001,7 +1000,6 @@ mod test_rpc { #[cfg(test)] mod test { - use namada_core::ledger::storage_api; use namada_core::tendermint::block; use namada_core::types::storage::Epoch; use namada_core::types::token; @@ -1013,7 +1011,7 @@ mod test { /// Test all the possible paths in `TEST_RPC` router. #[tokio::test] - async fn test_router_macro() -> storage_api::Result<()> { + async fn test_router_macro() -> namada_storage::Result<()> { let client = TestClient::new(TEST_RPC); // Test request with an invalid path diff --git a/sdk/src/queries/shell.rs b/sdk/src/queries/shell.rs index 813348dacb..a3c64b9b38 100644 --- a/sdk/src/queries/shell.rs +++ b/sdk/src/queries/shell.rs @@ -7,19 +7,18 @@ use borsh_ext::BorshSerializeExt; use masp_primitives::asset_type::AssetType; use masp_primitives::merkle_tree::MerklePath; use masp_primitives::sapling::Node; +use namada_account::{Account, AccountPublicKeysMap}; use namada_core::hints; -use namada_core::ledger::storage::traits::StorageHasher; -use namada_core::ledger::storage::{DBIter, LastBlock, DB}; -use namada_core::ledger::storage_api::{self, ResultExt, StorageRead}; -use namada_core::types::account::{Account, AccountPublicKeysMap}; use namada_core::types::address::Address; use namada_core::types::hash::Hash; use namada_core::types::storage::{ self, BlockHeight, BlockResults, Epoch, KeySeg, PrefixValue, }; use namada_core::types::token::MaspDenom; +use namada_state::{DBIter, LastBlock, StorageHasher, DB}; +use namada_storage::{self, ResultExt, StorageRead}; #[cfg(any(test, feature = "async-client"))] -use namada_core::types::transaction::TxResult; +use namada_tx::data::TxResult; use self::eth_bridge::{EthBridge, ETH_BRIDGE}; use crate::events::log::dumb_queries; @@ -115,7 +114,7 @@ router! {SHELL, fn dry_run_tx( _ctx: RequestCtx<'_, D, H, V, T>, _request: &RequestQuery, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -126,7 +125,7 @@ where /// Query to read block results from storage pub fn read_results( ctx: RequestCtx<'_, D, H, V, T>, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -138,19 +137,19 @@ where ]; for (key, value, _gas) in iter { let key = u64::parse(key.clone()).map_err(|_| { - storage_api::Error::new(std::io::Error::new( + namada_storage::Error::new(std::io::Error::new( std::io::ErrorKind::NotFound, format!("expected integer for block height {}", key), )) })?; let value = BlockResults::try_from_slice(&value).map_err(|_| { - storage_api::Error::new(std::io::Error::new( + namada_storage::Error::new(std::io::Error::new( std::io::ErrorKind::InvalidData, "expected BlockResults bytes", )) })?; let idx: usize = key.try_into().map_err(|_| { - storage_api::Error::new(std::io::Error::new( + namada_storage::Error::new(std::io::Error::new( std::io::ErrorKind::InvalidData, "expected block height to fit into usize", )) @@ -163,7 +162,7 @@ where /// Query to read the conversion state fn read_conversions( ctx: RequestCtx<'_, D, H, V, T>, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -184,7 +183,7 @@ where fn read_conversion( ctx: RequestCtx<'_, D, H, V, T>, asset_type: AssetType, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -207,7 +206,7 @@ where ctx.wl_storage.storage.conversion_state.tree.path(*pos), )) } else { - Err(storage_api::Error::new(std::io::Error::new( + Err(namada_storage::Error::new(std::io::Error::new( std::io::ErrorKind::NotFound, format!("No conversion found for asset type: {}", asset_type), ))) @@ -217,7 +216,7 @@ where /// Query to read the tokens that earn masp rewards. fn masp_reward_tokens( ctx: RequestCtx<'_, D, H, V, T>, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -227,7 +226,7 @@ where fn epoch( ctx: RequestCtx<'_, D, H, V, T>, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -238,7 +237,7 @@ where fn native_token( ctx: RequestCtx<'_, D, H, V, T>, -) -> storage_api::Result
+) -> namada_storage::Result
where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -250,7 +249,7 @@ where fn epoch_at_height( ctx: RequestCtx<'_, D, H, V, T>, height: BlockHeight, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -260,7 +259,7 @@ where fn last_block( ctx: RequestCtx<'_, D, H, V, T>, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -276,7 +275,7 @@ fn storage_value( ctx: RequestCtx<'_, D, H, V, T>, request: &RequestQuery, storage_key: storage::Key, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -295,7 +294,7 @@ where if let Some(past_height_limit) = ctx.storage_read_past_height_limit { if queried_height + past_height_limit < last_committed_height { - return Err(storage_api::Error::new(std::io::Error::new( + return Err(namada_storage::Error::new(std::io::Error::new( std::io::ErrorKind::InvalidInput, format!( "Cannot query more than {past_height_limit} blocks in the \ @@ -353,15 +352,15 @@ fn storage_prefix( ctx: RequestCtx<'_, D, H, V, T>, request: &RequestQuery, storage_key: storage::Key, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { require_latest_height(&ctx, request)?; - let iter = storage_api::iter_prefix_bytes(ctx.wl_storage, &storage_key)?; - let data: storage_api::Result> = iter + let iter = namada_storage::iter_prefix_bytes(ctx.wl_storage, &storage_key)?; + let data: namada_storage::Result> = iter .map(|iter_result| { let (key, value) = iter_result?; Ok(PrefixValue { key, value }) @@ -408,7 +407,7 @@ where fn storage_has_key( ctx: RequestCtx<'_, D, H, V, T>, storage_key: storage::Key, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -420,7 +419,7 @@ where fn accepted( ctx: RequestCtx<'_, D, H, V, T>, tx_hash: Hash, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -437,7 +436,7 @@ where fn applied( ctx: RequestCtx<'_, D, H, V, T>, tx_hash: Hash, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -455,7 +454,7 @@ fn ibc_client_update( ctx: RequestCtx<'_, D, H, V, T>, client_id: ClientId, consensus_height: BlockHeight, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -480,7 +479,7 @@ fn ibc_packet( destination_port: PortId, destination_channel: ChannelId, sequence: Sequence, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -504,18 +503,16 @@ where fn account( ctx: RequestCtx<'_, D, H, V, T>, owner: Address, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - let account_exists = storage_api::account::exists(ctx.wl_storage, &owner)?; + let account_exists = namada_account::exists(ctx.wl_storage, &owner)?; if account_exists { - let public_keys = - storage_api::account::public_keys(ctx.wl_storage, &owner)?; - let threshold = - storage_api::account::threshold(ctx.wl_storage, &owner)?; + let public_keys = namada_account::public_keys(ctx.wl_storage, &owner)?; + let threshold = namada_account::threshold(ctx.wl_storage, &owner)?; Ok(Some(Account { public_keys_map: AccountPublicKeysMap::from_iter(public_keys), @@ -530,13 +527,12 @@ where fn revealed( ctx: RequestCtx<'_, D, H, V, T>, owner: Address, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - let public_keys = - storage_api::account::public_keys(ctx.wl_storage, &owner)?; + let public_keys = namada_account::public_keys(ctx.wl_storage, &owner)?; Ok(!public_keys.is_empty()) } @@ -544,6 +540,7 @@ where #[cfg(test)] mod test { use namada_core::types::{address, token}; + use namada_token::storage_key::balance_key; use crate::queries::RPC; @@ -554,7 +551,7 @@ mod test { let token_addr = address::testing::established_address_1(); let owner = address::testing::established_address_2(); - let key = token::balance_key(&token_addr, &owner); + let key = balance_key(&token_addr, &owner); let path = RPC.shell().storage_value_path(&key); assert_eq!(format!("/shell/value/{}", key), path); diff --git a/sdk/src/queries/shell/eth_bridge.rs b/sdk/src/queries/shell/eth_bridge.rs index b113eef1f8..4d59564159 100644 --- a/sdk/src/queries/shell/eth_bridge.rs +++ b/sdk/src/queries/shell/eth_bridge.rs @@ -7,12 +7,6 @@ use std::str::FromStr; use borsh::{BorshDeserialize, BorshSerialize}; use borsh_ext::BorshSerializeExt; use namada_core::hints; -use namada_core::ledger::eth_bridge::storage::bridge_pool::get_key_from_hash; -use namada_core::ledger::storage::merkle_tree::StoreRef; -use namada_core::ledger::storage::{DBIter, StorageHasher, StoreType, DB}; -use namada_core::ledger::storage_api::{ - self, CustomError, ResultExt, StorageRead, -}; use namada_core::types::address::Address; use namada_core::types::eth_abi::{Encode, EncodeCell}; use namada_core::types::eth_bridge_pool::{ @@ -23,16 +17,13 @@ use namada_core::types::ethereum_events::{ }; use namada_core::types::ethereum_structs; use namada_core::types::keccak::KeccakHash; -use namada_core::types::storage::MembershipProof::BridgePool; use namada_core::types::storage::{BlockHeight, DbKeySeg, Epoch, Key}; use namada_core::types::token::Amount; -use namada_core::types::vote_extensions::validator_set_update::{ - ValidatorSetArgs, VotingPowersMap, -}; use namada_core::types::voting_power::FractionalVotingPower; use namada_ethereum_bridge::protocol::transactions::votes::{ EpochedVotingPower, EpochedVotingPowerExt, }; +use namada_ethereum_bridge::storage::bridge_pool::get_key_from_hash; use namada_ethereum_bridge::storage::eth_bridge_queries::EthBridgeQueries; use namada_ethereum_bridge::storage::parameters::UpgradeableContract; use namada_ethereum_bridge::storage::proof::{sort_sigs, EthereumProof}; @@ -41,6 +32,12 @@ use namada_ethereum_bridge::storage::{ bridge_contract_key, native_erc20_key, vote_tallies, }; use namada_proof_of_stake::pos_queries::PosQueries; +use namada_state::MembershipProof::BridgePool; +use namada_state::{DBIter, StorageHasher, StoreRef, StoreType, DB}; +use namada_storage::{self, CustomError, ResultExt, StorageRead}; +use namada_vote_ext::validator_set_update::{ + ValidatorSetArgs, VotingPowersMap, +}; use serde::{Deserialize, Serialize}; use crate::eth_bridge::ethers::abi::AbiDecode; @@ -222,7 +219,7 @@ router! {ETH_BRIDGE, fn pending_eth_transfer_status( ctx: RequestCtx<'_, D, H, V, T>, request: &RequestQuery, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -339,7 +336,7 @@ where fn get_erc20_flow_control( ctx: RequestCtx<'_, D, H, V, T>, asset: EthAddress, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -363,14 +360,14 @@ where fn read_contract( key: &Key, ctx: RequestCtx<'_, D, H, V, U>, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, T: BorshDeserialize, { let Some(contract) = StorageRead::read(ctx.wl_storage, key)? else { - return Err(storage_api::Error::SimpleMessage( + return Err(namada_storage::Error::SimpleMessage( "Failed to read contract: The Ethereum bridge \ storage is not initialized", )); @@ -383,7 +380,7 @@ where #[inline] fn read_bridge_contract( ctx: RequestCtx<'_, D, H, V, T>, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -396,7 +393,7 @@ where #[inline] fn read_native_erc20_contract( ctx: RequestCtx<'_, D, H, V, T>, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -408,7 +405,7 @@ where /// pool. fn read_ethereum_bridge_pool( ctx: RequestCtx<'_, D, H, V, T>, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -423,7 +420,7 @@ where /// pool covered by the latest signed root. fn read_signed_ethereum_bridge_pool( ctx: RequestCtx<'_, D, H, V, T>, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -433,7 +430,7 @@ where .wl_storage .ethbridge_queries() .get_signed_bridge_pool_root() - .ok_or(storage_api::Error::SimpleMessage( + .ok_or(namada_storage::Error::SimpleMessage( "No signed root for the Ethereum bridge pool exists in storage.", )) .into_storage_result()?; @@ -483,7 +480,7 @@ where fn generate_bridge_pool_proof( ctx: RequestCtx<'_, D, H, V, T>, request: &RequestQuery, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -499,7 +496,7 @@ where .wl_storage .ethbridge_queries() .get_signed_bridge_pool_root() - .ok_or(storage_api::Error::SimpleMessage( + .ok_or(namada_storage::Error::SimpleMessage( "No signed root for the Ethereum bridge pool exists in \ storage.", )) @@ -510,7 +507,7 @@ where let latest_bp_nonce = ctx.wl_storage.ethbridge_queries().get_bridge_pool_nonce(); if latest_bp_nonce != signed_root.data.1 { - return Err(storage_api::Error::Custom(CustomError( + return Err(namada_storage::Error::Custom(CustomError( format!( "Mismatch between the nonce in the Bridge pool root proof \ ({}) and the latest Bridge pool nonce in storage ({})", @@ -528,7 +525,7 @@ where .into_storage_result()?; // from the hashes of the transfers, get the actual values. let mut missing_hashes = vec![]; - let (keys, values): (Vec<_>, Vec<_>) = transfer_hashes + let (keys, values): (Vec<_>, Vec>) = transfer_hashes .iter() .filter_map(|hash| { let key = get_key_from_hash(hash); @@ -542,7 +539,7 @@ where }) .unzip(); if !missing_hashes.is_empty() { - return Err(storage_api::Error::Custom(CustomError( + return Err(namada_storage::Error::Custom(CustomError( format!( "One or more of the provided hashes had no corresponding \ transfer in storage: {:?}", @@ -601,10 +598,10 @@ where }) } Ok(_) => unreachable!(), - Err(e) => Err(storage_api::Error::new(e)), + Err(e) => Err(namada_storage::Error::new(e)), } } else { - Err(storage_api::Error::SimpleMessage( + Err(namada_storage::Error::SimpleMessage( "Could not deserialize transfers", )) } @@ -615,7 +612,7 @@ where /// backing each `TransferToEthereum` event. fn transfer_to_ethereum_progress( ctx: RequestCtx<'_, D, H, V, T>, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -690,13 +687,13 @@ where fn read_valset_upd_proof( ctx: RequestCtx<'_, D, H, V, T>, epoch: Epoch, -) -> storage_api::Result>> +) -> namada_storage::Result>> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { if epoch.0 == 0 { - return Err(storage_api::Error::Custom(CustomError( + return Err(namada_storage::Error::Custom(CustomError( "Validator set update proofs should only be requested from epoch \ 1 onwards" .into(), @@ -704,7 +701,7 @@ where } let current_epoch = ctx.wl_storage.storage.last_epoch; if epoch > current_epoch.next() { - return Err(storage_api::Error::Custom(CustomError( + return Err(namada_storage::Error::Custom(CustomError( format!( "Requesting validator set update proof for {epoch:?}, but the \ last installed epoch is still {current_epoch:?}" @@ -714,7 +711,7 @@ where } if !ctx.wl_storage.ethbridge_queries().valset_upd_seen(epoch) { - return Err(storage_api::Error::Custom(CustomError( + return Err(namada_storage::Error::Custom(CustomError( format!( "Validator set update proof is not yet available for the \ queried epoch: {epoch:?}" @@ -740,14 +737,14 @@ where fn read_bridge_valset( ctx: RequestCtx<'_, D, H, V, T>, epoch: Epoch, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { let current_epoch = ctx.wl_storage.storage.last_epoch; if epoch > current_epoch.next() { - Err(storage_api::Error::Custom(CustomError( + Err(namada_storage::Error::Custom(CustomError( format!( "Requesting Bridge validator set at {epoch:?}, but the last \ installed epoch is still {current_epoch:?}" @@ -770,14 +767,14 @@ where fn read_governance_valset( ctx: RequestCtx<'_, D, H, V, T>, epoch: Epoch, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { let current_epoch = ctx.wl_storage.storage.last_epoch; if epoch > current_epoch.next() { - Err(storage_api::Error::Custom(CustomError( + Err(namada_storage::Error::Custom(CustomError( format!( "Requesting Governance validator set at {epoch:?}, but the \ last installed epoch is still {current_epoch:?}" @@ -798,14 +795,14 @@ where fn voting_powers_at_height( ctx: RequestCtx<'_, D, H, V, T>, height: BlockHeight, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { let maybe_epoch = ctx.wl_storage.pos_queries().get_epoch(height); let Some(epoch) = maybe_epoch else { - return Err(storage_api::Error::SimpleMessage( + return Err(namada_storage::Error::SimpleMessage( "The epoch of the requested height does not exist", )); }; @@ -817,14 +814,14 @@ where fn voting_powers_at_epoch( ctx: RequestCtx<'_, D, H, V, T>, epoch: Epoch, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { let current_epoch = ctx.wl_storage.storage.get_current_epoch().0; if epoch > current_epoch + 1u64 { - return Err(storage_api::Error::SimpleMessage( + return Err(namada_storage::Error::SimpleMessage( "The requested epoch cannot be queried", )); } @@ -845,7 +842,6 @@ mod test_ethbridge_router { }; use namada_core::ledger::eth_bridge::storage::whitelist; use namada_core::ledger::storage::mockdb::MockDBWriteBatch; - use namada_core::ledger::storage_api::StorageWrite; use namada_core::types::address::nam; use namada_core::types::address::testing::established_address_1; use namada_core::types::eth_abi::Encode; @@ -864,6 +860,7 @@ mod test_ethbridge_router { use namada_ethereum_bridge::protocol::transactions::validator_set_update::aggregate_votes; use namada_ethereum_bridge::storage::proof::BridgePoolRootProof; use namada_proof_of_stake::pos_queries::PosQueries; + use namada_storage::StorageWrite; use super::test_utils::bertha_address; use super::*; diff --git a/sdk/src/queries/types.rs b/sdk/src/queries/types.rs index a9e2fb3566..6263a5419d 100644 --- a/sdk/src/queries/types.rs +++ b/sdk/src/queries/types.rs @@ -1,8 +1,7 @@ use std::fmt::Debug; -use namada_core::ledger::storage::{DBIter, StorageHasher, WlStorage, DB}; -use namada_core::ledger::storage_api; use namada_core::types::storage::BlockHeight; +use namada_state::{DBIter, StorageHasher, WlStorage, DB}; use thiserror::Error; use crate::events::log::EventLog; @@ -39,7 +38,7 @@ pub trait Router { &self, ctx: RequestCtx<'_, D, H, V, T>, request: &RequestQuery, - ) -> storage_api::Result + ) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -58,7 +57,7 @@ pub trait Router { ctx: RequestCtx<'_, D, H, V, T>, request: &RequestQuery, start: usize, - ) -> storage_api::Result + ) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync; diff --git a/sdk/src/queries/vp/governance.rs b/sdk/src/queries/vp/governance.rs index 1e3a5a8ece..28368ca76b 100644 --- a/sdk/src/queries/vp/governance.rs +++ b/sdk/src/queries/vp/governance.rs @@ -1,10 +1,9 @@ // cd shared && cargo expand ledger::queries::vp::governance -use namada_core::ledger::governance::parameters::GovernanceParameters; -use namada_core::ledger::governance::storage::proposal::StorageProposal; -use namada_core::ledger::governance::utils::Vote; -use namada_core::ledger::storage::{DBIter, StorageHasher, DB}; -use namada_core::ledger::storage_api; +use namada_governance::parameters::GovernanceParameters; +use namada_governance::storage::proposal::StorageProposal; +use namada_governance::utils::Vote; +use namada_state::{DBIter, StorageHasher, DB}; use crate::queries::types::RequestCtx; @@ -19,33 +18,33 @@ router! {GOV, fn proposal_id( ctx: RequestCtx<'_, D, H, V, T>, id: u64, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - storage_api::governance::get_proposal_by_id(ctx.wl_storage, id) + namada_governance::storage::get_proposal_by_id(ctx.wl_storage, id) } /// Find if the given address belongs to a validator account. fn proposal_id_votes( ctx: RequestCtx<'_, D, H, V, T>, id: u64, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - storage_api::governance::get_proposal_votes(ctx.wl_storage, id) + namada_governance::storage::get_proposal_votes(ctx.wl_storage, id) } /// Get the governane parameters fn parameters( ctx: RequestCtx<'_, D, H, V, T>, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - storage_api::governance::get_parameters(ctx.wl_storage) + namada_governance::storage::get_parameters(ctx.wl_storage) } diff --git a/sdk/src/queries/vp/pgf.rs b/sdk/src/queries/vp/pgf.rs index a7400f2732..4b8431e854 100644 --- a/sdk/src/queries/vp/pgf.rs +++ b/sdk/src/queries/vp/pgf.rs @@ -1,9 +1,8 @@ -use namada_core::ledger::governance::storage::proposal::StoragePgfFunding; -use namada_core::ledger::pgf::parameters::PgfParameters; -use namada_core::ledger::pgf::storage::steward::StewardDetail; -use namada_core::ledger::storage::{DBIter, StorageHasher, DB}; -use namada_core::ledger::storage_api; use namada_core::types::address::Address; +use namada_governance::pgf::parameters::PgfParameters; +use namada_governance::pgf::storage::steward::StewardDetail; +use namada_governance::storage::proposal::StoragePgfFunding; +use namada_state::{DBIter, StorageHasher, DB}; use crate::queries::types::RequestCtx; @@ -18,44 +17,44 @@ router! {PGF, /// Query the current pgf steward set fn stewards( ctx: RequestCtx<'_, D, H, V, T>, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - storage_api::pgf::get_stewards(ctx.wl_storage) + namada_governance::pgf::storage::get_stewards(ctx.wl_storage) } /// Check if an address is a pgf steward fn is_steward( ctx: RequestCtx<'_, D, H, V, T>, address: Address, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - storage_api::pgf::is_steward(ctx.wl_storage, &address) + namada_governance::pgf::storage::is_steward(ctx.wl_storage, &address) } /// Query the continuous pgf fundings fn funding( ctx: RequestCtx<'_, D, H, V, T>, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - storage_api::pgf::get_payments(ctx.wl_storage) + namada_governance::pgf::storage::get_payments(ctx.wl_storage) } /// Query the PGF parameters fn parameters( ctx: RequestCtx<'_, D, H, V, T>, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - storage_api::pgf::get_parameters(ctx.wl_storage) + namada_governance::pgf::storage::get_parameters(ctx.wl_storage) } diff --git a/sdk/src/queries/vp/pos.rs b/sdk/src/queries/vp/pos.rs index ff1b073dc4..0c06b51cb7 100644 --- a/sdk/src/queries/vp/pos.rs +++ b/sdk/src/queries/vp/pos.rs @@ -3,10 +3,6 @@ use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; -use namada_core::ledger::storage::{DBIter, StorageHasher, DB}; -use namada_core::ledger::storage_api; -use namada_core::ledger::storage_api::collections::lazy_map; -use namada_core::ledger::storage_api::OptionExt; use namada_core::types::address::Address; use namada_core::types::key::common; use namada_core::types::storage::Epoch; @@ -34,6 +30,9 @@ use namada_proof_of_stake::types::{ Slash, ValidatorMetaData, ValidatorState, WeightedValidator, }; use namada_proof_of_stake::{self, bond_amount, query_reward_tokens}; +use namada_state::{DBIter, StorageHasher, DB}; +use namada_storage::collections::lazy_map; +use namada_storage::OptionExt; use crate::queries::types::RequestCtx; @@ -174,7 +173,7 @@ impl Enriched { /// Get the PoS parameters fn pos_params( ctx: RequestCtx<'_, D, H, V, T>, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -186,7 +185,7 @@ where fn is_validator( ctx: RequestCtx<'_, D, H, V, T>, addr: Address, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -199,7 +198,7 @@ fn is_delegator( ctx: RequestCtx<'_, D, H, V, T>, addr: Address, epoch: Option, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -212,7 +211,7 @@ where fn validator_addresses( ctx: RequestCtx<'_, D, H, V, T>, epoch: Option, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -226,7 +225,7 @@ fn validator_commission( ctx: RequestCtx<'_, D, H, V, T>, validator: Address, epoch: Option, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -256,7 +255,7 @@ where fn validator_metadata( ctx: RequestCtx<'_, D, H, V, T>, validator: Address, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -284,7 +283,7 @@ fn validator_state( ctx: RequestCtx<'_, D, H, V, T>, validator: Address, epoch: Option, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -303,7 +302,7 @@ where fn validator_last_infraction_epoch( ctx: RequestCtx<'_, D, H, V, T>, validator: Address, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -320,7 +319,7 @@ fn validator_stake( ctx: RequestCtx<'_, D, H, V, T>, validator: Address, epoch: Option, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -342,7 +341,7 @@ fn validator_incoming_redelegation( ctx: RequestCtx<'_, D, H, V, T>, src_validator: Address, delegator: Address, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -355,7 +354,7 @@ where fn consensus_validator_set( ctx: RequestCtx<'_, D, H, V, T>, epoch: Option, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -368,7 +367,7 @@ where fn below_capacity_validator_set( ctx: RequestCtx<'_, D, H, V, T>, epoch: Option, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -384,7 +383,7 @@ where fn total_stake( ctx: RequestCtx<'_, D, H, V, T>, epoch: Option, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -398,7 +397,7 @@ fn bond_deltas( ctx: RequestCtx<'_, D, H, V, T>, source: Address, validator: Address, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -413,7 +412,7 @@ fn bond( source: Address, validator: Address, epoch: Option, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -433,7 +432,7 @@ fn bond_with_slashing( source: Address, validator: Address, epoch: Option, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -448,7 +447,7 @@ fn unbond( ctx: RequestCtx<'_, D, H, V, T>, source: Address, validator: Address, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -473,7 +472,7 @@ fn unbond_with_slashing( ctx: RequestCtx<'_, D, H, V, T>, source: Address, validator: Address, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -500,7 +499,7 @@ fn withdrawable_tokens( source: Address, validator: Address, epoch: Option, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -528,7 +527,7 @@ fn rewards( ctx: RequestCtx<'_, D, H, V, T>, validator: Address, source: Option
, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -546,7 +545,7 @@ fn bonds_and_unbonds( ctx: RequestCtx<'_, D, H, V, T>, source: Option
, validator: Option
, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -563,7 +562,7 @@ where fn delegation_validators( ctx: RequestCtx<'_, D, H, V, T>, owner: Address, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -577,7 +576,7 @@ fn delegations( ctx: RequestCtx<'_, D, H, V, T>, owner: Address, epoch: Option, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -590,7 +589,7 @@ where fn validator_slashes( ctx: RequestCtx<'_, D, H, V, T>, validator: Address, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -602,7 +601,7 @@ where /// All slashes fn slashes( ctx: RequestCtx<'_, D, H, V, T>, -) -> storage_api::Result>> +) -> namada_storage::Result>> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -613,7 +612,7 @@ where /// Enqueued slashes fn enqueued_slashes( ctx: RequestCtx<'_, D, H, V, T>, -) -> storage_api::Result>>> +) -> namada_storage::Result>>> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -626,7 +625,7 @@ where fn validator_by_tm_addr( ctx: RequestCtx<'_, D, H, V, T>, tm_addr: String, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -640,7 +639,7 @@ where /// Native validator address by looking up the Tendermint address fn consensus_key_set( ctx: RequestCtx<'_, D, H, V, T>, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -652,7 +651,7 @@ where fn has_bonds( ctx: RequestCtx<'_, D, H, V, T>, source: Address, -) -> storage_api::Result +) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, diff --git a/sdk/src/queries/vp/token.rs b/sdk/src/queries/vp/token.rs index 5e2e5aa95b..b885e4dc66 100644 --- a/sdk/src/queries/vp/token.rs +++ b/sdk/src/queries/vp/token.rs @@ -1,10 +1,9 @@ //! Token validity predicate queries -use namada_core::ledger::storage::{DBIter, StorageHasher, DB}; -use namada_core::ledger::storage_api; -use namada_core::ledger::storage_api::token::read_denom; use namada_core::types::address::Address; use namada_core::types::token; +use namada_state::{DBIter, StorageHasher, DB}; +use namada_token::read_denom; use crate::queries::RequestCtx; @@ -17,7 +16,7 @@ router! {TOKEN, fn denomination( ctx: RequestCtx<'_, D, H, V, T>, addr: Address, -) -> storage_api::Result> +) -> namada_storage::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -30,6 +29,7 @@ pub mod client_only_methods { use borsh::BorshDeserialize; use namada_core::types::address::Address; use namada_core::types::token; + use namada_token::storage_key::balance_key; use super::Token; use crate::queries::{Client, RPC}; @@ -45,7 +45,7 @@ pub mod client_only_methods { where CLIENT: Client + Sync, { - let balance_key = token::balance_key(token, owner); + let balance_key = balance_key(token, owner); let response = RPC .shell() .storage_value(client, None, None, false, &balance_key) diff --git a/sdk/src/rpc.rs b/sdk/src/rpc.rs index 2bb7192a89..9f58e24217 100644 --- a/sdk/src/rpc.rs +++ b/sdk/src/rpc.rs @@ -9,14 +9,7 @@ use borsh::BorshDeserialize; use masp_primitives::asset_type::AssetType; use masp_primitives::merkle_tree::MerklePath; use masp_primitives::sapling::Node; -use namada_core::ledger::governance::parameters::GovernanceParameters; -use namada_core::ledger::governance::storage::proposal::StorageProposal; -use namada_core::ledger::governance::utils::Vote; -use namada_core::ledger::ibc::storage::{ - ibc_denom_key, ibc_denom_key_prefix, is_ibc_denom_key, -}; -use namada_core::ledger::storage::LastBlock; -use namada_core::types::account::Account; +use namada_account::Account; use namada_core::types::address::{Address, InternalAddress}; use namada_core::types::hash::Hash; use namada_core::types::key::common; @@ -26,12 +19,19 @@ use namada_core::types::storage::{ use namada_core::types::token::{ Amount, DenominatedAmount, Denomination, MaspDenom, }; -use namada_core::types::transaction::{ResultCode, TxResult}; use namada_core::types::{storage, token}; +use namada_governance::parameters::GovernanceParameters; +use namada_governance::storage::proposal::StorageProposal; +use namada_governance::utils::Vote; +use namada_ibc::storage::{ + ibc_denom_key, ibc_denom_key_prefix, is_ibc_denom_key, +}; use namada_proof_of_stake::parameters::PosParams; use namada_proof_of_stake::types::{ BondsAndUnbondsDetails, CommissionPair, ValidatorMetaData, ValidatorState, }; +use namada_state::LastBlock; +use namada_tx::data::{ResultCode, TxResult}; use serde::Serialize; use crate::args::InputAmount; @@ -40,7 +40,6 @@ use crate::error::{EncodingError, Error, QueryError, TxError}; use crate::events::Event; use crate::internal_macros::echo_error; use crate::io::Io; -use crate::proto::Tx; use crate::queries::vp::pos::EnrichedBondsAndUnbondsDetails; use crate::queries::{Client, RPC}; use crate::tendermint::block::Height; @@ -48,7 +47,7 @@ use crate::tendermint::merkle::proof::ProofOps; use crate::tendermint_rpc::error::Error as TError; use crate::tendermint_rpc::query::Query; use crate::tendermint_rpc::Order; -use crate::{display_line, edisplay_line, error, Namada}; +use crate::{display_line, edisplay_line, error, Namada, Tx}; /// Query the status of a given transaction. /// @@ -484,7 +483,7 @@ pub async fn query_tx_events( pub async fn dry_run_tx( context: &N, tx_bytes: Vec, -) -> Result { +) -> Result { let (data, height, prove) = (Some(tx_bytes), None, false); let result = convert_response::( RPC.shell() diff --git a/sdk/src/signing.rs b/sdk/src/signing.rs index 38aec72d4b..23ed003bdd 100644 --- a/sdk/src/signing.rs +++ b/sdk/src/signing.rs @@ -10,26 +10,29 @@ use masp_primitives::asset_type::AssetType; use masp_primitives::transaction::components::sapling::fees::{ InputView, OutputView, }; -use namada_core::ledger::parameters::storage as parameter_storage; -use namada_core::proto::SignatureIndex; -use namada_core::types::account::AccountPublicKeysMap; +use namada_account::AccountPublicKeysMap; use namada_core::types::address::{ masp_tx_key, Address, ImplicitAddress, InternalAddress, MASP, }; use namada_core::types::key::*; use namada_core::types::masp::{ExtendedViewingKey, PaymentAddress}; +use namada_core::types::sign::SignatureIndex; use namada_core::types::storage::Epoch; use namada_core::types::token; use namada_core::types::token::Transfer; // use namada_core::types::storage::Key; use namada_core::types::token::{Amount, DenominatedAmount, MaspDenom}; -use namada_core::types::transaction::account::{InitAccount, UpdateAccount}; -use namada_core::types::transaction::governance::{ - InitProposalData, VoteProposalData, +use namada_governance::storage::proposal::{ + InitProposalData, ProposalType, VoteProposalData, }; -use namada_core::types::transaction::pgf::UpdateStewardCommission; -use namada_core::types::transaction::pos::BecomeValidator; -use namada_core::types::transaction::{pos, Fee}; +use namada_governance::storage::vote::{StorageProposalVote, VoteType}; +use namada_parameters::storage as parameter_storage; +use namada_token::storage_key::balance_key; +use namada_tx::data::account::{InitAccount, UpdateAccount}; +use namada_tx::data::pgf::UpdateStewardCommission; +use namada_tx::data::pos::BecomeValidator; +use namada_tx::data::{pos, Fee}; +use namada_tx::{MaspBuilder, Section, Tx}; use prost::Message; use rand::rngs::OsRng; use serde::{Deserialize, Serialize}; @@ -38,17 +41,12 @@ use tokio::sync::RwLock; use super::masp::{ShieldedContext, ShieldedTransfer}; use crate::args::SdkTypes; -use crate::core::ledger::governance::storage::proposal::ProposalType; -use crate::core::ledger::governance::storage::vote::{ - StorageProposalVote, VoteType, -}; use crate::core::types::eth_bridge_pool::PendingTransfer; use crate::error::{EncodingError, Error, TxError}; use crate::ibc::apps::transfer::types::msgs::transfer::MsgTransfer; use crate::ibc::primitives::proto::Any; use crate::io::*; use crate::masp::make_asset_type; -use crate::proto::{MaspBuilder, Section, Tx}; use crate::rpc::validate_amount; use crate::tx::{ TX_BECOME_VALIDATOR_WASM, TX_BOND_WASM, TX_BRIDGE_POOL_WASM, @@ -509,8 +507,7 @@ pub async fn wrap_tx( token, }) if token == args.fee_token && source == fee_payer_address => balance, _ => { - let balance_key = - token::balance_key(&args.fee_token, &fee_payer_address); + let balance_key = balance_key(&args.fee_token, &fee_payer_address); rpc::query_storage_value::<_, token::Amount>( context.client(), diff --git a/sdk/src/tx.rs b/sdk/src/tx.rs index de0474615b..63efb8b086 100644 --- a/sdk/src/tx.rs +++ b/sdk/src/tx.rs @@ -24,14 +24,6 @@ use namada_core::ibc::core::channel::types::timeout::TimeoutHeight; use namada_core::ibc::core::client::types::Height as IbcHeight; use namada_core::ibc::core::host::types::identifiers::{ChannelId, PortId}; use namada_core::ibc::primitives::{Msg, Timestamp as IbcTimestamp}; -use namada_core::ledger::governance::cli::onchain::{ - DefaultProposal, OnChainProposal, PgfFundingProposal, PgfStewardProposal, - ProposalVote, -}; -use namada_core::ledger::governance::storage::proposal::ProposalType; -use namada_core::ledger::governance::storage::vote::StorageProposalVote; -use namada_core::ledger::ibc::storage::channel_key; -use namada_core::ledger::pgf::cli::steward::Commission; use namada_core::types::address::{Address, InternalAddress, MASP}; use namada_core::types::dec::Dec; use namada_core::types::hash::Hash; @@ -41,15 +33,24 @@ use namada_core::types::masp::{TransferSource, TransferTarget}; use namada_core::types::storage::Epoch; use namada_core::types::time::DateTimeUtc; use namada_core::types::token::MaspDenom; -use namada_core::types::transaction::account::{InitAccount, UpdateAccount}; -use namada_core::types::transaction::governance::{ - InitProposalData, VoteProposalData, -}; -use namada_core::types::transaction::pgf::UpdateStewardCommission; -use namada_core::types::transaction::{pos, ResultCode, TxResult}; use namada_core::types::{storage, token}; +use namada_governance::cli::onchain::{ + DefaultProposal, OnChainProposal, PgfFundingProposal, PgfStewardProposal, + ProposalVote, +}; +use namada_governance::pgf::cli::steward::Commission; +use namada_governance::storage::proposal::{ + InitProposalData, ProposalType, VoteProposalData, +}; +use namada_governance::storage::vote::StorageProposalVote; +use namada_ibc::storage::channel_key; use namada_proof_of_stake::parameters::PosParams; use namada_proof_of_stake::types::{CommissionPair, ValidatorState}; +use namada_token::storage_key::balance_key; +use namada_tx::data::account::{InitAccount, UpdateAccount}; +use namada_tx::data::pgf::UpdateStewardCommission; +use namada_tx::data::{pos, ResultCode, TxResult}; +use namada_tx::{MaspBuilder, Tx}; use crate::args::{self, InputAmount}; use crate::control_flow::time; @@ -57,7 +58,6 @@ use crate::error::{EncodingError, Error, QueryError, Result, TxError}; use crate::io::Io; use crate::masp::TransferErr::Build; use crate::masp::{make_asset_type, ShieldedContext, ShieldedTransfer}; -use crate::proto::{MaspBuilder, Tx}; use crate::queries::Client; use crate::rpc::{ self, query_wasm_code_hash, validate_amount, InnerTxResult, @@ -1716,7 +1716,7 @@ pub async fn build_bond( // Check bond's source (source for delegation or validator for self-bonds) // balance let bond_source = source.as_ref().unwrap_or(&validator); - let balance_key = token::balance_key(native_token, bond_source); + let balance_key = balance_key(native_token, bond_source); // TODO Should we state the same error message for the native token? let post_balance = check_balance_too_low_err( @@ -2015,7 +2015,7 @@ pub async fn build_ibc_transfer( } // Check source balance - let balance_key = token::balance_key(&args.token, &source); + let balance_key = balance_key(&args.token, &source); let post_balance = check_balance_too_low_err( &args.token, @@ -2307,7 +2307,7 @@ pub async fn build_transfer( // Check that the target address exists on chain target_exists_or_err(target.clone(), args.tx.force, context).await?; // Check source balance - let balance_key = token::balance_key(&args.token, &source); + let balance_key = balance_key(&args.token, &source); // validate the amount given let validated_amount = @@ -2657,7 +2657,7 @@ pub async fn gen_ibc_shielded_transfer( let prefixed_denom = ibc_denom .parse() .map_err(|_| Error::Other(format!("Invalid IBC denom: {ibc_denom}")))?; - let token = namada_core::ledger::ibc::received_ibc_token( + let token = namada_ibc::received_ibc_token( &prefixed_denom, &src_port_id, &src_channel_id, diff --git a/state/src/lib.rs b/state/src/lib.rs index 94d195b4bf..84d0a5ab16 100644 --- a/state/src/lib.rs +++ b/state/src/lib.rs @@ -33,11 +33,10 @@ use namada_gas::{ MEMORY_ACCESS_GAS_PER_BYTE, STORAGE_ACCESS_GAS_PER_BYTE, STORAGE_WRITE_GAS_PER_BYTE, }; -use namada_merkle_tree::{ - Error as MerkleTreeError, MembershipProof, MerkleRoot, -}; +use namada_merkle_tree::{Error as MerkleTreeError, MerkleRoot}; pub use namada_merkle_tree::{ - MerkleTree, MerkleTreeStoresRead, MerkleTreeStoresWrite, StoreType, + MembershipProof, MerkleTree, MerkleTreeStoresRead, MerkleTreeStoresWrite, + StoreRef, StoreType, }; use namada_parameters::{self, EpochDuration, Parameters}; use thiserror::Error; From 735656c04c9316864f8656ceb97c43af9cb2cf36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Thu, 4 Jan 2024 10:26:19 +0000 Subject: [PATCH 074/118] wasm: fix build --- Cargo.lock | 2 + account/src/lib.rs | 2 + .../data/account.rs => account/src/types.rs | 0 ethereum_bridge/src/lib.rs | 2 + governance/src/lib.rs | 4 + governance/src/storage/mod.rs | 5 +- sdk/src/signing.rs | 3 +- sdk/src/tx.rs | 2 +- tx/src/data/mod.rs | 2 - tx_prelude/Cargo.toml | 1 + tx_prelude/src/account.rs | 2 +- tx_prelude/src/ibc.rs | 1 + tx_prelude/src/lib.rs | 8 +- vp_prelude/Cargo.toml | 1 + vp_prelude/src/lib.rs | 5 + wasm/Cargo.lock | 237 +++++++++++++++++- wasm/wasm_source/Cargo.toml | 1 - wasm/wasm_source/src/tx_bridge_pool.rs | 2 - wasm/wasm_source/src/tx_init_account.rs | 2 +- wasm/wasm_source/src/tx_init_proposal.rs | 5 +- wasm/wasm_source/src/tx_transfer.rs | 2 +- wasm/wasm_source/src/tx_update_account.rs | 16 +- wasm/wasm_source/src/tx_vote_proposal.rs | 5 +- wasm/wasm_source/src/vp_implicit.rs | 13 +- wasm/wasm_source/src/vp_user.rs | 11 +- 25 files changed, 292 insertions(+), 42 deletions(-) rename tx/src/data/account.rs => account/src/types.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 6fff41381c..98246c7519 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4816,6 +4816,7 @@ dependencies = [ "masp_primitives", "namada_account", "namada_core", + "namada_ethereum_bridge", "namada_governance", "namada_ibc", "namada_macros", @@ -4870,6 +4871,7 @@ dependencies = [ "namada_account", "namada_core", "namada_governance", + "namada_ibc", "namada_macros", "namada_parameters", "namada_proof_of_stake", diff --git a/account/src/lib.rs b/account/src/lib.rs index faff8c98ff..212584aa83 100644 --- a/account/src/lib.rs +++ b/account/src/lib.rs @@ -4,6 +4,7 @@ mod storage; mod storage_key; +mod types; use borsh::{BorshDeserialize, BorshSerialize}; use namada_core::types::address::Address; @@ -12,6 +13,7 @@ pub use namada_core::types::key::AccountPublicKeysMap; use serde::{Deserialize, Serialize}; pub use storage::*; pub use storage_key::*; +pub use types::*; #[derive( Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, diff --git a/tx/src/data/account.rs b/account/src/types.rs similarity index 100% rename from tx/src/data/account.rs rename to account/src/types.rs diff --git a/ethereum_bridge/src/lib.rs b/ethereum_bridge/src/lib.rs index 028dead0fa..86b5d5fa37 100644 --- a/ethereum_bridge/src/lib.rs +++ b/ethereum_bridge/src/lib.rs @@ -5,3 +5,5 @@ pub mod protocol; pub mod storage; #[cfg(any(test, feature = "testing"))] pub mod test_utils; + +pub use namada_core::ledger::eth_bridge::ADDRESS; diff --git a/governance/src/lib.rs b/governance/src/lib.rs index 4cd29205b3..9ab79eb344 100644 --- a/governance/src/lib.rs +++ b/governance/src/lib.rs @@ -12,5 +12,9 @@ pub mod storage; /// Governance utility functions/structs pub mod utils; +pub use storage::proposal::{InitProposalData, ProposalType, VoteProposalData}; +pub use storage::vote::{StorageProposalVote, VoteType}; +pub use storage::{init_proposal, vote_proposal}; + /// The governance internal address pub const ADDRESS: Address = address::GOV; diff --git a/governance/src/storage/mod.rs b/governance/src/storage/mod.rs index 6e30941ee4..52369e862e 100644 --- a/governance/src/storage/mod.rs +++ b/governance/src/storage/mod.rs @@ -15,10 +15,11 @@ use namada_core::types::storage::Epoch; use namada_storage::{iter_prefix, Error, Result, StorageRead, StorageWrite}; use namada_trans_token as token; -use self::proposal::{InitProposalData, VoteProposalData}; use crate::parameters::GovernanceParameters; use crate::storage::keys as governance_keys; -use crate::storage::proposal::{ProposalType, StorageProposal}; +use crate::storage::proposal::{ + InitProposalData, ProposalType, StorageProposal, VoteProposalData, +}; use crate::storage::vote::StorageProposalVote; use crate::utils::Vote; use crate::ADDRESS as governance_address; diff --git a/sdk/src/signing.rs b/sdk/src/signing.rs index 23ed003bdd..0ceb1cf8dc 100644 --- a/sdk/src/signing.rs +++ b/sdk/src/signing.rs @@ -10,7 +10,7 @@ use masp_primitives::asset_type::AssetType; use masp_primitives::transaction::components::sapling::fees::{ InputView, OutputView, }; -use namada_account::AccountPublicKeysMap; +use namada_account::{AccountPublicKeysMap, InitAccount, UpdateAccount}; use namada_core::types::address::{ masp_tx_key, Address, ImplicitAddress, InternalAddress, MASP, }; @@ -28,7 +28,6 @@ use namada_governance::storage::proposal::{ use namada_governance::storage::vote::{StorageProposalVote, VoteType}; use namada_parameters::storage as parameter_storage; use namada_token::storage_key::balance_key; -use namada_tx::data::account::{InitAccount, UpdateAccount}; use namada_tx::data::pgf::UpdateStewardCommission; use namada_tx::data::pos::BecomeValidator; use namada_tx::data::{pos, Fee}; diff --git a/sdk/src/tx.rs b/sdk/src/tx.rs index 63efb8b086..cbbb5d3f74 100644 --- a/sdk/src/tx.rs +++ b/sdk/src/tx.rs @@ -17,6 +17,7 @@ use masp_primitives::transaction::components::transparent::fees::{ InputView as TransparentInputView, OutputView as TransparentOutputView, }; use masp_primitives::transaction::components::I128Sum; +use namada_account::{InitAccount, UpdateAccount}; use namada_core::ibc::apps::transfer::types::msgs::transfer::MsgTransfer; use namada_core::ibc::apps::transfer::types::packet::PacketData; use namada_core::ibc::apps::transfer::types::PrefixedCoin; @@ -47,7 +48,6 @@ use namada_ibc::storage::channel_key; use namada_proof_of_stake::parameters::PosParams; use namada_proof_of_stake::types::{CommissionPair, ValidatorState}; use namada_token::storage_key::balance_key; -use namada_tx::data::account::{InitAccount, UpdateAccount}; use namada_tx::data::pgf::UpdateStewardCommission; use namada_tx::data::{pos, ResultCode, TxResult}; use namada_tx::{MaspBuilder, Tx}; diff --git a/tx/src/data/mod.rs b/tx/src/data/mod.rs index 3958dd9c36..a4923e1de4 100644 --- a/tx/src/data/mod.rs +++ b/tx/src/data/mod.rs @@ -1,7 +1,5 @@ //! Data-Types that are used in transactions. -/// txs to manage accounts -pub mod account; /// txs that contain decrypted payloads or assertions of /// non-decryptability pub mod decrypted; diff --git a/tx_prelude/Cargo.toml b/tx_prelude/Cargo.toml index f561dfdd3e..a7dcb6b9de 100644 --- a/tx_prelude/Cargo.toml +++ b/tx_prelude/Cargo.toml @@ -18,6 +18,7 @@ default = [] [dependencies] namada_account = { path = "../account" } namada_core = { path = "../core", default-features = false } +namada_ethereum_bridge = { path = "../ethereum_bridge" } namada_governance = { path = "../governance" } namada_ibc = { path = "../ibc" } namada_macros = { path = "../macros" } diff --git a/tx_prelude/src/account.rs b/tx_prelude/src/account.rs index 5054411a90..dacb659f65 100644 --- a/tx_prelude/src/account.rs +++ b/tx_prelude/src/account.rs @@ -1,4 +1,4 @@ -use namada_tx::data::account::InitAccount; +pub use namada_account::*; use super::*; diff --git a/tx_prelude/src/ibc.rs b/tx_prelude/src/ibc.rs index 45a6c6e448..0a8ccd4acb 100644 --- a/tx_prelude/src/ibc.rs +++ b/tx_prelude/src/ibc.rs @@ -6,6 +6,7 @@ use std::rc::Rc; use namada_core::types::address::{Address, InternalAddress}; pub use namada_core::types::ibc::{IbcEvent, IbcShieldedTransfer}; use namada_core::types::token::DenominatedAmount; +pub use namada_ibc::storage::is_ibc_key; pub use namada_ibc::{ IbcActions, IbcCommonContext, IbcStorageContext, ProofSpec, TransferModule, }; diff --git a/tx_prelude/src/lib.rs b/tx_prelude/src/lib.rs index e3a6648744..c8906dbce5 100644 --- a/tx_prelude/src/lib.rs +++ b/tx_prelude/src/lib.rs @@ -20,7 +20,6 @@ use masp_primitives::transaction::Transaction; pub use namada_core::borsh::{ BorshDeserialize, BorshSerialize, BorshSerializeExt, }; -pub use namada_core::ledger::eth_bridge; pub use namada_core::types::address::Address; use namada_core::types::chain::CHAIN_ID_LENGTH; pub use namada_core::types::ethereum_events::EthAddress; @@ -38,11 +37,14 @@ pub use namada_storage::{ iter_prefix, iter_prefix_bytes, Error, OptionExt, ResultExt, StorageRead, StorageWrite, }; -pub use namada_tx::{Section, Tx}; +pub use namada_tx::{data as transaction, Section, Tx}; pub use namada_tx_env::TxEnv; use namada_vm_env::tx::*; use namada_vm_env::{read_from_buffer, read_key_val_bytes_from_buffer}; -pub use {namada_governance as governance, namada_parameters as parameters}; +pub use { + namada_ethereum_bridge as eth_bridge, namada_governance as governance, + namada_parameters as parameters, +}; /// Log a string. The message will be printed at the `tracing::Level::Info`. pub fn log_string>(msg: T) { diff --git a/vp_prelude/Cargo.toml b/vp_prelude/Cargo.toml index 6de70ae749..227197c51a 100644 --- a/vp_prelude/Cargo.toml +++ b/vp_prelude/Cargo.toml @@ -19,6 +19,7 @@ default = [] namada_account = { path = "../account" } namada_core = { path = "../core", default-features = false } namada_governance = { path = "../governance" } +namada_ibc = { path = "../ibc" } namada_macros = { path = "../macros" } namada_parameters = { path = "../parameters" } namada_proof_of_stake = { path = "../proof_of_stake", default-features = false } diff --git a/vp_prelude/src/lib.rs b/vp_prelude/src/lib.rs index 190a64c16c..e73bf84d56 100644 --- a/vp_prelude/src/lib.rs +++ b/vp_prelude/src/lib.rs @@ -6,6 +6,11 @@ #![deny(rustdoc::broken_intra_doc_links)] #![deny(rustdoc::private_intra_doc_links)] +pub mod ibc { + pub use namada_core::types::ibc::IbcEvent; + pub use namada_ibc::storage::is_ibc_key; +} + // used in the VP input use core::convert::AsRef; use core::slice; diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index 58825e1a3f..79fb909b19 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -3364,6 +3364,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "namada_account" +version = "0.29.0" +dependencies = [ + "borsh", + "namada_core", + "namada_macros", + "namada_storage", + "serde", +] + [[package]] name = "namada_core" version = "0.29.0" @@ -3411,7 +3422,6 @@ dependencies = [ "tendermint-proto", "thiserror", "tiny-keccak", - "tonic-build", "tracing", "uint", "zeroize", @@ -3422,19 +3432,73 @@ name = "namada_ethereum_bridge" version = "0.29.0" dependencies = [ "borsh", - "borsh-ext", + "ethabi", "ethers", "eyre", "itertools 0.10.5", "namada_core", "namada_macros", + "namada_parameters", "namada_proof_of_stake", + "namada_state", + "namada_storage", + "namada_trans_token", + "namada_tx", + "namada_vote_ext", "rand 0.8.5", "serde", "serde_json", "tendermint", "tendermint-proto", "tendermint-rpc", + "thiserror", + "tracing", +] + +[[package]] +name = "namada_gas" +version = "0.29.0" +dependencies = [ + "borsh", + "namada_core", + "namada_parameters", + "namada_storage", + "serde", + "thiserror", +] + +[[package]] +name = "namada_governance" +version = "0.29.0" +dependencies = [ + "borsh", + "namada_core", + "namada_macros", + "namada_parameters", + "namada_storage", + "namada_trans_token", + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "namada_ibc" +version = "0.29.0" +dependencies = [ + "borsh", + "ibc", + "ibc-derive", + "ics23", + "namada_core", + "namada_parameters", + "namada_storage", + "namada_trans_token", + "primitive-types", + "prost 0.12.3", + "sha2 0.9.9", + "thiserror", "tracing", ] @@ -3447,6 +3511,30 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "namada_merkle_tree" +version = "0.29.0" +dependencies = [ + "borsh", + "eyre", + "ics23", + "namada_core", + "prost 0.12.3", + "sparse-merkle-tree", + "thiserror", +] + +[[package]] +name = "namada_parameters" +version = "0.29.0" +dependencies = [ + "borsh", + "namada_core", + "namada_macros", + "namada_storage", + "thiserror", +] + [[package]] name = "namada_proof_of_stake" version = "0.29.0" @@ -3454,7 +3542,12 @@ dependencies = [ "borsh", "data-encoding", "derivative", + "namada_account", "namada_core", + "namada_governance", + "namada_parameters", + "namada_storage", + "namada_trans_token", "once_cell", "proptest", "serde", @@ -3480,9 +3573,18 @@ dependencies = [ "lazy_static", "masp_primitives", "masp_proofs", + "namada_account", "namada_core", "namada_ethereum_bridge", + "namada_governance", + "namada_ibc", + "namada_parameters", "namada_proof_of_stake", + "namada_state", + "namada_storage", + "namada_token", + "namada_tx", + "namada_vote_ext", "num256", "orion", "owo-colors", @@ -3508,6 +3610,49 @@ dependencies = [ "zeroize", ] +[[package]] +name = "namada_shielded_token" +version = "0.29.0" +dependencies = [ + "masp_primitives", + "namada_core", + "namada_parameters", + "namada_state", + "namada_storage", + "namada_trans_token", + "tracing", +] + +[[package]] +name = "namada_state" +version = "0.29.0" +dependencies = [ + "borsh", + "ics23", + "itertools 0.10.5", + "namada_core", + "namada_gas", + "namada_merkle_tree", + "namada_parameters", + "namada_storage", + "namada_trans_token", + "namada_tx", + "sha2 0.9.9", + "sparse-merkle-tree", + "thiserror", + "tiny-keccak", + "tracing", +] + +[[package]] +name = "namada_storage" +version = "0.29.0" +dependencies = [ + "itertools 0.10.5", + "namada_core", + "thiserror", +] + [[package]] name = "namada_test_utils" version = "0.29.0" @@ -3551,16 +3696,71 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "namada_token" +version = "0.29.0" +dependencies = [ + "namada_core", + "namada_shielded_token", + "namada_storage", + "namada_trans_token", +] + +[[package]] +name = "namada_trans_token" +version = "0.29.0" +dependencies = [ + "namada_core", + "namada_storage", +] + +[[package]] +name = "namada_tx" +version = "0.29.0" +dependencies = [ + "ark-bls12-381", + "borsh", + "data-encoding", + "masp_primitives", + "namada_core", + "namada_gas", + "num-derive", + "num-traits", + "prost 0.12.3", + "prost-types 0.12.3", + "serde", + "serde_json", + "sha2 0.9.9", + "thiserror", + "tonic-build", +] + +[[package]] +name = "namada_tx_env" +version = "0.29.0" +dependencies = [ + "namada_core", + "namada_storage", +] + [[package]] name = "namada_tx_prelude" version = "0.29.0" dependencies = [ "borsh", - "borsh-ext", "masp_primitives", + "namada_account", "namada_core", + "namada_ethereum_bridge", + "namada_governance", + "namada_ibc", "namada_macros", + "namada_parameters", "namada_proof_of_stake", + "namada_storage", + "namada_token", + "namada_tx", + "namada_tx_env", "namada_vm_env", "sha2 0.9.9", "thiserror", @@ -3575,16 +3775,45 @@ dependencies = [ "namada_core", ] +[[package]] +name = "namada_vote_ext" +version = "0.29.0" +dependencies = [ + "borsh", + "namada_core", + "namada_tx", + "serde", +] + +[[package]] +name = "namada_vp_env" +version = "0.29.0" +dependencies = [ + "derivative", + "masp_primitives", + "namada_core", + "namada_storage", + "namada_tx", + "thiserror", +] + [[package]] name = "namada_vp_prelude" version = "0.29.0" dependencies = [ "borsh", - "borsh-ext", + "namada_account", "namada_core", + "namada_governance", + "namada_ibc", "namada_macros", + "namada_parameters", "namada_proof_of_stake", + "namada_storage", + "namada_token", + "namada_tx", "namada_vm_env", + "namada_vp_env", "sha2 0.9.9", "thiserror", ] diff --git a/wasm/wasm_source/Cargo.toml b/wasm/wasm_source/Cargo.toml index 73a713c60a..f894cf6160 100644 --- a/wasm/wasm_source/Cargo.toml +++ b/wasm/wasm_source/Cargo.toml @@ -36,7 +36,6 @@ tx_withdraw = ["namada_tx_prelude"] tx_update_steward_commission = ["namada_tx_prelude"] tx_resign_steward = ["namada_tx_prelude"] vp_implicit = ["namada_vp_prelude", "once_cell"] -vp_token = ["namada_vp_prelude"] vp_user = ["namada_vp_prelude", "once_cell"] [dependencies] diff --git a/wasm/wasm_source/src/tx_bridge_pool.rs b/wasm/wasm_source/src/tx_bridge_pool.rs index 84de025fd4..6b93bf672e 100644 --- a/wasm/wasm_source/src/tx_bridge_pool.rs +++ b/wasm/wasm_source/src/tx_bridge_pool.rs @@ -1,9 +1,7 @@ //! A tx for adding a transfer request across the Ethereum bridge //! into the bridge pool. -use borsh::BorshDeserialize; use eth_bridge::storage::{bridge_pool, native_erc20_key}; use eth_bridge_pool::{GasFee, PendingTransfer, TransferToEthereum}; -use namada_tx_prelude::borsh_ext::BorshSerializeExt; use namada_tx_prelude::*; #[transaction(gas = 1038546)] diff --git a/wasm/wasm_source/src/tx_init_account.rs b/wasm/wasm_source/src/tx_init_account.rs index 5cc2d3f29f..5b21855c4a 100644 --- a/wasm/wasm_source/src/tx_init_account.rs +++ b/wasm/wasm_source/src/tx_init_account.rs @@ -10,7 +10,7 @@ fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { ctx.set_commitment_sentinel(); err })?; - let tx_data = transaction::account::InitAccount::try_from_slice(&data[..]) + let tx_data = account::InitAccount::try_from_slice(&data[..]) .wrap_err("failed to decode InitAccount")?; debug_log!("apply_tx called to init a new established account"); diff --git a/wasm/wasm_source/src/tx_init_proposal.rs b/wasm/wasm_source/src/tx_init_proposal.rs index 64aa02bcb1..dd7018a399 100644 --- a/wasm/wasm_source/src/tx_init_proposal.rs +++ b/wasm/wasm_source/src/tx_init_proposal.rs @@ -8,9 +8,8 @@ fn apply_tx(ctx: &mut Ctx, tx: Tx) -> TxResult { ctx.set_commitment_sentinel(); err })?; - let tx_data = - transaction::governance::InitProposalData::try_from_slice(&data[..]) - .wrap_err("failed to decode InitProposalData")?; + let tx_data = governance::InitProposalData::try_from_slice(&data[..]) + .wrap_err("failed to decode InitProposalData")?; // Get the content from the referred to section let content = tx diff --git a/wasm/wasm_source/src/tx_transfer.rs b/wasm/wasm_source/src/tx_transfer.rs index e521be4f75..10ec51f30b 100644 --- a/wasm/wasm_source/src/tx_transfer.rs +++ b/wasm/wasm_source/src/tx_transfer.rs @@ -38,7 +38,7 @@ fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { }) .transpose()?; if let Some(shielded) = shielded { - token::masp_utils::handle_masp_tx(ctx, &transfer, &shielded)?; + token::utils::handle_masp_tx(ctx, &transfer, &shielded)?; update_masp_note_commitment_tree(&shielded)?; } Ok(()) diff --git a/wasm/wasm_source/src/tx_update_account.rs b/wasm/wasm_source/src/tx_update_account.rs index adfc1301c5..4919182ce5 100644 --- a/wasm/wasm_source/src/tx_update_account.rs +++ b/wasm/wasm_source/src/tx_update_account.rs @@ -2,7 +2,6 @@ //! This tx wraps the validity predicate inside `SignedTxData` as //! its input as declared in `shared` crate. -use namada_tx_prelude::key::pks_handle; use namada_tx_prelude::*; #[transaction(gas = 968137)] @@ -12,9 +11,8 @@ fn apply_tx(ctx: &mut Ctx, tx: Tx) -> TxResult { ctx.set_commitment_sentinel(); err })?; - let tx_data = - transaction::account::UpdateAccount::try_from_slice(&data[..]) - .wrap_err("failed to decode UpdateAccount")?; + let tx_data = account::UpdateAccount::try_from_slice(&data[..]) + .wrap_err("failed to decode UpdateAccount")?; let owner = &tx_data.addr; debug_log!("update VP for: {:#?}", tx_data.addr); @@ -42,15 +40,19 @@ fn apply_tx(ctx: &mut Ctx, tx: Tx) -> TxResult { } if let Some(threshold) = tx_data.threshold { - let threshold_key = key::threshold_key(owner); + let threshold_key = account::threshold_key(owner); ctx.write(&threshold_key, threshold)?; } if !tx_data.public_keys.is_empty() { - storage_api::account::clear_public_keys(ctx, owner)?; + account::clear_public_keys(ctx, owner)?; for (index, public_key) in tx_data.public_keys.iter().enumerate() { let index = index as u8; - pks_handle(owner).insert(ctx, index, public_key.clone())?; + account::pks_handle(owner).insert( + ctx, + index, + public_key.clone(), + )?; } } diff --git a/wasm/wasm_source/src/tx_vote_proposal.rs b/wasm/wasm_source/src/tx_vote_proposal.rs index 66f0c77bf1..bd68cd0c73 100644 --- a/wasm/wasm_source/src/tx_vote_proposal.rs +++ b/wasm/wasm_source/src/tx_vote_proposal.rs @@ -9,9 +9,8 @@ fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { ctx.set_commitment_sentinel(); err })?; - let tx_data = - transaction::governance::VoteProposalData::try_from_slice(&data[..]) - .wrap_err("failed to decode VoteProposalData")?; + let tx_data = governance::VoteProposalData::try_from_slice(&data[..]) + .wrap_err("failed to decode VoteProposalData")?; debug_log!("apply_tx called to vote a governance proposal"); diff --git a/wasm/wasm_source/src/vp_implicit.rs b/wasm/wasm_source/src/vp_implicit.rs index 8f36c31d3a..493cf0f672 100644 --- a/wasm/wasm_source/src/vp_implicit.rs +++ b/wasm/wasm_source/src/vp_implicit.rs @@ -34,13 +34,16 @@ enum KeyType<'a> { impl<'a> From<&'a storage::Key> for KeyType<'a> { fn from(key: &'a storage::Key) -> KeyType<'a> { - if let Some(address) = key::is_pks_key(key) { + if let Some(address) = account::is_pks_key(key) { Self::Pk(address) - } else if let Some([_, owner]) = token::is_any_token_balance_key(key) { + } else if let Some([_, owner]) = + token::storage_key::is_any_token_balance_key(key) + { Self::TokenBalance { owner } - } else if token::is_any_minted_balance_key(key).is_some() { + } else if token::storage_key::is_any_minted_balance_key(key).is_some() { Self::TokenMinted - } else if let Some(minter) = token::is_any_minter_key(key) { + } else if let Some(minter) = token::storage_key::is_any_minter_key(key) + { Self::TokenMinter(minter) } else if proof_of_stake::storage_key::is_pos_key(key) { Self::PoS @@ -53,7 +56,7 @@ impl<'a> From<&'a storage::Key> for KeyType<'a> { } else { Self::Unknown } - } else if token::is_masp_key(key) { + } else if token::storage_key::is_masp_key(key) { Self::Masp } else if ibc::is_ibc_key(key) { Self::Ibc diff --git a/wasm/wasm_source/src/vp_user.rs b/wasm/wasm_source/src/vp_user.rs index 29c861d04d..2aa503c76c 100644 --- a/wasm/wasm_source/src/vp_user.rs +++ b/wasm/wasm_source/src/vp_user.rs @@ -45,11 +45,14 @@ enum KeyType<'a> { impl<'a> From<&'a storage::Key> for KeyType<'a> { fn from(key: &'a storage::Key) -> KeyType<'a> { - if let Some([_, owner]) = token::is_any_token_balance_key(key) { + if let Some([_, owner]) = + token::storage_key::is_any_token_balance_key(key) + { Self::TokenBalance { owner } - } else if token::is_any_minted_balance_key(key).is_some() { + } else if token::storage_key::is_any_minted_balance_key(key).is_some() { Self::TokenMinted - } else if let Some(minter) = token::is_any_minter_key(key) { + } else if let Some(minter) = token::storage_key::is_any_minter_key(key) + { Self::TokenMinter(minter) } else if is_pos_key(key) { Self::PoS @@ -64,7 +67,7 @@ impl<'a> From<&'a storage::Key> for KeyType<'a> { Self::PgfSteward(address) } else if let Some(address) = key.is_validity_predicate() { Self::Vp(address) - } else if token::is_masp_key(key) { + } else if token::storage_key::is_masp_key(key) { Self::Masp } else if ibc::is_ibc_key(key) { Self::Ibc From 22a5ef838b77d65dd50ae4a9b6c91856452e9650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Thu, 4 Jan 2024 12:47:13 +0000 Subject: [PATCH 075/118] shared: fix build --- Cargo.lock | 13 + account/src/lib.rs | 2 +- .../lib/node/ledger/shell/finalize_block.rs | 12 +- apps/src/lib/node/ledger/shell/mod.rs | 10 +- core/src/types/account.rs | 80 +++++ core/src/types/key/mod.rs | 73 ----- core/src/types/mod.rs | 1 + ethereum_bridge/Cargo.toml | 2 + .../transactions/bridge_pool_roots.rs | 2 +- .../transactions/ethereum_events/events.rs | 2 +- .../transactions/ethereum_events/mod.rs | 2 +- .../src/protocol/transactions/read.rs | 2 +- .../src/protocol/transactions/update.rs | 2 +- .../src/protocol/transactions/votes/update.rs | 2 +- ethereum_bridge/src/storage/parameters.rs | 2 +- ethereum_bridge/src/test_utils.rs | 2 +- governance/src/cli/offline.rs | 3 +- governance/src/lib.rs | 2 +- light_sdk/src/transaction/account.rs | 2 +- light_sdk/src/transaction/bridge.rs | 2 +- light_sdk/src/transaction/governance.rs | 2 +- light_sdk/src/transaction/ibc.rs | 2 +- light_sdk/src/transaction/pgf.rs | 2 +- light_sdk/src/transaction/pos.rs | 2 +- light_sdk/src/transaction/transfer.rs | 2 +- proof_of_stake/Cargo.toml | 2 + proof_of_stake/src/epoched.rs | 2 +- proof_of_stake/src/tests/helpers.rs | 2 +- proof_of_stake/src/tests/state_machine.rs | 2 +- proof_of_stake/src/tests/state_machine_v2.rs | 2 +- proof_of_stake/src/tests/test_helper_fns.rs | 2 +- proof_of_stake/src/tests/test_pos.rs | 2 +- .../src/tests/test_slash_and_redel.rs | 2 +- proof_of_stake/src/tests/test_validator.rs | 2 +- sdk/Cargo.toml | 6 +- sdk/src/queries/mod.rs | 2 +- shared/Cargo.toml | 92 +++--- shared/src/ledger/governance/mod.rs | 55 ++-- shared/src/ledger/governance/utils.rs | 2 +- shared/src/ledger/ibc/mod.rs | 12 +- shared/src/ledger/mod.rs | 44 ++- .../ethereum_bridge/bridge_pool_vp.rs | 22 +- .../ledger/native_vp/ethereum_bridge/nut.rs | 18 +- .../ledger/native_vp/ethereum_bridge/vp.rs | 24 +- shared/src/ledger/native_vp/ibc/context.rs | 69 +++-- shared/src/ledger/native_vp/ibc/mod.rs | 31 +- shared/src/ledger/native_vp/masp.rs | 38 +-- shared/src/ledger/native_vp/mod.rs | 130 ++++---- shared/src/ledger/native_vp/multitoken.rs | 28 +- shared/src/ledger/native_vp/parameters.rs | 27 +- shared/src/ledger/native_vp/slash_fund.rs | 8 +- shared/src/ledger/pgf/mod.rs | 26 +- shared/src/ledger/pos/mod.rs | 3 +- shared/src/ledger/pos/vp.rs | 26 +- shared/src/ledger/protocol/mod.rs | 80 +++-- shared/src/ledger/storage/mod.rs | 4 +- shared/src/ledger/storage_api.rs | 3 - shared/src/ledger/vp_host_fns.rs | 70 +++-- shared/src/lib.rs | 2 +- shared/src/types/mod.rs | 4 +- shared/src/vm/host_env.rs | 291 +++++++++--------- shared/src/vm/prefix_iter.rs | 10 +- shared/src/vm/types.rs | 3 +- shared/src/vm/wasm/host_env.rs | 11 +- shared/src/vm/wasm/memory.rs | 4 +- shared/src/vm/wasm/run.rs | 22 +- shielded_token/src/storage_key.rs | 37 +++ state/Cargo.toml | 8 + state/src/lib.rs | 6 +- tx/src/lib.rs | 2 +- tx/src/types.rs | 3 +- tx_prelude/src/lib.rs | 3 +- vp_env/src/lib.rs | 5 +- 73 files changed, 809 insertions(+), 668 deletions(-) create mode 100644 core/src/types/account.rs delete mode 100644 shared/src/ledger/storage_api.rs diff --git a/Cargo.lock b/Cargo.lock index 98246c7519..096392b626 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4203,11 +4203,23 @@ dependencies = [ "loupe", "masp_primitives", "masp_proofs", + "namada_account", "namada_core", "namada_ethereum_bridge", + "namada_gas", + "namada_governance", + "namada_ibc", + "namada_parameters", "namada_proof_of_stake", "namada_sdk", + "namada_state", + "namada_storage", "namada_test_utils", + "namada_token", + "namada_tx", + "namada_tx_env", + "namada_vote_ext", + "namada_vp_env", "num256", "orion", "owo-colors", @@ -4584,6 +4596,7 @@ dependencies = [ "namada_core", "namada_governance", "namada_parameters", + "namada_state", "namada_storage", "namada_trans_token", "once_cell", diff --git a/account/src/lib.rs b/account/src/lib.rs index 212584aa83..e7f79a3e55 100644 --- a/account/src/lib.rs +++ b/account/src/lib.rs @@ -7,9 +7,9 @@ mod storage_key; mod types; use borsh::{BorshDeserialize, BorshSerialize}; +pub use namada_core::types::account::AccountPublicKeysMap; use namada_core::types::address::Address; use namada_core::types::key::common; -pub use namada_core::types::key::AccountPublicKeysMap; use serde::{Deserialize, Serialize}; pub use storage::*; pub use storage_key::*; diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 0e46577a0d..d96de7bbb0 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -3380,12 +3380,12 @@ mod test_finalize_block { ))); let fee_amount = wrapper.header().wrapper().unwrap().get_tx_fee().unwrap(); - let fee_amount = fee_amount - .to_amount( - &wrapper.header().wrapper().unwrap().fee.token, - &shell.wl_storage, - ) - .unwrap(); + let fee_amount = namada_token::denom_to_amount( + fee_amount, + &wrapper.header().wrapper().unwrap().fee.token, + &shell.wl_storage, + ) + .unwrap(); let signer_balance = storage_api::token::read_balance( &shell.wl_storage, diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index fc4e8a85a8..b228134fcd 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -1377,11 +1377,11 @@ where } }; - match wrapper - .fee - .amount_per_gas_unit - .to_amount(&wrapper.fee.token, &self.wl_storage) - { + match namada_token::denom_to_amount( + wrapper.fee.amount_per_gas_unit, + &wrapper.fee.token, + &self.wl_storage, + ) { Ok(amount_per_gas_unit) if amount_per_gas_unit < minimum_gas_price => { diff --git a/core/src/types/account.rs b/core/src/types/account.rs new file mode 100644 index 0000000000..7020315c73 --- /dev/null +++ b/core/src/types/account.rs @@ -0,0 +1,80 @@ +//! Account types + +use std::collections::{BTreeMap, HashMap}; + +use borsh::{BorshDeserialize, BorshSerialize}; +use serde::{Deserialize, Serialize}; + +use super::key::{common, RefTo}; +use crate::hints; + +#[derive( + Debug, + Clone, + BorshSerialize, + BorshDeserialize, + Serialize, + Deserialize, + Default, +)] +/// Holds the public key map data as a bimap for efficient quering +pub struct AccountPublicKeysMap { + /// Hashmap from public key to index + pub pk_to_idx: HashMap, + /// Hashmap from index key to public key + pub idx_to_pk: HashMap, +} + +impl FromIterator for AccountPublicKeysMap { + fn from_iter>(iter: T) -> Self { + let mut pk_to_idx = HashMap::new(); + let mut idx_to_pk = HashMap::new(); + + for (index, public_key) in iter.into_iter().enumerate() { + if hints::unlikely(index > u8::MAX as usize) { + panic!( + "Only up to 255 signers are allowed in a multisig account" + ); + } + pk_to_idx.insert(public_key.to_owned(), index as u8); + idx_to_pk.insert(index as u8, public_key.to_owned()); + } + + Self { + pk_to_idx, + idx_to_pk, + } + } +} + +impl AccountPublicKeysMap { + /// Retrive a public key from the index + pub fn get_public_key_from_index( + &self, + index: u8, + ) -> Option { + self.idx_to_pk.get(&index).cloned() + } + + /// Retrive the index of a public key + pub fn get_index_from_public_key( + &self, + public_key: &common::PublicKey, + ) -> Option { + self.pk_to_idx.get(public_key).cloned() + } + + /// Index the given set of secret keys + pub fn index_secret_keys( + &self, + secret_keys: Vec, + ) -> BTreeMap { + secret_keys + .into_iter() + .filter_map(|secret_key: common::SecretKey| { + self.get_index_from_public_key(&secret_key.ref_to()) + .map(|index| (index, secret_key)) + }) + .collect() + } +} diff --git a/core/src/types/key/mod.rs b/core/src/types/key/mod.rs index eb7baac984..8bfb51d80c 100644 --- a/core/src/types/key/mod.rs +++ b/core/src/types/key/mod.rs @@ -4,7 +4,6 @@ pub mod common; pub mod ed25519; pub mod secp256k1; -use std::collections::{BTreeMap, HashMap}; use std::fmt::{Debug, Display}; use std::hash::Hash; use std::str::FromStr; @@ -18,7 +17,6 @@ use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; use thiserror::Error; -use crate::hints; use crate::types::address; use crate::types::hash::{KeccakHasher, Sha256Hasher, StorageHasher}; use crate::types::keccak::{keccak_hash, KeccakHash}; @@ -75,77 +73,6 @@ pub enum ParseSecretKeyError { MismatchedScheme, } -#[derive( - Debug, - Clone, - BorshSerialize, - BorshDeserialize, - Serialize, - Deserialize, - Default, -)] -/// Holds the public key map data as a bimap for efficient quering -pub struct AccountPublicKeysMap { - /// Hashmap from public key to index - pub pk_to_idx: HashMap, - /// Hashmap from index key to public key - pub idx_to_pk: HashMap, -} - -impl FromIterator for AccountPublicKeysMap { - fn from_iter>(iter: T) -> Self { - let mut pk_to_idx = HashMap::new(); - let mut idx_to_pk = HashMap::new(); - - for (index, public_key) in iter.into_iter().enumerate() { - if hints::unlikely(index > u8::MAX as usize) { - panic!( - "Only up to 255 signers are allowed in a multisig account" - ); - } - pk_to_idx.insert(public_key.to_owned(), index as u8); - idx_to_pk.insert(index as u8, public_key.to_owned()); - } - - Self { - pk_to_idx, - idx_to_pk, - } - } -} - -impl AccountPublicKeysMap { - /// Retrive a public key from the index - pub fn get_public_key_from_index( - &self, - index: u8, - ) -> Option { - self.idx_to_pk.get(&index).cloned() - } - - /// Retrive the index of a public key - pub fn get_index_from_public_key( - &self, - public_key: &common::PublicKey, - ) -> Option { - self.pk_to_idx.get(public_key).cloned() - } - - /// Index the given set of secret keys - pub fn index_secret_keys( - &self, - secret_keys: Vec, - ) -> BTreeMap { - secret_keys - .into_iter() - .filter_map(|secret_key: common::SecretKey| { - self.get_index_from_public_key(&secret_key.ref_to()) - .map(|index| (index, secret_key)) - }) - .collect() - } -} - /// A value-to-value conversion that consumes the input value. pub trait RefTo { /// Performs the conversion. diff --git a/core/src/types/mod.rs b/core/src/types/mod.rs index d297b084d8..25e29e63da 100644 --- a/core/src/types/mod.rs +++ b/core/src/types/mod.rs @@ -1,5 +1,6 @@ //! Types definitions. +pub mod account; pub mod address; pub mod chain; pub mod dec; diff --git a/ethereum_bridge/Cargo.toml b/ethereum_bridge/Cargo.toml index ca85d3ac72..65a158c6da 100644 --- a/ethereum_bridge/Cargo.toml +++ b/ethereum_bridge/Cargo.toml @@ -47,6 +47,8 @@ tracing = "0.1.30" # Added "testing" feature. namada_core = {path = "../core", default-features = false, features = ["ethers-derive", "testing"]} namada_proof_of_stake = {path = "../proof_of_stake", default-features = false, features = ["testing"]} +namada_state = { path = "../state", features = ["testing"] } + assert_matches.workspace = true data-encoding.workspace = true ethabi.workspace = true diff --git a/ethereum_bridge/src/protocol/transactions/bridge_pool_roots.rs b/ethereum_bridge/src/protocol/transactions/bridge_pool_roots.rs index 9ac83fae05..56918a9e43 100644 --- a/ethereum_bridge/src/protocol/transactions/bridge_pool_roots.rs +++ b/ethereum_bridge/src/protocol/transactions/bridge_pool_roots.rs @@ -225,7 +225,6 @@ mod test_apply_bp_roots_to_storage { use assert_matches::assert_matches; use borsh::BorshDeserialize; - use namada_core::ledger::storage::testing::TestWlStorage; use namada_core::proto::{SignableEthMessage, Signed}; use namada_core::types::address; use namada_core::types::ethereum_events::Uint; @@ -234,6 +233,7 @@ mod test_apply_bp_roots_to_storage { use namada_core::types::voting_power::FractionalVotingPower; use namada_proof_of_stake::parameters::OwnedPosParams; use namada_proof_of_stake::storage::write_pos_params; + use namada_state::testing::TestWlStorage; use namada_storage::StorageRead; use namada_vote_ext::bridge_pool_roots; diff --git a/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs b/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs index e06f47d657..07d820191d 100644 --- a/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs +++ b/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs @@ -583,7 +583,6 @@ mod tests { use borsh_ext::BorshSerializeExt; use eyre::Result; use namada_core::ledger::storage::mockdb::MockDBWriteBatch; - use namada_core::ledger::storage::testing::TestWlStorage; use namada_core::ledger::storage::types::encode; use namada_core::types::address::testing::gen_implicit_address; use namada_core::types::address::{gen_established_address, nam, wnam}; @@ -595,6 +594,7 @@ mod tests { use namada_core::types::token::Amount; use namada_core::types::{address, eth_bridge_pool}; use namada_parameters::{update_epoch_parameter, EpochDuration}; + use namada_state::testing::TestWlStorage; use super::*; use crate::storage::bridge_pool::get_pending_key; diff --git a/ethereum_bridge/src/protocol/transactions/ethereum_events/mod.rs b/ethereum_bridge/src/protocol/transactions/ethereum_events/mod.rs index 02bd26a95e..1193381106 100644 --- a/ethereum_bridge/src/protocol/transactions/ethereum_events/mod.rs +++ b/ethereum_bridge/src/protocol/transactions/ethereum_events/mod.rs @@ -301,7 +301,7 @@ mod tests { use borsh::BorshDeserialize; use namada_core::ledger::storage::mockdb::MockDBWriteBatch; - use namada_core::ledger::storage::testing::TestWlStorage; + use namada_state::testing::TestWlStorage; use namada_core::types::address; use namada_core::types::ethereum_events::testing::{ arbitrary_amount, arbitrary_eth_address, arbitrary_nonce, diff --git a/ethereum_bridge/src/protocol/transactions/read.rs b/ethereum_bridge/src/protocol/transactions/read.rs index 9aae435d0f..a17339a4bb 100644 --- a/ethereum_bridge/src/protocol/transactions/read.rs +++ b/ethereum_bridge/src/protocol/transactions/read.rs @@ -55,9 +55,9 @@ where mod tests { use assert_matches::assert_matches; use borsh_ext::BorshSerializeExt; - use namada_core::ledger::storage::testing::TestWlStorage; use namada_core::types::storage; use namada_core::types::token::Amount; + use namada_state::testing::TestWlStorage; use namada_storage::StorageWrite; use crate::protocol::transactions::read; diff --git a/ethereum_bridge/src/protocol/transactions/update.rs b/ethereum_bridge/src/protocol/transactions/update.rs index 2be80c6cea..ea9f1a5877 100644 --- a/ethereum_bridge/src/protocol/transactions/update.rs +++ b/ethereum_bridge/src/protocol/transactions/update.rs @@ -45,8 +45,8 @@ mod tests { use borsh::BorshDeserialize; use borsh_ext::BorshSerializeExt; use eyre::{eyre, Result}; - use namada_core::ledger::storage::testing::TestWlStorage; use namada_core::types::storage; + use namada_state::testing::TestWlStorage; use namada_storage::{StorageRead, StorageWrite}; #[test] diff --git a/ethereum_bridge/src/protocol/transactions/votes/update.rs b/ethereum_bridge/src/protocol/transactions/votes/update.rs index 4335d864af..47027cde41 100644 --- a/ethereum_bridge/src/protocol/transactions/votes/update.rs +++ b/ethereum_bridge/src/protocol/transactions/votes/update.rs @@ -209,10 +209,10 @@ fn keys_changed( mod tests { use std::collections::BTreeMap; - use namada_core::ledger::storage::testing::TestWlStorage; use namada_core::types::address; use namada_core::types::ethereum_events::EthereumEvent; use namada_core::types::voting_power::FractionalVotingPower; + use namada_state::testing::TestWlStorage; use self::helpers::{default_event, default_total_stake, TallyParams}; use super::*; diff --git a/ethereum_bridge/src/storage/parameters.rs b/ethereum_bridge/src/storage/parameters.rs index 9f1357388b..e9e1ae65a4 100644 --- a/ethereum_bridge/src/storage/parameters.rs +++ b/ethereum_bridge/src/storage/parameters.rs @@ -369,8 +369,8 @@ where mod tests { use borsh_ext::BorshSerializeExt; use eyre::Result; - use namada_core::ledger::storage::testing::TestWlStorage; use namada_core::types::ethereum_events::EthAddress; + use namada_state::testing::TestWlStorage; use super::*; diff --git a/ethereum_bridge/src/test_utils.rs b/ethereum_bridge/src/test_utils.rs index c56ebfa083..de74d10d36 100644 --- a/ethereum_bridge/src/test_utils.rs +++ b/ethereum_bridge/src/test_utils.rs @@ -3,7 +3,6 @@ use std::collections::HashMap; use std::num::NonZeroU64; -use namada_core::ledger::storage::testing::TestWlStorage; use namada_core::types::address::{self, wnam, Address}; use namada_core::types::dec::Dec; use namada_core::types::ethereum_events::EthAddress; @@ -18,6 +17,7 @@ use namada_proof_of_stake::{ become_validator, bond_tokens, compute_and_store_total_consensus_stake, staking_token_address, BecomeValidator, }; +use namada_state::testing::TestWlStorage; use namada_storage::token::credit_tokens; use namada_storage::{StorageRead, StorageWrite}; diff --git a/governance/src/cli/offline.rs b/governance/src/cli/offline.rs index 6938b3fca9..358c6f49aa 100644 --- a/governance/src/cli/offline.rs +++ b/governance/src/cli/offline.rs @@ -3,9 +3,10 @@ use std::fs::{File, ReadDir}; use std::path::PathBuf; use namada_core::borsh::{BorshDeserialize, BorshSerialize, BorshSerializeExt}; +use namada_core::types::account::AccountPublicKeysMap; use namada_core::types::address::Address; use namada_core::types::hash::Hash; -use namada_core::types::key::{common, AccountPublicKeysMap, RefTo, SigScheme}; +use namada_core::types::key::{common, RefTo, SigScheme}; use namada_core::types::sign::SignatureIndex; use namada_core::types::storage::Epoch; use serde::{Deserialize, Serialize}; diff --git a/governance/src/lib.rs b/governance/src/lib.rs index 9ab79eb344..f30dffe5fd 100644 --- a/governance/src/lib.rs +++ b/governance/src/lib.rs @@ -14,7 +14,7 @@ pub mod utils; pub use storage::proposal::{InitProposalData, ProposalType, VoteProposalData}; pub use storage::vote::{StorageProposalVote, VoteType}; -pub use storage::{init_proposal, vote_proposal}; +pub use storage::{init_proposal, is_proposal_accepted, vote_proposal}; /// The governance internal address pub const ADDRESS: Address = address::GOV; diff --git a/light_sdk/src/transaction/account.rs b/light_sdk/src/transaction/account.rs index 146915881a..bbf2e5acb7 100644 --- a/light_sdk/src/transaction/account.rs +++ b/light_sdk/src/transaction/account.rs @@ -1,7 +1,7 @@ -use namada_core::proto::Tx; use namada_core::types::address::Address; use namada_core::types::hash::Hash; use namada_core::types::key::common; +use namada_tx::Tx; use super::GlobalArgs; use crate::transaction; diff --git a/light_sdk/src/transaction/bridge.rs b/light_sdk/src/transaction/bridge.rs index c41407f31e..98a7ca4f93 100644 --- a/light_sdk/src/transaction/bridge.rs +++ b/light_sdk/src/transaction/bridge.rs @@ -1,7 +1,7 @@ -use namada_core::proto::Tx; pub use namada_core::types::eth_bridge_pool::{GasFee, TransferToEthereum}; use namada_core::types::hash::Hash; use namada_core::types::key::common; +use namada_tx::Tx; use super::GlobalArgs; use crate::transaction; diff --git a/light_sdk/src/transaction/governance.rs b/light_sdk/src/transaction/governance.rs index ca6183569e..6bb24ac1c0 100644 --- a/light_sdk/src/transaction/governance.rs +++ b/light_sdk/src/transaction/governance.rs @@ -1,10 +1,10 @@ use namada_core::ledger::governance::storage::proposal::ProposalType; use namada_core::ledger::governance::storage::vote::StorageProposalVote; -use namada_core::proto::Tx; use namada_core::types::address::Address; use namada_core::types::hash::Hash; use namada_core::types::key::common; use namada_core::types::storage::Epoch; +use namada_tx::Tx; use super::GlobalArgs; use crate::transaction; diff --git a/light_sdk/src/transaction/ibc.rs b/light_sdk/src/transaction/ibc.rs index 98a28aa226..8496c9bb7a 100644 --- a/light_sdk/src/transaction/ibc.rs +++ b/light_sdk/src/transaction/ibc.rs @@ -2,10 +2,10 @@ use std::str::FromStr; pub use namada_core::ibc::apps::transfer::types::msgs::transfer::MsgTransfer; use namada_core::ibc::primitives::Msg; -use namada_core::proto::Tx; use namada_core::types::hash::Hash; use namada_core::types::key::common; use namada_core::types::time::DateTimeUtc; +use namada_tx::Tx; use super::GlobalArgs; use crate::transaction; diff --git a/light_sdk/src/transaction/pgf.rs b/light_sdk/src/transaction/pgf.rs index 7a53a7e4ca..24b7b038dd 100644 --- a/light_sdk/src/transaction/pgf.rs +++ b/light_sdk/src/transaction/pgf.rs @@ -1,10 +1,10 @@ use std::collections::HashMap; -use namada_core::proto::Tx; use namada_core::types::address::Address; use namada_core::types::dec::Dec; use namada_core::types::hash::Hash; use namada_core::types::key::common; +use namada_tx::Tx; use super::GlobalArgs; use crate::transaction; diff --git a/light_sdk/src/transaction/pos.rs b/light_sdk/src/transaction/pos.rs index a436e1588a..9e370889b4 100644 --- a/light_sdk/src/transaction/pos.rs +++ b/light_sdk/src/transaction/pos.rs @@ -1,4 +1,3 @@ -use namada_core::proto::Tx; use namada_core::types::address::Address; use namada_core::types::dec::Dec; use namada_core::types::hash::Hash; @@ -6,6 +5,7 @@ use namada_core::types::key::{common, secp256k1}; use namada_core::types::token; use namada_core::types::token::Amount; use namada_core::types::transaction::pos::Redelegation; +use namada_tx::Tx; use super::GlobalArgs; use crate::transaction; diff --git a/light_sdk/src/transaction/transfer.rs b/light_sdk/src/transaction/transfer.rs index cce45f1e91..c5ae2864d9 100644 --- a/light_sdk/src/transaction/transfer.rs +++ b/light_sdk/src/transaction/transfer.rs @@ -1,9 +1,9 @@ use borsh_ext::BorshSerializeExt; -use namada_core::proto::Tx; use namada_core::types::address::Address; use namada_core::types::hash::Hash; use namada_core::types::key::common; use namada_core::types::token::DenominatedAmount; +use namada_tx::Tx; use super::GlobalArgs; use crate::transaction; diff --git a/proof_of_stake/Cargo.toml b/proof_of_stake/Cargo.toml index 53edb78f1c..9b03792421 100644 --- a/proof_of_stake/Cargo.toml +++ b/proof_of_stake/Cargo.toml @@ -37,6 +37,8 @@ tracing.workspace = true [dev-dependencies] namada_core = { path = "../core", features = ["testing"] } +namada_state = { path = "../state", features = ["testing"] } + assert_matches.workspace = true itertools.workspace = true proptest.workspace = true diff --git a/proof_of_stake/src/epoched.rs b/proof_of_stake/src/epoched.rs index 50160421ab..5db83af399 100644 --- a/proof_of_stake/src/epoched.rs +++ b/proof_of_stake/src/epoched.rs @@ -1085,10 +1085,10 @@ pub trait EpochOffset: #[cfg(test)] mod test { - use namada_core::ledger::storage::testing::TestWlStorage; use namada_core::types::address::testing::established_address_1; use namada_core::types::dec::Dec; use namada_core::types::{key, token}; + use namada_state::testing::TestWlStorage; use test_log::test; use super::*; diff --git a/proof_of_stake/src/tests/helpers.rs b/proof_of_stake/src/tests/helpers.rs index ddf8bba4c6..8a54eddd2e 100644 --- a/proof_of_stake/src/tests/helpers.rs +++ b/proof_of_stake/src/tests/helpers.rs @@ -1,7 +1,6 @@ use std::cmp::max; use std::ops::Range; -use namada_core::ledger::storage::testing::TestWlStorage; use namada_core::types::address::testing::address_from_simple_seed; use namada_core::types::dec::Dec; use namada_core::types::key::testing::common_sk_from_simple_seed; @@ -9,6 +8,7 @@ use namada_core::types::key::{self, RefTo}; use namada_core::types::storage::Epoch; use namada_core::types::token; use namada_core::types::token::testing::arb_amount_non_zero_ceiled; +use namada_state::testing::TestWlStorage; use proptest::strategy::{Just, Strategy}; use crate::parameters::testing::arb_pos_params; diff --git a/proof_of_stake/src/tests/state_machine.rs b/proof_of_stake/src/tests/state_machine.rs index 8732a7e02a..6fc0f911f6 100644 --- a/proof_of_stake/src/tests/state_machine.rs +++ b/proof_of_stake/src/tests/state_machine.rs @@ -6,7 +6,6 @@ use std::ops::Deref; use assert_matches::assert_matches; use itertools::Itertools; -use namada_core::ledger::storage::testing::TestWlStorage; use namada_core::types::address::{self, Address}; use namada_core::types::dec::Dec; use namada_core::types::key; @@ -14,6 +13,7 @@ use namada_core::types::key::common::PublicKey; use namada_core::types::storage::Epoch; use namada_core::types::token::Change; use namada_namada_trans_token::read_balance; +use namada_state::testing::TestWlStorage; use namada_storage::collections::lazy_map::{ Collectable, NestedSubKey, SubKey, }; diff --git a/proof_of_stake/src/tests/state_machine_v2.rs b/proof_of_stake/src/tests/state_machine_v2.rs index 2d3baf0ae2..a349eb5516 100644 --- a/proof_of_stake/src/tests/state_machine_v2.rs +++ b/proof_of_stake/src/tests/state_machine_v2.rs @@ -7,7 +7,6 @@ use std::{cmp, mem}; use assert_matches::assert_matches; use derivative::Derivative; use itertools::Itertools; -use namada_core::ledger::storage::testing::TestWlStorage; use namada_core::types::address::{self, Address}; use namada_core::types::dec::Dec; use namada_core::types::key; @@ -15,6 +14,7 @@ use namada_core::types::key::common::PublicKey; use namada_core::types::storage::Epoch; use namada_core::types::token::Change; use namada_namada_trans_token::read_balance; +use namada_state::testing::TestWlStorage; use namada_storage::collections::lazy_map::{NestedSubKey, SubKey}; use namada_storage::{token, StorageRead}; use proptest::prelude::*; diff --git a/proof_of_stake/src/tests/test_helper_fns.rs b/proof_of_stake/src/tests/test_helper_fns.rs index 594965fe43..f270e6e0dc 100644 --- a/proof_of_stake/src/tests/test_helper_fns.rs +++ b/proof_of_stake/src/tests/test_helper_fns.rs @@ -1,6 +1,5 @@ use std::collections::{BTreeMap, BTreeSet}; -use namada_core::ledger::storage::testing::TestWlStorage; use namada_core::ledger::storage_api::collections::lazy_map::NestedMap; use namada_core::ledger::storage_api::collections::LazyCollection; use namada_core::types::address::testing::{ @@ -9,6 +8,7 @@ use namada_core::types::address::testing::{ use namada_core::types::dec::Dec; use namada_core::types::storage::{Epoch, Key}; use namada_core::types::token; +use namada_state::testing::TestWlStorage; use crate::slashing::{ apply_list_slashes, compute_amount_after_slashing_unbond, diff --git a/proof_of_stake/src/tests/test_pos.rs b/proof_of_stake/src/tests/test_pos.rs index a37268a1a6..027d5cdc99 100644 --- a/proof_of_stake/src/tests/test_pos.rs +++ b/proof_of_stake/src/tests/test_pos.rs @@ -2,7 +2,6 @@ use std::collections::{BTreeMap, HashSet}; -use namada_core::ledger::storage::testing::TestWlStorage; use namada_core::ledger::storage_api::collections::lazy_map::Collectable; use namada_core::ledger::storage_api::token::{credit_tokens, read_balance}; use namada_core::ledger::storage_api::StorageRead; @@ -14,6 +13,7 @@ use namada_core::types::key::testing::{ use namada_core::types::key::RefTo; use namada_core::types::storage::{BlockHeight, Epoch}; use namada_core::types::{address, key, token}; +use namada_state::testing::TestWlStorage; use proptest::prelude::*; use proptest::test_runner::Config; // Use `RUST_LOG=info` (or another tracing level) and `--nocapture` to see diff --git a/proof_of_stake/src/tests/test_slash_and_redel.rs b/proof_of_stake/src/tests/test_slash_and_redel.rs index 17b73494b6..587c0f12cf 100644 --- a/proof_of_stake/src/tests/test_slash_and_redel.rs +++ b/proof_of_stake/src/tests/test_slash_and_redel.rs @@ -2,7 +2,6 @@ use std::ops::Deref; use std::str::FromStr; use assert_matches::assert_matches; -use namada_core::ledger::storage::testing::TestWlStorage; use namada_core::ledger::storage_api::collections::lazy_map::Collectable; use namada_core::ledger::storage_api::token::{credit_tokens, read_balance}; use namada_core::ledger::storage_api::StorageRead; @@ -10,6 +9,7 @@ use namada_core::types::dec::Dec; use namada_core::types::storage::{BlockHeight, Epoch}; use namada_core::types::token::NATIVE_MAX_DECIMAL_PLACES; use namada_core::types::{address, token}; +use namada_state::testing::TestWlStorage; use proptest::prelude::*; use proptest::test_runner::Config; // Use `RUST_LOG=info` (or another tracing level) and `--nocapture` to see diff --git a/proof_of_stake/src/tests/test_validator.rs b/proof_of_stake/src/tests/test_validator.rs index 418c9527e5..c2329ac91e 100644 --- a/proof_of_stake/src/tests/test_validator.rs +++ b/proof_of_stake/src/tests/test_validator.rs @@ -1,6 +1,5 @@ use std::cmp::min; -use namada_core::ledger::storage::testing::TestWlStorage; use namada_core::ledger::storage::WlStorage; use namada_core::ledger::storage_api::collections::lazy_map; use namada_core::ledger::storage_api::token::credit_tokens; @@ -13,6 +12,7 @@ use namada_core::types::key::testing::{ use namada_core::types::key::{self, common, RefTo}; use namada_core::types::storage::Epoch; use namada_core::types::token; +use namada_state::testing::TestWlStorage; use proptest::prelude::*; use proptest::test_runner::Config; // Use `RUST_LOG=info` (or another tracing level) and `--nocapture` to see diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 843a44b295..e8fb80ae02 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -104,8 +104,6 @@ tokio = { workspace = true, default-features = false, features = ["sync"] } wasmtimer = "0.2.0" [dev-dependencies] -assert_matches.workspace = true -base58.workspace = true namada_core = { path = "../core", default-features = false, features = [ "rand", "testing", @@ -116,6 +114,10 @@ namada_ethereum_bridge = { path = "../ethereum_bridge", default-features = false namada_proof_of_stake = { path = "../proof_of_stake", default-features = false, features = [ "testing", ] } +namada_state = { path = "../state", features = ["testing"] } namada_test_utils = { path = "../test_utils" } + +assert_matches.workspace = true +base58.workspace = true proptest.workspace = true tempfile.workspace = true diff --git a/sdk/src/queries/mod.rs b/sdk/src/queries/mod.rs index 1813226039..8e69a1d653 100644 --- a/sdk/src/queries/mod.rs +++ b/sdk/src/queries/mod.rs @@ -97,8 +97,8 @@ pub fn require_no_data(request: &RequestQuery) -> namada_storage::Result<()> { #[cfg(any(test, feature = "testing"))] mod testing { - use namada_core::ledger::storage::testing::TestWlStorage; use namada_core::types::storage::BlockHeight; + use namada_state::testing::TestWlStorage; use tendermint_rpc::Response; use super::*; diff --git a/shared/Cargo.toml b/shared/Cargo.toml index 48572b2516..fba5c6299e 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -16,9 +16,7 @@ version.workspace = true [features] default = ["namada-sdk", "wasm-runtime"] -mainnet = [ - "namada_core/mainnet", -] +mainnet = ["namada_core/mainnet"] std = ["fd-lock"] wasm-runtime = [ "namada_core/wasm-runtime", @@ -34,10 +32,7 @@ wasm-runtime = [ "wasmer", ] # Enable queries support for an async client -async-client = [ - "async-trait", - "namada_sdk/async-client" -] +async-client = ["async-trait", "namada_sdk/async-client"] # Requires async traits to be safe to send across threads async-send = [] @@ -49,9 +44,7 @@ tendermint-rpc = [ "namada_sdk/tendermint-rpc", ] # tendermint-rpc HttpClient -http-client = [ - "tendermint-rpc/http-client" -] +http-client = ["tendermint-rpc/http-client"] # for integration tests and test utilities testing = [ @@ -59,6 +52,7 @@ testing = [ "namada_ethereum_bridge/testing", "namada_proof_of_stake/testing", "namada_sdk/testing", + "namada_state/testing", "async-client", "proptest", "tempfile", @@ -70,17 +64,27 @@ namada-sdk = [ "namada_sdk/namada-sdk", ] -multicore = [ - "masp_proofs/multicore", - "namada_sdk/multicore", -] +multicore = ["masp_proofs/multicore", "namada_sdk/multicore"] [dependencies] -namada_core = {path = "../core", default-features = false, features = ["rand"]} -namada_sdk = {path = "../sdk", default-features = false} -namada_proof_of_stake = {path = "../proof_of_stake", default-features = false} -namada_ethereum_bridge = {path = "../ethereum_bridge", default-features = false} -async-trait = {version = "0.1.51", optional = true} +namada_account = { path = "../account" } +namada_core = { path = "../core" } +namada_ethereum_bridge = { path = "../ethereum_bridge", default-features = false } +namada_gas = { path = "../gas" } +namada_governance = { path = "../governance" } +namada_ibc = { path = "../ibc" } +namada_parameters = { path = "../parameters" } +namada_proof_of_stake = { path = "../proof_of_stake" } +namada_sdk = { path = "../sdk", default-features = false } +namada_state = { path = "../state" } +namada_storage = { path = "../storage" } +namada_token = { path = "../token" } +namada_tx = { path = "../tx" } +namada_tx_env = { path = "../tx_env" } +namada_vote_ext = { path = "../vote_ext" } +namada_vp_env = { path = "../vp_env" } + +async-trait = { version = "0.1.51", optional = true } bimap.workspace = true borsh.workspace = true borsh-ext.workspace = true @@ -95,56 +99,66 @@ eyre.workspace = true fd-lock = { workspace = true, optional = true } futures.workspace = true itertools.workspace = true -loupe = {version = "0.1.3", optional = true} +loupe = { version = "0.1.3", optional = true } masp_primitives.workspace = true masp_proofs.workspace = true num256.workspace = true orion.workspace = true owo-colors = "3.5.0" -parity-wasm = {version = "0.45.0", features = ["sign_ext"], optional = true} +parity-wasm = { version = "0.45.0", features = ["sign_ext"], optional = true } parse_duration = "2.1.1" paste.workspace = true -proptest = {workspace = true, optional = true} +proptest = { workspace = true, optional = true } prost.workspace = true rand.workspace = true rand_core.workspace = true -rayon = {version = "=1.5.3", optional = true} +rayon = { version = "=1.5.3", optional = true } ripemd.workspace = true serde.workspace = true serde_json.workspace = true sha2.workspace = true slip10_ed25519.workspace = true -tempfile = {version = "3.2.0", optional = true} -tendermint-rpc = {workspace = true, optional = true} +tempfile = { version = "3.2.0", optional = true } +tendermint-rpc = { workspace = true, optional = true } thiserror.workspace = true tiny-bip39.workspace = true tiny-hderive.workspace = true toml.workspace = true tracing.workspace = true -wasm-instrument = {version = "0.4.0", features = ["sign_ext"], optional = true} -wasmer = {git = "https://github.com/heliaxdev/wasmer", rev = "833721a1b21cd192e7f044abe2139d03ba291149", optional = true} -wasmer-cache = {git = "https://github.com/heliaxdev/wasmer", rev = "833721a1b21cd192e7f044abe2139d03ba291149", optional = true} -wasmer-compiler-singlepass = {git = "https://github.com/heliaxdev/wasmer", rev = "833721a1b21cd192e7f044abe2139d03ba291149", optional = true} -wasmer-engine-dylib = {git = "https://github.com/heliaxdev/wasmer", rev = "833721a1b21cd192e7f044abe2139d03ba291149", optional = true} -wasmer-engine-universal = {git = "https://github.com/heliaxdev/wasmer", rev = "833721a1b21cd192e7f044abe2139d03ba291149", optional = true} -wasmer-vm = {git = "https://github.com/heliaxdev/wasmer", rev = "833721a1b21cd192e7f044abe2139d03ba291149", optional = true} +wasm-instrument = { version = "0.4.0", features = [ + "sign_ext", +], optional = true } +wasmer = { git = "https://github.com/heliaxdev/wasmer", rev = "833721a1b21cd192e7f044abe2139d03ba291149", optional = true } +wasmer-cache = { git = "https://github.com/heliaxdev/wasmer", rev = "833721a1b21cd192e7f044abe2139d03ba291149", optional = true } +wasmer-compiler-singlepass = { git = "https://github.com/heliaxdev/wasmer", rev = "833721a1b21cd192e7f044abe2139d03ba291149", optional = true } +wasmer-engine-dylib = { git = "https://github.com/heliaxdev/wasmer", rev = "833721a1b21cd192e7f044abe2139d03ba291149", optional = true } +wasmer-engine-universal = { git = "https://github.com/heliaxdev/wasmer", rev = "833721a1b21cd192e7f044abe2139d03ba291149", optional = true } +wasmer-vm = { git = "https://github.com/heliaxdev/wasmer", rev = "833721a1b21cd192e7f044abe2139d03ba291149", optional = true } # Greater versions break in `test_tx_stack_limiter` and `test_vp_stack_limiter` wat = "=1.0.71" wasmparser.workspace = true zeroize.workspace = true [target.'cfg(not(target_family = "wasm"))'.dependencies] -tokio = {workspace = true, features = ["full"]} +tokio = { workspace = true, features = ["full"] } [target.'cfg(target_family = "wasm")'.dependencies] -tokio = {workspace = true, default-features = false, features = ["sync"]} +tokio = { workspace = true, default-features = false, features = ["sync"] } wasmtimer = "0.2.0" [dev-dependencies] -namada_core = {path = "../core", default-features = false, features = ["testing"]} -namada_ethereum_bridge = {path = "../ethereum_bridge", default-features = false, features = ["testing"]} -namada_proof_of_stake = {path = "../proof_of_stake", default-features = false, features = ["testing"]} -namada_test_utils = {path = "../test_utils"} +namada_core = { path = "../core", default-features = false, features = [ + "testing", +] } +namada_ethereum_bridge = { path = "../ethereum_bridge", default-features = false, features = [ + "testing", +] } +namada_proof_of_stake = { path = "../proof_of_stake", default-features = false, features = [ + "testing", +] } +namada_state = { path = "../state", features = ["testing"] } +namada_test_utils = { path = "../test_utils" } + assert_matches.workspace = true async-trait.workspace = true base58.workspace = true @@ -155,5 +169,5 @@ pretty_assertions.workspace = true proptest.workspace = true tempfile.workspace = true test-log.workspace = true -tokio = {workspace = true, features = ["rt", "macros"]} +tokio = { workspace = true, features = ["rt", "macros"] } tracing-subscriber.workspace = true diff --git a/shared/src/ledger/governance/mod.rs b/shared/src/ledger/governance/mod.rs index 610aaa831f..b6b93116e6 100644 --- a/shared/src/ledger/governance/mod.rs +++ b/shared/src/ledger/governance/mod.rs @@ -5,27 +5,22 @@ pub mod utils; use std::collections::BTreeSet; use borsh::BorshDeserialize; -use namada_core::ledger::governance::storage::keys as gov_storage; -use namada_core::ledger::governance::storage::proposal::{ - AddRemove, ProposalType, -}; -use namada_core::ledger::governance::storage::vote::StorageProposalVote; -use namada_core::ledger::governance::utils::is_valid_validator_voting_period; -use namada_core::ledger::storage; -use namada_core::ledger::storage_api::account; -use namada_core::ledger::storage_api::governance::is_proposal_accepted; -use namada_core::ledger::vp_env::VpEnv; -use namada_core::proto::Tx; +use namada_governance::storage::proposal::{AddRemove, ProposalType}; +use namada_governance::storage::vote::StorageProposalVote; +use namada_governance::storage::{is_proposal_accepted, keys as gov_storage}; +use namada_governance::utils::is_valid_validator_voting_period; use namada_proof_of_stake::is_validator; +use namada_storage::StorageRead; +use namada_token as token; +use namada_tx::Tx; +use namada_vp_env::VpEnv; use thiserror::Error; use self::utils::ReadType; use crate::ledger::native_vp::{Ctx, NativeVp}; -use crate::ledger::storage_api::StorageRead; use crate::ledger::{native_vp, pos}; use crate::types::address::{Address, InternalAddress}; use crate::types::storage::{Epoch, Key}; -use crate::types::token; use crate::vm::WasmCacheAccess; /// for handling Governance NativeVP errors @@ -53,8 +48,8 @@ pub enum Error { /// Governance VP pub struct GovernanceVp<'a, DB, H, CA> where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, - H: storage::StorageHasher, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, + H: namada_state::StorageHasher, CA: WasmCacheAccess, { /// Context to interact with the host structures. @@ -63,8 +58,8 @@ where impl<'a, DB, H, CA> NativeVp for GovernanceVp<'a, DB, H, CA> where - DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, - H: 'static + storage::StorageHasher, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, + H: 'static + namada_state::StorageHasher, CA: 'static + WasmCacheAccess, { type Error = Error; @@ -139,8 +134,8 @@ where impl<'a, DB, H, CA> GovernanceVp<'a, DB, H, CA> where - DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, - H: 'static + storage::StorageHasher, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, + H: 'static + namada_state::StorageHasher, CA: 'static + WasmCacheAccess, { fn is_valid_key_set(&self, keys: &BTreeSet) -> Result<(bool, u64)> { @@ -511,8 +506,10 @@ where native_token_address: &Address, ) -> Result { let funds_key = gov_storage::get_funds_key(proposal_id); - let balance_key = - token::balance_key(native_token_address, self.ctx.address); + let balance_key = token::storage_key::balance_key( + native_token_address, + self.ctx.address, + ); let min_funds_parameter_key = gov_storage::get_min_proposal_fund_key(); let min_funds_parameter: token::Amount = @@ -534,8 +531,10 @@ where /// Validate a balance key fn is_valid_balance(&self, native_token_address: &Address) -> Result { - let balance_key = - token::balance_key(native_token_address, self.ctx.address); + let balance_key = token::storage_key::balance_key( + native_token_address, + self.ctx.address, + ); let min_funds_parameter_key = gov_storage::get_min_proposal_fund_key(); let pre_balance: Option = @@ -569,7 +568,7 @@ where let author = self.force_read(&author_key, ReadType::Post)?; let author_exists = - account::exists(&self.ctx.pre(), &author).unwrap_or(false); + namada_account::exists(&self.ctx.pre(), &author).unwrap_or(false); Ok(author_exists && verifiers.contains(&author)) } @@ -615,8 +614,8 @@ where delegation_address: &Address, ) -> Result where - DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, - H: 'static + storage::StorageHasher, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, + H: 'static + namada_state::StorageHasher, CA: 'static + WasmCacheAccess, { if !address.eq(delegation_address) { @@ -742,7 +741,9 @@ impl KeyType { KeyType::COUNTER } else if gov_storage::is_parameter_key(key) { KeyType::PARAMETER - } else if token::is_balance_key(native_token, key).is_some() { + } else if token::storage_key::is_balance_key(native_token, key) + .is_some() + { KeyType::BALANCE } else if gov_storage::is_governance_key(key) { KeyType::UNKNOWN_GOVERNANCE diff --git a/shared/src/ledger/governance/utils.rs b/shared/src/ledger/governance/utils.rs index a254556cce..3b79168723 100644 --- a/shared/src/ledger/governance/utils.rs +++ b/shared/src/ledger/governance/utils.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; -use namada_core::ledger::governance::utils::TallyResult; +use namada_governance::utils::TallyResult; use namada_sdk::events::{Event, EventLevel}; use thiserror::Error; diff --git a/shared/src/ledger/ibc/mod.rs b/shared/src/ledger/ibc/mod.rs index e332a3044d..78b520813e 100644 --- a/shared/src/ledger/ibc/mod.rs +++ b/shared/src/ledger/ibc/mod.rs @@ -1,18 +1,16 @@ //! IBC integration -pub use namada_core::ledger::ibc::storage; -use namada_core::ledger::ibc::storage::{ +pub use namada_ibc::storage; +use namada_ibc::storage::{ channel_counter_key, client_counter_key, connection_counter_key, }; -use namada_core::ledger::storage::WlStorage; -use namada_core::ledger::storage_api::StorageWrite; - -use crate::ledger::storage::{self as ledger_storage, StorageHasher}; +use namada_state::{StorageHasher, WlStorage}; +use namada_storage::StorageWrite; /// Initialize storage in the genesis block. pub fn init_genesis_storage(storage: &mut WlStorage) where - DB: ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, { // In ibc-go, u64 like a counter is encoded with big-endian: diff --git a/shared/src/ledger/mod.rs b/shared/src/ledger/mod.rs index abd96064bb..cee83e6350 100644 --- a/shared/src/ledger/mod.rs +++ b/shared/src/ledger/mod.rs @@ -14,17 +14,19 @@ pub mod vp_host_fns; #[cfg(feature = "wasm-runtime")] pub use dry_run_tx::dry_run_tx; -pub use namada_core::ledger::{ - gas, parameters, replay_protection, storage_api, tx_env, vp_env, +pub use namada_core::ledger::replay_protection; +pub use { + namada_gas as gas, namada_parameters as parameters, + namada_tx_env as tx_env, namada_vp_env as vp_env, }; #[cfg(feature = "wasm-runtime")] mod dry_run_tx { - use namada_core::ledger::storage::{DBIter, StorageHasher, DB}; - use namada_core::ledger::storage_api::ResultExt; use namada_sdk::queries::{EncodedResponseQuery, RequestCtx, RequestQuery}; + use namada_state::{DBIter, StorageHasher, DB}; + use namada_storage::ResultExt; - use super::{protocol, storage_api}; + use super::protocol; use crate::vm::wasm::{TxCache, VpCache}; use crate::vm::WasmCacheAccess; @@ -32,21 +34,20 @@ mod dry_run_tx { pub fn dry_run_tx( mut ctx: RequestCtx<'_, D, H, VpCache, TxCache>, request: &RequestQuery, - ) -> storage_api::Result + ) -> namada_storage::Result where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, CA: 'static + WasmCacheAccess + Sync, { use borsh_ext::BorshSerializeExt; - use namada_core::ledger::gas::{Gas, GasMetering, TxGasMeter}; - use namada_core::ledger::storage::TempWlStorage; - use namada_core::proto::Tx; - use namada_core::types::transaction::DecryptedTx; + use namada_gas::{Gas, GasMetering, TxGasMeter}; + use namada_state::TempWlStorage; + use namada_tx::data::{DecryptedTx, TxType}; + use namada_tx::Tx; use crate::ledger::protocol::ShellParams; use crate::types::storage::TxIndex; - use crate::types::transaction::TxType; let mut tx = Tx::try_from(&request.data[..]).into_storage_result()?; tx.validate_tx().into_storage_result()?; @@ -84,9 +85,7 @@ mod dry_run_tx { // If dry run only the inner tx, use the max block gas as the // gas limit TxGasMeter::new( - namada_core::ledger::gas::get_max_block_gas(ctx.wl_storage) - .unwrap() - .into(), + namada_gas::get_max_block_gas(ctx.wl_storage).unwrap(), ) } TxType::Raw => { @@ -96,9 +95,7 @@ mod dry_run_tx { // If dry run only the inner tx, use the max block gas as the // gas limit TxGasMeter::new( - namada_core::ledger::gas::get_max_block_gas(ctx.wl_storage) - .unwrap() - .into(), + namada_gas::get_max_block_gas(ctx.wl_storage).unwrap(), ) } }; @@ -116,9 +113,7 @@ mod dry_run_tx { .into_storage_result()?; cumulated_gas = cumulated_gas .checked_add(tx_gas_meter.get_tx_consumed_gas()) - .ok_or(namada_core::ledger::storage_api::Error::SimpleMessage( - "Overflow in gas", - ))?; + .ok_or(namada_storage::Error::SimpleMessage("Overflow in gas"))?; // Account gas for both inner and wrapper (if available) data.gas_used = cumulated_gas; // NOTE: the keys changed by the wrapper transaction (if any) are not @@ -136,8 +131,6 @@ mod dry_run_tx { mod test { use borsh::BorshDeserialize; use borsh_ext::BorshSerializeExt; - use namada_core::ledger::storage::testing::TestWlStorage; - use namada_core::ledger::storage_api::{self, StorageWrite}; use namada_core::types::hash::Hash; use namada_core::types::storage::{BlockHeight, Key}; use namada_core::types::transaction::decrypted::DecryptedTx; @@ -146,6 +139,8 @@ mod test { use namada_sdk::queries::{ EncodedResponseQuery, RequestCtx, RequestQuery, Router, RPC, }; + use namada_state::testing::TestWlStorage; + use namada_storage::StorageWrite; use namada_test_utils::TestWasms; use tempfile::TempDir; use tendermint_rpc::{Error as RpcError, Response}; @@ -189,8 +184,7 @@ mod test { // Initialize mock gas limit let max_block_gas_key = - namada_core::ledger::parameters::storage::get_max_block_gas_key( - ); + namada_parameters::storage::get_max_block_gas_key(); wl_storage .storage .write( @@ -272,7 +266,7 @@ mod test { } #[tokio::test] - async fn test_shell_queries_router_with_client() -> storage_api::Result<()> + async fn test_shell_queries_router_with_client() -> namada_storageResult<()> { // Initialize the `TestClient` let mut client = TestClient::new(RPC); diff --git a/shared/src/ledger/native_vp/ethereum_bridge/bridge_pool_vp.rs b/shared/src/ledger/native_vp/ethereum_bridge/bridge_pool_vp.rs index 6c32539e9a..e1724a680c 100644 --- a/shared/src/ledger/native_vp/ethereum_bridge/bridge_pool_vp.rs +++ b/shared/src/ledger/native_vp/ethereum_bridge/bridge_pool_vp.rs @@ -18,23 +18,23 @@ use std::marker::PhantomData; use borsh::BorshDeserialize; use eyre::eyre; use namada_core::hints; -use namada_core::ledger::eth_bridge::storage::bridge_pool::{ +use namada_core::ledger::eth_bridge::ADDRESS as BRIDGE_ADDRESS; +use namada_core::types::eth_bridge_pool::erc20_token_address; +use namada_ethereum_bridge::storage::bridge_pool::{ get_pending_key, is_bridge_pool_key, BRIDGE_POOL_ADDRESS, }; -use namada_core::ledger::eth_bridge::storage::whitelist; -use namada_core::ledger::eth_bridge::ADDRESS as BRIDGE_ADDRESS; use namada_ethereum_bridge::storage::parameters::read_native_erc20_address; -use namada_ethereum_bridge::storage::wrapped_erc20s; +use namada_ethereum_bridge::storage::whitelist; +use namada_state::{DBIter, StorageHasher, DB}; +use namada_token::storage_key::balance_key; +use namada_token::Amount; +use namada_tx::Tx; use crate::ledger::native_vp::{Ctx, NativeVp, StorageReader}; -use crate::ledger::storage::traits::StorageHasher; -use crate::ledger::storage::{DBIter, DB}; -use crate::proto::Tx; use crate::types::address::{Address, InternalAddress}; use crate::types::eth_bridge_pool::{PendingTransfer, TransferToEthereumKind}; use crate::types::ethereum_events::EthAddress; use crate::types::storage::Key; -use crate::types::token::{balance_key, Amount}; use crate::vm::WasmCacheAccess; #[derive(thiserror::Error, Debug)] @@ -211,7 +211,7 @@ where gas_check: EscrowDelta<'_, GasCheck>, ) -> Result { if hints::unlikely( - *gas_check.token == wrapped_erc20s::token(wnam_address), + *gas_check.token == erc20_token_address(wnam_address), ) { // NB: this should never be possible: protocol tx state updates // never result in wNAM ERC20s being minted @@ -644,11 +644,12 @@ mod test_bridge_pool_vp { use borsh::BorshDeserialize; use borsh_ext::BorshSerializeExt; use namada_core::ledger::eth_bridge::storage::bridge_pool::get_signed_root_key; - use namada_core::ledger::gas::TxGasMeter; use namada_core::types::address; use namada_ethereum_bridge::storage::parameters::{ Contracts, EthereumBridgeParams, UpgradeableContract, }; + use namada_gas::TxGasMeter; + use namada_storage::StorageWrite; use super::*; use crate::ledger::gas::VpGasMeter; @@ -656,7 +657,6 @@ mod test_bridge_pool_vp { use crate::ledger::storage::traits::Sha256Hasher; use crate::ledger::storage::write_log::WriteLog; use crate::ledger::storage::{Storage, WlStorage}; - use crate::ledger::storage_api::StorageWrite; use crate::types::address::{nam, wnam, InternalAddress}; use crate::types::chain::ChainId; use crate::types::eth_bridge_pool::{GasFee, TransferToEthereum}; diff --git a/shared/src/ledger/native_vp/ethereum_bridge/nut.rs b/shared/src/ledger/native_vp/ethereum_bridge/nut.rs index 6dbb79d788..c8e02873a6 100644 --- a/shared/src/ledger/native_vp/ethereum_bridge/nut.rs +++ b/shared/src/ledger/native_vp/ethereum_bridge/nut.rs @@ -3,15 +3,15 @@ use std::collections::BTreeSet; use eyre::WrapErr; -use namada_core::ledger::storage as ledger_storage; -use namada_core::ledger::storage::traits::StorageHasher; use namada_core::types::address::{Address, InternalAddress}; use namada_core::types::storage::Key; use namada_core::types::token::Amount; +use namada_state::StorageHasher; +use namada_token::storage_key::is_any_token_balance_key; +use namada_tx::Tx; +use namada_vp_env::VpEnv; -use crate::ledger::native_vp::{Ctx, NativeVp, VpEnv}; -use crate::proto::Tx; -use crate::types::token::is_any_token_balance_key; +use crate::ledger::native_vp::{Ctx, NativeVp}; use crate::vm::WasmCacheAccess; /// Generic error that may be returned by the validity predicate @@ -25,7 +25,7 @@ pub struct Error(#[from] eyre::Report); /// address is not the Bridge pool escrow address. pub struct NonUsableTokens<'ctx, DB, H, CA> where - DB: ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: 'static + WasmCacheAccess, { @@ -35,7 +35,7 @@ where impl<'a, DB, H, CA> NativeVp for NonUsableTokens<'a, DB, H, CA> where - DB: 'static + ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { @@ -122,14 +122,14 @@ mod test_nuts { use assert_matches::assert_matches; use borsh_ext::BorshSerializeExt; - use namada_core::ledger::storage::testing::TestWlStorage; - use namada_core::ledger::storage_api::StorageWrite; use namada_core::types::address::testing::arb_non_internal_address; use namada_core::types::ethereum_events::testing::DAI_ERC20_ETH_ADDRESS; use namada_core::types::storage::TxIndex; use namada_core::types::token::balance_key; use namada_core::types::transaction::TxType; use namada_ethereum_bridge::storage::wrapped_erc20s; + use namada_state::testing::TestWlStorage; + use namada_storage::StorageWrite; use proptest::prelude::*; use super::*; diff --git a/shared/src/ledger/native_vp/ethereum_bridge/vp.rs b/shared/src/ledger/native_vp/ethereum_bridge/vp.rs index 6c5855b61d..320f6b25e4 100644 --- a/shared/src/ledger/native_vp/ethereum_bridge/vp.rs +++ b/shared/src/ledger/native_vp/ethereum_bridge/vp.rs @@ -2,15 +2,17 @@ use std::collections::{BTreeSet, HashSet}; use eyre::{eyre, Result}; -use namada_core::ledger::eth_bridge::storage::{self, escrow_key}; -use namada_core::ledger::storage::traits::StorageHasher; -use namada_core::ledger::{eth_bridge, storage as ledger_storage}; +use namada_core::ledger::eth_bridge; use namada_core::types::address::Address; +use namada_core::types::hash::StorageHasher; use namada_core::types::storage::Key; -use namada_core::types::token::{balance_key, is_balance_key, Amount}; +use namada_ethereum_bridge::storage; +use namada_ethereum_bridge::storage::escrow_key; +use namada_token::storage_key::{balance_key, is_balance_key}; +use namada_token::Amount; +use namada_tx::Tx; use crate::ledger::native_vp::{Ctx, NativeVp, StorageReader}; -use crate::proto::Tx; use crate::vm::WasmCacheAccess; /// Generic error that may be returned by the validity predicate @@ -21,7 +23,7 @@ pub struct Error(#[from] eyre::Error); /// Validity predicate for the Ethereum bridge pub struct EthBridge<'ctx, DB, H, CA> where - DB: ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: 'static + WasmCacheAccess, { @@ -31,7 +33,7 @@ where impl<'ctx, DB, H, CA> EthBridge<'ctx, DB, H, CA> where - DB: 'static + ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { @@ -83,7 +85,7 @@ where impl<'a, DB, H, CA> NativeVp for EthBridge<'a, DB, H, CA> where - DB: 'static + ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { @@ -168,11 +170,12 @@ mod tests { use namada_core::ledger::eth_bridge; use namada_core::ledger::eth_bridge::storage::bridge_pool::BRIDGE_POOL_ADDRESS; use namada_core::ledger::eth_bridge::storage::wrapped_erc20s; - use namada_core::ledger::gas::TxGasMeter; - use namada_core::ledger::storage_api::StorageWrite; use namada_ethereum_bridge::storage::parameters::{ Contracts, EthereumBridgeParams, UpgradeableContract, }; + use namada_gas::TxGasMeter; + use namada_storage::StorageWrite; + use namada_tx::Tx; use rand::Rng; use super::*; @@ -181,7 +184,6 @@ mod tests { use crate::ledger::storage::traits::Sha256Hasher; use crate::ledger::storage::write_log::WriteLog; use crate::ledger::storage::{Storage, WlStorage}; - use crate::proto::Tx; use crate::types::address::testing::established_address_1; use crate::types::address::{nam, wnam}; use crate::types::ethereum_events; diff --git a/shared/src/ledger/native_vp/ibc/context.rs b/shared/src/ledger/native_vp/ibc/context.rs index 27b9ee4324..25d70273a3 100644 --- a/shared/src/ledger/native_vp/ibc/context.rs +++ b/shared/src/ledger/native_vp/ibc/context.rs @@ -3,30 +3,30 @@ use std::collections::{BTreeSet, HashMap, HashSet}; use borsh_ext::BorshSerializeExt; -use namada_core::ledger::ibc::{IbcCommonContext, IbcStorageContext}; -use namada_core::ledger::masp_utils; +use namada_core::types::storage::Epochs; +use namada_ibc::{IbcCommonContext, IbcStorageContext}; +use namada_storage::{StorageRead, StorageWrite}; +use namada_token::{self as token, Amount, DenominatedAmount}; use crate::ledger::ibc::storage::is_ibc_key; use crate::ledger::native_vp::CtxPreStorageRead; use crate::ledger::storage::write_log::StorageModification; use crate::ledger::storage::{self as ledger_storage, StorageHasher}; -use crate::ledger::storage_api::{self, StorageRead, StorageWrite}; use crate::types::address::{Address, InternalAddress}; use crate::types::ibc::{IbcEvent, IbcShieldedTransfer}; use crate::types::storage::{ BlockHash, BlockHeight, Epoch, Header, Key, TxIndex, }; -use crate::types::token::{self, Amount, DenominatedAmount}; use crate::vm::WasmCacheAccess; /// Result of a storage API call. -pub type Result = std::result::Result; +pub type Result = std::result::Result; /// Pseudo execution environment context for ibc native vp #[derive(Debug)] pub struct PseudoExecutionContext<'view, 'a, DB, H, CA> where - DB: 'static + ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { @@ -40,7 +40,7 @@ where impl<'view, 'a, DB, H, CA> PseudoExecutionContext<'view, 'a, DB, H, CA> where - DB: 'static + ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { @@ -70,7 +70,7 @@ where impl<'view, 'a, DB, H, CA> StorageRead for PseudoExecutionContext<'view, 'a, DB, H, CA> where - DB: 'static + ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { @@ -139,12 +139,16 @@ where fn get_native_token(&self) -> Result
{ self.ctx.get_native_token() } + + fn get_pred_epochs(&self) -> Result { + self.ctx.get_pred_epochs() + } } impl<'view, 'a, DB, H, CA> StorageWrite for PseudoExecutionContext<'view, 'a, DB, H, CA> where - DB: 'static + ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { @@ -171,7 +175,7 @@ where impl<'view, 'a, DB, H, CA> IbcStorageContext for PseudoExecutionContext<'view, 'a, DB, H, CA> where - DB: 'static + ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { @@ -199,9 +203,9 @@ where token: &Address, amount: DenominatedAmount, ) -> Result<()> { - let amount = amount.to_amount(token, self)?; - let src_key = token::balance_key(token, src); - let dest_key = token::balance_key(token, dest); + let amount = namada_token::denom_to_amount(amount, token, self)?; + let src_key = token::storage_key::balance_key(token, src); + let dest_key = token::storage_key::balance_key(token, dest); let src_bal: Option = self.ctx.read(&src_key)?; let mut src_bal = src_bal.expect("The source has no balance"); src_bal.spend(&amount); @@ -214,12 +218,15 @@ where } fn handle_masp_tx(&mut self, shielded: &IbcShieldedTransfer) -> Result<()> { - masp_utils::handle_masp_tx( + namada_token::utils::handle_masp_tx( self, &shielded.transfer, &shielded.masp_tx, )?; - masp_utils::update_note_commitment_tree(self, &shielded.masp_tx) + namada_token::utils::update_note_commitment_tree( + self, + &shielded.masp_tx, + ) } fn mint_token( @@ -228,13 +235,13 @@ where token: &Address, amount: DenominatedAmount, ) -> Result<()> { - let amount = amount.to_amount(token, self)?; - let target_key = token::balance_key(token, target); + let amount = namada_token::denom_to_amount(amount, token, self)?; + let target_key = token::storage_key::balance_key(token, target); let mut target_bal: Amount = self.ctx.read(&target_key)?.unwrap_or_default(); target_bal.receive(&amount); - let minted_key = token::minted_balance_key(token); + let minted_key = token::storage_key::minted_balance_key(token); let mut minted_bal: Amount = self.ctx.read(&minted_key)?.unwrap_or_default(); minted_bal.receive(&amount); @@ -242,7 +249,7 @@ where self.write(&target_key, target_bal.serialize_to_vec())?; self.write(&minted_key, minted_bal.serialize_to_vec())?; - let minter_key = token::minter_key(token); + let minter_key = token::storage_key::minter_key(token); self.write( &minter_key, Address::Internal(InternalAddress::Ibc).serialize_to_vec(), @@ -255,13 +262,13 @@ where token: &Address, amount: DenominatedAmount, ) -> Result<()> { - let amount = amount.to_amount(token, self)?; - let target_key = token::balance_key(token, target); + let amount = namada_token::denom_to_amount(amount, token, self)?; + let target_key = token::storage_key::balance_key(token, target); let mut target_bal: Amount = self.ctx.read(&target_key)?.unwrap_or_default(); target_bal.spend(&amount); - let minted_key = token::minted_balance_key(token); + let minted_key = token::storage_key::minted_balance_key(token); let mut minted_bal: Amount = self.ctx.read(&minted_key)?.unwrap_or_default(); minted_bal.spend(&amount); @@ -278,7 +285,7 @@ where impl<'view, 'a, DB, H, CA> IbcCommonContext for PseudoExecutionContext<'view, 'a, DB, H, CA> where - DB: 'static + ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { @@ -288,7 +295,7 @@ where #[derive(Debug)] pub struct VpValidationContext<'view, 'a, DB, H, CA> where - DB: 'static + ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { @@ -298,7 +305,7 @@ where impl<'view, 'a, DB, H, CA> VpValidationContext<'view, 'a, DB, H, CA> where - DB: 'static + ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { @@ -311,7 +318,7 @@ where impl<'view, 'a, DB, H, CA> StorageRead for VpValidationContext<'view, 'a, DB, H, CA> where - DB: 'static + ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { @@ -366,12 +373,16 @@ where fn get_native_token(&self) -> Result
{ self.ctx.get_native_token() } + + fn get_pred_epochs(&self) -> Result { + self.ctx.get_pred_epochs() + } } impl<'view, 'a, DB, H, CA> StorageWrite for VpValidationContext<'view, 'a, DB, H, CA> where - DB: 'static + ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { @@ -391,7 +402,7 @@ where impl<'view, 'a, DB, H, CA> IbcStorageContext for VpValidationContext<'view, 'a, DB, H, CA> where - DB: 'static + ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { @@ -450,7 +461,7 @@ where impl<'view, 'a, DB, H, CA> IbcCommonContext for VpValidationContext<'view, 'a, DB, H, CA> where - DB: 'static + ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { diff --git a/shared/src/ledger/native_vp/ibc/mod.rs b/shared/src/ledger/native_vp/ibc/mod.rs index e4778ff82f..7097259296 100644 --- a/shared/src/ledger/native_vp/ibc/mod.rs +++ b/shared/src/ledger/native_vp/ibc/mod.rs @@ -8,23 +8,22 @@ use std::rc::Rc; use std::time::Duration; use context::{PseudoExecutionContext, VpValidationContext}; -use namada_core::ledger::gas::{ - IBC_ACTION_EXECUTE_GAS, IBC_ACTION_VALIDATE_GAS, -}; -use namada_core::ledger::ibc::{ - Error as ActionError, IbcActions, TransferModule, ValidationParams, -}; -use namada_core::ledger::storage::write_log::StorageModification; -use namada_core::ledger::storage::{self as ledger_storage, StorageHasher}; -use namada_core::proto::Tx; use namada_core::types::address::Address; use namada_core::types::storage::Key; +use namada_gas::{IBC_ACTION_EXECUTE_GAS, IBC_ACTION_VALIDATE_GAS}; +use namada_ibc::{ + Error as ActionError, IbcActions, TransferModule, ValidationParams, +}; use namada_proof_of_stake::storage::read_pos_params; +use namada_state::write_log::StorageModification; +use namada_state::StorageHasher; +use namada_tx::Tx; +use namada_vp_env::VpEnv; use thiserror::Error; use crate::ibc::core::host::types::identifiers::ChainId as IbcChainId; use crate::ledger::ibc::storage::{calc_hash, is_ibc_denom_key, is_ibc_key}; -use crate::ledger::native_vp::{self, Ctx, NativeVp, VpEnv}; +use crate::ledger::native_vp::{self, Ctx, NativeVp}; use crate::ledger::parameters::read_epoch_duration_parameter; use crate::vm::WasmCacheAccess; @@ -51,7 +50,7 @@ pub type VpResult = std::result::Result; /// IBC VP pub struct Ibc<'a, DB, H, CA> where - DB: ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: 'static + WasmCacheAccess, { @@ -61,7 +60,7 @@ where impl<'a, DB, H, CA> NativeVp for Ibc<'a, DB, H, CA> where - DB: 'static + ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { @@ -91,7 +90,7 @@ where impl<'a, DB, H, CA> Ibc<'a, DB, H, CA> where - DB: 'static + ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { @@ -163,7 +162,7 @@ where pub fn validation_params(&self) -> VpResult { use std::str::FromStr; let chain_id = self.ctx.get_chain_id().map_err(Error::NativeVpError)?; - let proof_specs = ledger_storage::ics23_specs::ibc_proof_specs::(); + let proof_specs = namada_state::ics23_specs::ibc_proof_specs::(); let pos_params = read_pos_params(&self.ctx.post()).map_err(Error::NativeVpError)?; let pipeline_len = pos_params.pipeline_len; @@ -318,8 +317,9 @@ mod tests { }; use ibc_testkit::testapp::ibc::clients::mock::consensus_state::MockConsensusState; use ibc_testkit::testapp::ibc::clients::mock::header::MockHeader; - use namada_core::ledger::gas::TxGasMeter; use namada_core::ledger::governance::parameters::GovernanceParameters; + use namada_gas::TxGasMeter; + use namada_storage::StorageRead; use prost::Message; use sha2::Digest; @@ -401,7 +401,6 @@ mod tests { get_epoch_duration_storage_key, get_max_expected_time_per_block_key, }; use crate::ledger::parameters::EpochDuration; - use crate::ledger::storage_api::StorageRead; use crate::ledger::{ibc, pos}; use crate::proto::{Code, Data, Section, Signature, Tx}; use crate::tendermint::time::Time as TmTime; diff --git a/shared/src/ledger/native_vp/masp.rs b/shared/src/ledger/native_vp/masp.rs index 31ee79848b..81c7659b4a 100644 --- a/shared/src/ledger/native_vp/masp.rs +++ b/shared/src/ledger/native_vp/masp.rs @@ -9,22 +9,22 @@ use masp_primitives::merkle_tree::CommitmentTree; use masp_primitives::sapling::Node; use masp_primitives::transaction::components::I128Sum; use masp_primitives::transaction::Transaction; -use namada_core::ledger::gas::MASP_VERIFY_SHIELDED_TX_GAS; -use namada_core::ledger::storage; -use namada_core::ledger::storage_api::OptionExt; -use namada_core::ledger::vp_env::VpEnv; -use namada_core::proto::Tx; use namada_core::types::address::InternalAddress::Masp; use namada_core::types::address::{Address, MASP}; use namada_core::types::storage::{BlockHeight, Epoch, Key, KeySeg, TxIndex}; -use namada_core::types::token::{ - self, is_masp_allowed_key, is_masp_key, is_masp_nullifier_key, - is_masp_tx_pin_key, is_masp_tx_prefix_key, Transfer, HEAD_TX_KEY, +use namada_gas::MASP_VERIFY_SHIELDED_TX_GAS; +use namada_sdk::masp::verify_shielded_tx; +use namada_storage::OptionExt; +use namada_token::storage_key::{ + is_masp_allowed_key, is_masp_key, is_masp_nullifier_key, + is_masp_tx_pin_key, is_masp_tx_prefix_key, HEAD_TX_KEY, MASP_CONVERT_ANCHOR_KEY, MASP_NOTE_COMMITMENT_ANCHOR_PREFIX, MASP_NOTE_COMMITMENT_TREE_KEY, MASP_NULLIFIERS_KEY, PIN_KEY_PREFIX, TX_KEY_PREFIX, }; -use namada_sdk::masp::verify_shielded_tx; +use namada_token::{self as token, Transfer}; +use namada_tx::Tx; +use namada_vp_env::VpEnv; use ripemd::Digest as RipemdDigest; use sha2::Digest as Sha2Digest; use thiserror::Error; @@ -46,8 +46,8 @@ pub type Result = std::result::Result; /// MASP VP pub struct MaspVp<'a, DB, H, CA> where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, - H: storage::StorageHasher, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, + H: namada_state::StorageHasher, CA: WasmCacheAccess, { /// Context to interact with the host structures. @@ -101,8 +101,8 @@ fn convert_amount( impl<'a, DB, H, CA> MaspVp<'a, DB, H, CA> where - DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, - H: 'static + storage::StorageHasher, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, + H: 'static + namada_state::StorageHasher, CA: 'static + WasmCacheAccess, { // Check that the transaction correctly revealed the nullifiers @@ -370,8 +370,8 @@ where impl<'a, DB, H, CA> NativeVp for MaspVp<'a, DB, H, CA> where - DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, - H: 'static + storage::StorageHasher, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, + H: 'static + namada_state::StorageHasher, CA: 'static + WasmCacheAccess, { type Error = Error; @@ -384,9 +384,11 @@ where ) -> Result { let epoch = self.ctx.get_block_epoch()?; let (transfer, shielded_tx) = self.ctx.get_shielded_action(tx_data)?; - let transfer_amount = transfer - .amount - .to_amount(&transfer.token, &self.ctx.pre())?; + let transfer_amount = namada_token::denom_to_amount( + transfer.amount, + &transfer.token, + &self.ctx.pre(), + )?; let mut transparent_tx_pool = I128Sum::zero(); // The Sapling value balance adds to the transparent tx pool transparent_tx_pool += shielded_tx.sapling_value_balance(); diff --git a/shared/src/ledger/native_vp/mod.rs b/shared/src/ledger/native_vp/mod.rs index a18d5f5404..12e191d31d 100644 --- a/shared/src/ledger/native_vp/mod.rs +++ b/shared/src/ledger/native_vp/mod.rs @@ -12,17 +12,18 @@ use std::collections::BTreeSet; use borsh::BorshDeserialize; use eyre::WrapErr; -use namada_core::ledger::gas::GasMetering; -pub use namada_core::ledger::vp_env::VpEnv; +use namada_core::types::storage::Epochs; use namada_core::types::validity_predicate::VpSentinel; +use namada_gas::GasMetering; +use namada_storage::{ResultExt, StorageRead}; +use namada_tx::Tx; +pub use namada_vp_env::VpEnv; -use super::storage_api::{self, ResultExt, StorageRead}; use super::vp_host_fns; use crate::ledger::gas::VpGasMeter; use crate::ledger::storage; use crate::ledger::storage::write_log::WriteLog; use crate::ledger::storage::{Storage, StorageHasher}; -use crate::proto::Tx; use crate::types::address::Address; use crate::types::hash::Hash; use crate::types::ibc::IbcEvent; @@ -33,9 +34,9 @@ use crate::vm::prefix_iter::PrefixIterators; use crate::vm::WasmCacheAccess; /// Possible error in a native VP host function call -/// The `storage_api::Error` may wrap the `vp_host_fns::RuntimeError` and can +/// The `namada_storage::Error` may wrap the `vp_host_fns::RuntimeError` and can /// be extended with other custom errors when using `trait VpEnv`. -pub type Error = storage_api::Error; +pub type Error = namada_storage::Error; /// A native VP module should implement its validation logic using this trait. pub trait NativeVp { @@ -59,7 +60,7 @@ pub trait NativeVp { #[derive(Debug)] pub struct Ctx<'a, DB, H, CA> where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -98,7 +99,7 @@ where #[derive(Debug)] pub struct CtxPreStorageRead<'view, 'a: 'view, DB, H, CA> where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -110,7 +111,7 @@ where #[derive(Debug)] pub struct CtxPostStorageRead<'view, 'a: 'view, DB, H, CA> where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -119,7 +120,7 @@ where impl<'a, DB, H, CA> Ctx<'a, DB, H, CA> where - DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { @@ -173,7 +174,7 @@ where impl<'view, 'a: 'view, DB, H, CA> StorageRead for CtxPreStorageRead<'view, 'a, DB, H, CA> where - DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { @@ -182,7 +183,7 @@ where fn read_bytes( &self, key: &crate::types::storage::Key, - ) -> Result>, storage_api::Error> { + ) -> Result>, namada_storage::Error> { vp_host_fns::read_pre( &mut self.ctx.gas_meter.borrow_mut(), self.ctx.storage, @@ -196,7 +197,7 @@ where fn has_key( &self, key: &crate::types::storage::Key, - ) -> Result { + ) -> Result { vp_host_fns::has_key_pre( &mut self.ctx.gas_meter.borrow_mut(), self.ctx.storage, @@ -210,7 +211,7 @@ where fn iter_prefix<'iter>( &'iter self, prefix: &crate::types::storage::Key, - ) -> Result, storage_api::Error> { + ) -> Result, namada_storage::Error> { vp_host_fns::iter_prefix_pre( &mut self.ctx.gas_meter.borrow_mut(), self.ctx.write_log, @@ -227,7 +228,7 @@ where fn iter_next<'iter>( &'iter self, iter: &mut Self::PrefixIter<'iter>, - ) -> Result)>, storage_api::Error> { + ) -> Result)>, namada_storage::Error> { vp_host_fns::iter_next::( &mut self.ctx.gas_meter.borrow_mut(), iter, @@ -236,42 +237,46 @@ where .into_storage_result() } - fn get_chain_id(&self) -> Result { + fn get_chain_id(&self) -> Result { self.ctx.get_chain_id() } - fn get_block_height(&self) -> Result { + fn get_block_height(&self) -> Result { self.ctx.get_block_height() } fn get_block_header( &self, height: BlockHeight, - ) -> Result, storage_api::Error> { + ) -> Result, namada_storage::Error> { self.ctx.get_block_header(height) } - fn get_block_hash(&self) -> Result { + fn get_block_hash(&self) -> Result { self.ctx.get_block_hash() } - fn get_block_epoch(&self) -> Result { + fn get_block_epoch(&self) -> Result { self.ctx.get_block_epoch() } - fn get_tx_index(&self) -> Result { + fn get_tx_index(&self) -> Result { self.ctx.get_tx_index().into_storage_result() } - fn get_native_token(&self) -> Result { + fn get_native_token(&self) -> Result { self.ctx.get_native_token() } + + fn get_pred_epochs(&self) -> namada_storage::Result { + self.ctx.get_pred_epochs() + } } impl<'view, 'a: 'view, DB, H, CA> StorageRead for CtxPostStorageRead<'view, 'a, DB, H, CA> where - DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { @@ -280,7 +285,7 @@ where fn read_bytes( &self, key: &crate::types::storage::Key, - ) -> Result>, storage_api::Error> { + ) -> Result>, namada_storage::Error> { vp_host_fns::read_post( &mut self.ctx.gas_meter.borrow_mut(), self.ctx.storage, @@ -294,7 +299,7 @@ where fn has_key( &self, key: &crate::types::storage::Key, - ) -> Result { + ) -> Result { vp_host_fns::has_key_post( &mut self.ctx.gas_meter.borrow_mut(), self.ctx.storage, @@ -308,7 +313,7 @@ where fn iter_prefix<'iter>( &'iter self, prefix: &crate::types::storage::Key, - ) -> Result, storage_api::Error> { + ) -> Result, namada_storage::Error> { vp_host_fns::iter_prefix_post( &mut self.ctx.gas_meter.borrow_mut(), self.ctx.write_log, @@ -325,7 +330,7 @@ where fn iter_next<'iter>( &'iter self, iter: &mut Self::PrefixIter<'iter>, - ) -> Result)>, storage_api::Error> { + ) -> Result)>, namada_storage::Error> { vp_host_fns::iter_next::( &mut self.ctx.gas_meter.borrow_mut(), iter, @@ -334,41 +339,45 @@ where .into_storage_result() } - fn get_chain_id(&self) -> Result { + fn get_chain_id(&self) -> Result { self.ctx.get_chain_id() } - fn get_block_height(&self) -> Result { + fn get_block_height(&self) -> Result { self.ctx.get_block_height() } fn get_block_header( &self, height: BlockHeight, - ) -> Result, storage_api::Error> { + ) -> Result, namada_storage::Error> { self.ctx.get_block_header(height) } - fn get_block_hash(&self) -> Result { + fn get_block_hash(&self) -> Result { self.ctx.get_block_hash() } - fn get_block_epoch(&self) -> Result { + fn get_block_epoch(&self) -> Result { self.ctx.get_block_epoch() } - fn get_tx_index(&self) -> Result { + fn get_tx_index(&self) -> Result { self.ctx.get_tx_index().into_storage_result() } - fn get_native_token(&self) -> Result { + fn get_native_token(&self) -> Result { Ok(self.ctx.storage.native_token.clone()) } + + fn get_pred_epochs(&self) -> namada_storage::Result { + self.ctx.get_pred_epochs() + } } impl<'view, 'a: 'view, DB, H, CA> VpEnv<'view> for Ctx<'a, DB, H, CA> where - DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { @@ -387,7 +396,7 @@ where fn read_temp( &self, key: &Key, - ) -> Result, storage_api::Error> { + ) -> Result, namada_storage::Error> { vp_host_fns::read_temp( &mut self.gas_meter.borrow_mut(), self.write_log, @@ -401,7 +410,7 @@ where fn read_bytes_temp( &self, key: &Key, - ) -> Result>, storage_api::Error> { + ) -> Result>, namada_storage::Error> { vp_host_fns::read_temp( &mut self.gas_meter.borrow_mut(), self.write_log, @@ -411,7 +420,7 @@ where .into_storage_result() } - fn get_chain_id(&self) -> Result { + fn get_chain_id(&self) -> Result { vp_host_fns::get_chain_id( &mut self.gas_meter.borrow_mut(), self.storage, @@ -420,7 +429,7 @@ where .into_storage_result() } - fn get_block_height(&self) -> Result { + fn get_block_height(&self) -> Result { vp_host_fns::get_block_height( &mut self.gas_meter.borrow_mut(), self.storage, @@ -432,7 +441,7 @@ where fn get_block_header( &self, height: BlockHeight, - ) -> Result, storage_api::Error> { + ) -> Result, namada_storage::Error> { vp_host_fns::get_block_header( &mut self.gas_meter.borrow_mut(), self.storage, @@ -442,7 +451,7 @@ where .into_storage_result() } - fn get_block_hash(&self) -> Result { + fn get_block_hash(&self) -> Result { vp_host_fns::get_block_hash( &mut self.gas_meter.borrow_mut(), self.storage, @@ -451,7 +460,7 @@ where .into_storage_result() } - fn get_block_epoch(&self) -> Result { + fn get_block_epoch(&self) -> Result { vp_host_fns::get_block_epoch( &mut self.gas_meter.borrow_mut(), self.storage, @@ -460,7 +469,7 @@ where .into_storage_result() } - fn get_tx_index(&self) -> Result { + fn get_tx_index(&self) -> Result { vp_host_fns::get_tx_index( &mut self.gas_meter.borrow_mut(), self.tx_index, @@ -469,7 +478,7 @@ where .into_storage_result() } - fn get_native_token(&self) -> Result { + fn get_native_token(&self) -> Result { vp_host_fns::get_native_token( &mut self.gas_meter.borrow_mut(), self.storage, @@ -478,10 +487,19 @@ where .into_storage_result() } + fn get_pred_epochs(&self) -> namada_storage::Result { + vp_host_fns::get_pred_epochs( + &mut self.gas_meter.borrow_mut(), + self.storage, + &mut self.sentinel.borrow_mut(), + ) + .into_storage_result() + } + fn get_ibc_events( &self, event_type: String, - ) -> Result, storage_api::Error> { + ) -> Result, namada_storage::Error> { vp_host_fns::get_ibc_events( &mut self.gas_meter.borrow_mut(), self.write_log, @@ -493,7 +511,7 @@ where fn iter_prefix<'iter>( &'iter self, prefix: &Key, - ) -> Result, storage_api::Error> { + ) -> Result, namada_storage::Error> { vp_host_fns::iter_prefix_pre( &mut self.gas_meter.borrow_mut(), self.write_log, @@ -508,7 +526,7 @@ where &self, vp_code_hash: Hash, input_data: Tx, - ) -> Result { + ) -> Result { #[cfg(feature = "wasm-runtime")] { use std::marker::PhantomData; @@ -565,13 +583,13 @@ where } } - fn charge_gas(&self, used_gas: u64) -> Result<(), storage_api::Error> { + fn charge_gas(&self, used_gas: u64) -> Result<(), namada_storage::Error> { self.gas_meter.borrow_mut().consume(used_gas).map_err(|_| { Error::SimpleMessage("Gas limit exceeded in native vp") }) } - fn get_tx_code_hash(&self) -> Result, storage_api::Error> { + fn get_tx_code_hash(&self) -> Result, namada_storage::Error> { vp_host_fns::get_tx_code_hash( &mut self.gas_meter.borrow_mut(), self.tx, @@ -583,36 +601,36 @@ where fn read_pre( &self, key: &Key, - ) -> Result, storage_api::Error> { + ) -> Result, namada_storage::Error> { self.pre().read(key).map_err(Into::into) } fn read_bytes_pre( &self, key: &Key, - ) -> Result>, storage_api::Error> { + ) -> Result>, namada_storage::Error> { self.pre().read_bytes(key).map_err(Into::into) } fn read_post( &self, key: &Key, - ) -> Result, storage_api::Error> { + ) -> Result, namada_storage::Error> { self.post().read(key).map_err(Into::into) } fn read_bytes_post( &self, key: &Key, - ) -> Result>, storage_api::Error> { + ) -> Result>, namada_storage::Error> { self.post().read_bytes(key).map_err(Into::into) } - fn has_key_pre(&self, key: &Key) -> Result { + fn has_key_pre(&self, key: &Key) -> Result { self.pre().has_key(key).map_err(Into::into) } - fn has_key_post(&self, key: &Key) -> Result { + fn has_key_post(&self, key: &Key) -> Result { self.post().has_key(key).map_err(Into::into) } } @@ -651,7 +669,7 @@ pub trait StorageReader { impl<'a, DB, H, CA> StorageReader for &Ctx<'a, DB, H, CA> where - DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { diff --git a/shared/src/ledger/native_vp/multitoken.rs b/shared/src/ledger/native_vp/multitoken.rs index 809e840848..78c345a56e 100644 --- a/shared/src/ledger/native_vp/multitoken.rs +++ b/shared/src/ledger/native_vp/multitoken.rs @@ -2,18 +2,18 @@ use std::collections::{BTreeSet, HashMap}; +use namada_token::storage_key::{ + is_any_minted_balance_key, is_any_minter_key, is_any_token_balance_key, + minter_key, +}; +use namada_token::{Amount, Change}; +use namada_tx::Tx; +use namada_vp_env::VpEnv; use thiserror::Error; use crate::ledger::native_vp::{self, Ctx, NativeVp}; -use crate::ledger::storage; -use crate::ledger::vp_env::VpEnv; -use crate::proto::Tx; use crate::types::address::{Address, InternalAddress}; use crate::types::storage::{Key, KeySeg}; -use crate::types::token::{ - is_any_minted_balance_key, is_any_minter_key, is_any_token_balance_key, - minter_key, Amount, Change, -}; use crate::vm::WasmCacheAccess; #[allow(missing_docs)] @@ -29,8 +29,8 @@ pub type Result = std::result::Result; /// Multitoken VP pub struct MultitokenVp<'a, DB, H, CA> where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, - H: storage::StorageHasher, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, + H: namada_state::StorageHasher, CA: WasmCacheAccess, { /// Context to interact with the host structures. @@ -39,8 +39,8 @@ where impl<'a, DB, H, CA> NativeVp for MultitokenVp<'a, DB, H, CA> where - DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, - H: 'static + storage::StorageHasher, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, + H: 'static + namada_state::StorageHasher, CA: 'static + WasmCacheAccess, { type Error = Error; @@ -102,8 +102,8 @@ where impl<'a, DB, H, CA> MultitokenVp<'a, DB, H, CA> where - DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, - H: 'static + storage::StorageHasher, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, + H: 'static + namada_state::StorageHasher, CA: 'static + WasmCacheAccess, { /// Return the minter if the minter is valid and the minter VP exists @@ -140,7 +140,7 @@ mod tests { use std::collections::BTreeSet; use borsh_ext::BorshSerializeExt; - use namada_core::ledger::gas::TxGasMeter; + use namada_gas::TxGasMeter; use super::*; use crate::core::ledger::storage::testing::TestWlStorage; diff --git a/shared/src/ledger/native_vp/parameters.rs b/shared/src/ledger/native_vp/parameters.rs index a2559ad0ab..b22f56260e 100644 --- a/shared/src/ledger/native_vp/parameters.rs +++ b/shared/src/ledger/native_vp/parameters.rs @@ -2,13 +2,11 @@ use std::collections::BTreeSet; -use namada_core::ledger::storage; -use namada_core::proto::Tx; use namada_core::types::address::Address; use namada_core::types::storage::Key; +use namada_tx::Tx; use thiserror::Error; -use crate::core::ledger::storage_api::governance; use crate::ledger::native_vp::{self, Ctx, NativeVp}; use crate::vm::WasmCacheAccess; @@ -25,8 +23,8 @@ pub type Result = std::result::Result; /// Parameters VP pub struct ParametersVp<'a, DB, H, CA> where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, - H: storage::StorageHasher, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, + H: namada_state::StorageHasher, CA: WasmCacheAccess, { /// Context to interact with the host structures. @@ -35,8 +33,8 @@ where impl<'a, DB, H, CA> NativeVp for ParametersVp<'a, DB, H, CA> where - DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, - H: 'static + storage::StorageHasher, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, + H: 'static + namada_state::StorageHasher, CA: 'static + WasmCacheAccess, { type Error = Error; @@ -56,8 +54,11 @@ where }; match key_type { KeyType::PARAMETER => { - governance::is_proposal_accepted(&self.ctx.pre(), &data) - .unwrap_or(false) + namada_governance::storage::is_proposal_accepted( + &self.ctx.pre(), + &data, + ) + .unwrap_or(false) } KeyType::UNKNOWN_PARAMETER => false, KeyType::UNKNOWN => true, @@ -86,13 +87,9 @@ enum KeyType { impl From<&Key> for KeyType { fn from(value: &Key) -> Self { - if namada_core::ledger::parameters::storage::is_protocol_parameter_key( - value, - ) { + if namada_parameters::storage::is_protocol_parameter_key(value) { KeyType::PARAMETER - } else if namada_core::ledger::parameters::storage::is_parameter_key( - value, - ) { + } else if namada_parameters::storage::is_parameter_key(value) { KeyType::UNKNOWN_PARAMETER } else { KeyType::UNKNOWN diff --git a/shared/src/ledger/native_vp/slash_fund.rs b/shared/src/ledger/native_vp/slash_fund.rs index 8f2ab54400..e282c92936 100644 --- a/shared/src/ledger/native_vp/slash_fund.rs +++ b/shared/src/ledger/native_vp/slash_fund.rs @@ -5,12 +5,12 @@ use std::collections::BTreeSet; use namada_core::ledger::slash_fund; /// SlashFund storage pub use namada_core::ledger::slash_fund::storage; +use namada_storage::StorageRead; +use namada_tx::Tx; use thiserror::Error; use crate::ledger::native_vp::{self, governance, Ctx, NativeVp}; use crate::ledger::storage::{self as ledger_storage, StorageHasher}; -use crate::ledger::storage_api::StorageRead; -use crate::proto::Tx; use crate::types::address::Address; use crate::types::storage::Key; use crate::types::token; @@ -29,7 +29,7 @@ pub type Result = std::result::Result; /// SlashFund VP pub struct SlashFundVp<'a, DB, H, CA> where - DB: ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -39,7 +39,7 @@ where impl<'a, DB, H, CA> NativeVp for SlashFundVp<'a, DB, H, CA> where - DB: 'static + ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { diff --git a/shared/src/ledger/pgf/mod.rs b/shared/src/ledger/pgf/mod.rs index 4e0da63b3f..c5d6504318 100644 --- a/shared/src/ledger/pgf/mod.rs +++ b/shared/src/ledger/pgf/mod.rs @@ -5,11 +5,9 @@ pub mod utils; use std::collections::BTreeSet; -use namada_core::ledger::pgf::storage::keys as pgf_storage; -use namada_core::ledger::storage; -use namada_core::ledger::storage_api::governance::is_proposal_accepted; -use namada_core::ledger::storage_api::pgf as pgf_api; -use namada_core::proto::Tx; +use namada_governance::pgf::storage::keys as pgf_storage; +use namada_governance::{is_proposal_accepted, pgf}; +use namada_tx::Tx; use thiserror::Error; use crate::ledger::native_vp; @@ -34,8 +32,8 @@ pub enum Error { /// Pgf VP pub struct PgfVp<'a, DB, H, CA> where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, - H: storage::StorageHasher, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, + H: namada_state::StorageHasher, CA: WasmCacheAccess, { /// Context to interact with the host structures. @@ -44,8 +42,8 @@ where impl<'a, DB, H, CA> NativeVp for PgfVp<'a, DB, H, CA> where - DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, - H: 'static + storage::StorageHasher, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, + H: 'static + namada_state::StorageHasher, CA: 'static + WasmCacheAccess, { type Error = Error; @@ -76,8 +74,10 @@ where // for signature authorization let steward_address = pgf_storage::is_stewards_key(key); if let Some(address) = steward_address { - let steward_post = - pgf_api::get_steward(&self.ctx.post(), address); + let steward_post = pgf::storage::get_steward( + &self.ctx.post(), + address, + ); match steward_post { Ok(Some(steward)) => { steward.is_valid_reward_distribution() @@ -108,8 +108,8 @@ where impl<'a, DB, H, CA> PgfVp<'a, DB, H, CA> where - DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, - H: 'static + storage::StorageHasher, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, + H: 'static + namada_state::StorageHasher, CA: 'static + WasmCacheAccess, { /// Validate a governance parameter diff --git a/shared/src/ledger/pos/mod.rs b/shared/src/ledger/pos/mod.rs index 620d0108d3..ccc64fd63a 100644 --- a/shared/src/ledger/pos/mod.rs +++ b/shared/src/ledger/pos/mod.rs @@ -4,12 +4,10 @@ pub mod vp; use std::convert::TryFrom; -pub use namada_core::ledger::storage_api; use namada_core::types::address; pub use namada_core::types::dec::Dec; pub use namada_core::types::key::common; pub use namada_core::types::token; -pub use namada_proof_of_stake; pub use namada_proof_of_stake::parameters::{OwnedPosParams, PosParams}; pub use namada_proof_of_stake::pos_queries::*; pub use namada_proof_of_stake::storage::*; @@ -17,6 +15,7 @@ pub use namada_proof_of_stake::storage::*; pub use namada_proof_of_stake::test_utils; pub use namada_proof_of_stake::{staking_token_address, types}; pub use vp::PosVP; +pub use {namada_proof_of_stake, namada_storage}; use crate::types::address::{Address, InternalAddress}; diff --git a/shared/src/ledger/pos/vp.rs b/shared/src/ledger/pos/vp.rs index 298746340c..f9700fefce 100644 --- a/shared/src/ledger/pos/vp.rs +++ b/shared/src/ledger/pos/vp.rs @@ -2,7 +2,6 @@ use std::collections::BTreeSet; -use namada_core::ledger::storage_api::governance; // use borsh::BorshDeserialize; pub use namada_proof_of_stake; pub use namada_proof_of_stake::parameters::PosParams; @@ -10,16 +9,16 @@ pub use namada_proof_of_stake::parameters::PosParams; use namada_proof_of_stake::storage::read_pos_params; use namada_proof_of_stake::storage_key::is_params_key; pub use namada_proof_of_stake::types; -use thiserror::Error; - -use crate::ledger::native_vp::{self, Ctx, NativeVp}; // use crate::ledger::pos::{ // is_validator_address_raw_hash_key, // is_validator_max_commission_rate_change_key, // }; -use crate::ledger::storage::{self as ledger_storage, StorageHasher}; -use crate::ledger::storage_api::StorageRead; -use crate::proto::Tx; +use namada_state::StorageHasher; +use namada_storage::StorageRead; +use namada_tx::Tx; +use thiserror::Error; + +use crate::ledger::native_vp::{self, Ctx, NativeVp}; use crate::types::address::{Address, InternalAddress}; use crate::types::storage::{Key, KeySeg}; use crate::vm::WasmCacheAccess; @@ -37,7 +36,7 @@ pub type Result = std::result::Result; /// Proof-of-Stake validity predicate pub struct PosVP<'a, DB, H, CA> where - DB: ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -47,7 +46,7 @@ where impl<'a, DB, H, CA> PosVP<'a, DB, H, CA> where - DB: 'static + ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { @@ -59,7 +58,7 @@ where impl<'a, DB, H, CA> NativeVp for PosVP<'a, DB, H, CA> where - DB: 'static + ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { @@ -88,8 +87,11 @@ where } else { return Ok(false); }; - if !governance::is_proposal_accepted(&self.ctx.pre(), &data) - .map_err(Error::NativeVpError)? + if !namada_governance::is_proposal_accepted( + &self.ctx.pre(), + &data, + ) + .map_err(Error::NativeVpError)? { return Ok(false); } diff --git a/shared/src/ledger/protocol/mod.rs b/shared/src/ledger/protocol/mod.rs index e51d073415..fa0d53a952 100644 --- a/shared/src/ledger/protocol/mod.rs +++ b/shared/src/ledger/protocol/mod.rs @@ -4,15 +4,17 @@ use std::collections::BTreeSet; use borsh_ext::BorshSerializeExt; use eyre::{eyre, WrapErr}; use masp_primitives::transaction::Transaction; -use namada_core::ledger::gas::TxGasMeter; -use namada_core::ledger::storage::wl_storage::WriteLogAndStorage; -use namada_core::ledger::storage_api::StorageRead; -use namada_core::proto::Section; use namada_core::types::hash::Hash; use namada_core::types::storage::Key; use namada_core::types::token::Amount; -use namada_core::types::transaction::WrapperTx; +use namada_gas::TxGasMeter; use namada_sdk::tx::TX_TRANSFER_WASM; +use namada_state::wl_storage::WriteLogAndStorage; +use namada_storage::StorageRead; +use namada_tx::data::protocol::ProtocolTxType; +use namada_tx::data::{DecryptedTx, TxResult, TxType, VpsResult, WrapperTx}; +use namada_tx::{Section, Tx}; +use namada_vote_ext::EthereumTxData; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use thiserror::Error; @@ -30,13 +32,9 @@ use crate::ledger::pgf::PgfVp; use crate::ledger::pos::{self, PosVP}; use crate::ledger::storage::write_log::WriteLog; use crate::ledger::storage::{DBIter, Storage, StorageHasher, WlStorage, DB}; -use crate::ledger::storage_api; -use crate::proto::{self, Tx}; use crate::types::address::{Address, InternalAddress}; use crate::types::storage; use crate::types::storage::TxIndex; -use crate::types::transaction::protocol::{EthereumTxData, ProtocolTxType}; -use crate::types::transaction::{DecryptedTx, TxResult, TxType, VpsResult}; use crate::vm::wasm::{TxCache, VpCache}; use crate::vm::{self, wasm, WasmCacheAccess}; @@ -46,9 +44,7 @@ pub enum Error { #[error("Missing tx section: {0}")] MissingSection(String), #[error("Storage error: {0}")] - StorageError(crate::ledger::storage::Error), - #[error("Error decoding a transaction from bytes: {0}")] - TxDecodingError(proto::Error), + StorageError(namada_state::Error), #[error("Transaction runner error: {0}")] TxRunnerError(vm::wasm::run::Error), #[error("{0:?}")] @@ -56,7 +52,7 @@ pub enum Error { #[error("Txs must either be encrypted or a decryption of an encrypted tx")] TxTypeError, #[error("Fee ushielding error: {0}")] - FeeUnshieldingError(crate::types::transaction::WrapperTxErr), + FeeUnshieldingError(namada_tx::data::WrapperTxErr), #[error("Gas error: {0}")] GasError(String), #[error("Error while processing transaction's fees: {0}")] @@ -237,7 +233,7 @@ where CA: 'static + WasmCacheAccess + Sync, D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, - WLS: WriteLogAndStorage, + WLS: WriteLogAndStorage + StorageRead, { let mut changed_keys = BTreeSet::default(); @@ -300,7 +296,7 @@ where CA: 'static + WasmCacheAccess + Sync, D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, - WLS: WriteLogAndStorage, + WLS: WriteLogAndStorage + StorageRead, { let ShellParams { tx_gas_meter: _, @@ -317,11 +313,11 @@ where TxGasMeter::new( wl_storage .read::( - &namada_core::ledger::parameters::storage::get_fee_unshielding_gas_limit_key( + &namada_parameters::storage::get_fee_unshielding_gas_limit_key( ), ) .expect("Error reading the storage") - .expect("Missing fee unshielding gas limit in storage").into(), + .expect("Missing fee unshielding gas limit in storage"), ); // If it fails, do not return early @@ -397,7 +393,7 @@ pub fn transfer_fee( where WLS: WriteLogAndStorage + StorageRead, { - let balance = storage_api::token::read_balance( + let balance = namada_token::read_balance( wl_storage, &wrapper.fee.token, &wrapper.fee_payer(), @@ -406,9 +402,12 @@ where match wrapper.get_tx_fee() { Ok(fees) => { - let fees = fees - .to_amount(&wrapper.fee.token, wl_storage) - .map_err(|e| Error::FeeError(e.to_string()))?; + let fees = namada_token::denom_to_amount( + fees, + &wrapper.fee.token, + wl_storage, + ) + .map_err(|e| Error::FeeError(e.to_string()))?; if balance.checked_sub(fees).is_some() { token_transfer( wl_storage, @@ -462,7 +461,7 @@ where /// Transfer `token` from `src` to `dest`. Returns an `Err` if `src` has /// insufficient balance or if the transfer the `dest` would overflow (This can /// only happen if the total supply doesn't fit in `token::Amount`). Contrary to -/// `storage_api::token::transfer` this function updates the tx write log and +/// `namada_token::transfer` this function updates the tx write log and /// not the block write log. fn token_transfer( wl_storage: &mut WLS, @@ -474,22 +473,18 @@ fn token_transfer( where WLS: WriteLogAndStorage + StorageRead, { - let src_key = namada_core::types::token::balance_key(token, src); - let src_balance = namada_core::ledger::storage_api::token::read_balance( - wl_storage, token, src, - ) - .expect("Token balance read in protocol must not fail"); + let src_key = namada_token::storage_key::balance_key(token, src); + let src_balance = namada_token::read_balance(wl_storage, token, src) + .expect("Token balance read in protocol must not fail"); match src_balance.checked_sub(amount) { Some(new_src_balance) => { if src == dest { return Ok(()); } - let dest_key = namada_core::types::token::balance_key(token, dest); + let dest_key = namada_token::storage_key::balance_key(token, dest); let dest_balance = - namada_core::ledger::storage_api::token::read_balance( - wl_storage, token, dest, - ) - .expect("Token balance read in protocol must not fail"); + namada_token::read_balance(wl_storage, token, dest) + .expect("Token balance read in protocol must not fail"); match dest_balance.checked_add(amount) { Some(new_dest_balance) => { wl_storage @@ -519,7 +514,7 @@ pub fn check_fees(wl_storage: &WLS, wrapper: &WrapperTx) -> Result<()> where WLS: WriteLogAndStorage + StorageRead, { - let balance = storage_api::token::read_balance( + let balance = namada_token::read_balance( wl_storage, &wrapper.fee.token, &wrapper.fee_payer(), @@ -530,9 +525,9 @@ where .get_tx_fee() .map_err(|e| Error::FeeError(e.to_string()))?; - let fees = fees - .to_amount(&wrapper.fee.token, wl_storage) - .map_err(|e| Error::FeeError(e.to_string()))?; + let fees = + namada_token::denom_to_amount(fees, &wrapper.fee.token, wl_storage) + .map_err(|e| Error::FeeError(e.to_string()))?; if balance.checked_sub(fees).is_some() { Ok(()) } else { @@ -553,7 +548,7 @@ where CA: 'static + WasmCacheAccess + Sync, D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, - WLS: WriteLogAndStorage, + WLS: WriteLogAndStorage + StorageRead, { let ShellParams { tx_gas_meter, @@ -631,10 +626,7 @@ where H: 'static + StorageHasher + Sync, { use namada_ethereum_bridge::protocol::transactions; - - use crate::types::vote_extensions::{ - ethereum_events, validator_set_update, - }; + use namada_vote_ext::{ethereum_events, validator_set_update}; let Some(data) = data else { return Err(Error::ProtocolTxError( @@ -651,7 +643,9 @@ where .map_err(Error::ProtocolTxError)?; match ethereum_tx_data { - EthereumTxData::EthEventsVext(ext) => { + EthereumTxData::EthEventsVext( + namada_vote_ext::ethereum_events::SignedVext(ext), + ) => { let ethereum_events::VextDigest { events, .. } = ethereum_events::VextDigest::singleton(ext); transactions::ethereum_events::apply_derived_tx(storage, events) @@ -1103,7 +1097,6 @@ mod tests { use borsh::BorshDeserialize; use eyre::Result; - use namada_core::ledger::storage_api::StorageRead; use namada_core::proto::{SignableEthMessage, Signed}; use namada_core::types::ethereum_events::testing::DAI_ERC20_ETH_ADDRESS; use namada_core::types::ethereum_events::{ @@ -1123,6 +1116,7 @@ mod tests { use namada_ethereum_bridge::storage::proof::EthereumProof; use namada_ethereum_bridge::storage::{vote_tallies, vp}; use namada_ethereum_bridge::test_utils; + use namada_storage::StorageRead; use super::*; diff --git a/shared/src/ledger/storage/mod.rs b/shared/src/ledger/storage/mod.rs index 84d751ca92..d78fd79ec8 100644 --- a/shared/src/ledger/storage/mod.rs +++ b/shared/src/ledger/storage/mod.rs @@ -1,5 +1,3 @@ //! Ledger's state storage with key-value backed store and a merkle tree -pub use namada_core::ledger::storage::{ - mockdb, traits, write_log, PrefixIter, WlStorage, *, -}; +pub use namada_state::{write_log, PrefixIter, WlStorage, *}; diff --git a/shared/src/ledger/storage_api.rs b/shared/src/ledger/storage_api.rs deleted file mode 100644 index 1a377fefc8..0000000000 --- a/shared/src/ledger/storage_api.rs +++ /dev/null @@ -1,3 +0,0 @@ -//! Re-export of core storage_api for backward compatibility - -pub use namada_core::ledger::storage_api::*; diff --git a/shared/src/ledger/vp_host_fns.rs b/shared/src/ledger/vp_host_fns.rs index 5b4bc4f671..d0c60ed4fc 100644 --- a/shared/src/ledger/vp_host_fns.rs +++ b/shared/src/ledger/vp_host_fns.rs @@ -2,20 +2,21 @@ use std::num::TryFromIntError; -use namada_core::ledger::gas::MEMORY_ACCESS_GAS_PER_BYTE; use namada_core::types::address::{Address, ESTABLISHED_ADDRESS_BYTES_LEN}; use namada_core::types::hash::{Hash, HASH_LENGTH}; use namada_core::types::storage::{ - BlockHash, BlockHeight, Epoch, Header, Key, TxIndex, TX_INDEX_LENGTH, + BlockHash, BlockHeight, Epoch, Epochs, Header, Key, TxIndex, + TX_INDEX_LENGTH, }; use namada_core::types::validity_predicate::VpSentinel; +use namada_gas::MEMORY_ACCESS_GAS_PER_BYTE; +use namada_state::write_log::WriteLog; +use namada_state::{write_log, Storage, StorageHasher}; +use namada_tx::{Section, Tx}; use thiserror::Error; use crate::ledger::gas; use crate::ledger::gas::{GasMetering, VpGasMeter}; -use crate::ledger::storage::write_log::WriteLog; -use crate::ledger::storage::{self, write_log, Storage, StorageHasher}; -use crate::proto::{Section, Tx}; use crate::types::ibc::IbcEvent; /// These runtime errors will abort VP execution immediately @@ -25,7 +26,7 @@ pub enum RuntimeError { #[error("Out of gas: {0}")] OutOfGas(gas::Error), #[error("Storage error: {0}")] - StorageError(storage::Error), + StorageError(namada_state::Error), #[error("Storage data error: {0}")] StorageDataError(crate::types::storage::Error), #[error("Encoding error: {0}")] @@ -68,7 +69,7 @@ pub fn read_pre( sentinel: &mut VpSentinel, ) -> EnvResult>> where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, { let (log_val, gas) = write_log.read_pre(key); @@ -110,7 +111,7 @@ pub fn read_post( sentinel: &mut VpSentinel, ) -> EnvResult>> where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, { // Try to read from the write log first @@ -173,7 +174,7 @@ pub fn has_key_pre( sentinel: &mut VpSentinel, ) -> EnvResult where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, { // Try to read from the write log first @@ -207,7 +208,7 @@ pub fn has_key_post( sentinel: &mut VpSentinel, ) -> EnvResult where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, { // Try to read from the write log first @@ -238,7 +239,7 @@ pub fn get_chain_id( sentinel: &mut VpSentinel, ) -> EnvResult where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, { let (chain_id, gas) = storage.get_chain_id(); @@ -254,7 +255,7 @@ pub fn get_block_height( sentinel: &mut VpSentinel, ) -> EnvResult where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, { let (height, gas) = storage.get_block_height(); @@ -270,7 +271,7 @@ pub fn get_block_header( sentinel: &mut VpSentinel, ) -> EnvResult> where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, { let (header, gas) = storage @@ -288,7 +289,7 @@ pub fn get_block_hash( sentinel: &mut VpSentinel, ) -> EnvResult where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, { let (hash, gas) = storage.get_block_hash(); @@ -323,7 +324,7 @@ pub fn get_block_epoch( sentinel: &mut VpSentinel, ) -> EnvResult where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, { let (epoch, gas) = storage.get_current_epoch(); @@ -353,7 +354,7 @@ pub fn get_native_token( sentinel: &mut VpSentinel, ) -> EnvResult
where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, { add_gas( @@ -364,6 +365,26 @@ where Ok(storage.native_token.clone()) } +/// Given the information about predecessor block epochs +pub fn get_pred_epochs( + gas_meter: &mut VpGasMeter, + storage: &Storage, + sentinel: &mut VpSentinel, +) -> EnvResult +where + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, + H: StorageHasher, +{ + add_gas( + gas_meter, + storage.block.pred_epochs.first_block_heights.len() as u64 + * 8 + * MEMORY_ACCESS_GAS_PER_BYTE, + sentinel, + )?; + Ok(storage.block.pred_epochs.clone()) +} + /// Getting the IBC event. pub fn get_ibc_events( _gas_meter: &mut VpGasMeter, @@ -386,12 +407,12 @@ pub fn iter_prefix_pre<'a, DB, H>( storage: &'a Storage, prefix: &Key, sentinel: &mut VpSentinel, -) -> EnvResult> +) -> EnvResult> where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, { - let (iter, gas) = storage::iter_prefix_pre(write_log, storage, prefix); + let (iter, gas) = namada_state::iter_prefix_pre(write_log, storage, prefix); add_gas(gas_meter, gas, sentinel)?; Ok(iter) } @@ -404,12 +425,13 @@ pub fn iter_prefix_post<'a, DB, H>( storage: &'a Storage, prefix: &Key, sentinel: &mut VpSentinel, -) -> EnvResult> +) -> EnvResult> where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, { - let (iter, gas) = storage::iter_prefix_post(write_log, storage, prefix); + let (iter, gas) = + namada_state::iter_prefix_post(write_log, storage, prefix); add_gas(gas_meter, gas, sentinel)?; Ok(iter) } @@ -417,11 +439,11 @@ where /// Get the next item in a storage prefix iterator (pre or post). pub fn iter_next( gas_meter: &mut VpGasMeter, - iter: &mut storage::PrefixIter, + iter: &mut namada_state::PrefixIter, sentinel: &mut VpSentinel, ) -> EnvResult)>> where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, { if let Some((key, val, gas)) = iter.next() { add_gas(gas_meter, gas, sentinel)?; diff --git a/shared/src/lib.rs b/shared/src/lib.rs index 5937c345c9..b6bfa42323 100644 --- a/shared/src/lib.rs +++ b/shared/src/lib.rs @@ -11,8 +11,8 @@ pub use namada_core::{ibc, tendermint, tendermint_proto}; pub use tendermint_rpc; pub use {bip39, namada_core as core, namada_proof_of_stake as proof_of_stake}; pub mod ledger; -pub use namada_core::proto; pub use namada_sdk; +pub use namada_tx::proto; pub mod types; pub mod vm; diff --git a/shared/src/types/mod.rs b/shared/src/types/mod.rs index 04801cf6c5..f56a9afc39 100644 --- a/shared/src/types/mod.rs +++ b/shared/src/types/mod.rs @@ -7,6 +7,6 @@ pub mod key; pub use namada_core::types::{ address, chain, dec, eth_abi, eth_bridge_pool, ethereum_events, hash, - internal, keccak, masp, storage, time, token, transaction, uint, - validity_predicate, vote_extensions, voting_power, + internal, keccak, masp, storage, time, token, uint, validity_predicate, + voting_power, }; diff --git a/shared/src/vm/host_env.rs b/shared/src/vm/host_env.rs index aa7399bc3a..1d53591a76 100644 --- a/shared/src/vm/host_env.rs +++ b/shared/src/vm/host_env.rs @@ -7,15 +7,23 @@ use std::num::TryFromIntError; use borsh::BorshDeserialize; use borsh_ext::BorshSerializeExt; use masp_primitives::transaction::Transaction; -use namada_core::ledger::gas::{ - GasMetering, TxGasMeter, MEMORY_ACCESS_GAS_PER_BYTE, -}; -use namada_core::ledger::masp_utils; use namada_core::types::address::ESTABLISHED_ADDRESS_BYTES_LEN; use namada_core::types::internal::KeyVal; -use namada_core::types::storage::TX_INDEX_LENGTH; -use namada_core::types::transaction::TxSentinel; +use namada_core::types::storage::{Epochs, TX_INDEX_LENGTH}; use namada_core::types::validity_predicate::VpSentinel; +use namada_gas::{ + self as gas, GasMetering, TxGasMeter, VpGasMeter, + MEMORY_ACCESS_GAS_PER_BYTE, +}; +use namada_state::write_log::{self, WriteLog}; +use namada_state::{Storage, StorageHasher}; +use namada_storage::{self, ResultExt}; +use namada_token::storage_key::{ + balance_key, is_any_minted_balance_key, is_any_minter_key, + is_any_token_balance_key, minted_balance_key, minter_key, +}; +use namada_tx::data::TxSentinel; +use namada_tx::Tx; use thiserror::Error; #[cfg(feature = "wasm-runtime")] @@ -23,20 +31,12 @@ use super::wasm::TxCache; #[cfg(feature = "wasm-runtime")] use super::wasm::VpCache; use super::WasmCacheAccess; -use crate::ledger::gas::{self, VpGasMeter}; -use crate::ledger::storage::write_log::{self, WriteLog}; -use crate::ledger::storage::{self, Storage, StorageHasher}; -use crate::ledger::storage_api::{self, ResultExt}; use crate::ledger::vp_host_fns; -use crate::proto::Tx; use crate::types::address::{self, Address}; use crate::types::hash::Hash; use crate::types::ibc::{IbcEvent, IbcShieldedTransfer}; use crate::types::internal::HostEnvResult; use crate::types::storage::{BlockHeight, Epoch, Key, TxIndex}; -use crate::types::token::{ - is_any_minted_balance_key, is_any_minter_key, is_any_token_balance_key, -}; use crate::vm::memory::VmMemory; use crate::vm::prefix_iter::{PrefixIteratorId, PrefixIterators}; use crate::vm::{HostRef, MutHostRef}; @@ -58,7 +58,7 @@ pub enum TxRuntimeError { #[error("Storage modification error: {0}")] StorageModificationError(write_log::Error), #[error("Storage error: {0}")] - StorageError(storage::Error), + StorageError(namada_state::Error), #[error("Storage data error: {0}")] StorageDataError(crate::types::storage::Error), #[error("Encoding error: {0}")] @@ -72,7 +72,7 @@ pub enum TxRuntimeError { #[error("Missing tx data")] MissingTxData, #[error("IBC: {0}")] - Ibc(#[from] namada_core::ledger::ibc::Error), + Ibc(#[from] namada_ibc::Error), } type TxResult = std::result::Result; @@ -81,7 +81,7 @@ type TxResult = std::result::Result; pub struct TxVmEnv<'a, MEM, DB, H, CA> where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -95,7 +95,7 @@ where #[derive(Debug)] pub struct TxCtx<'a, DB, H, CA> where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -133,7 +133,7 @@ where impl<'a, MEM, DB, H, CA> TxVmEnv<'a, MEM, DB, H, CA> where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -197,7 +197,7 @@ where impl Clone for TxVmEnv<'_, MEM, DB, H, CA> where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -211,7 +211,7 @@ where impl<'a, DB, H, CA> Clone for TxCtx<'a, DB, H, CA> where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -240,7 +240,7 @@ where pub struct VpVmEnv<'a, MEM, DB, H, EVAL, CA> where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -254,7 +254,7 @@ where /// A validity predicate's host context pub struct VpCtx<'a, DB, H, EVAL, CA> where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -296,7 +296,7 @@ where /// A Validity predicate runner for calls from the [`vp_eval`] function. pub trait VpEvaluator { /// Storage DB type - type Db: storage::DB + for<'iter> storage::DBIter<'iter>; + type Db: namada_state::DB + for<'iter> namada_state::DBIter<'iter>; /// Storage hasher type type H: StorageHasher; /// Recursive VP evaluator type @@ -320,7 +320,7 @@ pub trait VpEvaluator { impl<'a, MEM, DB, H, EVAL, CA> VpVmEnv<'a, MEM, DB, H, EVAL, CA> where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -373,7 +373,7 @@ where impl Clone for VpVmEnv<'_, MEM, DB, H, EVAL, CA> where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -388,7 +388,7 @@ where impl<'a, DB, H, EVAL, CA> VpCtx<'a, DB, H, EVAL, CA> where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -453,7 +453,7 @@ where impl<'a, DB, H, EVAL, CA> Clone for VpCtx<'a, DB, H, EVAL, CA> where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -487,7 +487,7 @@ pub fn tx_charge_gas( ) -> TxResult<()> where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -512,7 +512,7 @@ pub fn vp_charge_gas( ) -> vp_host_fns::EnvResult<()> where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -531,7 +531,7 @@ pub fn tx_has_key( ) -> TxResult where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -587,7 +587,7 @@ pub fn tx_read( ) -> TxResult where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -676,7 +676,7 @@ pub fn tx_result_buffer( ) -> TxResult<()> where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -699,7 +699,7 @@ pub fn tx_iter_prefix( ) -> TxResult where MEM: VmMemory, - DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -716,7 +716,8 @@ where let write_log = unsafe { env.ctx.write_log.get() }; let storage = unsafe { env.ctx.storage.get() }; - let (iter, gas) = storage::iter_prefix_post(write_log, storage, &prefix); + let (iter, gas) = + namada_state::iter_prefix_post(write_log, storage, &prefix); tx_charge_gas(env, gas)?; let iterators = unsafe { env.ctx.iterators.get() }; @@ -735,7 +736,7 @@ pub fn tx_iter_next( ) -> TxResult where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -814,7 +815,7 @@ pub fn tx_write( ) -> TxResult<()> where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -857,7 +858,7 @@ pub fn tx_write_temp( ) -> TxResult<()> where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -891,7 +892,7 @@ fn check_address_existence( ) -> TxResult<()> where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -947,7 +948,7 @@ pub fn tx_delete( ) -> TxResult<()> where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -980,7 +981,7 @@ pub fn tx_emit_ibc_event( ) -> TxResult<()> where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -1004,7 +1005,7 @@ pub fn tx_get_ibc_events( ) -> TxResult where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -1042,7 +1043,7 @@ pub fn vp_read_pre( ) -> vp_host_fns::EnvResult where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -1095,7 +1096,7 @@ pub fn vp_read_post( ) -> vp_host_fns::EnvResult where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -1143,7 +1144,7 @@ pub fn vp_read_temp( ) -> vp_host_fns::EnvResult where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -1191,7 +1192,7 @@ pub fn vp_result_buffer( ) -> vp_host_fns::EnvResult<()> where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -1216,7 +1217,7 @@ pub fn vp_has_key_pre( ) -> vp_host_fns::EnvResult where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -1251,7 +1252,7 @@ pub fn vp_has_key_post( ) -> vp_host_fns::EnvResult where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -1287,7 +1288,7 @@ pub fn vp_iter_prefix_pre( ) -> vp_host_fns::EnvResult where MEM: VmMemory, - DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -1326,7 +1327,7 @@ pub fn vp_iter_prefix_post( ) -> vp_host_fns::EnvResult where MEM: VmMemory, - DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -1365,7 +1366,7 @@ pub fn vp_iter_next( ) -> vp_host_fns::EnvResult where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -1402,7 +1403,7 @@ pub fn tx_insert_verifier( ) -> TxResult<()> where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -1437,7 +1438,7 @@ pub fn tx_update_validity_predicate( ) -> TxResult<()> where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -1485,7 +1486,7 @@ pub fn tx_init_account( ) -> TxResult<()> where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -1528,7 +1529,7 @@ pub fn tx_get_chain_id( ) -> TxResult<()> where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -1550,7 +1551,7 @@ pub fn tx_get_block_height( ) -> TxResult where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -1568,7 +1569,7 @@ pub fn tx_get_tx_index( ) -> TxResult where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -1585,7 +1586,7 @@ pub fn vp_get_tx_index( ) -> vp_host_fns::EnvResult where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -1605,7 +1606,7 @@ pub fn tx_get_block_hash( ) -> TxResult<()> where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -1627,7 +1628,7 @@ pub fn tx_get_block_epoch( ) -> TxResult where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -1644,7 +1645,7 @@ pub fn tx_get_native_token( ) -> TxResult<()> where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -1670,7 +1671,7 @@ pub fn tx_get_block_header( ) -> TxResult where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -1701,7 +1702,7 @@ pub fn vp_get_chain_id( ) -> vp_host_fns::EnvResult<()> where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -1725,7 +1726,7 @@ pub fn vp_get_block_height( ) -> vp_host_fns::EnvResult where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -1744,7 +1745,7 @@ pub fn vp_get_block_header( ) -> vp_host_fns::EnvResult where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -1779,7 +1780,7 @@ pub fn vp_get_block_hash( ) -> vp_host_fns::EnvResult<()> where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -1802,7 +1803,7 @@ pub fn vp_get_tx_code_hash( ) -> vp_host_fns::EnvResult<()> where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -1833,7 +1834,7 @@ pub fn vp_get_block_epoch( ) -> vp_host_fns::EnvResult where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -1853,7 +1854,7 @@ pub fn vp_get_ibc_events( ) -> vp_host_fns::EnvResult where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -1897,7 +1898,7 @@ pub fn vp_verify_tx_section_signature( ) -> vp_host_fns::EnvResult where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -1952,11 +1953,11 @@ where ) { Ok(_) => Ok(HostEnvResult::Success.to_i64()), Err(err) => match err { - namada_core::proto::Error::OutOfGas(inner) => { + namada_tx::VerifySigError::Gas(inner) => { sentinel.set_out_of_gas(); Err(vp_host_fns::RuntimeError::OutOfGas(inner)) } - namada_core::proto::Error::InvalidSectionSignature(_) => { + namada_tx::VerifySigError::InvalidSectionSignature(_) => { sentinel.set_invalid_signature(); Ok(HostEnvResult::Fail.to_i64()) } @@ -1975,7 +1976,7 @@ pub fn tx_log_string( ) -> TxResult<()> where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -1995,14 +1996,14 @@ pub fn tx_ibc_execute( ) -> TxResult<()> where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { use std::cell::RefCell; use std::rc::Rc; - use namada_core::ledger::ibc::{IbcActions, TransferModule}; + use namada_ibc::{IbcActions, TransferModule}; let tx_data = unsafe { env.ctx.tx.get().data() }.ok_or_else(|| { let sentinel = unsafe { env.ctx.sentinel.get() }; @@ -2026,7 +2027,7 @@ fn tx_validate_vp_code_hash( ) -> TxResult<()> where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -2083,7 +2084,7 @@ where pub fn tx_set_commitment_sentinel(env: &TxVmEnv) where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -2105,7 +2106,7 @@ pub fn tx_verify_tx_section_signature( ) -> TxResult where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -2153,11 +2154,11 @@ where ) { Ok(_) => Ok(HostEnvResult::Success.to_i64()), Err(err) => match err { - namada_core::proto::Error::OutOfGas(inner) => { + namada_tx::VerifySigError::Gas(inner) => { sentinel.set_out_of_gas(); Err(TxRuntimeError::OutOfGas(inner)) } - namada_core::proto::Error::InvalidSectionSignature(_) => { + namada_tx::VerifySigError::InvalidSectionSignature(_) => { Ok(HostEnvResult::Fail.to_i64()) } _ => Ok(HostEnvResult::Fail.to_i64()), @@ -2173,7 +2174,7 @@ pub fn tx_update_masp_note_commitment_tree( ) -> TxResult where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -2189,7 +2190,10 @@ where .map_err(TxRuntimeError::EncodingError)?; let mut ctx = env.ctx.clone(); - match masp_utils::update_note_commitment_tree(&mut ctx, &transaction) { + match namada_token::utils::update_note_commitment_tree( + &mut ctx, + &transaction, + ) { Ok(()) => Ok(HostEnvResult::Success.to_i64()), Err(_) => { // NOTE: sentinel for gas errors is already set by the @@ -2210,7 +2214,7 @@ pub fn vp_eval( ) -> vp_host_fns::EnvResult where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -2250,7 +2254,7 @@ pub fn vp_get_native_token( ) -> vp_host_fns::EnvResult<()> where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -2278,7 +2282,7 @@ pub fn vp_log_string( ) -> vp_host_fns::EnvResult<()> where MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -2292,12 +2296,12 @@ where } // Temp. workaround for -use namada_core::ledger::storage_api::StorageRead; +use namada_storage::StorageRead; use crate::types::storage::BlockHash; impl<'a, DB, H, CA> StorageRead for TxCtx<'a, DB, H, CA> where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -2307,7 +2311,7 @@ where fn read_bytes( &self, key: &Key, - ) -> std::result::Result>, storage_api::Error> { + ) -> std::result::Result>, namada_storage::Error> { let write_log = unsafe { self.write_log.get() }; let (log_val, gas) = write_log.read(key); ibc_tx_charge_gas(self, gas)?; @@ -2332,7 +2336,7 @@ where }) } - fn has_key(&self, key: &Key) -> Result { + fn has_key(&self, key: &Key) -> Result { // try to read from the write log first let write_log = unsafe { self.write_log.get() }; let (log_val, gas) = write_log.read(key); @@ -2356,10 +2360,11 @@ where fn iter_prefix<'iter>( &'iter self, prefix: &Key, - ) -> Result, storage_api::Error> { + ) -> Result, namada_storage::Error> { let write_log = unsafe { self.write_log.get() }; let storage = unsafe { self.storage.get() }; - let (iter, gas) = storage::iter_prefix_post(write_log, storage, prefix); + let (iter, gas) = + namada_state::iter_prefix_post(write_log, storage, prefix); ibc_tx_charge_gas(self, gas)?; let iterators = unsafe { self.iterators.get() }; @@ -2369,7 +2374,7 @@ where fn iter_next<'iter>( &'iter self, iter_id: &mut Self::PrefixIter<'iter>, - ) -> Result)>, storage_api::Error> { + ) -> Result)>, namada_storage::Error> { let write_log = unsafe { self.write_log.get() }; let iterators = unsafe { self.iterators.get() }; let iter_id = PrefixIteratorId::new(*iter_id); @@ -2402,14 +2407,14 @@ where Ok(None) } - fn get_chain_id(&self) -> Result { + fn get_chain_id(&self) -> Result { let storage = unsafe { self.storage.get() }; let (chain_id, gas) = storage.get_chain_id(); ibc_tx_charge_gas(self, gas)?; Ok(chain_id) } - fn get_block_height(&self) -> Result { + fn get_block_height(&self) -> Result { let storage = unsafe { self.storage.get() }; let (height, gas) = storage.get_block_height(); ibc_tx_charge_gas(self, gas)?; @@ -2419,8 +2424,10 @@ where fn get_block_header( &self, height: BlockHeight, - ) -> Result, storage_api::Error> - { + ) -> Result< + Option, + namada_storage::Error, + > { let storage = unsafe { self.storage.get() }; let (header, gas) = storage .get_block_header(Some(height)) @@ -2429,21 +2436,21 @@ where Ok(header) } - fn get_block_hash(&self) -> Result { + fn get_block_hash(&self) -> Result { let storage = unsafe { self.storage.get() }; let (hash, gas) = storage.get_block_hash(); ibc_tx_charge_gas(self, gas)?; Ok(hash) } - fn get_block_epoch(&self) -> Result { + fn get_block_epoch(&self) -> Result { let storage = unsafe { self.storage.get() }; let (epoch, gas) = storage.get_current_epoch(); ibc_tx_charge_gas(self, gas)?; Ok(epoch) } - fn get_tx_index(&self) -> Result { + fn get_tx_index(&self) -> Result { let tx_index = unsafe { self.tx_index.get() }; ibc_tx_charge_gas( self, @@ -2452,7 +2459,7 @@ where Ok(TxIndex(tx_index.0)) } - fn get_native_token(&self) -> Result { + fn get_native_token(&self) -> Result { let storage = unsafe { self.storage.get() }; let native_token = storage.native_token.clone(); ibc_tx_charge_gas( @@ -2461,13 +2468,22 @@ where )?; Ok(native_token) } + + fn get_pred_epochs(&self) -> namada_storage::Result { + let storage = unsafe { self.storage.get() }; + ibc_tx_charge_gas( + self, + crate::vm::host_env::gas::STORAGE_ACCESS_GAS_PER_BYTE, + )?; + Ok(storage.block.pred_epochs.clone()) + } } // Temp. workaround for -use namada_core::ledger::storage_api::StorageWrite; +use namada_storage::StorageWrite; impl<'a, DB, H, CA> StorageWrite for TxCtx<'a, DB, H, CA> where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -2475,7 +2491,7 @@ where &mut self, key: &Key, data: impl AsRef<[u8]>, - ) -> Result<(), storage_api::Error> { + ) -> Result<(), namada_storage::Error> { let write_log = unsafe { self.write_log.get() }; let (gas, _size_diff) = write_log .write(key, data.as_ref().to_vec()) @@ -2483,7 +2499,7 @@ where ibc_tx_charge_gas(self, gas) } - fn delete(&mut self, key: &Key) -> Result<(), storage_api::Error> { + fn delete(&mut self, key: &Key) -> Result<(), namada_storage::Error> { if key.is_validity_predicate().is_some() { return Err(TxRuntimeError::CannotDeleteVp).into_storage_result(); } @@ -2495,17 +2511,16 @@ where } // Temp. workaround for -impl<'a, DB, H, CA> namada_core::ledger::ibc::IbcStorageContext - for TxCtx<'a, DB, H, CA> +impl<'a, DB, H, CA> namada_ibc::IbcStorageContext for TxCtx<'a, DB, H, CA> where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { fn emit_ibc_event( &mut self, event: IbcEvent, - ) -> Result<(), storage_api::Error> { + ) -> Result<(), namada_storage::Error> { let write_log = unsafe { self.write_log.get() }; let gas = write_log.emit_ibc_event(event); ibc_tx_charge_gas(self, gas) @@ -2514,7 +2529,7 @@ where fn get_ibc_events( &self, event_type: impl AsRef, - ) -> Result, storage_api::Error> { + ) -> Result, namada_storage::Error> { let write_log = unsafe { self.write_log.get() }; Ok(write_log .get_ibc_events() @@ -2530,13 +2545,13 @@ where dest: &Address, token: &Address, amount: namada_core::types::token::DenominatedAmount, - ) -> Result<(), storage_api::Error> { - use namada_core::types::token; + ) -> Result<(), namada_storage::Error> { + use namada_token as token; - let amount = amount.to_amount(token, self)?; + let amount = token::denom_to_amount(amount, token, self)?; if amount != token::Amount::default() && src != dest { - let src_key = token::balance_key(token, src); - let dest_key = token::balance_key(token, dest); + let src_key = balance_key(token, src); + let dest_key = balance_key(token, dest); let src_bal = self.read::(&src_key)?; let mut src_bal = src_bal.unwrap_or_else(|| { self.log_string(format!("src {} has no balance", src_key)); @@ -2555,13 +2570,16 @@ where fn handle_masp_tx( &mut self, shielded: &IbcShieldedTransfer, - ) -> Result<(), storage_api::Error> { - masp_utils::handle_masp_tx( + ) -> Result<(), namada_storage::Error> { + namada_token::utils::handle_masp_tx( self, &shielded.transfer, &shielded.masp_tx, )?; - masp_utils::update_note_commitment_tree(self, &shielded.masp_tx) + namada_token::utils::update_note_commitment_tree( + self, + &shielded.masp_tx, + ) } fn mint_token( @@ -2569,16 +2587,16 @@ where target: &Address, token: &Address, amount: namada_core::types::token::DenominatedAmount, - ) -> Result<(), storage_api::Error> { - use namada_core::types::token; + ) -> Result<(), namada_storage::Error> { + use namada_token as token; - let amount = amount.to_amount(token, self)?; - let target_key = token::balance_key(token, target); + let amount = token::denom_to_amount(amount, token, self)?; + let target_key = balance_key(token, target); let mut target_bal = self.read::(&target_key)?.unwrap_or_default(); target_bal.receive(&amount); - let minted_key = token::minted_balance_key(token); + let minted_key = minted_balance_key(token); let mut minted_bal = self.read::(&minted_key)?.unwrap_or_default(); minted_bal.receive(&amount); @@ -2586,7 +2604,7 @@ where self.write(&target_key, target_bal)?; self.write(&minted_key, minted_bal)?; - let minter_key = token::minter_key(token); + let minter_key = minter_key(token); self.write( &minter_key, Address::Internal(address::InternalAddress::Ibc), @@ -2598,17 +2616,17 @@ where target: &Address, token: &Address, amount: namada_core::types::token::DenominatedAmount, - ) -> Result<(), storage_api::Error> { - use namada_core::types::token; + ) -> Result<(), namada_storage::Error> { + use namada_token as token; - let amount = amount.to_amount(token, self)?; - let target_key = token::balance_key(token, target); + let amount = token::denom_to_amount(amount, token, self)?; + let target_key = balance_key(token, target); let mut target_bal = self.read::(&target_key)?.unwrap_or_default(); target_bal.spend(&amount); // burn the minted amount - let minted_key = token::minted_balance_key(token); + let minted_key = minted_balance_key(token); let mut minted_bal = self.read::(&minted_key)?.unwrap_or_default(); minted_bal.spend(&amount); @@ -2627,9 +2645,9 @@ where fn ibc_tx_charge_gas<'a, DB, H, CA>( ctx: &TxCtx<'a, DB, H, CA>, used_gas: u64, -) -> Result<(), storage_api::Error> +) -> Result<(), namada_storage::Error> where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -2646,10 +2664,9 @@ where } // Temp. workaround for -impl<'a, DB, H, CA> namada_core::ledger::ibc::IbcCommonContext - for TxCtx<'a, DB, H, CA> +impl<'a, DB, H, CA> namada_ibc::IbcCommonContext for TxCtx<'a, DB, H, CA> where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -2660,9 +2677,9 @@ where pub mod testing { use std::collections::BTreeSet; + use namada_state::StorageHasher; + use super::*; - use crate::ledger::storage::traits::StorageHasher; - use crate::ledger::storage::{self}; use crate::vm::memory::testing::NativeMemory; /// Setup a transaction environment @@ -2681,7 +2698,7 @@ pub mod testing { #[cfg(feature = "wasm-runtime")] tx_wasm_cache: &mut TxCache, ) -> TxVmEnv<'static, NativeMemory, DB, H, CA> where - DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -2721,7 +2738,7 @@ pub mod testing { #[cfg(feature = "wasm-runtime")] vp_wasm_cache: &mut VpCache, ) -> VpVmEnv<'static, NativeMemory, DB, H, EVAL, CA> where - DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, diff --git a/shared/src/vm/prefix_iter.rs b/shared/src/vm/prefix_iter.rs index 49e58b33dd..0044e2d17a 100644 --- a/shared/src/vm/prefix_iter.rs +++ b/shared/src/vm/prefix_iter.rs @@ -3,16 +3,14 @@ use std::collections::HashMap; -use namada_core::ledger::storage::PrefixIter; - -use crate::ledger::storage; +use namada_state::PrefixIter; /// A temporary iterators storage, used during a wasm run after which it's /// dropped. Each iterator is assigned a [`PrefixIteratorId`]. #[derive(Debug)] pub struct PrefixIterators<'iter, DB> where - DB: storage::DB + storage::DBIter<'iter>, + DB: namada_state::DB + namada_state::DBIter<'iter>, { index: PrefixIteratorId, iterators: HashMap>, @@ -20,7 +18,7 @@ where impl<'iter, DB> PrefixIterators<'iter, DB> where - DB: storage::DB + storage::DBIter<'iter>, + DB: namada_state::DB + namada_state::DBIter<'iter>, { /// Insert a new prefix iterator to the temporary storage. pub fn insert(&mut self, iter: PrefixIter<'iter, DB>) -> PrefixIteratorId { @@ -49,7 +47,7 @@ where impl<'iter, DB> Default for PrefixIterators<'iter, DB> where - DB: storage::DB + storage::DBIter<'iter>, + DB: namada_state::DB + namada_state::DBIter<'iter>, { fn default() -> Self { Self { diff --git a/shared/src/vm/types.rs b/shared/src/vm/types.rs index 893496f1e6..54cc170371 100644 --- a/shared/src/vm/types.rs +++ b/shared/src/vm/types.rs @@ -11,7 +11,8 @@ use std::collections::BTreeSet; -use crate::proto::Tx; +use namada_tx::Tx; + use crate::types::address::Address; use crate::types::storage; diff --git a/shared/src/vm/wasm/host_env.rs b/shared/src/vm/wasm/host_env.rs index 304a27a7f2..7d9417596d 100644 --- a/shared/src/vm/wasm/host_env.rs +++ b/shared/src/vm/wasm/host_env.rs @@ -3,20 +3,19 @@ //! Here, we expose the host functions into wasm's //! imports, so they can be called from inside the wasm. +use namada_core::types::hash::StorageHasher; use wasmer::{ Function, HostEnvInitError, ImportObject, Instance, Memory, Store, WasmerEnv, }; -use crate::ledger::storage::traits::StorageHasher; -use crate::ledger::storage::{self}; use crate::vm::host_env::{TxVmEnv, VpEvaluator, VpVmEnv}; use crate::vm::wasm::memory::WasmMemory; use crate::vm::{host_env, WasmCacheAccess}; impl WasmerEnv for TxVmEnv<'_, WasmMemory, DB, H, CA> where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -30,7 +29,7 @@ where impl WasmerEnv for VpVmEnv<'_, WasmMemory, DB, H, EVAL, CA> where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, @@ -52,7 +51,7 @@ pub fn tx_imports( env: TxVmEnv<'static, WasmMemory, DB, H, CA>, ) -> ImportObject where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -101,7 +100,7 @@ pub fn vp_imports( env: VpVmEnv<'static, WasmMemory, DB, H, EVAL, CA>, ) -> ImportObject where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, EVAL: VpEvaluator, CA: WasmCacheAccess, diff --git a/shared/src/vm/wasm/memory.rs b/shared/src/vm/wasm/memory.rs index b93ecb79f1..07b1bf63df 100644 --- a/shared/src/vm/wasm/memory.rs +++ b/shared/src/vm/wasm/memory.rs @@ -6,7 +6,8 @@ use std::str::Utf8Error; use std::sync::Arc; use borsh_ext::BorshSerializeExt; -use namada_core::ledger::gas::MEMORY_ACCESS_GAS_PER_BYTE; +use namada_gas::MEMORY_ACCESS_GAS_PER_BYTE; +use namada_tx::Tx; use thiserror::Error; use wasmer::{ vm, BaseTunables, HostEnvInitError, LazyInit, Memory, MemoryError, @@ -16,7 +17,6 @@ use wasmer_vm::{ MemoryStyle, TableStyle, VMMemoryDefinition, VMTableDefinition, }; -use crate::proto::Tx; use crate::vm::memory::VmMemory; use crate::vm::types::VpInput; diff --git a/shared/src/vm/wasm/run.rs b/shared/src/vm/wasm/run.rs index 7b7a009c6a..ac72aeee81 100644 --- a/shared/src/vm/wasm/run.rs +++ b/shared/src/vm/wasm/run.rs @@ -4,10 +4,12 @@ use std::collections::BTreeSet; use std::marker::PhantomData; use borsh::BorshDeserialize; -use namada_core::ledger::gas::{GasMetering, TxGasMeter, WASM_MEMORY_PAGE_GAS}; -use namada_core::ledger::storage::write_log::StorageModification; -use namada_core::types::transaction::TxSentinel; use namada_core::types::validity_predicate::VpSentinel; +use namada_gas::{GasMetering, TxGasMeter, WASM_MEMORY_PAGE_GAS}; +use namada_state::write_log::StorageModification; +use namada_state::{Storage, StorageHasher}; +use namada_tx::data::TxSentinel; +use namada_tx::{Commitment, Section, Tx}; use parity_wasm::elements; use thiserror::Error; use wasmer::{BaseTunables, Module, Store}; @@ -16,8 +18,6 @@ use super::memory::{Limit, WasmMemory}; use super::TxCache; use crate::ledger::gas::VpGasMeter; use crate::ledger::storage::write_log::WriteLog; -use crate::ledger::storage::{self, Storage, StorageHasher}; -use crate::proto::{Commitment, Section, Tx}; use crate::types::address::Address; use crate::types::hash::{Error as TxHashError, Hash}; use crate::types::internal::HostEnvResult; @@ -101,7 +101,7 @@ pub fn tx( tx_wasm_cache: &mut TxCache, ) -> Result> where - DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { @@ -232,7 +232,7 @@ pub fn vp( mut vp_wasm_cache: VpCache, ) -> Result where - DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { @@ -383,7 +383,7 @@ fn run_vp( #[derive(Default, Debug)] pub struct VpEvalWasm where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, + DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -397,7 +397,7 @@ where impl VpEvaluator for VpEvalWasm where - DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CA: WasmCacheAccess, { @@ -424,7 +424,7 @@ where impl VpEvalWasm where - DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CA: WasmCacheAccess, { @@ -512,7 +512,7 @@ fn fetch_or_compile( gas_meter: &mut dyn GasMetering, ) -> Result<(Module, Store)> where - DB: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, + DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: 'static + StorageHasher, CN: 'static + CacheName, CA: 'static + WasmCacheAccess, diff --git a/shielded_token/src/storage_key.rs b/shielded_token/src/storage_key.rs index 2e241638ec..af4f725feb 100644 --- a/shielded_token/src/storage_key.rs +++ b/shielded_token/src/storage_key.rs @@ -70,6 +70,43 @@ pub fn is_masp_key(key: &storage::Key) -> bool { false } } +/// Check if the given storage key is allowed to be touched by a masp transfer +pub fn is_masp_allowed_key(key: &storage::Key) -> bool { + match &key.segments[..] { + [DbKeySeg::AddressSeg(addr), DbKeySeg::StringSeg(key)] + if *addr == address::MASP + && (key == HEAD_TX_KEY + || key.starts_with(TX_KEY_PREFIX) + || key.starts_with(PIN_KEY_PREFIX) + || key == MASP_NOTE_COMMITMENT_TREE_KEY) => + { + true + } + + [ + DbKeySeg::AddressSeg(addr), + DbKeySeg::StringSeg(key), + DbKeySeg::StringSeg(_nullifier), + ] if *addr == address::MASP && key == MASP_NULLIFIERS_KEY => true, + _ => false, + } +} + +/// Check if the given storage key is a masp tx prefix key +pub fn is_masp_tx_prefix_key(key: &storage::Key) -> bool { + matches!(&key.segments[..], + [DbKeySeg::AddressSeg(addr), + DbKeySeg::StringSeg(prefix), + ] if *addr == address::MASP && prefix.starts_with(TX_KEY_PREFIX)) +} + +/// Check if the given storage key is a masp tx pin key +pub fn is_masp_tx_pin_key(key: &storage::Key) -> bool { + matches!(&key.segments[..], + [DbKeySeg::AddressSeg(addr), + DbKeySeg::StringSeg(prefix), + ] if *addr == address::MASP && prefix.starts_with(PIN_KEY_PREFIX)) +} /// Check if the given storage key is a masp nullifier key pub fn is_masp_nullifier_key(key: &storage::Key) -> bool { diff --git a/state/Cargo.toml b/state/Cargo.toml index 54620d7f81..128e8bbb50 100644 --- a/state/Cargo.toml +++ b/state/Cargo.toml @@ -12,6 +12,14 @@ readme.workspace = true repository.workspace = true version.workspace = true +[features] +default = [] + +# for integration tests and test utilities +testing = [ + "namada_core/testing", +] + [dependencies] namada_core = { path = "../core", default-features = false } namada_gas = { path = "../gas" } diff --git a/state/src/lib.rs b/state/src/lib.rs index 84d0a5ab16..e46d03578d 100644 --- a/state/src/lib.rs +++ b/state/src/lib.rs @@ -33,11 +33,11 @@ use namada_gas::{ MEMORY_ACCESS_GAS_PER_BYTE, STORAGE_ACCESS_GAS_PER_BYTE, STORAGE_WRITE_GAS_PER_BYTE, }; -use namada_merkle_tree::{Error as MerkleTreeError, MerkleRoot}; pub use namada_merkle_tree::{ - MembershipProof, MerkleTree, MerkleTreeStoresRead, MerkleTreeStoresWrite, - StoreRef, StoreType, + ics23_specs, MembershipProof, MerkleTree, MerkleTreeStoresRead, + MerkleTreeStoresWrite, StoreRef, StoreType, }; +use namada_merkle_tree::{Error as MerkleTreeError, MerkleRoot}; use namada_parameters::{self, EpochDuration, Parameters}; use thiserror::Error; use tx_queue::{ExpiredTxsQueue, TxQueue}; diff --git a/tx/src/lib.rs b/tx/src/lib.rs index e40ff69533..caabfc6bd8 100644 --- a/tx/src/lib.rs +++ b/tx/src/lib.rs @@ -8,7 +8,7 @@ pub use namada_core::types::sign::SignatureIndex; pub use types::{ standalone_signature, verify_standalone_sig, Code, Commitment, CompressedSignature, Data, DecodeError, Header, MaspBuilder, Section, - Signature, Signed, Signer, Tx, TxError, + Signature, Signed, Signer, Tx, TxError, VerifySigError, }; #[cfg(test)] diff --git a/tx/src/types.rs b/tx/src/types.rs index 2d8c66d242..ce32a9e5fa 100644 --- a/tx/src/types.rs +++ b/tx/src/types.rs @@ -14,9 +14,10 @@ use namada_core::borsh::schema::{add_definition, Declaration, Definition}; use namada_core::borsh::{ BorshDeserialize, BorshSchema, BorshSerialize, BorshSerializeExt, }; +use namada_core::types::account::AccountPublicKeysMap; use namada_core::types::address::Address; use namada_core::types::chain::ChainId; -use namada_core::types::key::{AccountPublicKeysMap, *}; +use namada_core::types::key::*; use namada_core::types::sign::SignatureIndex; use namada_core::types::storage::Epoch; use namada_core::types::time::DateTimeUtc; diff --git a/tx_prelude/src/lib.rs b/tx_prelude/src/lib.rs index c8906dbce5..75d4e8e364 100644 --- a/tx_prelude/src/lib.rs +++ b/tx_prelude/src/lib.rs @@ -20,11 +20,12 @@ use masp_primitives::transaction::Transaction; pub use namada_core::borsh::{ BorshDeserialize, BorshSerialize, BorshSerializeExt, }; +use namada_core::types::account::AccountPublicKeysMap; pub use namada_core::types::address::Address; use namada_core::types::chain::CHAIN_ID_LENGTH; pub use namada_core::types::ethereum_events::EthAddress; use namada_core::types::internal::HostEnvResult; -use namada_core::types::key::{common, AccountPublicKeysMap}; +use namada_core::types::key::common; use namada_core::types::storage::TxIndex; pub use namada_core::types::storage::{ self, BlockHash, BlockHeight, Epoch, Header, BLOCK_HASH_LENGTH, diff --git a/vp_env/src/lib.rs b/vp_env/src/lib.rs index 57550206a8..a097e77c08 100644 --- a/vp_env/src/lib.rs +++ b/vp_env/src/lib.rs @@ -12,7 +12,7 @@ use namada_core::types::ibc::{ get_shielded_transfer, IbcEvent, MsgShieldedTransfer, EVENT_TYPE_PACKET, }; use namada_core::types::storage::{ - BlockHash, BlockHeight, Epoch, Header, Key, TxIndex, + BlockHash, BlockHeight, Epoch, Epochs, Header, Key, TxIndex, }; use namada_core::types::token::Transfer; use namada_storage::{OptionExt, ResultExt, StorageRead}; @@ -82,6 +82,9 @@ where /// Get the address of the native token. fn get_native_token(&self) -> Result; + /// Given the information about predecessor block epochs + fn get_pred_epochs(&self) -> namada_storage::Result; + /// Get the IBC events. fn get_ibc_events( &self, From e10e2ca8eec7ca099e0732ae2e4adc771981826a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Thu, 4 Jan 2024 18:53:48 +0000 Subject: [PATCH 076/118] apps: fix build --- Cargo.lock | 1 + apps/Cargo.toml | 1 + apps/src/lib/bench_utils.rs | 28 ++--- apps/src/lib/cli.rs | 2 +- apps/src/lib/client/rpc.rs | 25 ++-- apps/src/lib/client/tx.rs | 10 +- apps/src/lib/config/genesis.rs | 6 +- apps/src/lib/config/genesis/chain.rs | 11 +- apps/src/lib/config/genesis/transactions.rs | 10 +- apps/src/lib/config/genesis/utils.rs | 2 +- apps/src/lib/node/ledger/mod.rs | 8 +- apps/src/lib/node/ledger/shell/block_alloc.rs | 8 +- .../lib/node/ledger/shell/finalize_block.rs | 82 ++++++------ apps/src/lib/node/ledger/shell/governance.rs | 28 ++--- apps/src/lib/node/ledger/shell/init_chain.rs | 32 ++--- apps/src/lib/node/ledger/shell/mod.rs | 119 +++++++++--------- .../lib/node/ledger/shell/prepare_proposal.rs | 29 +++-- .../lib/node/ledger/shell/process_proposal.rs | 39 +++--- apps/src/lib/node/ledger/shell/queries.rs | 2 +- .../src/lib/node/ledger/shell/testing/node.rs | 4 +- apps/src/lib/node/ledger/shell/utils.rs | 4 +- .../lib/node/ledger/shell/vote_extensions.rs | 20 +-- .../shell/vote_extensions/bridge_pool_vext.rs | 11 +- .../shell/vote_extensions/eth_events.rs | 13 +- .../shell/vote_extensions/val_set_update.rs | 15 +-- apps/src/lib/node/ledger/shims/abcipp_shim.rs | 4 +- apps/src/lib/node/ledger/storage/mod.rs | 23 ++-- apps/src/lib/node/ledger/storage/rocksdb.rs | 17 ++- shared/Cargo.toml | 6 +- shared/src/lib.rs | 9 +- shared/src/types/mod.rs | 6 +- shielded_token/Cargo.toml | 4 +- shielded_token/src/conversion.rs | 9 +- state/src/lib.rs | 11 +- state/src/mockdb.rs | 32 ++--- token/Cargo.toml | 4 + tx/src/lib.rs | 1 + 37 files changed, 328 insertions(+), 308 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 096392b626..2ec954a5ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4681,6 +4681,7 @@ dependencies = [ "namada_state", "namada_storage", "namada_trans_token", + "rayon", "tracing", ] diff --git a/apps/Cargo.toml b/apps/Cargo.toml index a03d6dabf6..cfc173c25d 100644 --- a/apps/Cargo.toml +++ b/apps/Cargo.toml @@ -65,6 +65,7 @@ integration = [] namada = {path = "../shared", features = ["multicore", "http-client", "tendermint-rpc"]} namada_sdk = {path = "../sdk", default-features = false, features = ["wasm-runtime", "download-params"]} namada_test_utils = {path = "../test_utils", optional = true} + ark-serialize.workspace = true ark-std.workspace = true arse-merkle-tree = { workspace = true, features = ["blake2b"] } diff --git a/apps/src/lib/bench_utils.rs b/apps/src/lib/bench_utils.rs index ddfae1a4cc..6aa5eb94eb 100644 --- a/apps/src/lib/bench_utils.rs +++ b/apps/src/lib/bench_utils.rs @@ -13,12 +13,12 @@ use borsh_ext::BorshSerializeExt; use masp_primitives::transaction::Transaction; use masp_primitives::zip32::ExtendedFullViewingKey; use masp_proofs::prover::LocalTxProver; -use namada::core::ledger::governance::storage::proposal::ProposalType; use namada::core::ledger::ibc::storage::port_key; use namada::core::types::address::{self, Address}; use namada::core::types::key::common::SecretKey; use namada::core::types::storage::Key; use namada::core::types::token::{Amount, Transfer}; +use namada::governance::storage::proposal::ProposalType; use namada::ibc::apps::transfer::types::msgs::transfer::MsgTransfer; use namada::ibc::apps::transfer::types::packet::PacketData; use namada::ibc::apps::transfer::types::PrefixedCoin; @@ -59,10 +59,12 @@ use namada::ledger::native_vp::ibc::get_dummy_header; use namada::ledger::queries::{ Client, EncodedResponseQuery, RequestCtx, RequestQuery, Router, RPC, }; -use namada::ledger::storage_api::StorageRead; -use namada::proto::{Code, Data, Section, Signature, Tx}; +use namada::storage::StorageRead; use namada::tendermint::Hash; use namada::tendermint_rpc::{self}; +use namada::tx::data::governance::InitProposalData; +use namada::tx::data::pos::Bond; +use namada::tx::{Code, Data, Section, Signature, Tx}; use namada::types::address::InternalAddress; use namada::types::chain::ChainId; use namada::types::io::StdIo; @@ -72,8 +74,6 @@ use namada::types::masp::{ use namada::types::storage::{BlockHeight, Epoch, KeySeg, TxIndex}; use namada::types::time::DateTimeUtc; use namada::types::token::DenominatedAmount; -use namada::types::transaction::governance::InitProposalData; -use namada::types::transaction::pos::Bond; use namada::vm::wasm::run; use namada::{proof_of_stake, tendermint}; use namada_sdk::masp::{ @@ -284,10 +284,9 @@ impl BenchShell { extra_sections: Option>, signers: Vec<&SecretKey>, ) -> Tx { - let mut tx = - Tx::from_type(namada::types::transaction::TxType::Decrypted( - namada::types::transaction::DecryptedTx::Decrypted, - )); + let mut tx = Tx::from_type(namada::tx::data::TxType::Decrypted( + namada::tx::data::DecryptedTx::Decrypted, + )); // NOTE: here we use the code hash to avoid including the cost for the // wasm validation. The wasm codes (both txs and vps) are always @@ -327,10 +326,9 @@ impl BenchShell { pub fn generate_ibc_tx(&self, wasm_code_path: &str, msg: impl Msg) -> Tx { // This function avoid serializaing the tx data with Borsh - let mut tx = - Tx::from_type(namada::types::transaction::TxType::Decrypted( - namada::types::transaction::DecryptedTx::Decrypted, - )); + let mut tx = Tx::from_type(namada::tx::data::TxType::Decrypted( + namada::tx::data::DecryptedTx::Decrypted, + )); let code_hash = self .read_storage_key(&Key::wasm_hash(wasm_code_path)) .unwrap(); @@ -559,8 +557,8 @@ impl BenchShell { pub fn generate_foreign_key_tx(signer: &SecretKey) -> Tx { let wasm_code = std::fs::read("../wasm_for_tests/tx_write.wasm").unwrap(); - let mut tx = Tx::from_type(namada::types::transaction::TxType::Decrypted( - namada::types::transaction::DecryptedTx::Decrypted, + let mut tx = Tx::from_type(namada::tx::data::TxType::Decrypted( + namada::tx::data::DecryptedTx::Decrypted, )); tx.set_code(Code::new(wasm_code, None)); tx.set_data(Data::new( diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index fca330ec48..62dba917e3 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -2797,6 +2797,7 @@ pub mod args { use std::str::FromStr; use namada::ibc::core::host::types::identifiers::{ChannelId, PortId}; + use namada::tx::data::GasLimit; use namada::types::address::{Address, EstablishedAddress}; use namada::types::chain::{ChainId, ChainIdPrefix}; use namada::types::dec::Dec; @@ -2808,7 +2809,6 @@ pub mod args { use namada::types::time::DateTimeUtc; use namada::types::token; use namada::types::token::NATIVE_MAX_DECIMAL_PLACES; - use namada::types::transaction::GasLimit; pub use namada_sdk::args::*; pub use namada_sdk::tx::{ TX_BECOME_VALIDATOR_WASM, TX_BOND_WASM, TX_BRIDGE_POOL_WASM, diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index bc57f58f14..cf6965bcdd 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -15,20 +15,20 @@ use masp_primitives::asset_type::AssetType; use masp_primitives::merkle_tree::MerklePath; use masp_primitives::sapling::{Node, ViewingKey}; use masp_primitives::zip32::ExtendedFullViewingKey; -use namada::core::ledger::governance::cli::offline::{ +use namada::governance::cli::offline::{ find_offline_proposal, find_offline_votes, read_offline_files, OfflineSignedProposal, OfflineVote, }; -use namada::core::ledger::governance::parameters::GovernanceParameters; -use namada::core::ledger::governance::storage::keys as governance_storage; -use namada::core::ledger::governance::storage::proposal::{ +use namada::governance::parameters::GovernanceParameters; +use namada::governance::pgf::parameters::PgfParameters; +use namada::governance::pgf::storage::steward::StewardDetail; +use namada::governance::storage::keys as governance_storage; +use namada::governance::storage::proposal::{ StoragePgfFunding, StorageProposal, }; -use namada::core::ledger::governance::utils::{ +use namada::governance::utils::{ compute_proposal_result, ProposalVotes, TallyType, TallyVote, VotePower, }; -use namada::core::ledger::pgf::parameters::PgfParameters; -use namada::core::ledger::pgf::storage::steward::StewardDetail; use namada::ledger::events::Event; use namada::ledger::ibc::storage::{ ibc_denom_key, ibc_denom_key_prefix, is_ibc_denom_key, @@ -38,15 +38,16 @@ use namada::ledger::pos::types::{CommissionPair, Slash}; use namada::ledger::pos::PosParams; use namada::ledger::queries::RPC; use namada::proof_of_stake::types::{ValidatorState, WeightedValidator}; +use namada::token; use namada::types::address::{Address, InternalAddress, MASP}; use namada::types::hash::Hash; use namada::types::ibc::{is_ibc_denom, IbcTokenHash}; use namada::types::io::Io; use namada::types::key::*; use namada::types::masp::{BalanceOwner, ExtendedViewingKey, PaymentAddress}; +use namada::types::storage; use namada::types::storage::{BlockHeight, BlockResults, Epoch, Key, KeySeg}; use namada::types::token::{Change, MaspDenom}; -use namada::types::{storage, token}; use namada_sdk::error::{is_pinned_error, Error, PinnedBalanceError}; use namada_sdk::masp::{Conversions, MaspAmount, MaspChange}; use namada_sdk::proof_of_stake::types::ValidatorMetaData; @@ -321,7 +322,8 @@ pub async fn query_transparent_balance( let tokens = query_tokens(context, Some(&base_token), Some(&owner)).await; for (token_alias, token) in tokens { - let balance_key = token::balance_key(&token, &owner); + let balance_key = + token::storage_key::balance_key(&token, &owner); match query_storage_value::<_, token::Amount>( context.client(), &balance_key, @@ -365,7 +367,7 @@ pub async fn query_transparent_balance( (Some(base_token), None) => { let tokens = query_tokens(context, Some(&base_token), None).await; for (_, token) in tokens { - let prefix = token::balance_prefix(&token); + let prefix = token::storage_key::balance_prefix(&token); let balances = query_storage_prefix::(context, &prefix) .await; @@ -562,7 +564,8 @@ async fn print_balances( for (key, balance) in balances { // Get the token, the owner, and the balance with the token and the // owner - let (t, o, s) = match token::is_any_token_balance_key(&key) { + let (t, o, s) = match token::storage_key::is_any_token_balance_key(&key) + { Some([tok, owner]) => ( tok.clone(), owner.clone(), diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index f57cddd4eb..7253992d08 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -7,20 +7,20 @@ use borsh_ext::BorshSerializeExt; use ledger_namada_rs::{BIP44Path, NamadaApp}; use ledger_transport_hid::hidapi::HidApi; use ledger_transport_hid::TransportNativeHID; -use namada::core::ledger::governance::cli::offline::{ +use namada::governance::cli::offline::{ OfflineProposal, OfflineSignedProposal, OfflineVote, }; -use namada::core::ledger::governance::cli::onchain::{ +use namada::governance::cli::onchain::{ DefaultProposal, PgfFundingProposal, PgfStewardProposal, ProposalVote, }; -use namada::core::ledger::storage::EPOCH_SWITCH_BLOCKS_DELAY; use namada::ibc::apps::transfer::types::Memo; -use namada::proto::{CompressedSignature, Section, Signer, Tx}; +use namada::state::EPOCH_SWITCH_BLOCKS_DELAY; +use namada::tx::data::pos::{BecomeValidator, ConsensusKeyChange}; +use namada::tx::{CompressedSignature, Section, Signer, Tx}; use namada::types::address::{Address, ImplicitAddress}; use namada::types::dec::Dec; use namada::types::io::Io; use namada::types::key::{self, *}; -use namada::types::transaction::pos::{BecomeValidator, ConsensusKeyChange}; use namada_sdk::rpc::{InnerTxResult, TxBroadcastData, TxResponse}; use namada_sdk::wallet::alias::validator_consensus_key; use namada_sdk::wallet::{Wallet, WalletIo}; diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index 8649d2a4d1..f4a445a32e 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -11,9 +11,9 @@ use std::str::FromStr; use borsh::{BorshDeserialize, BorshSerialize}; use derivative::Derivative; -use namada::core::ledger::governance::parameters::GovernanceParameters; -use namada::core::ledger::pgf::parameters::PgfParameters; use namada::core::types::string_encoding::StringEncoded; +use namada::governance::parameters::GovernanceParameters; +use namada::governance::pgf::parameters::PgfParameters; use namada::ledger::eth_bridge::EthereumBridgeParams; use namada::ledger::parameters::EpochDuration; use namada::ledger::pos::{Dec, GenesisValidator, OwnedPosParams}; @@ -314,7 +314,7 @@ pub fn make_dev_genesis( use namada::ledger::eth_bridge::{Contracts, UpgradeableContract}; use namada::ledger::pos::types::ValidatorMetaData; - use namada::proto::{standalone_signature, SerializeWithBorsh}; + use namada::tx::{standalone_signature, SerializeWithBorsh}; use namada::types::address::wnam; use namada::types::chain::ChainIdPrefix; use namada::types::ethereum_events::EthAddress; diff --git a/apps/src/lib/config/genesis/chain.rs b/apps/src/lib/config/genesis/chain.rs index cca64103cf..a30ff6293c 100644 --- a/apps/src/lib/config/genesis/chain.rs +++ b/apps/src/lib/config/genesis/chain.rs @@ -376,8 +376,7 @@ impl Finalized { pub fn get_gov_params( &self, - ) -> namada::core::ledger::governance::parameters::GovernanceParameters - { + ) -> namada::governance::parameters::GovernanceParameters { let templates::GovernanceParams { min_proposal_fund, max_proposal_code_size, @@ -386,7 +385,7 @@ impl Finalized { max_proposal_content_size, min_proposal_grace_epochs, } = self.parameters.gov_params.clone(); - namada::core::ledger::governance::parameters::GovernanceParameters { + namada::governance::parameters::GovernanceParameters { min_proposal_fund: Amount::native_whole(min_proposal_fund), max_proposal_code_size, max_proposal_period, @@ -398,7 +397,7 @@ impl Finalized { pub fn get_pgf_params( &self, - ) -> namada::core::ledger::pgf::parameters::PgfParameters { + ) -> namada::governance::pgf::parameters::PgfParameters { self.parameters.pgf_params.clone() } @@ -686,7 +685,7 @@ pub struct FinalizedParameters { pub parameters: templates::ChainParams, pub pos_params: templates::PosParams, pub gov_params: templates::GovernanceParams, - pub pgf_params: namada::core::ledger::pgf::parameters::PgfParameters, + pub pgf_params: namada::governance::pgf::parameters::PgfParameters, pub eth_bridge_params: Option, } @@ -700,7 +699,7 @@ impl FinalizedParameters { eth_bridge_params, }: templates::Parameters, ) -> Self { - use namada::core::ledger::pgf::parameters::PgfParameters; + use namada::governance::pgf::parameters::PgfParameters; let finalized_pgf_params = PgfParameters { stewards: pgf_params.stewards, pgf_inflation_rate: pgf_params.pgf_inflation_rate, diff --git a/apps/src/lib/config/genesis/transactions.rs b/apps/src/lib/config/genesis/transactions.rs index ae8d892a29..129ef4d2b4 100644 --- a/apps/src/lib/config/genesis/transactions.rs +++ b/apps/src/lib/config/genesis/transactions.rs @@ -13,12 +13,13 @@ use ledger_transport_hid::TransportNativeHID; use namada::core::types::account::AccountPublicKeysMap; use namada::core::types::address::{Address, EstablishedAddress}; use namada::core::types::chain::ChainId; +use namada::core::types::key::SerializeWithBorsh; use namada::core::types::string_encoding::StringEncoded; use namada::ledger::pos::common::PublicKey; use namada::ledger::pos::types::ValidatorMetaData; -use namada::proto::{ - verify_standalone_sig, Code, Commitment, Data, Section, SerializeWithBorsh, - SignatureIndex, Tx, +use namada::tx::data::{pos, Fee, TxType}; +use namada::tx::{ + verify_standalone_sig, Code, Commitment, Data, Section, SignatureIndex, Tx, }; use namada::types::address::nam; use namada::types::dec::Dec; @@ -26,7 +27,6 @@ use namada::types::key::{common, ed25519, RefTo, SigScheme}; use namada::types::time::DateTimeUtc; use namada::types::token; use namada::types::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; -use namada::types::transaction::{pos, Fee, TxType}; use namada_sdk::args::Tx as TxArgs; use namada_sdk::signing::{sign_tx, SigningTxData}; use namada_sdk::tx::{TX_BECOME_VALIDATOR_WASM, TX_BOND_WASM}; @@ -347,7 +347,7 @@ pub async fn sign_validator_account_tx( tx_data: &T, keypair: &common::SecretKey, ) -> StringEncoded { - StringEncoded::new(namada::proto::standalone_signature::< + StringEncoded::new(namada::tx::standalone_signature::< T, SerializeWithBorsh, >(keypair, tx_data)) diff --git a/apps/src/lib/config/genesis/utils.rs b/apps/src/lib/config/genesis/utils.rs index a5a012c398..a17f09843d 100644 --- a/apps/src/lib/config/genesis/utils.rs +++ b/apps/src/lib/config/genesis/utils.rs @@ -4,7 +4,7 @@ use std::path::Path; use eyre::Context; use ledger_namada_rs::NamadaApp; use ledger_transport_hid::TransportNativeHID; -use namada::proto::Tx; +use namada::tx::Tx; use namada::types::key::common; use namada_sdk::wallet::Wallet; use namada_sdk::{error, signing}; diff --git a/apps/src/lib/node/ledger/mod.rs b/apps/src/lib/node/ledger/mod.rs index e2ce7ee2d6..7ed221f260 100644 --- a/apps/src/lib/node/ledger/mod.rs +++ b/apps/src/lib/node/ledger/mod.rs @@ -14,8 +14,8 @@ use std::thread; use byte_unit::Byte; use futures::future::TryFutureExt; -use namada::core::ledger::governance::storage::keys as governance_storage; use namada::eth_bridge::ethers::providers::{Http, Provider}; +use namada::governance::storage::keys as governance_storage; use namada::types::storage::Key; use namada::types::time::{DateTimeUtc, Utc}; use namada_sdk::tendermint::abci::request::CheckTxKind; @@ -215,7 +215,7 @@ pub fn dump_db( historic, }: args::LedgerDumpDb, ) { - use namada::ledger::storage::DB; + use namada::state::DB; let chain_id = config.chain_id; let db_path = config.shell.db_dir(&chain_id); @@ -758,8 +758,8 @@ pub fn test_genesis_files( genesis: config::genesis::chain::Finalized, wasm_dir: PathBuf, ) { - use namada::ledger::storage::mockdb::MockDB; - use namada::ledger::storage::Sha256Hasher; + use namada::state::mockdb::MockDB; + use namada::types::hash::Sha256Hasher; // Channels for validators to send protocol txs to be broadcast to the // broadcaster service diff --git a/apps/src/lib/node/ledger/shell/block_alloc.rs b/apps/src/lib/node/ledger/shell/block_alloc.rs index 254db45b9a..9bb35df5e7 100644 --- a/apps/src/lib/node/ledger/shell/block_alloc.rs +++ b/apps/src/lib/node/ledger/shell/block_alloc.rs @@ -55,8 +55,8 @@ pub mod states; use std::marker::PhantomData; -use namada::core::ledger::storage::{self, WlStorage}; use namada::proof_of_stake::pos_queries::PosQueries; +use namada::state::{self, WlStorage}; #[allow(unused_imports)] use crate::facade::tendermint_proto::abci::RequestPrepareProposal; @@ -144,14 +144,14 @@ pub struct BlockAllocator { impl From<&WlStorage> for BlockAllocator> where - D: 'static + storage::DB + for<'iter> storage::DBIter<'iter>, - H: 'static + storage::StorageHasher, + D: 'static + state::DB + for<'iter> state::DBIter<'iter>, + H: 'static + state::StorageHasher, { #[inline] fn from(storage: &WlStorage) -> Self { Self::init( storage.pos_queries().get_max_proposal_bytes().get(), - namada::core::ledger::gas::get_max_block_gas(storage).unwrap(), + namada::gas::get_max_block_gas(storage).unwrap(), ) } } diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index d96de7bbb0..e450d3137f 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -4,31 +4,30 @@ use data_encoding::HEXUPPER; use masp_primitives::merkle_tree::CommitmentTree; use masp_primitives::sapling::Node; use masp_proofs::bls12_381; -use namada::core::ledger::masp_conversions::update_allowed_conversions; -use namada::core::ledger::pgf::inflation as pgf_inflation; use namada::core::types::storage::KeySeg; +use namada::governance::pgf::inflation as pgf_inflation; use namada::ledger::events::EventType; use namada::ledger::gas::{GasMetering, TxGasMeter}; use namada::ledger::pos::namada_proof_of_stake; use namada::ledger::protocol; -use namada::ledger::storage::wl_storage::WriteLogAndStorage; -use namada::ledger::storage::write_log::StorageModification; -use namada::ledger::storage::EPOCH_SWITCH_BLOCKS_DELAY; -use namada::ledger::storage_api::{ResultExt, StorageRead, StorageWrite}; use namada::proof_of_stake::storage::{ find_validator_by_raw_hash, read_last_block_proposer_address, write_last_block_proposer_address, }; +use namada::state::wl_storage::WriteLogAndStorage; +use namada::state::write_log::StorageModification; +use namada::state::EPOCH_SWITCH_BLOCKS_DELAY; +use namada::storage::{ResultExt, StorageRead, StorageWrite}; +use namada::token::conversion::update_allowed_conversions; +use namada::token::storage_key::{ + MASP_NOTE_COMMITMENT_ANCHOR_PREFIX, MASP_NOTE_COMMITMENT_TREE_KEY, +}; +use namada::tx::data::protocol::ProtocolTxType; use namada::types::address::MASP; use namada::types::key::tm_raw_hash_to_string; use namada::types::storage::{BlockHash, BlockResults, Epoch, Header}; -use namada::types::token::{ - MASP_NOTE_COMMITMENT_ANCHOR_PREFIX, MASP_NOTE_COMMITMENT_TREE_KEY, -}; -use namada::types::transaction::protocol::{ - ethereum_tx_data_variants, ProtocolTxType, -}; -use namada::types::vote_extensions::ethereum_events::MultiSignedEthEvent; +use namada::vote_ext::ethereum_events::MultiSignedEthEvent; +use namada::vote_ext::ethereum_tx_data_variants; use super::governance::execute_governance_proposals; use super::*; @@ -815,24 +814,20 @@ mod test_finalize_block { use data_encoding::HEXUPPER; use namada::core::ledger::eth_bridge::storage::wrapped_erc20s; - use namada::core::ledger::governance::storage::keys::get_proposal_execution_key; - use namada::core::ledger::governance::storage::proposal::ProposalType; - use namada::core::ledger::governance::storage::vote::{ - StorageProposalVote, VoteType, - }; use namada::core::ledger::replay_protection; use namada::core::types::storage::KeySeg; use namada::eth_bridge::storage::bridge_pool::{ self, get_key_from_hash, get_nonce_key, get_signed_root_key, }; use namada::eth_bridge::storage::min_confirmations_key; + use namada::governance::storage::keys::get_proposal_execution_key; + use namada::governance::storage::proposal::ProposalType; + use namada::governance::storage::vote::{StorageProposalVote, VoteType}; use namada::ledger::gas::VpGasMeter; use namada::ledger::native_vp::parameters::ParametersVp; use namada::ledger::native_vp::NativeVp; use namada::ledger::parameters::EpochDuration; use namada::ledger::pos::PosQueries; - use namada::ledger::storage_api; - use namada::ledger::storage_api::StorageWrite; use namada::proof_of_stake::storage::{ enqueued_slashes_handle, get_num_consensus_validators, read_consensus_validator_set_addresses_with_stake, read_total_stake, @@ -847,7 +842,11 @@ mod test_finalize_block { BondId, SlashType, ValidatorState, WeightedValidator, }; use namada::proof_of_stake::{unjail_validator, ADDRESS as pos_address}; - use namada::proto::{Code, Data, Section, Signature}; + use namada::storage; + use namada::storage::StorageWrite; + use namada::tx::data::governance::{InitProposalData, VoteProposalData}; + use namada::tx::data::{Fee, WrapperTx}; + use namada::tx::{Code, Data, Section, Signature}; use namada::types::dec::{Dec, POS_DECIMAL_PRECISION}; use namada::types::ethereum_events::{EthAddress, Uint as ethUint}; use namada::types::hash::Hash; @@ -859,13 +858,8 @@ mod test_finalize_block { use namada::types::token::{ Amount, DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES, }; - use namada::types::transaction::governance::{ - InitProposalData, VoteProposalData, - }; - use namada::types::transaction::protocol::EthereumTxData; - use namada::types::transaction::{Fee, WrapperTx}; use namada::types::uint::Uint; - use namada::types::vote_extensions::ethereum_events; + use namada::vote_ext::{ethereum_events, EthereumTxData}; use namada_sdk::eth_bridge::MinimumConfirmations; use namada_sdk::proof_of_stake::storage::{ liveness_missed_votes_handle, liveness_sum_missed_votes_handle, @@ -1623,7 +1617,7 @@ mod test_finalize_block { r#type: ProposalType::Default(None), }; - storage_api::governance::init_proposal( + namada::governance::init_proposal( &mut shell.wl_storage, proposal, vec![], @@ -1639,7 +1633,7 @@ mod test_finalize_block { }; // Vote to accept the proposal (there's only one validator, so its // vote decides) - storage_api::governance::vote_proposal(&mut shell.wl_storage, vote) + namada::governance::vote_proposal(&mut shell.wl_storage, vote) .unwrap(); }; @@ -2383,7 +2377,7 @@ mod test_finalize_block { let delegator = address::testing::gen_implicit_address(); let del_amount = init_stake; let staking_token = shell.wl_storage.storage.native_token.clone(); - storage_api::token::credit_tokens( + namada::token::credit_tokens( &mut shell.wl_storage, &staking_token, &delegator, @@ -2508,21 +2502,21 @@ mod test_finalize_block { // Give the validators some tokens for txs let staking_token = shell.wl_storage.storage.native_token.clone(); - storage_api::token::credit_tokens( + namada::token::credit_tokens( &mut shell.wl_storage, &staking_token, &validator1.address, init_stake, ) .unwrap(); - storage_api::token::credit_tokens( + namada::token::credit_tokens( &mut shell.wl_storage, &staking_token, &validator2.address, init_stake, ) .unwrap(); - storage_api::token::credit_tokens( + namada::token::credit_tokens( &mut shell.wl_storage, &staking_token, &validator3.address, @@ -3344,7 +3338,7 @@ mod test_finalize_block { .decode(consensus_key.tm_raw_hash().as_bytes()) .unwrap(); - let proposer_balance = storage_api::token::read_balance( + let proposer_balance = namada::token::read_balance( &shell.wl_storage, &shell.wl_storage.storage.native_token, &validator, @@ -3380,14 +3374,14 @@ mod test_finalize_block { ))); let fee_amount = wrapper.header().wrapper().unwrap().get_tx_fee().unwrap(); - let fee_amount = namada_token::denom_to_amount( + let fee_amount = namada::token::denom_to_amount( fee_amount, &wrapper.header().wrapper().unwrap().fee.token, &shell.wl_storage, ) .unwrap(); - let signer_balance = storage_api::token::read_balance( + let signer_balance = namada::token::read_balance( &shell.wl_storage, &shell.wl_storage.storage.native_token, &wrapper.header().wrapper().unwrap().fee_payer(), @@ -3415,7 +3409,7 @@ mod test_finalize_block { let code = event.attributes.get("code").expect("Test failed").as_str(); assert_eq!(code, String::from(ResultCode::Ok).as_str()); - let new_proposer_balance = storage_api::token::read_balance( + let new_proposer_balance = namada::token::read_balance( &shell.wl_storage, &shell.wl_storage.storage.native_token, &validator, @@ -3426,7 +3420,7 @@ mod test_finalize_block { proposer_balance.checked_add(fee_amount).unwrap() ); - let new_signer_balance = storage_api::token::read_balance( + let new_signer_balance = namada::token::read_balance( &shell.wl_storage, &shell.wl_storage.storage.native_token, &wrapper.header().wrapper().unwrap().fee_payer(), @@ -3439,7 +3433,7 @@ mod test_finalize_block { } #[test] - fn test_ledger_slashing() -> storage_api::Result<()> { + fn test_ledger_slashing() -> namada::storage::Result<()> { let num_validators = 7_u64; let (mut shell, _recv, _, _) = setup_with_cfg(SetupCfg { last_height: 0, @@ -3645,7 +3639,7 @@ mod test_finalize_block { } } - let num_slashes = storage_api::iter_prefix_bytes( + let num_slashes = namada::storage::iter_prefix_bytes( &shell.wl_storage, &slashes_prefix(), )? @@ -3810,7 +3804,7 @@ mod test_finalize_block { /// NOTE: must call `get_default_true_votes` before every call to /// `next_block_for_inflation` #[test] - fn test_multiple_misbehaviors() -> storage_api::Result<()> { + fn test_multiple_misbehaviors() -> namada::storage::Result<()> { for num_validators in &[4_u64, 6_u64, 9_u64] { tracing::debug!("\nNUM VALIDATORS = {}", num_validators); test_multiple_misbehaviors_by_num_vals(*num_validators)?; @@ -3830,7 +3824,7 @@ mod test_finalize_block { /// 7) Discover misbehavior in epoch 4 fn test_multiple_misbehaviors_by_num_vals( num_validators: u64, - ) -> storage_api::Result<()> { + ) -> namada::storage::Result<()> { // Setup the network with pipeline_len = 2, unbonding_len = 4 // let num_validators = 8_u64; let (mut shell, _recv, _, _) = setup_with_cfg(SetupCfg { @@ -3892,7 +3886,7 @@ mod test_finalize_block { let delegator = address::testing::gen_implicit_address(); let del_1_amount = token::Amount::native_whole(37_231); let staking_token = shell.wl_storage.storage.native_token.clone(); - storage_api::token::credit_tokens( + namada::token::credit_tokens( &mut shell.wl_storage, &staking_token, &delegator, @@ -4647,7 +4641,7 @@ mod test_finalize_block { } #[test] - fn test_jail_validator_for_inactivity() -> storage_api::Result<()> { + fn test_jail_validator_for_inactivity() -> namada::storage::Result<()> { let num_validators = 5_u64; let (mut shell, _recv, _, _) = setup_with_cfg(SetupCfg { last_height: 0, diff --git a/apps/src/lib/node/ledger/shell/governance.rs b/apps/src/lib/node/ledger/shell/governance.rs index 4991310d09..1cac186d04 100644 --- a/apps/src/lib/node/ledger/shell/governance.rs +++ b/apps/src/lib/node/ledger/shell/governance.rs @@ -1,28 +1,28 @@ use std::collections::HashMap; -use namada::core::ledger::governance::storage::keys as gov_storage; -use namada::core::ledger::governance::storage::proposal::{ +use namada::core::types::encode; +use namada::governance::pgf::storage::keys as pgf_storage; +use namada::governance::pgf::storage::steward::StewardDetail; +use namada::governance::pgf::{storage as pgf, ADDRESS}; +use namada::governance::storage::keys as gov_storage; +use namada::governance::storage::proposal::{ AddRemove, PGFAction, ProposalType, StoragePgfFunding, }; -use namada::core::ledger::governance::utils::{ +use namada::governance::utils::{ compute_proposal_result, ProposalVotes, TallyResult, TallyType, TallyVote, VotePower, }; -use namada::core::ledger::governance::ADDRESS as gov_address; -use namada::core::ledger::pgf::storage::keys as pgf_storage; -use namada::core::ledger::pgf::storage::steward::StewardDetail; -use namada::core::ledger::pgf::ADDRESS; -use namada::core::ledger::storage_api::governance as gov_api; +use namada::governance::{storage as gov_api, ADDRESS as gov_address}; use namada::ledger::governance::utils::ProposalEvent; use namada::ledger::pos::BondId; use namada::ledger::protocol; -use namada::ledger::storage::types::encode; -use namada::ledger::storage::{DBIter, StorageHasher, DB}; -use namada::ledger::storage_api::{pgf, token, StorageWrite}; use namada::proof_of_stake::bond_amount; use namada::proof_of_stake::parameters::PosParams; use namada::proof_of_stake::storage::read_total_stake; -use namada::proto::{Code, Data}; +use namada::state::{DBIter, StorageHasher, DB}; +use namada::storage::StorageWrite; +use namada::token; +use namada::tx::{Code, Data}; use namada::types::address::Address; use namada::types::storage::Epoch; @@ -205,7 +205,7 @@ fn compute_proposal_votes( params: &PosParams, proposal_id: u64, epoch: Epoch, -) -> storage_api::Result +) -> namada::storage::Result where S: StorageRead, { @@ -262,7 +262,7 @@ fn execute_default_proposal( shell: &mut Shell, id: u64, proposal_code: Option>, -) -> storage_api::Result +) -> namada::storage::Result where D: DB + for<'iter> DBIter<'iter> + Sync + 'static, H: StorageHasher + Sync + 'static, diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index 074fe132ad..ffd37cb1b2 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -5,22 +5,21 @@ use std::ops::ControlFlow; use masp_primitives::merkle_tree::CommitmentTree; use masp_primitives::sapling::Node; use masp_proofs::bls12_381; +use namada::account::protocol_pk_key; use namada::core::types::storage::KeySeg; use namada::ledger::parameters::Parameters; -use namada::ledger::storage::traits::StorageHasher; -use namada::ledger::storage::{DBIter, DB}; -use namada::ledger::storage_api::token::{credit_tokens, write_denom}; -use namada::ledger::storage_api::StorageWrite; use namada::ledger::{ibc, pos}; use namada::proof_of_stake::BecomeValidator; -use namada::types::address::{Address, MASP}; -use namada::types::hash::Hash as CodeHash; -use namada::types::key::*; -use namada::types::time::{DateTimeUtc, TimeZone, Utc}; -use namada::types::token::{ +use namada::state::{DBIter, StorageHasher, DB}; +use namada::storage::StorageWrite; +use namada::token::storage_key::{ MASP_CONVERT_ANCHOR_KEY, MASP_NOTE_COMMITMENT_ANCHOR_PREFIX, MASP_NOTE_COMMITMENT_TREE_KEY, }; +use namada::token::{credit_tokens, write_denom}; +use namada::types::address::{Address, MASP}; +use namada::types::hash::Hash as CodeHash; +use namada::types::time::{DateTimeUtc, TimeZone, Utc}; use namada::vm::validate_untrusted_wasm; use namada_sdk::eth_bridge::EthBridgeStatus; use namada_sdk::proof_of_stake::PosParams; @@ -447,7 +446,12 @@ where } = token; // associate a token with its denomination. write_denom(&mut self.wl_storage, address, *denom).unwrap(); - parameters.init_storage(address, &mut self.wl_storage); + namada::token::write_params( + parameters, + &mut self.wl_storage, + address, + ) + .unwrap(); // add token addresses to the masp reward conversions lookup table. let alias = alias.to_string(); if masp_rewards.contains_key(&alias.as_str()) { @@ -482,7 +486,7 @@ where let mut total_token_balance = token::Amount::zero(); for (owner, balance) in balances { if let genesis::GenesisAddress::PublicKey(pk) = owner { - storage_api::account::init_account_storage( + namada::account::init_account_storage( &mut self.wl_storage, &owner.address(), std::slice::from_ref(&pk.raw), @@ -508,7 +512,7 @@ where // Write the total amount of tokens for the ratio self.wl_storage .write( - &token::minted_balance_key(token_address), + &token::storage_key::minted_balance_key(token_address), total_token_balance, ) .unwrap(); @@ -545,7 +549,7 @@ where let public_keys: Vec<_> = public_keys.iter().map(|pk| pk.raw.clone()).collect(); - storage_api::account::init_account_storage( + namada::account::init_account_storage( &mut self.wl_storage, address, &public_keys, @@ -949,7 +953,7 @@ mod test { use std::str::FromStr; use namada::core::types::string_encoding::StringEncoded; - use namada::ledger::storage::DBIter; + use namada::state::DBIter; use namada_sdk::wallet::alias::Alias; use super::*; diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index b228134fcd..fd06ed0348 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -31,44 +31,44 @@ use borsh::BorshDeserialize; use borsh_ext::BorshSerializeExt; use masp_primitives::transaction::Transaction; use namada::core::hints; -use namada::core::ledger::eth_bridge; -pub use namada::core::types::transaction::ResultCode; use namada::ledger::events::log::EventLog; use namada::ledger::events::Event; use namada::ledger::gas::{Gas, TxGasMeter}; -use namada::ledger::pos::into_tm_voting_power; use namada::ledger::pos::namada_proof_of_stake::types::{ ConsensusValidator, ValidatorSetUpdate, }; +use namada::ledger::pos::{into_tm_voting_power, PosQueries}; use namada::ledger::protocol::{ apply_wasm_tx, get_fee_unshielding_transaction, get_transfer_hash_from_storage, ShellParams, }; -use namada::ledger::storage::wl_storage::WriteLogAndStorage; -use namada::ledger::storage::write_log::WriteLog; -use namada::ledger::storage::{ - DBIter, Sha256Hasher, Storage, StorageHasher, TempWlStorage, WlStorage, DB, - EPOCH_SWITCH_BLOCKS_DELAY, -}; -use namada::ledger::storage_api::tx::validate_tx_bytes; -use namada::ledger::storage_api::{self, StorageRead}; use namada::ledger::{parameters, pos, protocol}; +use namada::parameters::validate_tx_bytes; use namada::proof_of_stake::slashing::{process_slashes, slash}; use namada::proof_of_stake::storage::read_pos_params; use namada::proof_of_stake::{self}; -use namada::proto::{self, Section, Tx}; +use namada::state::tx_queue::{ExpiredTx, TxInQueue}; +use namada::state::wl_storage::WriteLogAndStorage; +use namada::state::write_log::WriteLog; +use namada::state::{ + DBIter, Sha256Hasher, Storage, StorageHasher, TempWlStorage, WlStorage, DB, + EPOCH_SWITCH_BLOCKS_DELAY, +}; +use namada::storage::StorageRead; +use namada::token; +pub use namada::tx::data::ResultCode; +use namada::tx::data::{DecryptedTx, TxType, WrapperTx}; +use namada::tx::{Section, Tx}; +use namada::types::address; use namada::types::address::Address; use namada::types::chain::ChainId; use namada::types::ethereum_events::EthereumEvent; -use namada::types::internal::{ExpiredTx, TxInQueue}; use namada::types::key::*; use namada::types::storage::{BlockHeight, Key, TxIndex}; use namada::types::time::DateTimeUtc; -use namada::types::transaction::protocol::EthereumTxData; -use namada::types::transaction::{DecryptedTx, TxType, WrapperTx}; -use namada::types::{address, token}; use namada::vm::wasm::{TxCache, VpCache}; use namada::vm::{WasmCacheAccess, WasmCacheRwAccess}; +use namada::vote_ext::EthereumTxData; use namada_sdk::eth_bridge::{EthBridgeQueries, EthereumOracleConfig}; use namada_sdk::tendermint::AppHash; use thiserror::Error; @@ -106,7 +106,7 @@ pub enum Error { #[error("chain ID mismatch: {0}")] ChainId(String), #[error("Error decoding a transaction from bytes: {0}")] - TxDecoding(proto::Error), + TxDecoding(namada::tx::DecodeError), #[error("Error trying to apply a transaction: {0}")] TxApply(protocol::Error), #[error("{0}")] @@ -124,7 +124,7 @@ pub enum Error { #[error("Error loading wasm: {0}")] LoadingWasm(String), #[error("Error reading from or writing to storage: {0}")] - StorageApi(#[from] storage_api::Error), + Storage(#[from] namada::storage::Error), #[error("Transaction replay attempt: {0}")] ReplayAttempt(String), } @@ -165,7 +165,7 @@ pub fn rollback(config: config::Ledger) -> Result<()> { tracing::info!("Rollback Namada state"); db.rollback(tendermint_block_height) - .map_err(|e| Error::StorageApi(storage_api::Error::new(e))) + .map_err(|e| Error::Storage(namada::storage::Error::new(e))) } #[derive(Debug)] @@ -893,9 +893,11 @@ where .get_protocol_key() .expect("Validators should have protocol keys"); - let signed_tx = EthereumTxData::EthEventsVext(vote_extension) - .sign(protocol_key, self.chain_id.clone()) - .to_bytes(); + let signed_tx = EthereumTxData::EthEventsVext( + namada::vote_ext::ethereum_events::SignedVext(vote_extension), + ) + .sign(protocol_key, self.chain_id.clone()) + .to_bytes(); self.mode.broadcast(signed_tx); } @@ -965,7 +967,7 @@ where // initialized yet. let has_key = self .wl_storage - .has_key(ð_bridge::storage::active_key()) + .has_key(&namada::eth_bridge::storage::active_key()) .expect( "We should always be able to check whether a key exists \ in storage or not", @@ -984,7 +986,7 @@ where let active = if !self.wl_storage.ethbridge_queries().is_bridge_active() { if !changed_keys - .contains(ð_bridge::storage::active_key()) + .contains(&namada::eth_bridge::storage::active_key()) { tracing::info!( "Not starting oracle as the Ethereum bridge is \ @@ -1051,9 +1053,8 @@ where tx_bytes: &[u8], r#_type: MempoolTxType, ) -> response::CheckTx { - use namada::types::transaction::protocol::{ - ethereum_tx_data_variants, ProtocolTxType, - }; + use namada::tx::data::protocol::ProtocolTxType; + use namada::vote_ext::ethereum_tx_data_variants; let mut response = response::CheckTx::default(); @@ -1145,7 +1146,7 @@ where ); if let Err(err) = self .validate_eth_events_vext_and_get_it_back( - ext, + ext.0, self.wl_storage.storage.get_last_block_height(), ) { @@ -1168,7 +1169,7 @@ where ); if let Err(err) = self .validate_bp_roots_vext_and_get_it_back( - ext, + ext.0, self.wl_storage.storage.get_last_block_height(), ) { @@ -1236,10 +1237,7 @@ where // Max block gas let block_gas_limit: Gas = Gas::from_whole_units( - namada::core::ledger::gas::get_max_block_gas( - &self.wl_storage, - ) - .unwrap(), + namada::gas::get_max_block_gas(&self.wl_storage).unwrap(), ); if gas_meter.tx_gas_limit > block_gas_limit { response.code = ResultCode::AllocationError.into(); @@ -1377,7 +1375,7 @@ where } }; - match namada_token::denom_to_amount( + match namada::token::denom_to_amount( wrapper.fee.amount_per_gas_unit, &wrapper.fee.token, &self.wl_storage, @@ -1430,7 +1428,7 @@ where Error::TxApply(protocol::Error::FeeUnshieldingError(e)) })?; - let fee_unshielding_gas_limit = temp_wl_storage + let fee_unshielding_gas_limit: Gas = temp_wl_storage .read(¶meters::storage::get_fee_unshielding_gas_limit_key()) .expect("Error reading from storage") .expect("Missing fee unshielding gas limit in storage"); @@ -1458,19 +1456,25 @@ where Ok(result) => { if !result.is_accepted() { return Err(Error::TxApply( - protocol::Error::FeeUnshieldingError(namada::types::transaction::WrapperTxErr::InvalidUnshield(format!( - "Some VPs rejected fee unshielding: {:#?}", - result.vps_result.rejected_vps - ))), + protocol::Error::FeeUnshieldingError( + namada::tx::data::WrapperTxErr::InvalidUnshield( + format!( + "Some VPs rejected fee unshielding: \ + {:#?}", + result.vps_result.rejected_vps + ), + ), + ), )); } } Err(e) => { return Err(Error::TxApply( - protocol::Error::FeeUnshieldingError(namada::types::transaction::WrapperTxErr::InvalidUnshield(format!( - "Wasm run failed: {}", - e - ))), + protocol::Error::FeeUnshieldingError( + namada::tx::data::WrapperTxErr::InvalidUnshield( + format!("Wasm run failed: {}", e), + ), + ), )); } } @@ -1493,7 +1497,7 @@ where // because we're using domain types in InitChain, but FinalizeBlock is // shimmed with a different old type. The joy... mut validator_conv: F, - ) -> storage_api::Result> + ) -> namada::storage::Result> where F: FnMut(common::PublicKey, i64) -> V, { @@ -1558,13 +1562,14 @@ mod test_utils { use namada::core::ledger::masp_conversions::update_allowed_conversions; use namada::core::ledger::storage::EPOCH_SWITCH_BLOCKS_DELAY; use namada::ledger::parameters::{EpochDuration, Parameters}; - use namada::ledger::storage::mockdb::MockDB; - use namada::ledger::storage::{LastBlock, Sha256Hasher}; - use namada::ledger::storage_api::StorageWrite; use namada::proof_of_stake::parameters::PosParams; use namada::proof_of_stake::storage::validator_consensus_key_handle; - use namada::proto::{Code, Data}; + use namada::state::mockdb::MockDB; + use namada::state::{LastBlock, Sha256Hasher}; + use namada::storage::StorageWrite; use namada::tendermint::abci::types::VoteInfo; + use namada::tx::data::{Fee, TxType, WrapperTx}; + use namada::tx::{Code, Data}; use namada::types::address; use namada::types::chain::ChainId; use namada::types::ethereum_events::Uint; @@ -1573,7 +1578,6 @@ mod test_utils { use namada::types::key::*; use namada::types::storage::{BlockHash, Epoch, Header}; use namada::types::time::{DateTimeUtc, DurationSecs}; - use namada::types::transaction::{Fee, TxType, WrapperTx}; use tempfile::tempdir; use tokio::sync::mpsc::{Sender, UnboundedReceiver}; @@ -2266,18 +2270,18 @@ mod test_utils { #[cfg(test)] mod shell_tests { use namada::core::ledger::replay_protection; - use namada::ledger::storage_api::token::read_denom; - use namada::proto::{ + use namada::storage::token::read_denom; + use namada::tx::data::protocol::{ + ethereum_tx_data_variants, ProtocolTx, ProtocolTxType, + }; + use namada::tx::data::{Fee, WrapperTx}; + use namada::tx::{ Code, Data, Section, SignableEthMessage, Signature, Signed, Tx, }; use namada::types::ethereum_events::EthereumEvent; use namada::types::key::RefTo; use namada::types::storage::{BlockHeight, Epoch}; - use namada::types::transaction::protocol::{ - ethereum_tx_data_variants, ProtocolTx, ProtocolTxType, - }; - use namada::types::transaction::{Fee, WrapperTx}; - use namada::types::vote_extensions::{bridge_pool_roots, ethereum_events}; + use namada::vote_ext::{bridge_pool_roots, ethereum_events}; use super::*; use crate::node::ledger::shell::test_utils; @@ -2850,8 +2854,7 @@ mod shell_tests { let (shell, _recv, _, _) = test_utils::setup(); let block_gas_limit = - namada::core::ledger::gas::get_max_block_gas(&shell.wl_storage) - .unwrap(); + namada::gas::get_max_block_gas(&shell.wl_storage).unwrap(); let keypair = super::test_utils::gen_keypair(); let mut wrapper = diff --git a/apps/src/lib/node/ledger/shell/prepare_proposal.rs b/apps/src/lib/node/ledger/shell/prepare_proposal.rs index 96ce52a89d..2d956b9206 100644 --- a/apps/src/lib/node/ledger/shell/prepare_proposal.rs +++ b/apps/src/lib/node/ledger/shell/prepare_proposal.rs @@ -1,17 +1,17 @@ //! Implementation of the [`RequestPrepareProposal`] ABCI++ method for the Shell use namada::core::hints; -use namada::core::ledger::gas::TxGasMeter; +use namada::gas::TxGasMeter; use namada::ledger::pos::PosQueries; use namada::ledger::protocol::get_fee_unshielding_transaction; -use namada::ledger::storage::{DBIter, StorageHasher, TempWlStorage, DB}; +use namada::ledger::storage::tx_queue::TxInQueue; use namada::proof_of_stake::storage::find_validator_by_raw_hash; -use namada::proto::Tx; +use namada::state::{DBIter, StorageHasher, TempWlStorage, DB}; +use namada::tx::data::{DecryptedTx, TxType}; +use namada::tx::Tx; use namada::types::address::Address; -use namada::types::internal::TxInQueue; use namada::types::key::tm_raw_hash_to_string; use namada::types::time::DateTimeUtc; -use namada::types::transaction::{DecryptedTx, TxType}; use namada::vm::wasm::{TxCache, VpCache}; use namada::vm::WasmCacheAccess; @@ -374,10 +374,10 @@ mod test_prepare_proposal { use std::collections::BTreeSet; use borsh_ext::BorshSerializeExt; - use namada::core::ledger::storage_api::collections::lazy_map::{ + use namada::core::ledger::namada::storage::collections::lazy_map::{ NestedSubKey, SubKey, }; - use namada::core::ledger::storage_api::token::read_denom; + use namada::core::ledger::namada::token::read_denom; use namada::ledger::gas::Gas; use namada::ledger::pos::PosQueries; use namada::ledger::replay_protection; @@ -387,18 +387,18 @@ mod test_prepare_proposal { }; use namada::proof_of_stake::types::WeightedValidator; use namada::proof_of_stake::Epoch; - use namada::proto::{Code, Data, Header, Section, Signature, Signed}; + use namada::tx::data::protocol::{ + ethereum_tx_data_variants, EthereumTxData, + }; + use namada::tx::data::{Fee, TxType, WrapperTx}; + use namada::tx::{Code, Data, Header, Section, Signature, Signed}; use namada::types::address::{self, Address}; use namada::types::ethereum_events::EthereumEvent; use namada::types::key::RefTo; use namada::types::storage::{BlockHeight, InnerEthEventsQueue}; use namada::types::token; use namada::types::token::{Amount, DenominatedAmount}; - use namada::types::transaction::protocol::{ - ethereum_tx_data_variants, EthereumTxData, - }; - use namada::types::transaction::{Fee, TxType, WrapperTx}; - use namada::types::vote_extensions::ethereum_events; + use namada::vote_ext::ethereum_events; use super::*; use crate::config::ValidatorLocalConfig; @@ -1045,8 +1045,7 @@ mod test_prepare_proposal { let (shell, _recv, _, _) = test_utils::setup(); let block_gas_limit = - namada::core::ledger::gas::get_max_block_gas(&shell.wl_storage) - .unwrap(); + namada::gas::get_max_block_gas(&shell.wl_storage).unwrap(); let keypair = gen_keypair(); let wrapper = WrapperTx::new( diff --git a/apps/src/lib/node/ledger/shell/process_proposal.rs b/apps/src/lib/node/ledger/shell/process_proposal.rs index 827ea2a881..26a5391ee5 100644 --- a/apps/src/lib/node/ledger/shell/process_proposal.rs +++ b/apps/src/lib/node/ledger/shell/process_proposal.rs @@ -3,16 +3,14 @@ use data_encoding::HEXUPPER; use namada::core::hints; -use namada::core::ledger::storage::WlStorage; use namada::ledger::pos::PosQueries; use namada::ledger::protocol::get_fee_unshielding_transaction; -use namada::ledger::storage::TempWlStorage; -use namada::ledger::storage_api::tx::validate_tx_bytes; +use namada::ledger::storage::tx_queue::TxInQueue; +use namada::parameters::validate_tx_bytes; use namada::proof_of_stake::storage::find_validator_by_raw_hash; -use namada::types::internal::TxInQueue; -use namada::types::transaction::protocol::{ - ethereum_tx_data_variants, ProtocolTxType, -}; +use namada::state::{TempWlStorage, WlStorage}; +use namada::tx::data::protocol::ProtocolTxType; +use namada::vote_ext::ethereum_tx_data_variants; use namada_sdk::eth_bridge::{EthBridgeQueries, SendValsetUpd}; use super::block_alloc::{BlockSpace, EncryptedTxsBins}; @@ -49,8 +47,7 @@ where fn from(wl_storage: &WlStorage) -> Self { let max_proposal_bytes = wl_storage.pos_queries().get_max_proposal_bytes().get(); - let max_block_gas = - namada::core::ledger::gas::get_max_block_gas(wl_storage).unwrap(); + let max_block_gas = namada::gas::get_max_block_gas(wl_storage).unwrap(); let encrypted_txs_bin = EncryptedTxsBins::new(max_proposal_bytes, max_block_gas); let txs_bin = TxBin::init(max_proposal_bytes); @@ -374,7 +371,7 @@ where .map_err(|err| err.to_string()) .and_then(|ext| { self.validate_eth_events_vext_and_get_it_back( - ext, + ext.0, self.wl_storage .storage .get_last_block_height(), @@ -401,7 +398,7 @@ where .map_err(|err| err.to_string()) .and_then(|ext| { self.validate_bp_roots_vext_and_get_it_back( - ext, + ext.0, self.wl_storage .storage .get_last_block_height(), @@ -480,7 +477,9 @@ where ethereum_tx_data_variants::BridgePool::try_from( &tx, ) - .unwrap(); + .unwrap() + .into_iter() + .map(|vext| vext.0); let valid_extensions = self .validate_bp_roots_vext_list(digest) .map(|maybe_ext| { @@ -705,9 +704,10 @@ where #[cfg(test)] mod test_process_proposal { use namada::ledger::replay_protection; - use namada::ledger::storage_api::token::read_denom; - use namada::ledger::storage_api::StorageWrite; - use namada::proto::{ + use namada::storage::token::read_denom; + use namada::storage::StorageWrite; + use namada::tx::data::{Fee, WrapperTx}; + use namada::tx::{ Code, Data, Section, SignableEthMessage, Signature, Signed, }; use namada::types::ethereum_events::EthereumEvent; @@ -716,9 +716,9 @@ mod test_process_proposal { use namada::types::time::DateTimeUtc; use namada::types::token; use namada::types::token::{Amount, DenominatedAmount}; - use namada::types::transaction::protocol::EthereumTxData; - use namada::types::transaction::{Fee, WrapperTx}; - use namada::types::vote_extensions::{bridge_pool_roots, ethereum_events}; + use namada::vote_ext::{ + bridge_pool_roots, ethereum_events, EthereumTxData, + }; use super::*; use crate::node::ledger::shell::test_utils::{ @@ -1821,8 +1821,7 @@ mod test_process_proposal { let (shell, _recv, _, _) = test_utils::setup(); let block_gas_limit = - namada::core::ledger::gas::get_max_block_gas(&shell.wl_storage) - .unwrap(); + namada::gas::get_max_block_gas(&shell.wl_storage).unwrap(); let keypair = super::test_utils::gen_keypair(); let mut wrapper = diff --git a/apps/src/lib/node/ledger/shell/queries.rs b/apps/src/lib/node/ledger/shell/queries.rs index 6adb53969d..2badfa9d11 100644 --- a/apps/src/lib/node/ledger/shell/queries.rs +++ b/apps/src/lib/node/ledger/shell/queries.rs @@ -2,7 +2,7 @@ use namada::ledger::dry_run_tx; use namada::ledger::queries::{RequestCtx, ResponseQuery}; -use namada::ledger::storage_api::token; +use namada::token; use namada::types::address::Address; use super::*; diff --git a/apps/src/lib/node/ledger/shell/testing/node.rs b/apps/src/lib/node/ledger/shell/testing/node.rs index b3ef78027e..27d9635963 100644 --- a/apps/src/lib/node/ledger/shell/testing/node.rs +++ b/apps/src/lib/node/ledger/shell/testing/node.rs @@ -17,15 +17,13 @@ use namada::ledger::events::log::dumb_queries; use namada::ledger::queries::{ EncodedResponseQuery, RequestCtx, RequestQuery, Router, RPC, }; -use namada::ledger::storage::{ - LastBlock, Sha256Hasher, EPOCH_SWITCH_BLOCKS_DELAY, -}; use namada::proof_of_stake::pos_queries::PosQueries; use namada::proof_of_stake::storage::{ read_consensus_validator_set_addresses_with_stake, validator_consensus_key_handle, }; use namada::proof_of_stake::types::WeightedValidator; +use namada::state::{LastBlock, Sha256Hasher, EPOCH_SWITCH_BLOCKS_DELAY}; use namada::tendermint::abci::response::Info; use namada::tendermint::abci::types::VoteInfo; use namada::tendermint_rpc::SimpleRequest; diff --git a/apps/src/lib/node/ledger/shell/utils.rs b/apps/src/lib/node/ledger/shell/utils.rs index e55f54e8f8..6246902def 100644 --- a/apps/src/lib/node/ledger/shell/utils.rs +++ b/apps/src/lib/node/ledger/shell/utils.rs @@ -1,8 +1,8 @@ use borsh::BorshDeserialize; -use namada::ledger::storage_api::{self, StorageRead}; +use namada::storage::{self, StorageRead}; use namada::types::storage::Key; -pub(super) fn force_read(storage: &S, key: &Key) -> storage_api::Result +pub(super) fn force_read(storage: &S, key: &Key) -> storage::Result where S: StorageRead, T: BorshDeserialize, diff --git a/apps/src/lib/node/ledger/shell/vote_extensions.rs b/apps/src/lib/node/ledger/shell/vote_extensions.rs index 7d26f6195d..4e96e8e8d5 100644 --- a/apps/src/lib/node/ledger/shell/vote_extensions.rs +++ b/apps/src/lib/node/ledger/shell/vote_extensions.rs @@ -4,11 +4,11 @@ pub mod bridge_pool_vext; pub mod eth_events; pub mod val_set_update; -use namada::proto::{SignableEthMessage, Signed}; +use namada::tx::{SignableEthMessage, Signed}; use namada::types::keccak::keccak_hash; -use namada::types::transaction::protocol::EthereumTxData; -use namada::types::vote_extensions::{ - bridge_pool_roots, ethereum_events, validator_set_update, VoteExtension, +use namada::vote_ext::{ + bridge_pool_roots, ethereum_events, validator_set_update, EthereumTxData, + VoteExtension, }; use namada_sdk::eth_bridge::{EthBridgeQueries, SendValsetUpd}; @@ -75,7 +75,9 @@ where pub fn craft_extension(&mut self) -> VoteExtension { VoteExtension { ethereum_events: self.extend_vote_with_ethereum_events(), - bridge_pool_root: self.extend_vote_with_bp_roots(), + bridge_pool_root: self + .extend_vote_with_bp_roots() + .map(namada::vote_ext::bridge_pool_roots::SignedVext), validator_set_update: self.extend_vote_with_valset_update(), } } @@ -159,7 +161,7 @@ where }; let protocol_key = self.mode.get_protocol_key().expect(VALIDATOR_EXPECT_MSG); - Some(ext.sign(protocol_key)) + Some(ext.sign(protocol_key).0) } /// Extend PreCommit votes with [`validator_set_update::Vext`] @@ -274,7 +276,11 @@ pub fn iter_protocol_txs( validator_set_update, } = ext; [ - ethereum_events.map(EthereumTxData::EthEventsVext), + ethereum_events.map(|e| { + EthereumTxData::EthEventsVext( + namada::vote_ext::ethereum_events::SignedVext(e), + ) + }), bridge_pool_root.map(EthereumTxData::BridgePoolVext), validator_set_update.map(EthereumTxData::ValSetUpdateVext), ] diff --git a/apps/src/lib/node/ledger/shell/vote_extensions/bridge_pool_vext.rs b/apps/src/lib/node/ledger/shell/vote_extensions/bridge_pool_vext.rs index 629cce4e8d..4c77229c64 100644 --- a/apps/src/lib/node/ledger/shell/vote_extensions/bridge_pool_vext.rs +++ b/apps/src/lib/node/ledger/shell/vote_extensions/bridge_pool_vext.rs @@ -2,9 +2,8 @@ //! bridge pool root and nonce seen by a quorum of validators. use itertools::Itertools; use namada::ledger::pos::PosQueries; -use namada::ledger::storage::traits::StorageHasher; -use namada::ledger::storage::{DBIter, DB}; -use namada::proto::Signed; +use namada::state::{DBIter, StorageHasher, DB}; +use namada::tx::Signed; use namada::types::keccak::keccak_hash; use namada::types::storage::BlockHeight; use namada::types::token; @@ -202,7 +201,6 @@ where mod test_bp_vote_extensions { use namada::core::ledger::eth_bridge::storage::bridge_pool::get_key_from_hash; use namada::ledger::pos::PosQueries; - use namada::ledger::storage_api::StorageWrite; use namada::proof_of_stake::storage::{ consensus_validator_set_handle, read_consensus_validator_set_addresses_with_stake, @@ -211,14 +209,15 @@ mod test_bp_vote_extensions { Position as ValidatorPosition, WeightedValidator, }; use namada::proof_of_stake::{become_validator, BecomeValidator, Epoch}; - use namada::proto::{SignableEthMessage, Signed}; + use namada::storage::StorageWrite; use namada::tendermint::abci::types::VoteInfo; + use namada::tx::{SignableEthMessage, Signed}; use namada::types::ethereum_events::Uint; use namada::types::keccak::{keccak_hash, KeccakHash}; use namada::types::key::*; use namada::types::storage::BlockHeight; use namada::types::token; - use namada::types::vote_extensions::bridge_pool_roots; + use namada::vote_ext::bridge_pool_roots; use namada_sdk::eth_bridge::EthBridgeQueries; use crate::node::ledger::shell::test_utils::*; diff --git a/apps/src/lib/node/ledger/shell/vote_extensions/eth_events.rs b/apps/src/lib/node/ledger/shell/vote_extensions/eth_events.rs index 23c3013458..3832f417d2 100644 --- a/apps/src/lib/node/ledger/shell/vote_extensions/eth_events.rs +++ b/apps/src/lib/node/ledger/shell/vote_extensions/eth_events.rs @@ -3,15 +3,12 @@ use std::collections::{BTreeMap, HashMap}; use namada::ledger::pos::PosQueries; -use namada::ledger::storage::traits::StorageHasher; -use namada::ledger::storage::{DBIter, DB}; -use namada::proto::Signed; +use namada::state::{DBIter, StorageHasher, DB}; +use namada::tx::Signed; use namada::types::ethereum_events::EthereumEvent; use namada::types::storage::BlockHeight; use namada::types::token; -use namada::types::vote_extensions::ethereum_events::{ - self, MultiSignedEthEvent, -}; +use namada::vote_ext::ethereum_events::{self, MultiSignedEthEvent}; use namada_sdk::eth_bridge::EthBridgeQueries; use super::*; @@ -293,7 +290,7 @@ mod test_vote_extensions { use std::convert::TryInto; use borsh_ext::BorshSerializeExt; - use namada::core::ledger::storage_api::collections::lazy_map::{ + use namada::core::ledger::namada::storage::collections::lazy_map::{ NestedSubKey, SubKey, }; use namada::eth_bridge::storage::bridge_pool; @@ -312,7 +309,7 @@ mod test_vote_extensions { use namada::types::hash::Hash; use namada::types::key::*; use namada::types::storage::{Epoch, InnerEthEventsQueue}; - use namada::types::vote_extensions::ethereum_events; + use namada::vote_ext::ethereum_events; use crate::node::ledger::shell::test_utils::*; use crate::node::ledger::shims::abcipp_shim_types::shim::request::FinalizeBlock; diff --git a/apps/src/lib/node/ledger/shell/vote_extensions/val_set_update.rs b/apps/src/lib/node/ledger/shell/vote_extensions/val_set_update.rs index 91ecadbdc5..8893033268 100644 --- a/apps/src/lib/node/ledger/shell/vote_extensions/val_set_update.rs +++ b/apps/src/lib/node/ledger/shell/vote_extensions/val_set_update.rs @@ -4,11 +4,10 @@ use std::collections::HashMap; use namada::ledger::pos::PosQueries; -use namada::ledger::storage::traits::StorageHasher; -use namada::ledger::storage::{DBIter, DB}; +use namada::state::{DBIter, StorageHasher, DB}; use namada::types::storage::Epoch; use namada::types::token; -use namada::types::vote_extensions::validator_set_update; +use namada::vote_ext::validator_set_update; use super::*; use crate::node::ledger::shell::Shell; @@ -208,8 +207,10 @@ where let mut voting_powers = None; let mut signatures = HashMap::new(); - for (_validator_voting_power, mut vote_extension) in - self.filter_invalid_valset_upd_vexts(vote_extensions) + for ( + _validator_voting_power, + validator_set_update::SignedVext(mut vote_extension), + ) in self.filter_invalid_valset_upd_vexts(vote_extensions) { if voting_powers.is_none() { voting_powers = Some(std::mem::take( @@ -254,7 +255,7 @@ where #[cfg(test)] mod test_vote_extensions { - use namada::core::ledger::storage_api::collections::lazy_map::{ + use namada::core::ledger::namada::storage::collections::lazy_map::{ NestedSubKey, SubKey, }; use namada::ledger::pos::PosQueries; @@ -266,7 +267,7 @@ mod test_vote_extensions { use namada::proof_of_stake::Epoch; use namada::tendermint::abci::types::VoteInfo; use namada::types::key::RefTo; - use namada::types::vote_extensions::validator_set_update; + use namada::vote_ext::validator_set_update; use namada_sdk::eth_bridge::EthBridgeQueries; use crate::node::ledger::shell::test_utils::{self, get_pkh_from_address}; diff --git a/apps/src/lib/node/ledger/shims/abcipp_shim.rs b/apps/src/lib/node/ledger/shims/abcipp_shim.rs index a8140ce56a..12848d86e0 100644 --- a/apps/src/lib/node/ledger/shims/abcipp_shim.rs +++ b/apps/src/lib/node/ledger/shims/abcipp_shim.rs @@ -6,11 +6,11 @@ use std::task::{Context, Poll}; use futures::future::FutureExt; use namada::proof_of_stake::storage::find_validator_by_raw_hash; -use namada::proto::Tx; +use namada::tx::data::hash_tx; +use namada::tx::Tx; use namada::types::hash::Hash; use namada::types::key::tm_raw_hash_to_string; use namada::types::storage::{BlockHash, BlockHeight}; -use namada::types::transaction::hash_tx; use tokio::sync::broadcast; use tokio::sync::mpsc::UnboundedSender; use tower::Service; diff --git a/apps/src/lib/node/ledger/storage/mod.rs b/apps/src/lib/node/ledger/storage/mod.rs index 58ae90ce88..102c0e0ebc 100644 --- a/apps/src/lib/node/ledger/storage/mod.rs +++ b/apps/src/lib/node/ledger/storage/mod.rs @@ -9,8 +9,7 @@ use arse_merkle_tree::blake2b::Blake2bHasher; use arse_merkle_tree::traits::Hasher; use arse_merkle_tree::H256; use blake2b_rs::{Blake2b, Blake2bBuilder}; -use namada::ledger::storage::traits::StorageHasher; -use namada::ledger::storage::Storage; +use namada::state::{Storage, StorageHasher}; #[derive(Default)] pub struct PersistentStorageHasher(Blake2bHasher); @@ -60,9 +59,9 @@ mod tests { use namada::ledger::gas::STORAGE_ACCESS_GAS_PER_BYTE; use namada::ledger::ibc::storage::ibc_key; use namada::ledger::parameters::{EpochDuration, Parameters}; - use namada::ledger::storage::write_log::WriteLog; - use namada::ledger::storage::{types, StoreType, WlStorage}; - use namada::ledger::storage_api::{self, StorageWrite}; + use namada::state::write_log::WriteLog; + use namada::state::{types, StoreType, WlStorage}; + use namada::storage::{self, StorageWrite}; use namada::types::chain::ChainId; use namada::types::ethereum_events::Uint; use namada::types::hash::Hash; @@ -366,7 +365,7 @@ mod tests { /// value, if any. fn test_read_with_height_aux( blocks_write_value: Vec, - ) -> namada::ledger::storage::Result<()> { + ) -> namada::state::Result<()> { let db_path = TempDir::new().expect("Unable to create a temporary DB directory"); let mut storage = PersistentStorage::open( @@ -459,7 +458,7 @@ mod tests { /// Test the restore of the merkle tree fn test_get_merkle_tree_aux( blocks_write_type: Vec, - ) -> namada::ledger::storage::Result<()> { + ) -> namada::state::Result<()> { let db_path = TempDir::new().expect("Unable to create a temporary DB directory"); let mut storage = PersistentStorage::open( @@ -711,7 +710,7 @@ mod tests { } // Then try to iterate over their prefix - let iter = storage_api::iter_prefix(&storage, &prefix) + let iter = namada::storage::iter_prefix(&storage, &prefix) .unwrap() .map(Result::unwrap); @@ -726,7 +725,7 @@ mod tests { storage.commit_block().unwrap(); // Again, try to iterate over their prefix - let iter = storage_api::iter_prefix(&storage, &prefix) + let iter = namada::storage::iter_prefix(&storage, &prefix) .unwrap() .map(Result::unwrap); itertools::assert_equal(iter, expected); @@ -744,7 +743,7 @@ mod tests { storage.write(&key, i / 2).unwrap(); } - let iter = storage_api::iter_prefix(&storage, &prefix) + let iter = namada::storage::iter_prefix(&storage, &prefix) .unwrap() .map(Result::unwrap); @@ -764,7 +763,7 @@ mod tests { } // Check that iter_prefix doesn't return deleted keys anymore - let iter = storage_api::iter_prefix(&storage, &prefix) + let iter = namada::storage::iter_prefix(&storage, &prefix) .unwrap() .map(Result::unwrap); let expected = merged @@ -777,7 +776,7 @@ mod tests { storage.commit_block().unwrap(); // And check again - let iter = storage_api::iter_prefix(&storage, &prefix) + let iter = namada::storage::iter_prefix(&storage, &prefix) .unwrap() .map(Result::unwrap); itertools::assert_equal(iter, expected); diff --git a/apps/src/lib/node/ledger/storage/rocksdb.rs b/apps/src/lib/node/ledger/storage/rocksdb.rs index de11b97aa6..ad16f7e840 100644 --- a/apps/src/lib/node/ledger/storage/rocksdb.rs +++ b/apps/src/lib/node/ledger/storage/rocksdb.rs @@ -48,26 +48,25 @@ use borsh::BorshDeserialize; use borsh_ext::BorshSerializeExt; use data_encoding::HEXLOWER; use itertools::Either; -use namada::core::ledger::masp_conversions::ConversionState; use namada::core::types::ethereum_structs; use namada::eth_bridge::storage::proof::BridgePoolRootProof; use namada::ledger::eth_bridge::storage::bridge_pool; use namada::ledger::replay_protection; -use namada::ledger::storage::merkle_tree::{ - base_tree_key_prefix, subtree_key_prefix, -}; -use namada::ledger::storage::types::PrefixIterator; -use namada::ledger::storage::{ - types, BlockStateRead, BlockStateWrite, DBIter, DBWriteBatch, Error, +use namada::ledger::storage::tx_queue::TxQueue; +use namada::state::merkle_tree::{base_tree_key_prefix, subtree_key_prefix}; +use namada::state::types::PrefixIterator; +use namada::state::{ + BlockStateRead, BlockStateWrite, DBIter, DBWriteBatch, Error, MerkleTreeStoresRead, Result, StoreType, DB, }; +use namada::types; use namada::types::ethereum_events::Uint; -use namada::types::internal::TxQueue; use namada::types::storage::{ BlockHeight, BlockResults, Epoch, EthEventsQueue, Header, Key, KeySeg, KEY_SEGMENT_SEPARATOR, }; use namada::types::time::DateTimeUtc; +use namada::types::token::ConversionState; use rayon::prelude::*; use rocksdb::{ BlockBasedOptions, ColumnFamily, ColumnFamilyDescriptor, Direction, @@ -1731,7 +1730,7 @@ mod imp { #[cfg(test)] mod test { - use namada::ledger::storage::{MerkleTree, Sha256Hasher}; + use namada::state::{MerkleTree, Sha256Hasher}; use namada::types::address::{ gen_established_address, EstablishedAddressGen, }; diff --git a/shared/Cargo.toml b/shared/Cargo.toml index fba5c6299e..9e2bc82c48 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -64,7 +64,11 @@ namada-sdk = [ "namada_sdk/namada-sdk", ] -multicore = ["masp_proofs/multicore", "namada_sdk/multicore"] +multicore = [ + "masp_proofs/multicore", + "namada_sdk/multicore", + "namada_token/multicore", +] [dependencies] namada_account = { path = "../account" } diff --git a/shared/src/lib.rs b/shared/src/lib.rs index b6bfa42323..1812400f0e 100644 --- a/shared/src/lib.rs +++ b/shared/src/lib.rs @@ -9,9 +9,14 @@ pub use namada_core::{ibc, tendermint, tendermint_proto}; #[cfg(feature = "tendermint-rpc")] pub use tendermint_rpc; -pub use {bip39, namada_core as core, namada_proof_of_stake as proof_of_stake}; +pub use { + bip39, namada_account as account, namada_core as core, namada_gas as gas, + namada_governance as governance, namada_parameters as parameters, + namada_proof_of_stake as proof_of_stake, namada_sdk as sdk, + namada_state as state, namada_storage as storage, namada_token as token, + namada_tx as tx, namada_vote_ext as vote_ext, +}; pub mod ledger; -pub use namada_sdk; pub use namada_tx::proto; pub mod types; pub mod vm; diff --git a/shared/src/types/mod.rs b/shared/src/types/mod.rs index f56a9afc39..73daa72a69 100644 --- a/shared/src/types/mod.rs +++ b/shared/src/types/mod.rs @@ -6,7 +6,7 @@ pub use namada_sdk::io; pub mod key; pub use namada_core::types::{ - address, chain, dec, eth_abi, eth_bridge_pool, ethereum_events, hash, - internal, keccak, masp, storage, time, token, uint, validity_predicate, - voting_power, + address, chain, dec, decode, encode, eth_abi, eth_bridge_pool, + ethereum_events, hash, internal, keccak, masp, storage, time, token, uint, + validity_predicate, voting_power, }; diff --git a/shielded_token/Cargo.toml b/shielded_token/Cargo.toml index 9fcfea99e6..7eb57e7c19 100644 --- a/shielded_token/Cargo.toml +++ b/shielded_token/Cargo.toml @@ -14,7 +14,8 @@ version.workspace = true [features] default = [] -testing = ["namada_core/testing"] +multicore = ["dep:rayon"] +testing = ["multicore", "namada_core/testing"] [dependencies] namada_core = { path = "../core", default-features = false } @@ -24,4 +25,5 @@ namada_storage = { path = "../storage" } namada_trans_token = { path = "../trans_token" } masp_primitives.workspace = true +rayon = { workspace = true, optional = true } tracing.workspace = true diff --git a/shielded_token/src/conversion.rs b/shielded_token/src/conversion.rs index 3c932d10d3..3279842513 100644 --- a/shielded_token/src/conversion.rs +++ b/shielded_token/src/conversion.rs @@ -174,7 +174,7 @@ where } // This is only enabled when "wasm-runtime" is on, because we're using rayon -#[cfg(any(feature = "wasm-runtime", test))] +#[cfg(any(feature = "multicore", test))] /// Update the MASP's allowed conversions pub fn update_allowed_conversions( wl_storage: &mut WlStorage, @@ -184,17 +184,22 @@ where H: 'static + StorageHasher, { use std::cmp::Ordering; + use std::collections::BTreeMap; use masp_primitives::bls12_381; + use masp_primitives::convert::AllowedConversion; use masp_primitives::ff::PrimeField; + use masp_primitives::merkle_tree::FrozenCommitmentTree; + use masp_primitives::sapling::Node; use masp_primitives::transaction::components::I128Sum as MaspAmount; use namada_core::types::storage::{Key, KeySeg}; - use namada_core::types::{address, MASP_CONVERT_ANCHOR_KEY}; use rayon::iter::{ IndexedParallelIterator, IntoParallelIterator, ParallelIterator, }; use rayon::prelude::ParallelSlice; + use crate::storage_key::MASP_CONVERT_ANCHOR_KEY; + // The derived conversions will be placed in MASP address space let masp_addr = MASP; diff --git a/state/src/lib.rs b/state/src/lib.rs index e46d03578d..bd7672fce5 100644 --- a/state/src/lib.rs +++ b/state/src/lib.rs @@ -1,6 +1,5 @@ //! Ledger's state storage with key-value backed store and a merkle tree -#[cfg(any(test, feature = "testing"))] pub mod mockdb; pub mod tx_queue; pub mod types; @@ -19,8 +18,8 @@ use namada_core::types::address::{ use namada_core::types::chain::{ChainId, CHAIN_ID_LENGTH}; use namada_core::types::eth_bridge_pool::is_pending_transfer_key; use namada_core::types::ethereum_events::Uint; -pub use namada_core::types::hash::StorageHasher; use namada_core::types::hash::{Error as HashError, Hash}; +pub use namada_core::types::hash::{Sha256Hasher, StorageHasher}; use namada_core::types::storage::{ BlockHash, BlockHeight, BlockResults, Epoch, Epochs, EthEventsQueue, Header, Key, KeySeg, TxIndex, BLOCK_HASH_LENGTH, BLOCK_HEIGHT_LENGTH, @@ -34,8 +33,8 @@ use namada_gas::{ STORAGE_WRITE_GAS_PER_BYTE, }; pub use namada_merkle_tree::{ - ics23_specs, MembershipProof, MerkleTree, MerkleTreeStoresRead, - MerkleTreeStoresWrite, StoreRef, StoreType, + self as merkle_tree, ics23_specs, MembershipProof, MerkleTree, + MerkleTreeStoresRead, MerkleTreeStoresWrite, StoreRef, StoreType, }; use namada_merkle_tree::{Error as MerkleTreeError, MerkleRoot}; use namada_parameters::{self, EpochDuration, Parameters}; @@ -156,7 +155,7 @@ pub enum Error { #[error("Storage key error {0}")] KeyError(namada_core::types::storage::Error), #[error("Coding error: {0}")] - CodingError(namada_core::types::DecodeError), + CodingError(#[from] namada_core::types::DecodeError), #[error("Merkle tree error: {0}")] MerkleTreeError(MerkleTreeError), #[error("DB error: {0}")] @@ -1293,10 +1292,10 @@ impl From for Error { #[cfg(any(test, feature = "testing"))] pub mod testing { use namada_core::types::address; + use namada_core::types::hash::Sha256Hasher; use super::mockdb::MockDB; use super::*; - use crate::ledger::storage::traits::Sha256Hasher; /// `WlStorage` with a mock DB for testing pub type TestWlStorage = WlStorage; diff --git a/state/src/mockdb.rs b/state/src/mockdb.rs index b200583197..b5e81e52a6 100644 --- a/state/src/mockdb.rs +++ b/state/src/mockdb.rs @@ -6,28 +6,28 @@ use std::ops::Bound::{Excluded, Included}; use std::path::Path; use std::str::FromStr; -use borsh::BorshDeserialize; -use borsh_ext::BorshSerializeExt; use itertools::Either; - -use super::merkle_tree::{ +use namada_core::borsh::{BorshDeserialize, BorshSerializeExt}; +use namada_core::ledger::replay_protection; +use namada_core::types; +use namada_core::types::ethereum_events::Uint; +use namada_core::types::ethereum_structs; +use namada_core::types::hash::Hash; +use namada_core::types::storage::{ + BlockHeight, BlockResults, Epoch, EthEventsQueue, Header, Key, KeySeg, + KEY_SEGMENT_SEPARATOR, +}; +use namada_core::types::time::DateTimeUtc; +use namada_core::types::token::ConversionState; +use namada_merkle_tree::{ base_tree_key_prefix, subtree_key_prefix, MerkleTreeStoresRead, StoreType, }; + use super::{ BlockStateRead, BlockStateWrite, DBIter, DBWriteBatch, Error, Result, DB, }; -use crate::ledger::replay_protection; -use crate::ledger::storage::types::{self, KVBytes, PrefixIterator}; -use crate::types::ethereum_events::Uint; -use crate::types::ethereum_structs; -use crate::types::hash::Hash; -use crate::types::internal::TxQueue; -use crate::types::storage::{ - BlockHeight, BlockResults, Epoch, EthEventsQueue, Header, Key, KeySeg, - KEY_SEGMENT_SEPARATOR, -}; -use crate::types::time::DateTimeUtc; -use crate::types::token::ConversionState; +use crate::tx_queue::TxQueue; +use crate::types::{KVBytes, PrefixIterator}; /// An in-memory DB for testing. #[derive(Debug, Default)] diff --git a/token/Cargo.toml b/token/Cargo.toml index 5b5138e014..9d11800361 100644 --- a/token/Cargo.toml +++ b/token/Cargo.toml @@ -12,6 +12,10 @@ readme.workspace = true repository.workspace = true version.workspace = true +[features] +default = [] +multicore = ["namada_shielded_token/multicore"] + [dependencies] namada_core = { path = "../core", default-features = false } namada_shielded_token = { path = "../shielded_token" } diff --git a/tx/src/lib.rs b/tx/src/lib.rs index caabfc6bd8..2364b28833 100644 --- a/tx/src/lib.rs +++ b/tx/src/lib.rs @@ -4,6 +4,7 @@ pub mod data; pub mod proto; mod types; +pub use namada_core::types::key::SignableEthMessage; pub use namada_core::types::sign::SignatureIndex; pub use types::{ standalone_signature, verify_standalone_sig, Code, Commitment, From c3183fe3c4f5ea20c85f66ca51beb632b5ac4287 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 5 Jan 2024 09:39:52 +0000 Subject: [PATCH 077/118] encoding_spec: fix build --- encoding_spec/src/main.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/encoding_spec/src/main.rs b/encoding_spec/src/main.rs index 5ac27daf22..85f4762c04 100644 --- a/encoding_spec/src/main.rs +++ b/encoding_spec/src/main.rs @@ -24,12 +24,13 @@ use borsh::{schema, schema_container_of}; use itertools::Itertools; use lazy_static::lazy_static; use madato::types::TableRow; +use namada::account; use namada::ledger::parameters::Parameters; +use namada::tx::data::{pos, TxType, WrapperTx}; use namada::types::address::Address; use namada::types::key::ed25519::{PublicKey, Signature}; use namada::types::storage::{self, Epoch}; -use namada::types::transaction::pos; -use namada::types::{token, transaction}; +use namada::types::token; /// This generator will write output into this `docs` file. const OUTPUT_PATH: &str = @@ -79,19 +80,16 @@ fn main() -> Result<(), Box> { let public_key_schema = schema_container_of::(); // TODO update after let signature_schema = schema_container_of::(); - let init_account_schema = - schema_container_of::(); - let init_validator_schema = - schema_container_of::(); + let init_account_schema = schema_container_of::(); + let init_validator_schema = schema_container_of::(); let token_transfer_schema = schema_container_of::(); - let update_account = - schema_container_of::(); + let update_account = schema_container_of::(); let pos_bond_schema = schema_container_of::(); let pos_withdraw_schema = schema_container_of::(); - let wrapper_tx_schema = schema_container_of::(); + let wrapper_tx_schema = schema_container_of::(); // TODO derive BorshSchema after // let tx_result_schema = schema_container_of::(); - let tx_type_schema = schema_container_of::(); + let tx_type_schema = schema_container_of::(); let prefix_value_schema = schema_container_of::(); // PoS From 25637bd89715216a1afbb892d0bb0376f11deabd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 5 Jan 2024 09:40:08 +0000 Subject: [PATCH 078/118] light_sdk: fix build --- Cargo.lock | 1 - apps/src/lib/client/tx.rs | 36 ++++-- apps/src/lib/node/ledger/shell/mod.rs | 4 +- light_sdk/Cargo.toml | 10 +- light_sdk/src/lib.rs | 6 +- light_sdk/src/reading/account.rs | 6 +- light_sdk/src/reading/governance.rs | 6 +- light_sdk/src/reading/mod.rs | 8 +- light_sdk/src/reading/pos.rs | 6 +- light_sdk/src/reading/tx.rs | 4 +- light_sdk/src/transaction/account.rs | 32 +++-- light_sdk/src/transaction/bridge.rs | 10 +- light_sdk/src/transaction/governance.rs | 45 ++++--- light_sdk/src/transaction/ibc.rs | 14 +-- light_sdk/src/transaction/mod.rs | 10 +- light_sdk/src/transaction/pgf.rs | 12 +- light_sdk/src/transaction/pos.rs | 94 +++++++------- light_sdk/src/transaction/transfer.rs | 12 +- light_sdk/src/transaction/wrapper.rs | 10 +- sdk/src/args.rs | 12 +- sdk/src/error.rs | 4 +- sdk/src/eth_bridge/bridge_pool.rs | 4 +- sdk/src/lib.rs | 10 +- sdk/src/rpc.rs | 10 +- sdk/src/signing.rs | 38 +++--- sdk/src/tx.rs | 161 ++++++++++++++---------- 26 files changed, 300 insertions(+), 265 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2ec954a5ef..a1b236692e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4542,7 +4542,6 @@ dependencies = [ "borsh", "borsh-ext", "ibc 0.47.0", - "namada_core", "namada_sdk", "prost 0.12.3", "tendermint-config", diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 7253992d08..28f839e8d1 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -995,12 +995,14 @@ where let (mut tx_builder, signing_data) = if args.is_offline { let proposal = OfflineProposal::try_from(args.proposal_data.as_ref()) .map_err(|e| { - error::TxError::FailedGovernaneProposalDeserialize( + error::TxSubmitError::FailedGovernaneProposalDeserialize( e.to_string(), ) })? .validate(current_epoch, args.tx.force) - .map_err(|e| error::TxError::InvalidProposal(e.to_string()))?; + .map_err(|e| { + error::TxSubmitError::InvalidProposal(e.to_string()) + })?; let default_signer = Some(proposal.author.clone()); let signing_data = aux_signing_data( @@ -1024,7 +1026,7 @@ where let output_file_path = signed_offline_proposal .serialize(args.tx.output_folder) .map_err(|e| { - error::TxError::FailedGovernaneProposalDeserialize( + error::TxSubmitError::FailedGovernaneProposalDeserialize( e.to_string(), ) })?; @@ -1039,12 +1041,14 @@ where let proposal = PgfFundingProposal::try_from(args.proposal_data.as_ref()) .map_err(|e| { - error::TxError::FailedGovernaneProposalDeserialize( + error::TxSubmitError::FailedGovernaneProposalDeserialize( e.to_string(), ) })? .validate(&governance_parameters, current_epoch, args.tx.force) - .map_err(|e| error::TxError::InvalidProposal(e.to_string()))?; + .map_err(|e| { + error::TxSubmitError::InvalidProposal(e.to_string()) + })?; submit_reveal_aux(namada, args.tx.clone(), &proposal.proposal.author) .await?; @@ -1055,7 +1059,9 @@ where args.proposal_data.as_ref(), ) .map_err(|e| { - error::TxError::FailedGovernaneProposalDeserialize(e.to_string()) + error::TxSubmitError::FailedGovernaneProposalDeserialize( + e.to_string(), + ) })?; let author_balance = rpc::get_token_balance( namada.client(), @@ -1070,7 +1076,9 @@ where author_balance, args.tx.force, ) - .map_err(|e| error::TxError::InvalidProposal(e.to_string()))?; + .map_err(|e| { + error::TxSubmitError::InvalidProposal(e.to_string()) + })?; submit_reveal_aux(namada, args.tx.clone(), &proposal.proposal.author) .await?; @@ -1079,7 +1087,9 @@ where } else { let proposal = DefaultProposal::try_from(args.proposal_data.as_ref()) .map_err(|e| { - error::TxError::FailedGovernaneProposalDeserialize(e.to_string()) + error::TxSubmitError::FailedGovernaneProposalDeserialize( + e.to_string(), + ) })?; let author_balane = rpc::get_token_balance( namada.client(), @@ -1094,7 +1104,9 @@ where author_balane, args.tx.force, ) - .map_err(|e| error::TxError::InvalidProposal(e.to_string()))?; + .map_err(|e| { + error::TxSubmitError::InvalidProposal(e.to_string()) + })?; submit_reveal_aux(namada, args.tx.clone(), &proposal.proposal.author) .await?; @@ -1131,18 +1143,18 @@ where .await?; let proposal_vote = ProposalVote::try_from(args.vote) - .map_err(|_| error::TxError::InvalidProposalVote)?; + .map_err(|_| error::TxSubmitError::InvalidProposalVote)?; let proposal = OfflineSignedProposal::try_from( args.proposal_data.clone().unwrap().as_ref(), ) - .map_err(|e| error::TxError::InvalidProposal(e.to_string()))? + .map_err(|e| error::TxSubmitError::InvalidProposal(e.to_string()))? .validate( &signing_data.account_public_keys_map.clone().unwrap(), signing_data.threshold, args.tx.force, ) - .map_err(|e| error::TxError::InvalidProposal(e.to_string()))?; + .map_err(|e| error::TxSubmitError::InvalidProposal(e.to_string()))?; let delegations = rpc::get_delegators_delegation_at( namada.client(), &args.voter, diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index fd06ed0348..fc88f2db77 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -742,8 +742,8 @@ where /// Get the next epoch for which we can request validator set changed pub fn get_validator_set_update_epoch( &self, - current_epoch: namada_sdk::core::types::storage::Epoch, - ) -> namada_sdk::core::types::storage::Epoch { + current_epoch: namada_sdk::types::storage::Epoch, + ) -> namada_sdk::types::storage::Epoch { if let Some(delay) = self.wl_storage.storage.update_epoch_blocks_delay { if delay == EPOCH_SWITCH_BLOCKS_DELAY { // If we're about to update validator sets for the diff --git a/light_sdk/Cargo.toml b/light_sdk/Cargo.toml index 63669efc45..4c9e8b5188 100644 --- a/light_sdk/Cargo.toml +++ b/light_sdk/Cargo.toml @@ -12,15 +12,13 @@ readme.workspace = true repository.workspace = true version.workspace = true -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] +namada_sdk = { path = "../sdk" } + borsh.workspace = true borsh-ext.workspace = true ibc = "0.47.0" -namada_core = {path = "../core"} -namada_sdk = {path = "../sdk"} prost.workspace = true tendermint-config.workspace = true -tendermint-rpc = { worskpace = true, features = ["http-client"] } -tokio = {workspace = true, features = ["rt"]} +tendermint-rpc = { workspace = true, features = ["http-client"] } +tokio = { workspace = true, features = ["rt"] } diff --git a/light_sdk/src/lib.rs b/light_sdk/src/lib.rs index 317eaada4c..06b659a518 100644 --- a/light_sdk/src/lib.rs +++ b/light_sdk/src/lib.rs @@ -3,8 +3,8 @@ //! with ease-of-use and interoperability in mind so that it should be possible //! to wrap it for usage in an FFI context. //! -//! The [`namada_core`] crate of Namada is also re-exported to allow access to -//! its types. +//! The [`namada_sdk`] crate of Namada is also re-exported to allow access +//! to its types. //! //! # Structure //! @@ -18,4 +18,4 @@ pub mod reading; pub mod transaction; pub mod writing; -pub use namada_core; +pub use namada_sdk; diff --git a/light_sdk/src/reading/account.rs b/light_sdk/src/reading/account.rs index 920580dbbe..bd75e8afb3 100644 --- a/light_sdk/src/reading/account.rs +++ b/light_sdk/src/reading/account.rs @@ -1,6 +1,6 @@ -use namada_core::types::account::Account; -use namada_core::types::address::Address; -use namada_core::types::key::common; +use namada_sdk::account::Account; +use namada_sdk::types::address::Address; +use namada_sdk::types::key::common; use super::*; diff --git a/light_sdk/src/reading/governance.rs b/light_sdk/src/reading/governance.rs index 7f7399ef64..a1af6af1a1 100644 --- a/light_sdk/src/reading/governance.rs +++ b/light_sdk/src/reading/governance.rs @@ -1,6 +1,6 @@ -use namada_core::ledger::governance::parameters::GovernanceParameters; -use namada_core::ledger::governance::storage::proposal::StorageProposal; -use namada_core::ledger::governance::utils::Vote; +use namada_sdk::governance::parameters::GovernanceParameters; +use namada_sdk::governance::storage::proposal::StorageProposal; +use namada_sdk::governance::utils::Vote; use super::*; diff --git a/light_sdk/src/reading/mod.rs b/light_sdk/src/reading/mod.rs index 690ea074d4..92a4cc26c1 100644 --- a/light_sdk/src/reading/mod.rs +++ b/light_sdk/src/reading/mod.rs @@ -1,12 +1,12 @@ use std::str::FromStr; -use namada_core::ledger::storage::LastBlock; -use namada_core::types::address::Address; -use namada_core::types::storage::BlockResults; -use namada_core::types::token; use namada_sdk::error::Error; use namada_sdk::queries::RPC; use namada_sdk::rpc; +use namada_sdk::state::LastBlock; +use namada_sdk::types::address::Address; +use namada_sdk::types::storage::BlockResults; +use namada_sdk::types::token; use tendermint_config::net::Address as TendermintAddress; use tendermint_rpc::HttpClient; use tokio::runtime::Runtime; diff --git a/light_sdk/src/reading/pos.rs b/light_sdk/src/reading/pos.rs index 86aea2dc01..0612ada1a6 100644 --- a/light_sdk/src/reading/pos.rs +++ b/light_sdk/src/reading/pos.rs @@ -1,13 +1,13 @@ use std::collections::{BTreeSet, HashMap, HashSet}; -use namada_core::types::address::Address; -use namada_core::types::key::common; -use namada_core::types::storage::{BlockHeight, Epoch}; use namada_sdk::proof_of_stake::types::{ BondsAndUnbondsDetails, CommissionPair, ValidatorMetaData, ValidatorState, }; use namada_sdk::proof_of_stake::PosParams; use namada_sdk::queries::vp::pos::EnrichedBondsAndUnbondsDetails; +use namada_sdk::types::address::Address; +use namada_sdk::types::key::common; +use namada_sdk::types::storage::{BlockHeight, Epoch}; use super::*; diff --git a/light_sdk/src/reading/tx.rs b/light_sdk/src/reading/tx.rs index 036f38ee59..35ffeaaddc 100644 --- a/light_sdk/src/reading/tx.rs +++ b/light_sdk/src/reading/tx.rs @@ -24,7 +24,7 @@ pub fn query_tx_events( pub fn dry_run_tx( tendermint_addr: &str, tx_bytes: Vec, -) -> Result { +) -> Result { let client = HttpClient::new( TendermintAddress::from_str(tendermint_addr) .map_err(|e| Error::Other(e.to_string()))?, @@ -68,6 +68,6 @@ pub async fn query_tx_status( if let Some(e) = maybe_event { Ok(e) } else { - Err(Error::Tx(namada_sdk::error::TxError::AppliedTimeout)) + Err(Error::Tx(namada_sdk::error::TxSubmitError::AppliedTimeout)) } } diff --git a/light_sdk/src/transaction/account.rs b/light_sdk/src/transaction/account.rs index bbf2e5acb7..2a7742a684 100644 --- a/light_sdk/src/transaction/account.rs +++ b/light_sdk/src/transaction/account.rs @@ -1,7 +1,7 @@ -use namada_core::types::address::Address; -use namada_core::types::hash::Hash; -use namada_core::types::key::common; -use namada_tx::Tx; +use namada_sdk::tx::Tx; +use namada_sdk::types::address::Address; +use namada_sdk::types::hash::Hash; +use namada_sdk::types::key::common; use super::GlobalArgs; use crate::transaction; @@ -21,12 +21,11 @@ impl InitAccount { threshold: u8, args: GlobalArgs, ) -> Self { - let init_account = - namada_core::types::transaction::account::InitAccount { - public_keys, - vp_code_hash, - threshold, - }; + let init_account = namada_sdk::account::InitAccount { + public_keys, + vp_code_hash, + threshold, + }; Self(transaction::build_tx( args, @@ -105,13 +104,12 @@ impl UpdateAccount { threshold: Option, args: GlobalArgs, ) -> Self { - let update_account = - namada_core::types::transaction::account::UpdateAccount { - addr, - vp_code_hash, - public_keys, - threshold, - }; + let update_account = namada_sdk::account::UpdateAccount { + addr, + vp_code_hash, + public_keys, + threshold, + }; Self(transaction::build_tx( args, diff --git a/light_sdk/src/transaction/bridge.rs b/light_sdk/src/transaction/bridge.rs index 98a7ca4f93..b0a669969d 100644 --- a/light_sdk/src/transaction/bridge.rs +++ b/light_sdk/src/transaction/bridge.rs @@ -1,7 +1,7 @@ -pub use namada_core::types::eth_bridge_pool::{GasFee, TransferToEthereum}; -use namada_core::types::hash::Hash; -use namada_core::types::key::common; -use namada_tx::Tx; +use namada_sdk::tx::Tx; +pub use namada_sdk::types::eth_bridge_pool::{GasFee, TransferToEthereum}; +use namada_sdk::types::hash::Hash; +use namada_sdk::types::key::common; use super::GlobalArgs; use crate::transaction; @@ -19,7 +19,7 @@ impl BridgeTransfer { args: GlobalArgs, ) -> Self { let pending_transfer = - namada_core::types::eth_bridge_pool::PendingTransfer { + namada_sdk::types::eth_bridge_pool::PendingTransfer { transfer, gas_fee, }; diff --git a/light_sdk/src/transaction/governance.rs b/light_sdk/src/transaction/governance.rs index 6bb24ac1c0..839f8f2f8a 100644 --- a/light_sdk/src/transaction/governance.rs +++ b/light_sdk/src/transaction/governance.rs @@ -1,10 +1,9 @@ -use namada_core::ledger::governance::storage::proposal::ProposalType; -use namada_core::ledger::governance::storage::vote::StorageProposalVote; -use namada_core::types::address::Address; -use namada_core::types::hash::Hash; -use namada_core::types::key::common; -use namada_core::types::storage::Epoch; -use namada_tx::Tx; +use namada_sdk::governance::{ProposalType, StorageProposalVote}; +use namada_sdk::tx::Tx; +use namada_sdk::types::address::Address; +use namada_sdk::types::hash::Hash; +use namada_sdk::types::key::common; +use namada_sdk::types::storage::Epoch; use super::GlobalArgs; use crate::transaction; @@ -28,16 +27,15 @@ impl InitProposal { grace_epoch: Epoch, args: GlobalArgs, ) -> Self { - let init_proposal = - namada_core::types::transaction::governance::InitProposalData { - id, - content, - author, - r#type, - voting_start_epoch, - voting_end_epoch, - grace_epoch, - }; + let init_proposal = namada_sdk::governance::InitProposalData { + id, + content, + author, + r#type, + voting_start_epoch, + voting_end_epoch, + grace_epoch, + }; Self(transaction::build_tx( args, @@ -80,13 +78,12 @@ impl VoteProposal { delegations: Vec
, args: GlobalArgs, ) -> Self { - let vote_proposal = - namada_core::types::transaction::governance::VoteProposalData { - id, - vote, - voter, - delegations, - }; + let vote_proposal = namada_sdk::governance::VoteProposalData { + id, + vote, + voter, + delegations, + }; Self(transaction::build_tx( args, diff --git a/light_sdk/src/transaction/ibc.rs b/light_sdk/src/transaction/ibc.rs index 8496c9bb7a..57aadc1631 100644 --- a/light_sdk/src/transaction/ibc.rs +++ b/light_sdk/src/transaction/ibc.rs @@ -1,11 +1,11 @@ use std::str::FromStr; -pub use namada_core::ibc::apps::transfer::types::msgs::transfer::MsgTransfer; -use namada_core::ibc::primitives::Msg; -use namada_core::types::hash::Hash; -use namada_core::types::key::common; -use namada_core::types::time::DateTimeUtc; -use namada_tx::Tx; +pub use namada_sdk::ibc::apps::transfer::types::msgs::transfer::MsgTransfer; +use namada_sdk::ibc::primitives::Msg; +use namada_sdk::tx::Tx; +use namada_sdk::types::hash::Hash; +use namada_sdk::types::key::common; +use namada_sdk::types::time::DateTimeUtc; use super::GlobalArgs; use crate::transaction; @@ -32,7 +32,7 @@ impl IbcTransfer { let mut data = vec![]; prost::Message::encode(&packet_data.to_any(), &mut data).unwrap(); - tx.set_data(namada_core::proto::Data::new(data)); + tx.set_data(namada_sdk::tx::Data::new(data)); Self(tx) } diff --git a/light_sdk/src/transaction/mod.rs b/light_sdk/src/transaction/mod.rs index 2c755da39f..2d20a9a08c 100644 --- a/light_sdk/src/transaction/mod.rs +++ b/light_sdk/src/transaction/mod.rs @@ -1,11 +1,11 @@ use std::str::FromStr; use borsh::BorshSerialize; -use namada_core::proto::{Section, Signature, Signer, Tx}; -use namada_core::types::chain::ChainId; -use namada_core::types::hash::Hash; -use namada_core::types::key::common; -use namada_core::types::time::DateTimeUtc; +use namada_sdk::tx::{Section, Signature, Signer, Tx}; +use namada_sdk::types::chain::ChainId; +use namada_sdk::types::hash::Hash; +use namada_sdk::types::key::common; +use namada_sdk::types::time::DateTimeUtc; pub mod account; pub mod bridge; diff --git a/light_sdk/src/transaction/pgf.rs b/light_sdk/src/transaction/pgf.rs index 24b7b038dd..b5bf4f638a 100644 --- a/light_sdk/src/transaction/pgf.rs +++ b/light_sdk/src/transaction/pgf.rs @@ -1,10 +1,10 @@ use std::collections::HashMap; -use namada_core::types::address::Address; -use namada_core::types::dec::Dec; -use namada_core::types::hash::Hash; -use namada_core::types::key::common; -use namada_tx::Tx; +use namada_sdk::tx::Tx; +use namada_sdk::types::address::Address; +use namada_sdk::types::dec::Dec; +use namada_sdk::types::hash::Hash; +use namada_sdk::types::key::common; use super::GlobalArgs; use crate::transaction; @@ -59,7 +59,7 @@ impl UpdateStewardCommission { args: GlobalArgs, ) -> Self { let update_commission = - namada_core::types::transaction::pgf::UpdateStewardCommission { + namada_sdk::tx::data::pgf::UpdateStewardCommission { steward, commission, }; diff --git a/light_sdk/src/transaction/pos.rs b/light_sdk/src/transaction/pos.rs index 9e370889b4..310ae72a3b 100644 --- a/light_sdk/src/transaction/pos.rs +++ b/light_sdk/src/transaction/pos.rs @@ -1,11 +1,11 @@ -use namada_core::types::address::Address; -use namada_core::types::dec::Dec; -use namada_core::types::hash::Hash; -use namada_core::types::key::{common, secp256k1}; -use namada_core::types::token; -use namada_core::types::token::Amount; -use namada_core::types::transaction::pos::Redelegation; -use namada_tx::Tx; +use namada_sdk::tx::data::pos::Redelegation; +use namada_sdk::tx::Tx; +use namada_sdk::types::address::Address; +use namada_sdk::types::dec::Dec; +use namada_sdk::types::hash::Hash; +use namada_sdk::types::key::{common, secp256k1}; +use namada_sdk::types::token; +use namada_sdk::types::token::Amount; use super::GlobalArgs; use crate::transaction; @@ -34,7 +34,7 @@ impl Bond { source: Option
, args: GlobalArgs, ) -> Self { - let unbond = namada_core::types::transaction::pos::Bond { + let unbond = namada_sdk::tx::data::pos::Bond { validator, amount, source, @@ -80,7 +80,7 @@ impl Unbond { source: Option
, args: GlobalArgs, ) -> Self { - let unbond = namada_core::types::transaction::pos::Unbond { + let unbond = namada_sdk::tx::data::pos::Unbond { validator, amount, source, @@ -135,20 +135,19 @@ impl BecomeValidator { discord_handle: Option, args: GlobalArgs, ) -> Self { - let update_account = - namada_core::types::transaction::pos::BecomeValidator { - address, - consensus_key, - eth_cold_key, - eth_hot_key, - protocol_key, - commission_rate, - max_commission_rate_change, - email, - description, - website, - discord_handle, - }; + let update_account = namada_sdk::tx::data::pos::BecomeValidator { + address, + consensus_key, + eth_cold_key, + eth_hot_key, + protocol_key, + commission_rate, + max_commission_rate_change, + email, + description, + website, + discord_handle, + }; Self(transaction::build_tx( args, @@ -294,10 +293,8 @@ impl ClaimRewards { source: Option
, args: GlobalArgs, ) -> Self { - let init_proposal = namada_core::types::transaction::pos::Withdraw { - validator, - source, - }; + let init_proposal = + namada_sdk::tx::data::pos::Withdraw { validator, source }; Self(transaction::build_tx( args, @@ -342,15 +339,14 @@ impl ChangeMetaData { commission_rate: Option, args: GlobalArgs, ) -> Self { - let init_proposal = - namada_core::types::transaction::pos::MetaDataChange { - validator, - email, - description, - website, - discord_handle, - commission_rate, - }; + let init_proposal = namada_sdk::tx::data::pos::MetaDataChange { + validator, + email, + description, + website, + discord_handle, + commission_rate, + }; Self(transaction::build_tx( args, @@ -391,11 +387,10 @@ impl ChangeConsensusKey { consensus_key: common::PublicKey, args: GlobalArgs, ) -> Self { - let init_proposal = - namada_core::types::transaction::pos::ConsensusKeyChange { - validator, - consensus_key, - }; + let init_proposal = namada_sdk::tx::data::pos::ConsensusKeyChange { + validator, + consensus_key, + }; Self(transaction::build_tx( args, @@ -432,11 +427,10 @@ pub struct ChangeCommission(Tx); impl ChangeCommission { /// Build a raw ChangeCommission transaction from the given parameters pub fn new(validator: Address, new_rate: Dec, args: GlobalArgs) -> Self { - let init_proposal = - namada_core::types::transaction::pos::CommissionChange { - validator, - new_rate, - }; + let init_proposal = namada_sdk::tx::data::pos::CommissionChange { + validator, + new_rate, + }; Self(transaction::build_tx( args, @@ -477,10 +471,8 @@ impl Withdraw { source: Option
, args: GlobalArgs, ) -> Self { - let init_proposal = namada_core::types::transaction::pos::Withdraw { - validator, - source, - }; + let init_proposal = + namada_sdk::tx::data::pos::Withdraw { validator, source }; Self(transaction::build_tx( args, diff --git a/light_sdk/src/transaction/transfer.rs b/light_sdk/src/transaction/transfer.rs index c5ae2864d9..aedd2df3c6 100644 --- a/light_sdk/src/transaction/transfer.rs +++ b/light_sdk/src/transaction/transfer.rs @@ -1,9 +1,9 @@ use borsh_ext::BorshSerializeExt; -use namada_core::types::address::Address; -use namada_core::types::hash::Hash; -use namada_core::types::key::common; -use namada_core::types::token::DenominatedAmount; -use namada_tx::Tx; +use namada_sdk::tx::Tx; +use namada_sdk::types::address::Address; +use namada_sdk::types::hash::Hash; +use namada_sdk::types::key::common; +use namada_sdk::types::token::DenominatedAmount; use super::GlobalArgs; use crate::transaction; @@ -25,7 +25,7 @@ impl Transfer { shielded: Option, args: GlobalArgs, ) -> Self { - let init_proposal = namada_core::types::token::Transfer { + let init_proposal = namada_sdk::types::token::Transfer { source, target, token, diff --git a/light_sdk/src/transaction/wrapper.rs b/light_sdk/src/transaction/wrapper.rs index eb4373a0c2..8f396cb144 100644 --- a/light_sdk/src/transaction/wrapper.rs +++ b/light_sdk/src/transaction/wrapper.rs @@ -1,8 +1,8 @@ -use namada_core::proto::{Section, Signature, Signer, Tx, TxError}; -use namada_core::types::hash::Hash; -use namada_core::types::key::common; -use namada_core::types::storage::Epoch; -use namada_core::types::transaction::{Fee, GasLimit}; +use namada_sdk::tx::data::{Fee, GasLimit}; +use namada_sdk::tx::{Section, Signature, Signer, Tx, TxError}; +use namada_sdk::types::hash::Hash; +use namada_sdk::types::key::common; +use namada_sdk::types::storage::Epoch; #[allow(missing_docs)] pub struct Wrapper(Tx); diff --git a/sdk/src/args.rs b/sdk/src/args.rs index 1fa2a7d323..904eaea875 100644 --- a/sdk/src/args.rs +++ b/sdk/src/args.rs @@ -496,13 +496,13 @@ impl InitProposal { self.proposal_data.as_ref(), ) .map_err(|e| { - crate::error::TxError::FailedGovernaneProposalDeserialize( + crate::error::TxSubmitError::FailedGovernaneProposalDeserialize( e.to_string(), ) })? .validate(&governance_parameters, current_epoch, self.tx.force) .map_err(|e| { - crate::error::TxError::InvalidProposal(e.to_string()) + crate::error::TxSubmitError::InvalidProposal(e.to_string()) })?; tx::build_pgf_funding_proposal(context, self, proposal).await @@ -511,7 +511,7 @@ impl InitProposal { self.proposal_data.as_ref(), ) .map_err(|e| { - crate::error::TxError::FailedGovernaneProposalDeserialize( + crate::error::TxSubmitError::FailedGovernaneProposalDeserialize( e.to_string(), ) })?; @@ -530,7 +530,7 @@ impl InitProposal { self.tx.force, ) .map_err(|e| { - crate::error::TxError::InvalidProposal(e.to_string()) + crate::error::TxSubmitError::InvalidProposal(e.to_string()) })?; tx::build_pgf_stewards_proposal(context, self, proposal).await @@ -539,7 +539,7 @@ impl InitProposal { self.proposal_data.as_ref(), ) .map_err(|e| { - crate::error::TxError::FailedGovernaneProposalDeserialize( + crate::error::TxSubmitError::FailedGovernaneProposalDeserialize( e.to_string(), ) })?; @@ -558,7 +558,7 @@ impl InitProposal { self.tx.force, ) .map_err(|e| { - crate::error::TxError::InvalidProposal(e.to_string()) + crate::error::TxSubmitError::InvalidProposal(e.to_string()) })?; tx::build_default_proposal(context, self, proposal).await } diff --git a/sdk/src/error.rs b/sdk/src/error.rs index 0216e3a059..b04b50da13 100644 --- a/sdk/src/error.rs +++ b/sdk/src/error.rs @@ -29,7 +29,7 @@ pub enum Error { KeyRetrival(#[from] storage::Error), /// Transaction Errors #[error("{0}")] - Tx(#[from] TxError), + Tx(#[from] TxSubmitError), /// Event Errors #[error("{0}")] Event(#[from] EventError), @@ -117,7 +117,7 @@ pub enum EncodingError { /// Errors to do with transaction events. #[derive(Error, Debug, Clone)] -pub enum TxError { +pub enum TxSubmitError { /// Accepted tx timeout #[error("Timed out waiting for tx to be accepted")] AcceptTimeout, diff --git a/sdk/src/eth_bridge/bridge_pool.rs b/sdk/src/eth_bridge/bridge_pool.rs index 98c394019d..10a8376f2c 100644 --- a/sdk/src/eth_bridge/bridge_pool.rs +++ b/sdk/src/eth_bridge/bridge_pool.rs @@ -29,7 +29,7 @@ use super::{block_on_eth_sync, eth_sync_or_exit, BlockOnEthSync}; use crate::control_flow::install_shutdown_signal; use crate::control_flow::time::{Duration, Instant}; use crate::error::{ - EncodingError, Error, EthereumBridgeError, QueryError, TxError, + EncodingError, Error, EthereumBridgeError, QueryError, TxSubmitError, }; use crate::eth_bridge::ethers::abi::AbiDecode; use crate::internal_macros::echo_error; @@ -294,7 +294,7 @@ async fn validate_bridge_pool_tx( err_tokens.or(err_fees) }; if let Some((token, amount)) = maybe_balance_error { - return Err(Error::Tx(TxError::NegativeBalanceAfterTransfer( + return Err(Error::Tx(TxSubmitError::NegativeBalanceAfterTransfer( Box::new(transfer.transfer.sender), amount.to_string(), Box::new(token), diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index 88a7b9edf4..0e366a5bb8 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -1,13 +1,12 @@ extern crate alloc; -pub use namada_core::{ibc, tendermint, tendermint_proto}; -pub use namada_tx::proto; -use namada_tx::Tx; +pub use namada_core::{borsh, ibc, tendermint, tendermint_proto, types}; #[cfg(feature = "tendermint-rpc")] pub use tendermint_rpc; pub use { - bip39, borsh, masp_primitives, masp_proofs, namada_core as core, - namada_proof_of_stake as proof_of_stake, zeroize, + bip39, masp_primitives, masp_proofs, namada_account as account, + namada_governance as governance, namada_proof_of_stake as proof_of_stake, + namada_state as state, namada_storage as storage, zeroize, }; pub mod eth_bridge; @@ -45,6 +44,7 @@ use namada_core::types::key::*; use namada_core::types::masp::{TransferSource, TransferTarget}; use namada_core::types::token; use namada_tx::data::wrapper::GasLimit; +use namada_tx::Tx; use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use crate::io::Io; diff --git a/sdk/src/rpc.rs b/sdk/src/rpc.rs index 9f58e24217..71846f4577 100644 --- a/sdk/src/rpc.rs +++ b/sdk/src/rpc.rs @@ -36,7 +36,7 @@ use serde::Serialize; use crate::args::InputAmount; use crate::control_flow::time; -use crate::error::{EncodingError, Error, QueryError, TxError}; +use crate::error::{EncodingError, Error, QueryError, TxSubmitError}; use crate::events::Event; use crate::internal_macros::echo_error; use crate::io::Io; @@ -97,8 +97,12 @@ pub async fn query_tx_status( "Transaction status query deadline of {deadline:?} exceeded" ); match status { - TxEventQuery::Accepted(_) => Error::Tx(TxError::AcceptTimeout), - TxEventQuery::Applied(_) => Error::Tx(TxError::AppliedTimeout), + TxEventQuery::Accepted(_) => { + Error::Tx(TxSubmitError::AcceptTimeout) + } + TxEventQuery::Applied(_) => { + Error::Tx(TxSubmitError::AppliedTimeout) + } } }) } diff --git a/sdk/src/signing.rs b/sdk/src/signing.rs index 0ceb1cf8dc..f63f173d6b 100644 --- a/sdk/src/signing.rs +++ b/sdk/src/signing.rs @@ -40,8 +40,7 @@ use tokio::sync::RwLock; use super::masp::{ShieldedContext, ShieldedTransfer}; use crate::args::SdkTypes; -use crate::core::types::eth_bridge_pool::PendingTransfer; -use crate::error::{EncodingError, Error, TxError}; +use crate::error::{EncodingError, Error, TxSubmitError}; use crate::ibc::apps::transfer::types::msgs::transfer::MsgTransfer; use crate::ibc::primitives::proto::Any; use crate::io::*; @@ -58,6 +57,7 @@ use crate::tx::{ TX_UPDATE_STEWARD_COMMISSION, TX_VOTE_PROPOSAL, TX_WITHDRAW_WASM, VP_USER_WASM, }; +use crate::types::eth_bridge_pool::PendingTransfer; pub use crate::wallet::store::AddressVpType; use crate::wallet::{Wallet, WalletIo}; use crate::{args, display_line, rpc, MaybeSend, Namada}; @@ -325,7 +325,7 @@ pub async fn aux_signing_data( if let Some(account) = account { (Some(account.public_keys_map), account.threshold) } else { - return Err(Error::from(TxError::InvalidAccount( + return Err(Error::from(TxSubmitError::InvalidAccount( owner.encode(), ))); } @@ -337,7 +337,7 @@ pub async fn aux_signing_data( Some(owner @ Address::Internal(internal)) => match internal { InternalAddress::Masp => (None, 0u8), _ => { - return Err(Error::from(TxError::InvalidAccount( + return Err(Error::from(TxSubmitError::InvalidAccount( owner.encode(), ))); } @@ -354,7 +354,10 @@ pub async fn aux_signing_data( } else { match &args.wrapper_fee_payer { Some(keypair) => keypair.clone(), - None => public_keys.get(0).ok_or(TxError::InvalidFeePayer)?.clone(), + None => public_keys + .get(0) + .ok_or(TxSubmitError::InvalidFeePayer)? + .clone(), } }; @@ -399,7 +402,10 @@ pub async fn init_validator_signing_data( } else { match &args.wrapper_fee_payer { Some(keypair) => keypair.clone(), - None => public_keys.get(0).ok_or(TxError::InvalidFeePayer)?.clone(), + None => public_keys + .get(0) + .ok_or(TxSubmitError::InvalidFeePayer)? + .clone(), } }; @@ -583,7 +589,7 @@ pub async fn wrap_tx( && !args.force { return Err(Error::from( - TxError::FeeUnshieldingError(format!( + TxSubmitError::FeeUnshieldingError(format!( "Descriptions exceed the limit: found \ {descriptions}, limit \ {descriptions_limit}" @@ -597,7 +603,7 @@ pub async fn wrap_tx( Ok(None) => { if !args.force { return Err(Error::from( - TxError::FeeUnshieldingError( + TxSubmitError::FeeUnshieldingError( "Missing unshielding transaction" .to_string(), ), @@ -609,7 +615,7 @@ pub async fn wrap_tx( Err(e) => { if !args.force { return Err(Error::from( - TxError::FeeUnshieldingError(e.to_string()), + TxSubmitError::FeeUnshieldingError(e.to_string()), )); } @@ -625,12 +631,14 @@ pub async fn wrap_tx( let balance = context .format_amount(&token_addr, updated_balance) .await; - return Err(Error::from(TxError::BalanceTooLowForFees( - fee_payer_address, - token_addr, - fee_amount, - balance, - ))); + return Err(Error::from( + TxSubmitError::BalanceTooLowForFees( + fee_payer_address, + token_addr, + fee_amount, + balance, + ), + )); } None diff --git a/sdk/src/tx.rs b/sdk/src/tx.rs index cbbb5d3f74..350e96a6e2 100644 --- a/sdk/src/tx.rs +++ b/sdk/src/tx.rs @@ -1,4 +1,5 @@ //! SDK functions to construct different types of transactions + use std::borrow::Cow; use std::collections::{BTreeMap, HashMap, HashSet}; use std::fs::File; @@ -50,11 +51,11 @@ use namada_proof_of_stake::types::{CommissionPair, ValidatorState}; use namada_token::storage_key::balance_key; use namada_tx::data::pgf::UpdateStewardCommission; use namada_tx::data::{pos, ResultCode, TxResult}; -use namada_tx::{MaspBuilder, Tx}; +pub use namada_tx::{Signature, *}; use crate::args::{self, InputAmount}; use crate::control_flow::time; -use crate::error::{EncodingError, Error, QueryError, Result, TxError}; +use crate::error::{EncodingError, Error, QueryError, Result, TxSubmitError}; use crate::io::Io; use crate::masp::TransferErr::Build; use crate::masp::{make_asset_type, ShieldedContext, ShieldedTransfer}; @@ -315,7 +316,9 @@ pub async fn broadcast_tx( wrapper_hash, decrypted_hash, } => Ok((tx, wrapper_hash, decrypted_hash)), - TxBroadcastData::DryRun(tx) => Err(TxError::ExpectLiveRun(tx.clone())), + TxBroadcastData::DryRun(tx) => { + Err(TxSubmitError::ExpectLiveRun(tx.clone())) + } }?; tracing::debug!( @@ -347,7 +350,7 @@ pub async fn broadcast_tx( } Ok(response) } else { - Err(Error::from(TxError::TxBroadcast(RpcError::server( + Err(Error::from(TxSubmitError::TxBroadcast(RpcError::server( serde_json::to_string(&response).map_err(|err| { Error::from(EncodingError::Serde(err.to_string())) })?, @@ -373,7 +376,9 @@ pub async fn submit_tx( wrapper_hash, decrypted_hash, } => Ok((tx, wrapper_hash, decrypted_hash)), - TxBroadcastData::DryRun(tx) => Err(TxError::ExpectLiveRun(tx.clone())), + TxBroadcastData::DryRun(tx) => { + Err(TxSubmitError::ExpectLiveRun(tx.clone())) + } }?; // Broadcast the supplied transaction @@ -603,7 +608,9 @@ pub async fn build_validator_commission_change( "Invalid new commission rate, received {}", rate ); - return Err(Error::from(TxError::InvalidCommissionRate(*rate))); + return Err(Error::from(TxSubmitError::InvalidCommissionRate( + *rate, + ))); } let pipeline_epoch_minus_one = epoch + params.pipeline_len - 1; @@ -627,7 +634,7 @@ pub async fn build_validator_commission_change( ); if !tx_args.force { return Err(Error::from( - TxError::InvalidCommissionRate(*rate), + TxSubmitError::InvalidCommissionRate(*rate), )); } } @@ -642,7 +649,7 @@ pub async fn build_validator_commission_change( ); if !tx_args.force { return Err(Error::from( - TxError::InvalidCommissionRate(*rate), + TxSubmitError::InvalidCommissionRate(*rate), )); } } @@ -650,7 +657,7 @@ pub async fn build_validator_commission_change( None => { edisplay_line!(context.io(), "Error retrieving from storage"); if !tx_args.force { - return Err(Error::from(TxError::Retrieval)); + return Err(Error::from(TxSubmitError::Retrieval)); } } } @@ -660,7 +667,7 @@ pub async fn build_validator_commission_change( "The given address {validator} is not a validator." ); if !tx_args.force { - return Err(Error::from(TxError::InvalidValidatorAddress( + return Err(Error::from(TxSubmitError::InvalidValidatorAddress( validator, ))); } @@ -725,7 +732,7 @@ pub async fn build_validator_metadata_change( "Cannot remove a validator's email, which was implied by the \ empty string" ); - return Err(Error::from(TxError::InvalidEmail)); + return Err(Error::from(TxSubmitError::InvalidEmail)); } } @@ -738,7 +745,9 @@ pub async fn build_validator_metadata_change( rate ); if !tx_args.force { - return Err(Error::from(TxError::InvalidCommissionRate(*rate))); + return Err(Error::from(TxSubmitError::InvalidCommissionRate( + *rate, + ))); } } let pipeline_epoch_minus_one = epoch + params.pipeline_len - 1; @@ -762,7 +771,7 @@ pub async fn build_validator_metadata_change( ); if !tx_args.force { return Err(Error::from( - TxError::InvalidCommissionRate(*rate), + TxSubmitError::InvalidCommissionRate(*rate), )); } } @@ -777,7 +786,7 @@ pub async fn build_validator_metadata_change( ); if !tx_args.force { return Err(Error::from( - TxError::InvalidCommissionRate(*rate), + TxSubmitError::InvalidCommissionRate(*rate), )); } } @@ -785,7 +794,7 @@ pub async fn build_validator_metadata_change( None => { edisplay_line!(context.io(), "Error retrieving from storage"); if !tx_args.force { - return Err(Error::from(TxError::Retrieval)); + return Err(Error::from(TxSubmitError::Retrieval)); } } } @@ -838,18 +847,20 @@ pub async fn build_update_steward_commission( "The given address {} is not a steward.", &steward ); - return Err(Error::from(TxError::InvalidSteward(steward.clone()))); + return Err(Error::from(TxSubmitError::InvalidSteward( + steward.clone(), + ))); }; let commission = Commission::try_from(commission.as_ref()) - .map_err(|e| TxError::InvalidStewardCommission(e.to_string()))?; + .map_err(|e| TxSubmitError::InvalidStewardCommission(e.to_string()))?; if !commission.is_valid() && !tx_args.force { edisplay_line!( context.io(), "The sum of all percentage must not be greater than 1." ); - return Err(Error::from(TxError::InvalidStewardCommission( + return Err(Error::from(TxSubmitError::InvalidStewardCommission( "Commission sum is greater than 1.".to_string(), ))); } @@ -896,7 +907,9 @@ pub async fn build_resign_steward( "The given address {} is not a steward.", &steward ); - return Err(Error::from(TxError::InvalidSteward(steward.clone()))); + return Err(Error::from(TxSubmitError::InvalidSteward( + steward.clone(), + ))); }; build( @@ -937,7 +950,7 @@ pub async fn build_unjail_validator( &validator ); if !tx_args.force { - return Err(Error::from(TxError::InvalidValidatorAddress( + return Err(Error::from(TxSubmitError::InvalidValidatorAddress( validator.clone(), ))); } @@ -961,9 +974,9 @@ pub async fn build_unjail_validator( &validator ); if !tx_args.force { - return Err(Error::from(TxError::ValidatorNotCurrentlyJailed( - validator.clone(), - ))); + return Err(Error::from( + TxSubmitError::ValidatorNotCurrentlyJailed(validator.clone()), + )); } } @@ -984,7 +997,7 @@ pub async fn build_unjail_validator( ); if !tx_args.force { return Err(Error::from( - TxError::ValidatorFrozenFromUnjailing( + TxSubmitError::ValidatorFrozenFromUnjailing( validator.clone(), ), )); @@ -1040,7 +1053,7 @@ pub async fn build_deactivate_validator( &validator ); if !tx_args.force { - return Err(Error::from(TxError::InvalidValidatorAddress( + return Err(Error::from(TxSubmitError::InvalidValidatorAddress( validator.clone(), ))); } @@ -1065,7 +1078,7 @@ pub async fn build_deactivate_validator( &pipeline_epoch ); if !tx_args.force { - return Err(Error::from(TxError::ValidatorInactive( + return Err(Error::from(TxSubmitError::ValidatorInactive( validator.clone(), pipeline_epoch, ))); @@ -1111,7 +1124,7 @@ pub async fn build_reactivate_validator( &validator ); if !tx_args.force { - return Err(Error::from(TxError::InvalidValidatorAddress( + return Err(Error::from(TxSubmitError::InvalidValidatorAddress( validator.clone(), ))); } @@ -1134,7 +1147,7 @@ pub async fn build_reactivate_validator( &epoch ); if !tx_args.force { - return Err(Error::from(TxError::ValidatorNotInactive( + return Err(Error::from(TxSubmitError::ValidatorNotInactive( validator.clone(), epoch, ))); @@ -1175,7 +1188,7 @@ pub async fn build_redelegation( be requested." ); if !tx_args.force { - return Err(Error::from(TxError::RedelegationIsZero)); + return Err(Error::from(TxSubmitError::RedelegationIsZero)); } } @@ -1198,7 +1211,7 @@ pub async fn build_redelegation( &owner ); if !tx_args.force { - return Err(Error::from(TxError::RedelegatorIsValidator( + return Err(Error::from(TxSubmitError::RedelegatorIsValidator( owner.clone(), ))); } @@ -1212,7 +1225,7 @@ pub async fn build_redelegation( Redelegation is not allowed to the same validator." ); if !tx_args.force { - return Err(Error::from(TxError::RedelegationSrcEqDest)); + return Err(Error::from(TxSubmitError::RedelegationSrcEqDest)); } } @@ -1242,10 +1255,12 @@ pub async fn build_redelegation( &owner ); if !tx_args.force { - return Err(Error::from(TxError::IncomingRedelIsStillSlashable( - src_validator.clone(), - owner.clone(), - ))); + return Err(Error::from( + TxSubmitError::IncomingRedelIsStillSlashable( + src_validator.clone(), + owner.clone(), + ), + )); } } @@ -1269,7 +1284,7 @@ pub async fn build_redelegation( &dest_validator, &pipeline_epoch ); - return Err(Error::from(TxError::ValidatorInactive( + return Err(Error::from(TxSubmitError::ValidatorInactive( dest_validator.clone(), pipeline_epoch, ))); @@ -1290,10 +1305,12 @@ pub async fn build_redelegation( bond_amount.to_string_native() ); if !tx_args.force { - return Err(Error::from(TxError::RedelegationAmountTooLarge( - redel_amount.to_string_native(), - bond_amount.to_string_native(), - ))); + return Err(Error::from( + TxSubmitError::RedelegationAmountTooLarge( + redel_amount.to_string_native(), + bond_amount.to_string_native(), + ), + )); } } else { display_line!( @@ -1389,7 +1406,7 @@ pub async fn build_withdraw( ); rpc::query_and_print_unbonds(context, &bond_source, &validator).await?; if !tx_args.force { - return Err(Error::from(TxError::NoUnbondReady(epoch))); + return Err(Error::from(TxSubmitError::NoUnbondReady(epoch))); } } else { display_line!( @@ -1485,7 +1502,7 @@ pub async fn build_unbond( requested." ); if !tx_args.force { - return Err(Error::from(TxError::BondIsZero)); + return Err(Error::from(TxSubmitError::BondIsZero)); } } @@ -1607,7 +1624,7 @@ pub async fn query_unbonds( occurred" ); } else { - return Err(Error::from(TxError::UnboundError)); + return Err(Error::from(TxSubmitError::UnboundError)); } } std::cmp::Ordering::Equal => { @@ -1659,7 +1676,7 @@ pub async fn build_bond( requested." ); if !tx_args.force { - return Err(Error::from(TxError::BondIsZero)); + return Err(Error::from(TxSubmitError::BondIsZero)); } } @@ -1697,7 +1714,7 @@ pub async fn build_bond( &validator, &pipeline_epoch ); - return Err(Error::from(TxError::ValidatorInactive( + return Err(Error::from(TxSubmitError::ValidatorInactive( validator.clone(), pipeline_epoch, ))); @@ -1777,7 +1794,7 @@ pub async fn build_default_proposal( .await?; let init_proposal_data = InitProposalData::try_from(proposal.clone()) - .map_err(|e| TxError::InvalidProposal(e.to_string()))?; + .map_err(|e| TxSubmitError::InvalidProposal(e.to_string()))?; let push_data = |tx_builder: &mut Tx, init_proposal_data: &mut InitProposalData| { @@ -1830,7 +1847,7 @@ pub async fn build_vote_proposal( .await?; let proposal_vote = ProposalVote::try_from(vote.clone()) - .map_err(|_| TxError::InvalidProposalVote)?; + .map_err(|_| TxSubmitError::InvalidProposalVote)?; let proposal_id = proposal_id.ok_or_else(|| { Error::Other("Proposal id must be defined.".to_string()) @@ -1840,13 +1857,15 @@ pub async fn build_vote_proposal( { proposal } else { - return Err(Error::from(TxError::ProposalDoesNotExist(proposal_id))); + return Err(Error::from(TxSubmitError::ProposalDoesNotExist( + proposal_id, + ))); }; let storage_vote = StorageProposalVote::build(&proposal_vote, &proposal.r#type) .ok_or_else(|| { - Error::from(TxError::Other( + Error::from(TxSubmitError::Other( "Should be able to build the proposal vote".to_string(), )) })?; @@ -1857,9 +1876,9 @@ pub async fn build_vote_proposal( if tx.force { eprintln!("Invalid proposal {} vote period.", proposal_id); } else { - return Err(Error::from(TxError::InvalidProposalVotingPeriod( - proposal_id, - ))); + return Err(Error::from( + TxSubmitError::InvalidProposalVotingPeriod(proposal_id), + )); } } @@ -1917,7 +1936,7 @@ pub async fn build_pgf_funding_proposal( .await?; let init_proposal_data = InitProposalData::try_from(proposal.clone()) - .map_err(|e| TxError::InvalidProposal(e.to_string()))?; + .map_err(|e| TxSubmitError::InvalidProposal(e.to_string()))?; let add_section = |tx: &mut Tx, data: &mut InitProposalData| { let (_, extra_section_hash) = @@ -1962,7 +1981,7 @@ pub async fn build_pgf_stewards_proposal( .await?; let init_proposal_data = InitProposalData::try_from(proposal.clone()) - .map_err(|e| TxError::InvalidProposal(e.to_string()))?; + .map_err(|e| TxSubmitError::InvalidProposal(e.to_string()))?; let add_section = |tx: &mut Tx, data: &mut InitProposalData| { let (_, extra_section_hash) = @@ -2138,7 +2157,7 @@ pub async fn build_ibc_transfer( let any_msg = message.to_any(); let mut data = vec![]; prost::Message::encode(&any_msg, &mut data) - .map_err(TxError::EncodeFailure)?; + .map_err(TxSubmitError::EncodeFailure)?; data } }; @@ -2431,14 +2450,16 @@ async fn construct_shielded_parts( Ok(Some(stx)) => stx, Ok(None) => return Ok(None), Err(Build(builder::Error::InsufficientFunds(_))) => { - return Err(TxError::NegativeBalanceAfterTransfer( + return Err(TxSubmitError::NegativeBalanceAfterTransfer( Box::new(source.effective_address()), amount.amount().to_string_native(), Box::new(token.clone()), ) .into()); } - Err(err) => return Err(TxError::MaspError(err.to_string()).into()), + Err(err) => { + return Err(TxSubmitError::MaspError(err.to_string()).into()); + } }; // Get the decoded asset types used in the transaction to give offline @@ -2472,7 +2493,9 @@ pub async fn build_init_account( if public_keys.len() == 1 { 1u8 } else { - return Err(Error::from(TxError::MissingAccountThreshold)); + return Err(Error::from( + TxSubmitError::MissingAccountThreshold, + )); } } }; @@ -2533,7 +2556,9 @@ pub async fn build_update_account( } else if tx_args.force { addr.clone() } else { - return Err(Error::from(TxError::LocationDoesNotExist(addr.clone()))); + return Err(Error::from(TxSubmitError::LocationDoesNotExist( + addr.clone(), + ))); }; let vp_code_hash = match vp_code_path { @@ -2679,7 +2704,7 @@ pub async fn gen_ibc_shielded_transfer( validated_amount, ) .await - .map_err(|err| TxError::MaspError(err.to_string()))?; + .map_err(|err| TxSubmitError::MaspError(err.to_string()))?; let transfer = token::Transfer { source: source.clone(), @@ -2767,12 +2792,12 @@ async fn expect_dry_broadcast( tx, wrapper_hash: _, decrypted_hash: _, - } => Err(Error::from(TxError::ExpectDryRun(tx))), + } => Err(Error::from(TxSubmitError::ExpectDryRun(tx))), } } fn lift_rpc_error(res: std::result::Result) -> Result { - res.map_err(|err| Error::from(TxError::TxBroadcast(err))) + res.map_err(|err| Error::from(TxSubmitError::TxBroadcast(err))) } /// Returns the given validator if the given address is a validator, @@ -2794,7 +2819,9 @@ async fn known_validator_or_err( ); Ok(validator) } else { - Err(Error::from(TxError::InvalidValidatorAddress(validator))) + Err(Error::from(TxSubmitError::InvalidValidatorAddress( + validator, + ))) } } else { Ok(validator) @@ -2838,7 +2865,7 @@ async fn source_exists_or_err( let message = format!("The source address {} doesn't exist on chain.", token); address_exists_or_err(token, force, context, message, |err| { - Error::from(TxError::SourceDoesNotExist(err)) + Error::from(TxSubmitError::SourceDoesNotExist(err)) }) .await } @@ -2854,7 +2881,7 @@ async fn target_exists_or_err( let message = format!("The target address {} doesn't exist on chain.", token); address_exists_or_err(token, force, context, message, |err| { - Error::from(TxError::TargetLocationDoesNotExist(err)) + Error::from(TxSubmitError::TargetLocationDoesNotExist(err)) }) .await } @@ -2892,7 +2919,7 @@ async fn check_balance_too_low_err( ); Ok(token::Amount::zero()) } else { - Err(Error::from(TxError::BalanceTooLow( + Err(Error::from(TxSubmitError::BalanceTooLow( source.clone(), token.clone(), amount.to_string_native(), @@ -2913,7 +2940,7 @@ async fn check_balance_too_low_err( ); Ok(token::Amount::zero()) } else { - Err(Error::from(TxError::NoBalanceForToken( + Err(Error::from(TxSubmitError::NoBalanceForToken( source.clone(), token.clone(), ))) From eb146ce72e98c52690d75d046e91583fd137ad17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 5 Jan 2024 09:42:32 +0000 Subject: [PATCH 079/118] wasm_for_tests: fix build --- vp_prelude/src/lib.rs | 4 + wasm_for_tests/wasm_source/Cargo.lock | 249 +++++++++++++++++++++++++- wasm_for_tests/wasm_source/src/lib.rs | 2 +- 3 files changed, 250 insertions(+), 5 deletions(-) diff --git a/vp_prelude/src/lib.rs b/vp_prelude/src/lib.rs index e73bf84d56..562a64e01a 100644 --- a/vp_prelude/src/lib.rs +++ b/vp_prelude/src/lib.rs @@ -361,6 +361,10 @@ impl<'view> VpEnv<'view> for Ctx { unsafe { namada_vp_charge_gas(used_gas) }; Ok(()) } + + fn get_pred_epochs(&self) -> namada_storage::Result { + todo!() + } } impl StorageRead for CtxPreStorageRead<'_> { diff --git a/wasm_for_tests/wasm_source/Cargo.lock b/wasm_for_tests/wasm_source/Cargo.lock index 1865570861..b5ac0953fd 100644 --- a/wasm_for_tests/wasm_source/Cargo.lock +++ b/wasm_for_tests/wasm_source/Cargo.lock @@ -3323,10 +3323,22 @@ dependencies = [ "loupe", "masp_primitives", "masp_proofs", + "namada_account", "namada_core", "namada_ethereum_bridge", + "namada_gas", + "namada_governance", + "namada_ibc", + "namada_parameters", "namada_proof_of_stake", "namada_sdk", + "namada_state", + "namada_storage", + "namada_token", + "namada_tx", + "namada_tx_env", + "namada_vote_ext", + "namada_vp_env", "num256", "orion", "owo-colors", @@ -3364,6 +3376,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "namada_account" +version = "0.29.0" +dependencies = [ + "borsh", + "namada_core", + "namada_macros", + "namada_storage", + "serde", +] + [[package]] name = "namada_core" version = "0.29.0" @@ -3411,7 +3434,6 @@ dependencies = [ "tendermint-proto", "thiserror", "tiny-keccak", - "tonic-build", "tracing", "uint", "zeroize", @@ -3422,19 +3444,73 @@ name = "namada_ethereum_bridge" version = "0.29.0" dependencies = [ "borsh", - "borsh-ext", + "ethabi", "ethers", "eyre", "itertools 0.10.5", "namada_core", "namada_macros", + "namada_parameters", "namada_proof_of_stake", + "namada_state", + "namada_storage", + "namada_trans_token", + "namada_tx", + "namada_vote_ext", "rand 0.8.5", "serde", "serde_json", "tendermint", "tendermint-proto", "tendermint-rpc", + "thiserror", + "tracing", +] + +[[package]] +name = "namada_gas" +version = "0.29.0" +dependencies = [ + "borsh", + "namada_core", + "namada_parameters", + "namada_storage", + "serde", + "thiserror", +] + +[[package]] +name = "namada_governance" +version = "0.29.0" +dependencies = [ + "borsh", + "namada_core", + "namada_macros", + "namada_parameters", + "namada_storage", + "namada_trans_token", + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "namada_ibc" +version = "0.29.0" +dependencies = [ + "borsh", + "ibc", + "ibc-derive", + "ics23", + "namada_core", + "namada_parameters", + "namada_storage", + "namada_trans_token", + "primitive-types", + "prost 0.12.3", + "sha2 0.9.9", + "thiserror", "tracing", ] @@ -3447,6 +3523,30 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "namada_merkle_tree" +version = "0.29.0" +dependencies = [ + "borsh", + "eyre", + "ics23", + "namada_core", + "prost 0.12.3", + "sparse-merkle-tree", + "thiserror", +] + +[[package]] +name = "namada_parameters" +version = "0.29.0" +dependencies = [ + "borsh", + "namada_core", + "namada_macros", + "namada_storage", + "thiserror", +] + [[package]] name = "namada_proof_of_stake" version = "0.29.0" @@ -3454,7 +3554,12 @@ dependencies = [ "borsh", "data-encoding", "derivative", + "namada_account", "namada_core", + "namada_governance", + "namada_parameters", + "namada_storage", + "namada_trans_token", "once_cell", "proptest", "serde", @@ -3480,9 +3585,18 @@ dependencies = [ "lazy_static", "masp_primitives", "masp_proofs", + "namada_account", "namada_core", "namada_ethereum_bridge", + "namada_governance", + "namada_ibc", + "namada_parameters", "namada_proof_of_stake", + "namada_state", + "namada_storage", + "namada_token", + "namada_tx", + "namada_vote_ext", "num256", "orion", "owo-colors", @@ -3508,6 +3622,49 @@ dependencies = [ "zeroize", ] +[[package]] +name = "namada_shielded_token" +version = "0.29.0" +dependencies = [ + "masp_primitives", + "namada_core", + "namada_parameters", + "namada_state", + "namada_storage", + "namada_trans_token", + "tracing", +] + +[[package]] +name = "namada_state" +version = "0.29.0" +dependencies = [ + "borsh", + "ics23", + "itertools 0.10.5", + "namada_core", + "namada_gas", + "namada_merkle_tree", + "namada_parameters", + "namada_storage", + "namada_trans_token", + "namada_tx", + "sha2 0.9.9", + "sparse-merkle-tree", + "thiserror", + "tiny-keccak", + "tracing", +] + +[[package]] +name = "namada_storage" +version = "0.29.0" +dependencies = [ + "itertools 0.10.5", + "namada_core", + "thiserror", +] + [[package]] name = "namada_test_utils" version = "0.29.0" @@ -3551,16 +3708,71 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "namada_token" +version = "0.29.0" +dependencies = [ + "namada_core", + "namada_shielded_token", + "namada_storage", + "namada_trans_token", +] + +[[package]] +name = "namada_trans_token" +version = "0.29.0" +dependencies = [ + "namada_core", + "namada_storage", +] + +[[package]] +name = "namada_tx" +version = "0.29.0" +dependencies = [ + "ark-bls12-381", + "borsh", + "data-encoding", + "masp_primitives", + "namada_core", + "namada_gas", + "num-derive", + "num-traits", + "prost 0.12.3", + "prost-types 0.12.3", + "serde", + "serde_json", + "sha2 0.9.9", + "thiserror", + "tonic-build", +] + +[[package]] +name = "namada_tx_env" +version = "0.29.0" +dependencies = [ + "namada_core", + "namada_storage", +] + [[package]] name = "namada_tx_prelude" version = "0.29.0" dependencies = [ "borsh", - "borsh-ext", "masp_primitives", + "namada_account", "namada_core", + "namada_ethereum_bridge", + "namada_governance", + "namada_ibc", "namada_macros", + "namada_parameters", "namada_proof_of_stake", + "namada_storage", + "namada_token", + "namada_tx", + "namada_tx_env", "namada_vm_env", "sha2 0.9.9", "thiserror", @@ -3575,16 +3787,45 @@ dependencies = [ "namada_core", ] +[[package]] +name = "namada_vote_ext" +version = "0.29.0" +dependencies = [ + "borsh", + "namada_core", + "namada_tx", + "serde", +] + +[[package]] +name = "namada_vp_env" +version = "0.29.0" +dependencies = [ + "derivative", + "masp_primitives", + "namada_core", + "namada_storage", + "namada_tx", + "thiserror", +] + [[package]] name = "namada_vp_prelude" version = "0.29.0" dependencies = [ "borsh", - "borsh-ext", + "namada_account", "namada_core", + "namada_governance", + "namada_ibc", "namada_macros", + "namada_parameters", "namada_proof_of_stake", + "namada_storage", + "namada_token", + "namada_tx", "namada_vm_env", + "namada_vp_env", "sha2 0.9.9", "thiserror", ] diff --git a/wasm_for_tests/wasm_source/src/lib.rs b/wasm_for_tests/wasm_source/src/lib.rs index 25f4d669ab..aa352a6bc6 100644 --- a/wasm_for_tests/wasm_source/src/lib.rs +++ b/wasm_for_tests/wasm_source/src/lib.rs @@ -187,7 +187,7 @@ pub mod main { _keys_changed: BTreeSet, _verifiers: BTreeSet
, ) -> VpResult { - use validity_predicate::EvalVp; + use namada_tx_prelude::transaction::eval_vp::EvalVp; let EvalVp { vp_code_hash, input, From 37e20ff953a24a133948b56573bd55307bdb30fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 5 Jan 2024 10:00:24 +0000 Subject: [PATCH 080/118] wasm: update lock file --- wasm/Cargo.lock | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index 79fb909b19..cb73f96200 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -3323,10 +3323,22 @@ dependencies = [ "loupe", "masp_primitives", "masp_proofs", + "namada_account", "namada_core", "namada_ethereum_bridge", + "namada_gas", + "namada_governance", + "namada_ibc", + "namada_parameters", "namada_proof_of_stake", "namada_sdk", + "namada_state", + "namada_storage", + "namada_token", + "namada_tx", + "namada_tx_env", + "namada_vote_ext", + "namada_vp_env", "num256", "orion", "owo-colors", From 2ed663684f42e59c63df32fb2d9021130e3bf845 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 5 Jan 2024 10:40:07 +0000 Subject: [PATCH 081/118] core: fix tests --- Cargo.lock | 4 ---- core/Cargo.toml | 5 ----- core/src/lib.rs | 5 ----- core/src/types/eth_abi.rs | 24 ------------------------ 4 files changed, 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a1b236692e..6c05538967 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4381,15 +4381,11 @@ dependencies = [ name = "namada_core" version = "0.29.0" dependencies = [ - "ark-bls12-381", - "ark-serialize", - "assert_matches", "bech32 0.8.1", "borsh", "borsh-ext", "chrono", "data-encoding", - "derivative", "ed25519-consensus 1.2.1", "ethabi", "ethbridge-structs", diff --git a/core/Cargo.toml b/core/Cargo.toml index ec181f34a8..39c80a7fea 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -30,15 +30,12 @@ testing = [ [dependencies] namada_macros = {path = "../macros"} -ark-bls12-381.workspace = true -ark-serialize.workspace = true arse-merkle-tree.workspace = true bech32.workspace = true borsh.workspace = true borsh-ext.workspace = true chrono.workspace = true data-encoding.workspace = true -derivative.workspace = true ed25519-consensus.workspace = true ethabi.workspace = true ethbridge-structs.workspace = true @@ -49,7 +46,6 @@ ibc-testkit = {workspace = true, optional = true} ics23.workspace = true impl-num-traits = "0.1.2" index-set.workspace = true -itertools.workspace = true k256.workspace = true masp_primitives.workspace = true num256.workspace = true @@ -77,7 +73,6 @@ uint = "0.9.5" zeroize.workspace = true [dev-dependencies] -assert_matches.workspace = true pretty_assertions.workspace = true proptest.workspace = true rand.workspace = true diff --git a/core/src/lib.rs b/core/src/lib.rs index 7aaad7beca..c0d837517b 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -17,8 +17,3 @@ pub mod borsh { pub use borsh::*; pub use borsh_ext::*; } - -// A handy macro for tests -#[cfg(test)] -#[macro_use] -extern crate assert_matches; diff --git a/core/src/types/eth_abi.rs b/core/src/types/eth_abi.rs index b7544cf1aa..886b5b9c46 100644 --- a/core/src/types/eth_abi.rs +++ b/core/src/types/eth_abi.rs @@ -130,7 +130,6 @@ mod tests { use super::*; use crate::types::ethereum_events::EthAddress; - use crate::types::vote_extensions::validator_set_update::ValidatorSetArgs; /// Checks if we get the same result as `abi.encode`, for some given /// input data. @@ -188,27 +187,4 @@ mod tests { let encoded = ethabi::encode(&[Token::Address(address.0.into())]); assert_eq!(expected, encoded); } - - #[test] - fn test_abi_encode_valset_args() { - let valset_update = ValidatorSetArgs { - validators: vec![ - EthAddress::from_str( - "0x241D37B7Cf5233b3b0b204321420A86e8f7bfdb5", - ) - .expect("Test failed"), - ], - voting_powers: vec![8828299u64.into()], - epoch: 0.into(), - }; - let encoded = valset_update.encode().into_inner(); - let encoded = HEXLOWER.encode(&encoded); - let expected = "000000000000000000000000000000000000000000000000000000000000002\ - 000000000000000000000000000000000000000000000000000000000000000\ - 400000000000000000000000000000000000000000000000000000000000000\ - 000000000000000000000000000000000000000000000000000000000000000\ - 0001241d37b7cf5233b3b0b204321420a86e8f7bfdb50000000000000000008\ - 6b58b"; - assert_eq!(expected, encoded); - } } From 6afb16922659d93ad88f205c084dd9e9d1cc7f4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 5 Jan 2024 14:50:15 +0000 Subject: [PATCH 082/118] move types, trait DB and MockDB impl from state to storage --- state/src/lib.rs | 247 +------------------------ state/src/traits.rs | 24 --- storage/src/db.rs | 288 +++++++++++++++++++++++++++++ storage/src/lib.rs | 6 + {state => storage}/src/mockdb.rs | 2 +- {state => storage}/src/tx_queue.rs | 0 {state => storage}/src/types.rs | 0 7 files changed, 296 insertions(+), 271 deletions(-) delete mode 100644 state/src/traits.rs create mode 100644 storage/src/db.rs rename {state => storage}/src/mockdb.rs (99%) rename {state => storage}/src/tx_queue.rs (100%) rename {state => storage}/src/types.rs (100%) diff --git a/state/src/lib.rs b/state/src/lib.rs index bd7672fce5..448fcfbe01 100644 --- a/state/src/lib.rs +++ b/state/src/lib.rs @@ -1,8 +1,5 @@ //! Ledger's state storage with key-value backed store and a merkle tree -pub mod mockdb; -pub mod tx_queue; -pub mod types; pub mod wl_storage; pub mod write_log; @@ -38,6 +35,7 @@ pub use namada_merkle_tree::{ }; use namada_merkle_tree::{Error as MerkleTreeError, MerkleRoot}; use namada_parameters::{self, EpochDuration, Parameters}; +pub use namada_storage::*; use thiserror::Error; use tx_queue::{ExpiredTxsQueue, TxQueue}; pub use wl_storage::{ @@ -168,249 +166,6 @@ pub enum Error { InvalidCodeHash(HashError), } -/// The block's state as stored in the database. -pub struct BlockStateRead { - /// Merkle tree stores - pub merkle_tree_stores: MerkleTreeStoresRead, - /// Hash of the block - pub hash: BlockHash, - /// Height of the block - pub height: BlockHeight, - /// Time of the block - pub time: DateTimeUtc, - /// Epoch of the block - pub epoch: Epoch, - /// Predecessor block epochs - pub pred_epochs: Epochs, - /// Minimum block height at which the next epoch may start - pub next_epoch_min_start_height: BlockHeight, - /// Minimum block time at which the next epoch may start - pub next_epoch_min_start_time: DateTimeUtc, - /// Update epoch delay - pub update_epoch_blocks_delay: Option, - /// Established address generator - pub address_gen: EstablishedAddressGen, - /// Results of applying transactions - pub results: BlockResults, - /// The conversion state - pub conversion_state: ConversionState, - /// Wrapper txs to be decrypted in the next block proposal - pub tx_queue: TxQueue, - /// The latest block height on Ethereum processed, if - /// the bridge is enabled. - pub ethereum_height: Option, - /// The queue of Ethereum events to be processed in order. - pub eth_events_queue: EthEventsQueue, -} - -/// The block's state to write into the database. -pub struct BlockStateWrite<'a> { - /// Merkle tree stores - pub merkle_tree_stores: MerkleTreeStoresWrite<'a>, - /// Header of the block - pub header: Option<&'a Header>, - /// Hash of the block - pub hash: &'a BlockHash, - /// Height of the block - pub height: BlockHeight, - /// Time of the block - pub time: DateTimeUtc, - /// Epoch of the block - pub epoch: Epoch, - /// Predecessor block epochs - pub pred_epochs: &'a Epochs, - /// Minimum block height at which the next epoch may start - pub next_epoch_min_start_height: BlockHeight, - /// Minimum block time at which the next epoch may start - pub next_epoch_min_start_time: DateTimeUtc, - /// Update epoch delay - pub update_epoch_blocks_delay: Option, - /// Established address generator - pub address_gen: &'a EstablishedAddressGen, - /// Results of applying transactions - pub results: &'a BlockResults, - /// The conversion state - pub conversion_state: &'a ConversionState, - /// Wrapper txs to be decrypted in the next block proposal - pub tx_queue: &'a TxQueue, - /// The latest block height on Ethereum processed, if - /// the bridge is enabled. - pub ethereum_height: Option<&'a ethereum_structs::BlockHeight>, - /// The queue of Ethereum events to be processed in order. - pub eth_events_queue: &'a EthEventsQueue, -} - -/// A database backend. -pub trait DB: std::fmt::Debug { - /// A DB's cache - type Cache; - /// A handle for batch writes - type WriteBatch: DBWriteBatch; - - /// Open the database from provided path - fn open( - db_path: impl AsRef, - cache: Option<&Self::Cache>, - ) -> Self; - - /// Flush data on the memory to persistent them - fn flush(&self, wait: bool) -> Result<()>; - - /// Read the last committed block's metadata - fn read_last_block(&self) -> Result>; - - /// Write block's metadata. Merkle tree sub-stores are committed only when - /// `is_full_commit` is `true` (typically on a beginning of a new epoch). - fn add_block_to_batch( - &self, - state: BlockStateWrite, - batch: &mut Self::WriteBatch, - is_full_commit: bool, - ) -> Result<()>; - - /// Read the block header with the given height from the DB - fn read_block_header(&self, height: BlockHeight) -> Result>; - - /// Read the merkle tree stores with the given epoch. If a store_type is - /// given, it reads only the the specified tree. Otherwise, it reads all - /// trees. - fn read_merkle_tree_stores( - &self, - epoch: Epoch, - base_height: BlockHeight, - store_type: Option, - ) -> Result>; - - /// Check if the given replay protection entry exists - fn has_replay_protection_entry(&self, hash: &Hash) -> Result; - - /// Read the latest value for account subspace key from the DB - fn read_subspace_val(&self, key: &Key) -> Result>>; - - /// Read the value for account subspace key at the given height from the DB. - /// In our `PersistentStorage` (rocksdb), to find a value from arbitrary - /// height requires looking for diffs from the given `height`, possibly - /// up to the `last_height`. - fn read_subspace_val_with_height( - &self, - key: &Key, - height: BlockHeight, - last_height: BlockHeight, - ) -> Result>>; - - /// Write the value with the given height and account subspace key to the - /// DB. Returns the size difference from previous value, if any, or the - /// size of the value otherwise. - fn write_subspace_val( - &mut self, - height: BlockHeight, - key: &Key, - value: impl AsRef<[u8]>, - ) -> Result; - - /// Delete the value with the given height and account subspace key from the - /// DB. Returns the size of the removed value, if any, 0 if no previous - /// value was found. - fn delete_subspace_val( - &mut self, - height: BlockHeight, - key: &Key, - ) -> Result; - - /// Start write batch. - fn batch() -> Self::WriteBatch; - - /// Execute write batch. - fn exec_batch(&mut self, batch: Self::WriteBatch) -> Result<()>; - - /// Batch write the value with the given height and account subspace key to - /// the DB. Returns the size difference from previous value, if any, or - /// the size of the value otherwise. - fn batch_write_subspace_val( - &self, - batch: &mut Self::WriteBatch, - height: BlockHeight, - key: &Key, - value: impl AsRef<[u8]>, - ) -> Result; - - /// Batch delete the value with the given height and account subspace key - /// from the DB. Returns the size of the removed value, if any, 0 if no - /// previous value was found. - fn batch_delete_subspace_val( - &self, - batch: &mut Self::WriteBatch, - height: BlockHeight, - key: &Key, - ) -> Result; - - /// Prune Merkle tree stores at the given epoch - fn prune_merkle_tree_store( - &mut self, - batch: &mut Self::WriteBatch, - store_type: &StoreType, - pruned_epoch: Epoch, - ) -> Result<()>; - - /// Read the signed nonce of Bridge Pool - fn read_bridge_pool_signed_nonce( - &self, - height: BlockHeight, - last_height: BlockHeight, - ) -> Result>; - - /// Write a replay protection entry - fn write_replay_protection_entry( - &mut self, - batch: &mut Self::WriteBatch, - key: &Key, - ) -> Result<()>; - - /// Delete a replay protection entry - fn delete_replay_protection_entry( - &mut self, - batch: &mut Self::WriteBatch, - key: &Key, - ) -> Result<()>; -} - -/// A database prefix iterator. -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: Option<&Key>) -> Self::PrefixIter; - - /// Read results subspace key value pairs from the DB - fn iter_results(&'iter self) -> Self::PrefixIter; - - /// Read subspace old diffs at a given height - fn iter_old_diffs( - &'iter self, - height: BlockHeight, - prefix: Option<&'iter Key>, - ) -> Self::PrefixIter; - - /// Read subspace new diffs at a given height - fn iter_new_diffs( - &'iter self, - height: BlockHeight, - prefix: Option<&'iter Key>, - ) -> Self::PrefixIter; - - /// Read replay protection storage from the last block - fn iter_replay_protection(&'iter self) -> Self::PrefixIter; -} - -/// Atomic batch write. -pub trait DBWriteBatch {} - impl Storage where D: DB + for<'iter> DBIter<'iter>, diff --git a/state/src/traits.rs b/state/src/traits.rs deleted file mode 100644 index 27296b7aa6..0000000000 --- a/state/src/traits.rs +++ /dev/null @@ -1,24 +0,0 @@ -//! Traits needed to provide a uniform interface over -//! all the different Merkle trees used for storage -use std::convert::TryInto; -use std::fmt; - -use arse_merkle_tree::traits::{Hasher, Value}; -use arse_merkle_tree::H256; -use ics23::commitment_proof::Proof as Ics23Proof; -use ics23::{CommitmentProof, ExistenceProof}; -use namada_core::borsh::{BorshDeserialize, BorshSerializeExt}; -use namada_core::types::hash::StorageHasher; -use namada_core::types::storage::Key; -use sha2::{Digest, Sha256}; -use tiny_keccak::Hasher as KHasher; - -use super::ics23_specs; -use super::merkle_tree::{Amt, Error, MerkleRoot, Smt}; -use crate::ledger::eth_bridge::storage::bridge_pool::BridgePoolTree; -use crate::ledger::storage::merkle_tree::StorageBytes; -use crate::types::eth_bridge_pool::PendingTransfer; -use crate::types::hash::Hash; -use crate::types::storage::{ - BlockHeight, Key, MembershipProof, StringKey, TreeBytes, IBC_KEY_LIMIT, -}; diff --git a/storage/src/db.rs b/storage/src/db.rs new file mode 100644 index 0000000000..05798f3232 --- /dev/null +++ b/storage/src/db.rs @@ -0,0 +1,288 @@ +use std::fmt::Debug; + +use namada_core::types::address::EstablishedAddressGen; +use namada_core::types::ethereum_structs; +use namada_core::types::hash::{Error as HashError, Hash}; +use namada_core::types::storage::{ + BlockHash, BlockHeight, BlockResults, Epoch, Epochs, EthEventsQueue, + Header, Key, +}; +use namada_core::types::time::DateTimeUtc; +use namada_core::types::token::ConversionState; +use namada_core::types::uint::Uint; +use namada_merkle_tree::{ + Error as MerkleTreeError, MerkleTreeStoresRead, MerkleTreeStoresWrite, + StoreType, +}; +use thiserror::Error; + +use crate::tx_queue::TxQueue; + +#[allow(missing_docs)] +#[derive(Error, Debug)] +pub enum Error { + #[error("TEMPORARY error: {error}")] + Temporary { error: String }, + #[error("Found an unknown key: {key}")] + UnknownKey { key: String }, + #[error("Storage key error {0}")] + KeyError(namada_core::types::storage::Error), + #[error("Coding error: {0}")] + CodingError(#[from] namada_core::types::DecodeError), + #[error("Merkle tree error: {0}")] + MerkleTreeError(#[from] MerkleTreeError), + #[error("DB error: {0}")] + DBError(String), + #[error("Borsh (de)-serialization error: {0}")] + BorshCodingError(std::io::Error), + #[error("Merkle tree at the height {height} is not stored")] + NoMerkleTree { height: BlockHeight }, + #[error("Code hash error: {0}")] + InvalidCodeHash(HashError), +} + +/// A result of a function that may fail +pub type Result = std::result::Result; + +/// The block's state as stored in the database. +pub struct BlockStateRead { + /// Merkle tree stores + pub merkle_tree_stores: MerkleTreeStoresRead, + /// Hash of the block + pub hash: BlockHash, + /// Height of the block + pub height: BlockHeight, + /// Time of the block + pub time: DateTimeUtc, + /// Epoch of the block + pub epoch: Epoch, + /// Predecessor block epochs + pub pred_epochs: Epochs, + /// Minimum block height at which the next epoch may start + pub next_epoch_min_start_height: BlockHeight, + /// Minimum block time at which the next epoch may start + pub next_epoch_min_start_time: DateTimeUtc, + /// Update epoch delay + pub update_epoch_blocks_delay: Option, + /// Established address generator + pub address_gen: EstablishedAddressGen, + /// Results of applying transactions + pub results: BlockResults, + /// The conversion state + pub conversion_state: ConversionState, + /// Wrapper txs to be decrypted in the next block proposal + pub tx_queue: TxQueue, + /// The latest block height on Ethereum processed, if + /// the bridge is enabled. + pub ethereum_height: Option, + /// The queue of Ethereum events to be processed in order. + pub eth_events_queue: EthEventsQueue, +} + +/// The block's state to write into the database. +pub struct BlockStateWrite<'a> { + /// Merkle tree stores + pub merkle_tree_stores: MerkleTreeStoresWrite<'a>, + /// Header of the block + pub header: Option<&'a Header>, + /// Hash of the block + pub hash: &'a BlockHash, + /// Height of the block + pub height: BlockHeight, + /// Time of the block + pub time: DateTimeUtc, + /// Epoch of the block + pub epoch: Epoch, + /// Predecessor block epochs + pub pred_epochs: &'a Epochs, + /// Minimum block height at which the next epoch may start + pub next_epoch_min_start_height: BlockHeight, + /// Minimum block time at which the next epoch may start + pub next_epoch_min_start_time: DateTimeUtc, + /// Update epoch delay + pub update_epoch_blocks_delay: Option, + /// Established address generator + pub address_gen: &'a EstablishedAddressGen, + /// Results of applying transactions + pub results: &'a BlockResults, + /// The conversion state + pub conversion_state: &'a ConversionState, + /// Wrapper txs to be decrypted in the next block proposal + pub tx_queue: &'a TxQueue, + /// The latest block height on Ethereum processed, if + /// the bridge is enabled. + pub ethereum_height: Option<&'a ethereum_structs::BlockHeight>, + /// The queue of Ethereum events to be processed in order. + pub eth_events_queue: &'a EthEventsQueue, +} + +/// A database backend. +pub trait DB: Debug { + /// A DB's cache + type Cache; + /// A handle for batch writes + type WriteBatch: DBWriteBatch; + + /// Open the database from provided path + fn open( + db_path: impl AsRef, + cache: Option<&Self::Cache>, + ) -> Self; + + /// Flush data on the memory to persistent them + fn flush(&self, wait: bool) -> Result<()>; + + /// Read the last committed block's metadata + fn read_last_block(&self) -> Result>; + + /// Write block's metadata. Merkle tree sub-stores are committed only when + /// `is_full_commit` is `true` (typically on a beginning of a new epoch). + fn add_block_to_batch( + &self, + state: BlockStateWrite, + batch: &mut Self::WriteBatch, + is_full_commit: bool, + ) -> Result<()>; + + /// Read the block header with the given height from the DB + fn read_block_header(&self, height: BlockHeight) -> Result>; + + /// Read the merkle tree stores with the given epoch. If a store_type is + /// given, it reads only the the specified tree. Otherwise, it reads all + /// trees. + fn read_merkle_tree_stores( + &self, + epoch: Epoch, + base_height: BlockHeight, + store_type: Option, + ) -> Result>; + + /// Check if the given replay protection entry exists + fn has_replay_protection_entry(&self, hash: &Hash) -> Result; + + /// Read the latest value for account subspace key from the DB + fn read_subspace_val(&self, key: &Key) -> Result>>; + + /// Read the value for account subspace key at the given height from the DB. + /// In our `PersistentStorage` (rocksdb), to find a value from arbitrary + /// height requires looking for diffs from the given `height`, possibly + /// up to the `last_height`. + fn read_subspace_val_with_height( + &self, + key: &Key, + height: BlockHeight, + last_height: BlockHeight, + ) -> Result>>; + + /// Write the value with the given height and account subspace key to the + /// DB. Returns the size difference from previous value, if any, or the + /// size of the value otherwise. + fn write_subspace_val( + &mut self, + height: BlockHeight, + key: &Key, + value: impl AsRef<[u8]>, + ) -> Result; + + /// Delete the value with the given height and account subspace key from the + /// DB. Returns the size of the removed value, if any, 0 if no previous + /// value was found. + fn delete_subspace_val( + &mut self, + height: BlockHeight, + key: &Key, + ) -> Result; + + /// Start write batch. + fn batch() -> Self::WriteBatch; + + /// Execute write batch. + fn exec_batch(&mut self, batch: Self::WriteBatch) -> Result<()>; + + /// Batch write the value with the given height and account subspace key to + /// the DB. Returns the size difference from previous value, if any, or + /// the size of the value otherwise. + fn batch_write_subspace_val( + &self, + batch: &mut Self::WriteBatch, + height: BlockHeight, + key: &Key, + value: impl AsRef<[u8]>, + ) -> Result; + + /// Batch delete the value with the given height and account subspace key + /// from the DB. Returns the size of the removed value, if any, 0 if no + /// previous value was found. + fn batch_delete_subspace_val( + &self, + batch: &mut Self::WriteBatch, + height: BlockHeight, + key: &Key, + ) -> Result; + + /// Prune Merkle tree stores at the given epoch + fn prune_merkle_tree_store( + &mut self, + batch: &mut Self::WriteBatch, + store_type: &StoreType, + pruned_epoch: Epoch, + ) -> Result<()>; + + /// Read the signed nonce of Bridge Pool + fn read_bridge_pool_signed_nonce( + &self, + height: BlockHeight, + last_height: BlockHeight, + ) -> Result>; + + /// Write a replay protection entry + fn write_replay_protection_entry( + &mut self, + batch: &mut Self::WriteBatch, + key: &Key, + ) -> Result<()>; + + /// Delete a replay protection entry + fn delete_replay_protection_entry( + &mut self, + batch: &mut Self::WriteBatch, + key: &Key, + ) -> Result<()>; +} + +/// A database prefix iterator. +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: Option<&Key>) -> Self::PrefixIter; + + /// Read results subspace key value pairs from the DB + fn iter_results(&'iter self) -> Self::PrefixIter; + + /// Read subspace old diffs at a given height + fn iter_old_diffs( + &'iter self, + height: BlockHeight, + prefix: Option<&'iter Key>, + ) -> Self::PrefixIter; + + /// Read subspace new diffs at a given height + fn iter_new_diffs( + &'iter self, + height: BlockHeight, + prefix: Option<&'iter Key>, + ) -> Self::PrefixIter; + + /// Read replay protection storage from the last block + fn iter_replay_protection(&'iter self) -> Self::PrefixIter; +} + +/// Atomic batch write. +pub trait DBWriteBatch {} diff --git a/storage/src/lib.rs b/storage/src/lib.rs index fa17755908..d2e0890cb8 100644 --- a/storage/src/lib.rs +++ b/storage/src/lib.rs @@ -2,11 +2,17 @@ //! and VPs (both native and WASM). pub mod collections; +mod db; mod error; +pub mod mockdb; +pub mod tx_queue; +pub mod types; +pub use db::*; pub use error::{CustomError, Error, OptionExt, Result, ResultExt}; use namada_core::borsh::{BorshDeserialize, BorshSerialize, BorshSerializeExt}; use namada_core::types::address::Address; +pub use namada_core::types::hash::StorageHasher; use namada_core::types::storage::{ self, BlockHash, BlockHeight, Epoch, Epochs, Header, TxIndex, }; diff --git a/state/src/mockdb.rs b/storage/src/mockdb.rs similarity index 99% rename from state/src/mockdb.rs rename to storage/src/mockdb.rs index b5e81e52a6..d953f24d03 100644 --- a/state/src/mockdb.rs +++ b/storage/src/mockdb.rs @@ -23,7 +23,7 @@ use namada_merkle_tree::{ base_tree_key_prefix, subtree_key_prefix, MerkleTreeStoresRead, StoreType, }; -use super::{ +use crate::db::{ BlockStateRead, BlockStateWrite, DBIter, DBWriteBatch, Error, Result, DB, }; use crate::tx_queue::TxQueue; diff --git a/state/src/tx_queue.rs b/storage/src/tx_queue.rs similarity index 100% rename from state/src/tx_queue.rs rename to storage/src/tx_queue.rs diff --git a/state/src/types.rs b/storage/src/types.rs similarity index 100% rename from state/src/types.rs rename to storage/src/types.rs From 2b0f77b323ca08cc7c92b38ac0e9b75b22eccbce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 5 Jan 2024 18:21:52 +0000 Subject: [PATCH 083/118] storage: fix tests --- Cargo.lock | 11 +- apps/src/lib/node/ledger/shell/block_alloc.rs | 2 +- apps/src/lib/node/ledger/shell/mod.rs | 5 +- .../lib/node/ledger/shell/prepare_proposal.rs | 2 +- .../lib/node/ledger/shell/process_proposal.rs | 5 +- core/src/types/mod.rs | 1 + core/src/types/parameters.rs | 78 +++++ ethereum_bridge/Cargo.toml | 3 + ethereum_bridge/src/storage/mod.rs | 7 +- ethereum_bridge/src/storage/wrapped_erc20s.rs | 9 +- ethereum_bridge/src/test_utils.rs | 5 +- gas/Cargo.toml | 2 - gas/src/lib.rs | 13 - merkle_tree/src/eth_bridge_pool.rs | 2 + parameters/src/lib.rs | 283 +++++++----------- parameters/src/storage.rs | 13 + proof_of_stake/src/lib.rs | 2 +- proof_of_stake/src/tests/state_machine.rs | 2 +- sdk/src/lib.rs | 28 +- shared/src/ledger/mod.rs | 6 +- state/Cargo.toml | 10 +- storage/Cargo.toml | 14 + storage/src/collections/lazy_map.rs | 17 +- storage/src/collections/lazy_set.rs | 9 +- storage/src/collections/lazy_vec.rs | 13 +- storage/src/lib.rs | 134 +++++++++ storage/src/mockdb.rs | 2 +- storage/src/tx_queue.rs | 2 +- tx/Cargo.toml | 11 +- vote_ext/src/validator_set_update.rs | 24 ++ 30 files changed, 462 insertions(+), 253 deletions(-) create mode 100644 core/src/types/parameters.rs diff --git a/Cargo.lock b/Cargo.lock index 6c05538967..f63e26ccab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4396,7 +4396,6 @@ dependencies = [ "ics23", "impl-num-traits", "index-set", - "itertools 0.10.5", "k256", "masp_primitives", "namada_macros", @@ -4452,6 +4451,7 @@ dependencies = [ "ethers", "eyre", "itertools 0.10.5", + "namada_account", "namada_core", "namada_macros", "namada_parameters", @@ -4489,8 +4489,6 @@ version = "0.29.0" dependencies = [ "borsh", "namada_core", - "namada_parameters", - "namada_storage", "serde 1.0.193", "thiserror", ] @@ -4694,6 +4692,7 @@ dependencies = [ "namada_storage", "namada_trans_token", "namada_tx", + "proptest", "sha2 0.9.9", "sparse-merkle-tree", "thiserror", @@ -4705,9 +4704,14 @@ dependencies = [ name = "namada_storage" version = "0.29.0" dependencies = [ + "borsh", "itertools 0.10.5", "namada_core", + "namada_gas", + "namada_merkle_tree", + "namada_tx", "thiserror", + "tracing", ] [[package]] @@ -4800,6 +4804,7 @@ dependencies = [ "namada_gas", "num-derive", "num-traits 0.2.17", + "proptest", "prost 0.12.3", "prost-types 0.12.3", "serde 1.0.193", diff --git a/apps/src/lib/node/ledger/shell/block_alloc.rs b/apps/src/lib/node/ledger/shell/block_alloc.rs index 9bb35df5e7..1fa09fa870 100644 --- a/apps/src/lib/node/ledger/shell/block_alloc.rs +++ b/apps/src/lib/node/ledger/shell/block_alloc.rs @@ -151,7 +151,7 @@ where fn from(storage: &WlStorage) -> Self { Self::init( storage.pos_queries().get_max_proposal_bytes().get(), - namada::gas::get_max_block_gas(storage).unwrap(), + namada::parameters::get_max_block_gas(storage).unwrap(), ) } } diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index fc88f2db77..ec18d9e568 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -1237,7 +1237,8 @@ where // Max block gas let block_gas_limit: Gas = Gas::from_whole_units( - namada::gas::get_max_block_gas(&self.wl_storage).unwrap(), + namada::parameters::get_max_block_gas(&self.wl_storage) + .unwrap(), ); if gas_meter.tx_gas_limit > block_gas_limit { response.code = ResultCode::AllocationError.into(); @@ -2854,7 +2855,7 @@ mod shell_tests { let (shell, _recv, _, _) = test_utils::setup(); let block_gas_limit = - namada::gas::get_max_block_gas(&shell.wl_storage).unwrap(); + namada::parameters::get_max_block_gas(&shell.wl_storage).unwrap(); let keypair = super::test_utils::gen_keypair(); let mut wrapper = diff --git a/apps/src/lib/node/ledger/shell/prepare_proposal.rs b/apps/src/lib/node/ledger/shell/prepare_proposal.rs index 2d956b9206..a0833cb59f 100644 --- a/apps/src/lib/node/ledger/shell/prepare_proposal.rs +++ b/apps/src/lib/node/ledger/shell/prepare_proposal.rs @@ -1045,7 +1045,7 @@ mod test_prepare_proposal { let (shell, _recv, _, _) = test_utils::setup(); let block_gas_limit = - namada::gas::get_max_block_gas(&shell.wl_storage).unwrap(); + namada::parameters::get_max_block_gas(&shell.wl_storage).unwrap(); let keypair = gen_keypair(); let wrapper = WrapperTx::new( diff --git a/apps/src/lib/node/ledger/shell/process_proposal.rs b/apps/src/lib/node/ledger/shell/process_proposal.rs index 26a5391ee5..0c439fba83 100644 --- a/apps/src/lib/node/ledger/shell/process_proposal.rs +++ b/apps/src/lib/node/ledger/shell/process_proposal.rs @@ -47,7 +47,8 @@ where fn from(wl_storage: &WlStorage) -> Self { let max_proposal_bytes = wl_storage.pos_queries().get_max_proposal_bytes().get(); - let max_block_gas = namada::gas::get_max_block_gas(wl_storage).unwrap(); + let max_block_gas = + namada::parameters::get_max_block_gas(wl_storage).unwrap(); let encrypted_txs_bin = EncryptedTxsBins::new(max_proposal_bytes, max_block_gas); let txs_bin = TxBin::init(max_proposal_bytes); @@ -1821,7 +1822,7 @@ mod test_process_proposal { let (shell, _recv, _, _) = test_utils::setup(); let block_gas_limit = - namada::gas::get_max_block_gas(&shell.wl_storage).unwrap(); + namada::parameters::get_max_block_gas(&shell.wl_storage).unwrap(); let keypair = super::test_utils::gen_keypair(); let mut wrapper = diff --git a/core/src/types/mod.rs b/core/src/types/mod.rs index 25e29e63da..e63a10463c 100644 --- a/core/src/types/mod.rs +++ b/core/src/types/mod.rs @@ -14,6 +14,7 @@ pub mod internal; pub mod keccak; pub mod key; pub mod masp; +pub mod parameters; pub mod sign; pub mod storage; pub mod string_encoding; diff --git a/core/src/types/parameters.rs b/core/src/types/parameters.rs new file mode 100644 index 0000000000..1fdd71e061 --- /dev/null +++ b/core/src/types/parameters.rs @@ -0,0 +1,78 @@ +//! Protocol parameters types + +use std::collections::BTreeMap; + +use super::address::Address; +use super::chain::ProposalBytes; +use super::dec::Dec; +use super::hash::Hash; +use super::time::DurationSecs; +use super::token; +use crate::borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; + +/// Protocol parameters +#[derive( + Clone, + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + BorshSerialize, + BorshDeserialize, + BorshSchema, +)] +pub struct Parameters { + /// Max payload size, in bytes, for a mempool tx. + pub max_tx_bytes: u32, + /// Epoch duration (read only) + pub epoch_duration: EpochDuration, + /// Maximum expected time per block (read only) + pub max_expected_time_per_block: DurationSecs, + /// Max payload size, in bytes, for a tx batch proposal. + pub max_proposal_bytes: ProposalBytes, + /// Max gas for block + pub max_block_gas: u64, + /// Whitelisted validity predicate hashes (read only) + pub vp_whitelist: Vec, + /// Whitelisted tx hashes (read only) + pub tx_whitelist: Vec, + /// Implicit accounts validity predicate WASM code hash + pub implicit_vp_code_hash: Option, + /// Expected number of epochs per year (read only) + pub epochs_per_year: u64, + /// Maximum number of signature per transaction + pub max_signatures_per_transaction: u8, + /// PoS staked ratio (read + write for every epoch) + pub staked_ratio: Dec, + /// PoS inflation amount from the last epoch (read + write for every epoch) + pub pos_inflation_amount: token::Amount, + /// Fee unshielding gas limit + pub fee_unshielding_gas_limit: u64, + /// Fee unshielding descriptions limit + pub fee_unshielding_descriptions_limit: u64, + /// Map of the cost per gas unit for every token allowed for fee payment + pub minimum_gas_price: BTreeMap, +} + +/// Epoch duration. A new epoch begins as soon as both the `min_num_of_blocks` +/// and `min_duration` have passed since the beginning of the current epoch. +#[derive( + Clone, + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + BorshSerialize, + BorshDeserialize, + BorshSchema, +)] +pub struct EpochDuration { + /// Minimum number of blocks in an epoch + pub min_num_of_blocks: u64, + /// Minimum duration of an epoch + pub min_duration: DurationSecs, +} diff --git a/ethereum_bridge/Cargo.toml b/ethereum_bridge/Cargo.toml index 65a158c6da..1ce8dccd5a 100644 --- a/ethereum_bridge/Cargo.toml +++ b/ethereum_bridge/Cargo.toml @@ -15,10 +15,12 @@ version.workspace = true [features] default = [] testing = [ + "namada_account", "namada_core/testing", ] [dependencies] +namada_account = {path = "../account", optional = true} namada_core = {path = "../core", default-features = false, features = ["ethers-derive"]} namada_macros = {path = "../macros"} namada_parameters = {path = "../parameters"} @@ -45,6 +47,7 @@ tracing = "0.1.30" [dev-dependencies] # Added "testing" feature. +namada_account = {path = "../account"} namada_core = {path = "../core", default-features = false, features = ["ethers-derive", "testing"]} namada_proof_of_stake = {path = "../proof_of_stake", default-features = false, features = ["testing"]} namada_state = { path = "../state", features = ["testing"] } diff --git a/ethereum_bridge/src/storage/mod.rs b/ethereum_bridge/src/storage/mod.rs index df16dd3103..bd4b24e374 100644 --- a/ethereum_bridge/src/storage/mod.rs +++ b/ethereum_bridge/src/storage/mod.rs @@ -71,10 +71,11 @@ pub fn bridge_contract_key() -> Key { #[cfg(test)] mod test { + use namada_core::types::address; + use namada_core::types::address::nam; + use namada_core::types::ethereum_events::testing::arbitrary_eth_address; + use super::*; - use crate::types::address; - use crate::types::address::nam; - use crate::types::ethereum_events::testing::arbitrary_eth_address; #[test] fn test_is_eth_bridge_key_returns_true_for_eth_bridge_address() { diff --git a/ethereum_bridge/src/storage/wrapped_erc20s.rs b/ethereum_bridge/src/storage/wrapped_erc20s.rs index c1f14e49e7..085d30a218 100644 --- a/ethereum_bridge/src/storage/wrapped_erc20s.rs +++ b/ethereum_bridge/src/storage/wrapped_erc20s.rs @@ -107,12 +107,13 @@ mod test { use std::result::Result; use std::str::FromStr; + use namada_core::types::address::{nam, Address}; + use namada_core::types::ethereum_events::testing::DAI_ERC20_ETH_ADDRESS; + use namada_core::types::storage::DbKeySeg; + use namada_core::types::token::BALANCE_STORAGE_KEY; + use super::*; use crate::ledger::eth_bridge::ADDRESS; - use crate::types::address::{nam, Address}; - use crate::types::ethereum_events::testing::DAI_ERC20_ETH_ADDRESS; - use crate::types::storage::DbKeySeg; - use crate::types::token::BALANCE_STORAGE_KEY; const MULTITOKEN_ADDRESS: Address = Address::Internal(InternalAddress::Multitoken); diff --git a/ethereum_bridge/src/test_utils.rs b/ethereum_bridge/src/test_utils.rs index de74d10d36..bc108be1d0 100644 --- a/ethereum_bridge/src/test_utils.rs +++ b/ethereum_bridge/src/test_utils.rs @@ -3,11 +3,12 @@ use std::collections::HashMap; use std::num::NonZeroU64; +use namada_account::protocol_pk_key; use namada_core::types::address::{self, wnam, Address}; use namada_core::types::dec::Dec; use namada_core::types::ethereum_events::EthAddress; use namada_core::types::keccak::KeccakHash; -use namada_core::types::key::{self, protocol_pk_key, RefTo}; +use namada_core::types::key::{self, RefTo}; use namada_core::types::storage::{BlockHeight, Key}; use namada_core::types::token; use namada_proof_of_stake::parameters::OwnedPosParams; @@ -18,8 +19,8 @@ use namada_proof_of_stake::{ staking_token_address, BecomeValidator, }; use namada_state::testing::TestWlStorage; -use namada_storage::token::credit_tokens; use namada_storage::{StorageRead, StorageWrite}; +use namada_trans_token::credit_tokens; use crate::storage::bridge_pool::get_key_from_hash; use crate::storage::parameters::{ diff --git a/gas/Cargo.toml b/gas/Cargo.toml index ed2857462d..c998d83ab6 100644 --- a/gas/Cargo.toml +++ b/gas/Cargo.toml @@ -14,8 +14,6 @@ version.workspace = true [dependencies] namada_core = { path = "../core", default-features = false } -namada_parameters = { path = "../parameters" } -namada_storage = { path = "../storage" } borsh.workspace = true serde.workspace = true diff --git a/gas/src/lib.rs b/gas/src/lib.rs index d3d757805d..c090a6f782 100644 --- a/gas/src/lib.rs +++ b/gas/src/lib.rs @@ -5,7 +5,6 @@ use std::fmt::Display; use std::ops::Div; use namada_core::borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; -use namada_storage::StorageRead; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -62,18 +61,6 @@ pub type Result = std::result::Result; /// Decimal scale of Gas units const SCALE: u64 = 10_000; -/// Helper function to retrieve the `max_block_gas` protocol parameter from -/// storage -pub fn get_max_block_gas( - storage: &impl StorageRead, -) -> std::result::Result { - storage - .read(&namada_parameters::storage::get_max_block_gas_key())? - .ok_or(namada_storage::Error::SimpleMessage( - "Missing max_block_gas parameter from storage", - )) -} - /// Representation of gas in sub-units. This effectively decouples gas metering /// from fee payment, allowing higher resolution when accounting for gas while, /// at the same time, providing a contained gas value when paying fees. diff --git a/merkle_tree/src/eth_bridge_pool.rs b/merkle_tree/src/eth_bridge_pool.rs index 5981e9b14c..7082489307 100644 --- a/merkle_tree/src/eth_bridge_pool.rs +++ b/merkle_tree/src/eth_bridge_pool.rs @@ -371,6 +371,8 @@ impl Encode<3> for BridgePoolProof { #[cfg(test)] mod test_bridge_pool_tree { + use std::assert_matches; + use itertools::Itertools; use proptest::prelude::*; diff --git a/parameters/src/lib.rs b/parameters/src/lib.rs index 090a732c4b..71a93a95f4 100644 --- a/parameters/src/lib.rs +++ b/parameters/src/lib.rs @@ -8,82 +8,17 @@ use namada_core::types::address::{Address, InternalAddress}; use namada_core::types::chain::ProposalBytes; use namada_core::types::dec::Dec; use namada_core::types::hash::Hash; +pub use namada_core::types::parameters::*; use namada_core::types::time::DurationSecs; use namada_core::types::token; use namada_storage::{self, ResultExt, StorageRead, StorageWrite}; +pub use storage::get_max_block_gas; use thiserror::Error; /// The internal address for storage keys representing parameters than /// can be changed via governance. pub const ADDRESS: Address = Address::Internal(InternalAddress::Parameters); -/// Protocol parameters -#[derive( - Clone, - Debug, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - BorshSerialize, - BorshDeserialize, - BorshSchema, -)] -pub struct Parameters { - /// Max payload size, in bytes, for a mempool tx. - pub max_tx_bytes: u32, - /// Epoch duration (read only) - pub epoch_duration: EpochDuration, - /// Maximum expected time per block (read only) - pub max_expected_time_per_block: DurationSecs, - /// Max payload size, in bytes, for a tx batch proposal. - pub max_proposal_bytes: ProposalBytes, - /// Max gas for block - pub max_block_gas: u64, - /// Whitelisted validity predicate hashes (read only) - pub vp_whitelist: Vec, - /// Whitelisted tx hashes (read only) - pub tx_whitelist: Vec, - /// Implicit accounts validity predicate WASM code hash - pub implicit_vp_code_hash: Option, - /// Expected number of epochs per year (read only) - pub epochs_per_year: u64, - /// Maximum number of signature per transaction - pub max_signatures_per_transaction: u8, - /// PoS staked ratio (read + write for every epoch) - pub staked_ratio: Dec, - /// PoS inflation amount from the last epoch (read + write for every epoch) - pub pos_inflation_amount: token::Amount, - /// Fee unshielding gas limit - pub fee_unshielding_gas_limit: u64, - /// Fee unshielding descriptions limit - pub fee_unshielding_descriptions_limit: u64, - /// Map of the cost per gas unit for every token allowed for fee payment - pub minimum_gas_price: BTreeMap, -} - -/// Epoch duration. A new epoch begins as soon as both the `min_num_of_blocks` -/// and `min_duration` have passed since the beginning of the current epoch. -#[derive( - Clone, - Debug, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - BorshSerialize, - BorshDeserialize, - BorshSchema, -)] -pub struct EpochDuration { - /// Minimum number of blocks in an epoch - pub min_num_of_blocks: u64, - /// Minimum duration of an epoch - pub min_duration: DurationSecs, -} - #[allow(missing_docs)] #[derive(Error, Debug)] pub enum ReadError { @@ -104,114 +39,114 @@ pub enum WriteError { SerializeError(String), } -impl Parameters { - /// Initialize parameters in storage in the genesis block. - pub fn init_storage(&self, storage: &mut S) -> namada_storage::Result<()> - where - S: StorageRead + StorageWrite, - { - let Self { - max_tx_bytes, - epoch_duration, - max_expected_time_per_block, - max_proposal_bytes, - max_block_gas, - vp_whitelist, - tx_whitelist, - implicit_vp_code_hash, - epochs_per_year, - max_signatures_per_transaction, - staked_ratio, - pos_inflation_amount, - minimum_gas_price, - fee_unshielding_gas_limit, - fee_unshielding_descriptions_limit, - } = self; - - // write max tx bytes parameter - let max_tx_bytes_key = storage::get_max_tx_bytes_key(); - storage.write(&max_tx_bytes_key, max_tx_bytes)?; - - // write max proposal bytes parameter - let max_proposal_bytes_key = storage::get_max_proposal_bytes_key(); - storage.write(&max_proposal_bytes_key, max_proposal_bytes)?; - - // write max block gas parameter - let max_block_gas_key = storage::get_max_block_gas_key(); - storage.write(&max_block_gas_key, max_block_gas)?; - - // write epoch parameters - let epoch_key = storage::get_epoch_duration_storage_key(); - storage.write(&epoch_key, epoch_duration)?; - - // write fee unshielding gas limit - let fee_unshielding_gas_limit_key = - storage::get_fee_unshielding_gas_limit_key(); - storage - .write(&fee_unshielding_gas_limit_key, fee_unshielding_gas_limit)?; - - // write fee unshielding descriptions limit - let fee_unshielding_descriptions_limit_key = - storage::get_fee_unshielding_descriptions_limit_key(); - storage.write( - &fee_unshielding_descriptions_limit_key, - fee_unshielding_descriptions_limit, - )?; - - // write vp whitelist parameter - let vp_whitelist_key = storage::get_vp_whitelist_storage_key(); - let vp_whitelist = vp_whitelist - .iter() - .map(|id| id.to_lowercase()) - .collect::>(); - storage.write(&vp_whitelist_key, vp_whitelist)?; +/// Initialize parameters in storage in the genesis block. +pub fn init_storage( + parameters: &Parameters, + storage: &mut S, +) -> namada_storage::Result<()> +where + S: StorageRead + StorageWrite, +{ + let Parameters { + max_tx_bytes, + epoch_duration, + max_expected_time_per_block, + max_proposal_bytes, + max_block_gas, + vp_whitelist, + tx_whitelist, + implicit_vp_code_hash, + epochs_per_year, + max_signatures_per_transaction, + staked_ratio, + pos_inflation_amount, + minimum_gas_price, + fee_unshielding_gas_limit, + fee_unshielding_descriptions_limit, + } = parameters; - // write tx whitelist parameter - let tx_whitelist_key = storage::get_tx_whitelist_storage_key(); - let tx_whitelist = tx_whitelist - .iter() - .map(|id| id.to_lowercase()) - .collect::>(); - storage.write(&tx_whitelist_key, tx_whitelist)?; - - // write max expected time per block - let max_expected_time_per_block_key = - storage::get_max_expected_time_per_block_key(); - storage.write( - &max_expected_time_per_block_key, - max_expected_time_per_block, - )?; - - // write implicit vp parameter - let implicit_vp_key = storage::get_implicit_vp_key(); - // Using `fn write_bytes` here, because implicit_vp code hash doesn't - // need to be encoded, it's bytes already. - storage.write_bytes( - &implicit_vp_key, - implicit_vp_code_hash.unwrap_or_default(), - )?; - - let epochs_per_year_key = storage::get_epochs_per_year_key(); - storage.write(&epochs_per_year_key, epochs_per_year)?; - - let max_signatures_per_transaction_key = - storage::get_max_signatures_per_transaction_key(); - storage.write( - &max_signatures_per_transaction_key, - max_signatures_per_transaction, - )?; - - let staked_ratio_key = storage::get_staked_ratio_key(); - storage.write(&staked_ratio_key, staked_ratio)?; - - let pos_inflation_key = storage::get_pos_inflation_amount_key(); - storage.write(&pos_inflation_key, pos_inflation_amount)?; - - let gas_cost_key = storage::get_gas_cost_key(); - storage.write(&gas_cost_key, minimum_gas_price)?; - - Ok(()) - } + // write max tx bytes parameter + let max_tx_bytes_key = storage::get_max_tx_bytes_key(); + storage.write(&max_tx_bytes_key, max_tx_bytes)?; + + // write max proposal bytes parameter + let max_proposal_bytes_key = storage::get_max_proposal_bytes_key(); + storage.write(&max_proposal_bytes_key, max_proposal_bytes)?; + + // write max block gas parameter + let max_block_gas_key = storage::get_max_block_gas_key(); + storage.write(&max_block_gas_key, max_block_gas)?; + + // write epoch parameters + let epoch_key = storage::get_epoch_duration_storage_key(); + storage.write(&epoch_key, epoch_duration)?; + + // write fee unshielding gas limit + let fee_unshielding_gas_limit_key = + storage::get_fee_unshielding_gas_limit_key(); + storage.write(&fee_unshielding_gas_limit_key, fee_unshielding_gas_limit)?; + + // write fee unshielding descriptions limit + let fee_unshielding_descriptions_limit_key = + storage::get_fee_unshielding_descriptions_limit_key(); + storage.write( + &fee_unshielding_descriptions_limit_key, + fee_unshielding_descriptions_limit, + )?; + + // write vp whitelist parameter + let vp_whitelist_key = storage::get_vp_whitelist_storage_key(); + let vp_whitelist = vp_whitelist + .iter() + .map(|id| id.to_lowercase()) + .collect::>(); + storage.write(&vp_whitelist_key, vp_whitelist)?; + + // write tx whitelist parameter + let tx_whitelist_key = storage::get_tx_whitelist_storage_key(); + let tx_whitelist = tx_whitelist + .iter() + .map(|id| id.to_lowercase()) + .collect::>(); + storage.write(&tx_whitelist_key, tx_whitelist)?; + + // write max expected time per block + let max_expected_time_per_block_key = + storage::get_max_expected_time_per_block_key(); + storage.write( + &max_expected_time_per_block_key, + max_expected_time_per_block, + )?; + + // write implicit vp parameter + let implicit_vp_key = storage::get_implicit_vp_key(); + // Using `fn write_bytes` here, because implicit_vp code hash doesn't + // need to be encoded, it's bytes already. + storage.write_bytes( + &implicit_vp_key, + implicit_vp_code_hash.unwrap_or_default(), + )?; + + let epochs_per_year_key = storage::get_epochs_per_year_key(); + storage.write(&epochs_per_year_key, epochs_per_year)?; + + let max_signatures_per_transaction_key = + storage::get_max_signatures_per_transaction_key(); + storage.write( + &max_signatures_per_transaction_key, + max_signatures_per_transaction, + )?; + + let staked_ratio_key = storage::get_staked_ratio_key(); + storage.write(&staked_ratio_key, staked_ratio)?; + + let pos_inflation_key = storage::get_pos_inflation_amount_key(); + storage.write(&pos_inflation_key, pos_inflation_amount)?; + + let gas_cost_key = storage::get_gas_cost_key(); + storage.write(&gas_cost_key, minimum_gas_price)?; + + Ok(()) } /// Get the max signatures per transactio parameter diff --git a/parameters/src/storage.rs b/parameters/src/storage.rs index 4fe761d17f..50c4775cb6 100644 --- a/parameters/src/storage.rs +++ b/parameters/src/storage.rs @@ -3,6 +3,7 @@ use namada_core::types::address::Address; use namada_core::types::storage::{DbKeySeg, Key}; use namada_macros::StorageKeys; +use namada_storage::StorageRead; use super::ADDRESS; @@ -188,3 +189,15 @@ pub fn get_gas_cost_key() -> Key { pub fn get_max_signatures_per_transaction_key() -> Key { get_max_signatures_per_transaction_key_at_addr(ADDRESS) } + +/// Helper function to retrieve the `max_block_gas` protocol parameter from +/// storage +pub fn get_max_block_gas( + storage: &impl StorageRead, +) -> std::result::Result { + storage.read(&get_max_block_gas_key())?.ok_or( + namada_storage::Error::SimpleMessage( + "Missing max_block_gas parameter from storage", + ), + ) +} diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index 516945c967..01da9e2053 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -2455,9 +2455,9 @@ where #[cfg(any(test, feature = "testing"))] /// PoS related utility functions to help set up tests. pub mod test_utils { - use namada_namada_trans_token::credit_tokens; use namada_storage; use namada_storage::{StorageRead, StorageWrite}; + use namada_trans_token::credit_tokens; use super::*; use crate::parameters::PosParams; diff --git a/proof_of_stake/src/tests/state_machine.rs b/proof_of_stake/src/tests/state_machine.rs index 6fc0f911f6..9903e34c00 100644 --- a/proof_of_stake/src/tests/state_machine.rs +++ b/proof_of_stake/src/tests/state_machine.rs @@ -12,12 +12,12 @@ use namada_core::types::key; use namada_core::types::key::common::PublicKey; use namada_core::types::storage::Epoch; use namada_core::types::token::Change; -use namada_namada_trans_token::read_balance; use namada_state::testing::TestWlStorage; use namada_storage::collections::lazy_map::{ Collectable, NestedSubKey, SubKey, }; use namada_storage::{token, StorageRead}; +use namada_trans_token::read_balance; use proptest::prelude::*; use proptest::test_runner::Config; use proptest_state_machine::{ diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index 0e366a5bb8..b70d0d2bc8 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -763,8 +763,8 @@ where #[cfg(any(test, feature = "testing"))] /// Tests and strategies for transactions pub mod testing { + use governance::ProposalType; use ibc::primitives::proto::Any; - use namada_core::ledger::governance::storage::proposal::ProposalType; use namada_core::ledger::ibc::testing::arb_ibc_any; use namada_core::types::address::testing::{ arb_established_address, arb_non_internal_address, @@ -792,26 +792,20 @@ pub mod testing { use prost::Message; use super::*; - use crate::core::types::chain::ChainId; - use crate::core::types::eth_bridge_pool::testing::arb_pending_transfer; - use crate::core::types::key::testing::arb_common_pk; - use crate::core::types::time::{DateTime, DateTimeUtc, Utc}; - use crate::core::types::transaction::account::tests::{ - arb_init_account, arb_update_account, - }; - use crate::core::types::transaction::governance::tests::{ - arb_init_proposal, arb_vote_proposal, - }; - use crate::core::types::transaction::pgf::tests::arb_update_steward_commission; - use crate::core::types::transaction::pos::tests::{ + use crate::account::tests::{arb_init_account, arb_update_account}; + use crate::governance::tests::{arb_init_proposal, arb_vote_proposal}; + use crate::tx::data::pgf::tests::arb_update_steward_commission; + use crate::tx::data::pos::tests::{ arb_become_validator, arb_bond, arb_commission_change, arb_consensus_key_change, arb_metadata_change, arb_redelegation, arb_withdraw, }; - use crate::core::types::transaction::{ - DecryptedTx, Fee, TxType, WrapperTx, - }; - use crate::proto::{Code, Commitment, Header, Section}; + use crate::tx::{Code, Commitment, Header, Section}; + use crate::types::chain::ChainId; + use crate::types::eth_bridge_pool::testing::arb_pending_transfer; + use crate::types::key::testing::arb_common_pk; + use crate::types::time::{DateTime, DateTimeUtc, Utc}; + use crate::types::transaction::{DecryptedTx, Fee, TxType, WrapperTx}; #[derive(Debug)] #[allow(clippy::large_enum_variant)] diff --git a/shared/src/ledger/mod.rs b/shared/src/ledger/mod.rs index cee83e6350..3e95cbd38a 100644 --- a/shared/src/ledger/mod.rs +++ b/shared/src/ledger/mod.rs @@ -85,7 +85,8 @@ mod dry_run_tx { // If dry run only the inner tx, use the max block gas as the // gas limit TxGasMeter::new( - namada_gas::get_max_block_gas(ctx.wl_storage).unwrap(), + namada_parameters::get_max_block_gas(ctx.wl_storage) + .unwrap(), ) } TxType::Raw => { @@ -95,7 +96,8 @@ mod dry_run_tx { // If dry run only the inner tx, use the max block gas as the // gas limit TxGasMeter::new( - namada_gas::get_max_block_gas(ctx.wl_storage).unwrap(), + namada_parameters::get_max_block_gas(ctx.wl_storage) + .unwrap(), ) } }; diff --git a/state/Cargo.toml b/state/Cargo.toml index 128e8bbb50..e8beaf298b 100644 --- a/state/Cargo.toml +++ b/state/Cargo.toml @@ -16,9 +16,7 @@ version.workspace = true default = [] # for integration tests and test utilities -testing = [ - "namada_core/testing", -] +testing = ["proptest", "namada_core/testing"] [dependencies] namada_core = { path = "../core", default-features = false } @@ -35,5 +33,9 @@ ics23.workspace = true itertools.workspace = true sha2.workspace = true thiserror.workspace = true -tiny-keccak = {version = "2.0.2", features = ["keccak"]} +tiny-keccak = { version = "2.0.2", features = ["keccak"] } tracing.workspace = true +proptest = { workspace = true, optional = true } + +[dev-dependencies] +proptest.workspace = true diff --git a/storage/Cargo.toml b/storage/Cargo.toml index 0c252d32d8..a9c542a950 100644 --- a/storage/Cargo.toml +++ b/storage/Cargo.toml @@ -12,8 +12,22 @@ readme.workspace = true repository.workspace = true version.workspace = true +[features] +default = [] +testing = [ + "namada_core/testing", +] + [dependencies] namada_core = { path = "../core", default-features = false } +namada_gas = { path = "../gas" } +namada_merkle_tree = { path = "../merkle_tree" } +namada_tx = { path = "../tx" } +borsh.workspace = true itertools.workspace = true thiserror.workspace = true +tracing.workspace = true + +[dev-dependencies] +namada_core = { path = "../core", features = ["testing"] } diff --git a/storage/src/collections/lazy_map.rs b/storage/src/collections/lazy_map.rs index 9a0f6a78b0..f2d24f7dd1 100644 --- a/storage/src/collections/lazy_map.rs +++ b/storage/src/collections/lazy_map.rs @@ -539,13 +539,14 @@ where #[cfg(test)] mod test { + use namada_core::types::address::{self, Address}; + use super::*; - use crate::ledger::storage::testing::TestWlStorage; - use crate::types::address::{self, Address}; + use crate::testing::TestStorage; #[test] fn test_lazy_map_basics() -> crate::Result<()> { - let mut storage = TestWlStorage::default(); + let mut storage = TestStorage::default(); let key = storage::Key::parse("test").unwrap(); let lazy_map = LazyMap::::open(key); @@ -639,7 +640,7 @@ mod test { #[test] fn test_lazy_map_with_addr_key() -> crate::Result<()> { - let mut storage = TestWlStorage::default(); + let mut storage = TestStorage::default(); let key = storage::Key::parse("test").unwrap(); let lazy_map = LazyMap::::open(key); @@ -687,7 +688,7 @@ mod test { #[test] fn test_nested_lazy_map_with_addr_key() -> crate::Result<()> { - let mut storage = TestWlStorage::default(); + let mut storage = TestStorage::default(); let key = storage::Key::parse("test").unwrap(); let lazy_map = NestedMap::>::open(key); @@ -740,7 +741,7 @@ mod test { #[test] fn test_nested_map_basics() -> crate::Result<()> { - let mut storage = TestWlStorage::default(); + let mut storage = TestStorage::default(); let key = storage::Key::parse("testing").unwrap(); // A nested map from u32 -> String -> u32 @@ -861,7 +862,7 @@ mod test { #[test] fn test_nested_map_key_prefix_removal() { - let mut storage = TestWlStorage::default(); + let mut storage = TestStorage::default(); let key = storage::Key::parse("testing").unwrap(); // A nested map from u32 -> String -> u32 @@ -892,7 +893,7 @@ mod test { #[test] fn test_lazy_map_collection() { - let mut storage = TestWlStorage::default(); + let mut storage = TestStorage::default(); let key_s = storage::Key::parse("testing_simple").unwrap(); let key_n = storage::Key::parse("testing_nested").unwrap(); diff --git a/storage/src/collections/lazy_set.rs b/storage/src/collections/lazy_set.rs index 267a14fe5d..ab4e41705b 100644 --- a/storage/src/collections/lazy_set.rs +++ b/storage/src/collections/lazy_set.rs @@ -213,13 +213,14 @@ where #[cfg(test)] mod test { + use namada_core::types::address::{self, Address}; + use super::*; - use crate::ledger::storage::testing::TestWlStorage; - use crate::types::address::{self, Address}; + use crate::testing::TestStorage; #[test] fn test_lazy_set_basics() -> crate::Result<()> { - let mut storage = TestWlStorage::default(); + let mut storage = TestStorage::default(); let key = storage::Key::parse("test").unwrap(); let lazy_set = LazySet::::open(key); @@ -299,7 +300,7 @@ mod test { #[test] fn test_lazy_set_with_addr_key() -> crate::Result<()> { - let mut storage = TestWlStorage::default(); + let mut storage = TestStorage::default(); let key = storage::Key::parse("test").unwrap(); let lazy_set = LazySet::
::open(key); diff --git a/storage/src/collections/lazy_vec.rs b/storage/src/collections/lazy_vec.rs index 6ac076d956..12398a3f23 100644 --- a/storage/src/collections/lazy_vec.rs +++ b/storage/src/collections/lazy_vec.rs @@ -284,14 +284,15 @@ where #[cfg(test)] mod test { + use namada_core::types::address::{self, Address}; + use super::*; - use crate::ledger::storage::testing::TestWlStorage; - use crate::ledger::storage_api::collections::lazy_map::{self, NestedMap}; - use crate::types::address::{self, Address}; + use crate::collections::lazy_map::{self, NestedMap}; + use crate::testing::TestStorage; #[test] fn test_lazy_vec_basics() -> crate::Result<()> { - let mut storage = TestWlStorage::default(); + let mut storage = TestStorage::default(); let key = storage::Key::parse("test").unwrap(); let lazy_vec = LazyVec::::open(key); @@ -339,7 +340,7 @@ mod test { #[test] fn test_lazy_vec_with_addr() -> crate::Result<()> { - let mut storage = TestWlStorage::default(); + let mut storage = TestStorage::default(); let key = storage::Key::parse("test").unwrap(); let lazy_vec = LazyVec::
::open(key); @@ -382,7 +383,7 @@ mod test { /// Test iterator on a `LazyVec` nested inside a `LazyMap` #[test] fn test_nested_lazy_vec_iter() -> crate::Result<()> { - let mut storage = TestWlStorage::default(); + let mut storage = TestStorage::default(); let prefix = storage::Key::parse("test").unwrap(); let handle = NestedMap::>::open(prefix); diff --git a/storage/src/lib.rs b/storage/src/lib.rs index d2e0890cb8..2b92068287 100644 --- a/storage/src/lib.rs +++ b/storage/src/lib.rs @@ -300,3 +300,137 @@ where }); Ok(iter) } + +/// Helpers for testing components that depend on storage +#[cfg(any(test, feature = "testing"))] +pub mod testing { + + use namada_core::types::address; + use namada_core::types::chain::ChainId; + + use super::mockdb::MockDB; + use super::*; + + /// Storage with a mock DB for testing + pub struct TestStorage { + db: MockDB, + chain_id: ChainId, + height: BlockHeight, + epoch: Epoch, + pred_epochs: Epochs, + native_token: Address, + } + + #[allow(clippy::derivable_impls)] + impl Default for TestStorage { + fn default() -> Self { + Self { + db: Default::default(), + chain_id: ChainId::default(), + height: BlockHeight::first(), + epoch: Epoch::default(), + pred_epochs: Epochs::default(), + native_token: address::nam(), + } + } + } + + impl StorageRead for TestStorage { + type PrefixIter<'iter> = PrefixIter<'iter> where Self: 'iter; + + fn read_bytes(&self, key: &storage::Key) -> Result>> { + self.db.read_subspace_val(key).into_storage_result() + } + + fn has_key(&self, key: &storage::Key) -> Result { + Ok(self.read_bytes(key)?.is_some()) + } + + fn iter_prefix<'iter>( + &'iter self, + prefix: &storage::Key, + ) -> Result> { + let storage_iter = self.db.iter_prefix(Some(prefix)); + Ok(PrefixIter { + db_iter: storage_iter, + }) + } + + fn iter_next<'iter>( + &'iter self, + iter: &mut Self::PrefixIter<'iter>, + ) -> Result)>> { + Ok(iter.next()) + } + + fn get_chain_id(&self) -> Result { + Ok(self.chain_id.to_string()) + } + + fn get_block_height(&self) -> Result { + Ok(self.height) + } + + fn get_block_header( + &self, + _height: BlockHeight, + ) -> Result> { + Ok(None) + } + + fn get_block_hash(&self) -> Result { + Ok(BlockHash::default()) + } + + fn get_block_epoch(&self) -> Result { + Ok(self.epoch) + } + + fn get_pred_epochs(&self) -> Result { + Ok(self.pred_epochs.clone()) + } + + fn get_tx_index(&self) -> Result { + Ok(TxIndex::default()) + } + + fn get_native_token(&self) -> Result
{ + Ok(self.native_token.clone()) + } + } + + impl StorageWrite for TestStorage { + fn write_bytes( + &mut self, + key: &storage::Key, + val: impl AsRef<[u8]>, + ) -> Result<()> { + self.db + .write_subspace_val(self.height, key, val) + .into_storage_result()?; + Ok(()) + } + + fn delete(&mut self, key: &storage::Key) -> Result<()> { + self.db + .delete_subspace_val(self.height, key) + .into_storage_result()?; + Ok(()) + } + } + + /// Prefix iterator for [`TestStorage`]. + #[derive(Debug)] + pub struct PrefixIter<'iter> { + /// DB iterator + pub db_iter: >::PrefixIter, + } + + impl<'iter> Iterator for PrefixIter<'iter> { + type Item = (String, Vec); + + fn next(&mut self) -> Option { + self.db_iter.next().map(|(key, val, _gas)| (key, val)) + } + } +} diff --git a/storage/src/mockdb.rs b/storage/src/mockdb.rs index d953f24d03..0959027029 100644 --- a/storage/src/mockdb.rs +++ b/storage/src/mockdb.rs @@ -10,7 +10,6 @@ use itertools::Either; use namada_core::borsh::{BorshDeserialize, BorshSerializeExt}; use namada_core::ledger::replay_protection; use namada_core::types; -use namada_core::types::ethereum_events::Uint; use namada_core::types::ethereum_structs; use namada_core::types::hash::Hash; use namada_core::types::storage::{ @@ -19,6 +18,7 @@ use namada_core::types::storage::{ }; use namada_core::types::time::DateTimeUtc; use namada_core::types::token::ConversionState; +use namada_core::types::uint::Uint; use namada_merkle_tree::{ base_tree_key_prefix, subtree_key_prefix, MerkleTreeStoresRead, StoreType, }; diff --git a/storage/src/tx_queue.rs b/storage/src/tx_queue.rs index dded7cb8ff..a183ec649f 100644 --- a/storage/src/tx_queue.rs +++ b/storage/src/tx_queue.rs @@ -1,4 +1,4 @@ -use borsh::{BorshDeserialize, BorshSerialize}; +use namada_core::borsh::{BorshDeserialize, BorshSerialize}; use namada_core::types::ethereum_events::EthereumEvent; use namada_gas::Gas; use namada_tx::Tx; diff --git a/tx/Cargo.toml b/tx/Cargo.toml index a9df94f97b..7f1f4a6522 100644 --- a/tx/Cargo.toml +++ b/tx/Cargo.toml @@ -12,6 +12,10 @@ readme.workspace = true repository.workspace = true version.workspace = true +[features] +default = [] +testing = ["proptest", "namada_core/testing"] + [dependencies] namada_core = { path = "../core", default-features = false } namada_gas = { path = "../gas" } @@ -22,6 +26,7 @@ data-encoding.workspace = true masp_primitives.workspace = true num-derive.workspace = true num-traits.workspace = true +proptest = { workspace = true, optional = true } prost-types.workspace = true prost.workspace = true serde_json.workspace = true @@ -29,5 +34,9 @@ serde.workspace = true sha2.workspace = true thiserror.workspace = true +[dev-dependencies] +namada_core = { path = "../core", features = ["testing"] } +proptest.workspace = true + [build-dependencies] -tonic-build.workspace = true +tonic-build.workspace = true \ No newline at end of file diff --git a/vote_ext/src/validator_set_update.rs b/vote_ext/src/validator_set_update.rs index 37d7cfec87..d2231f39e6 100644 --- a/vote_ext/src/validator_set_update.rs +++ b/vote_ext/src/validator_set_update.rs @@ -426,6 +426,7 @@ pub use tag::SerializeWithAbiEncode; #[cfg(test)] mod tests { use data_encoding::HEXLOWER; + use namada_core::types::ethereum_events::EthAddress; use super::*; @@ -544,4 +545,27 @@ mod tests { let y = voting_powers_2.get_abi_encoded(); assert_eq!(x, y); } + + #[test] + fn test_abi_encode_valset_args() { + let valset_update = ValidatorSetArgs { + validators: vec![ + EthAddress::from_str( + "0x241D37B7Cf5233b3b0b204321420A86e8f7bfdb5", + ) + .expect("Test failed"), + ], + voting_powers: vec![8828299u64.into()], + epoch: 0.into(), + }; + let encoded = valset_update.encode().into_inner(); + let encoded = HEXLOWER.encode(&encoded); + let expected = "000000000000000000000000000000000000000000000000000000000000002\ + 000000000000000000000000000000000000000000000000000000000000000\ + 400000000000000000000000000000000000000000000000000000000000000\ + 000000000000000000000000000000000000000000000000000000000000000\ + 0001241d37b7cf5233b3b0b204321420a86e8f7bfdb50000000000000000008\ + 6b58b"; + assert_eq!(expected, encoded); + } } From 56977bfdbe7c4a1a35026e575608fd5f89733538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 5 Jan 2024 18:24:18 +0000 Subject: [PATCH 084/118] rename struct Storage to State --- apps/src/lib/node/ledger/shell/mod.rs | 4 +-- apps/src/lib/node/ledger/storage/mod.rs | 4 +-- .../ethereum_bridge/bridge_pool_vp.rs | 6 ++-- .../ledger/native_vp/ethereum_bridge/vp.rs | 4 +-- shared/src/ledger/native_vp/mod.rs | 6 ++-- shared/src/ledger/protocol/mod.rs | 8 +++--- shared/src/ledger/vp_host_fns.rs | 28 +++++++++---------- shared/src/vm/host_env.rs | 16 +++++------ shared/src/vm/wasm/run.rs | 8 +++--- state/src/lib.rs | 10 +++---- state/src/wl_storage.rs | 26 ++++++++--------- state/src/write_log.rs | 4 +-- 12 files changed, 62 insertions(+), 62 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index ec18d9e568..1594ada060 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -51,7 +51,7 @@ use namada::state::tx_queue::{ExpiredTx, TxInQueue}; use namada::state::wl_storage::WriteLogAndStorage; use namada::state::write_log::WriteLog; use namada::state::{ - DBIter, Sha256Hasher, Storage, StorageHasher, TempWlStorage, WlStorage, DB, + DBIter, Sha256Hasher, State, StorageHasher, TempWlStorage, WlStorage, DB, EPOCH_SWITCH_BLOCKS_DELAY, }; use namada::storage::StorageRead; @@ -423,7 +423,7 @@ where }; // load last state from storage - let mut storage = Storage::open( + let mut storage = State::open( db_path, chain_id.clone(), native_token, diff --git a/apps/src/lib/node/ledger/storage/mod.rs b/apps/src/lib/node/ledger/storage/mod.rs index 102c0e0ebc..f7b8058ec7 100644 --- a/apps/src/lib/node/ledger/storage/mod.rs +++ b/apps/src/lib/node/ledger/storage/mod.rs @@ -9,14 +9,14 @@ use arse_merkle_tree::blake2b::Blake2bHasher; use arse_merkle_tree::traits::Hasher; use arse_merkle_tree::H256; use blake2b_rs::{Blake2b, Blake2bBuilder}; -use namada::state::{Storage, StorageHasher}; +use namada::state::{State, StorageHasher}; #[derive(Default)] pub struct PersistentStorageHasher(Blake2bHasher); pub type PersistentDB = rocksdb::RocksDB; -pub type PersistentStorage = Storage; +pub type PersistentStorage = State; impl Hasher for PersistentStorageHasher { fn write_bytes(&mut self, h: &[u8]) { diff --git a/shared/src/ledger/native_vp/ethereum_bridge/bridge_pool_vp.rs b/shared/src/ledger/native_vp/ethereum_bridge/bridge_pool_vp.rs index e1724a680c..4d152ba5ef 100644 --- a/shared/src/ledger/native_vp/ethereum_bridge/bridge_pool_vp.rs +++ b/shared/src/ledger/native_vp/ethereum_bridge/bridge_pool_vp.rs @@ -656,7 +656,7 @@ mod test_bridge_pool_vp { use crate::ledger::storage::mockdb::MockDB; use crate::ledger::storage::traits::Sha256Hasher; use crate::ledger::storage::write_log::WriteLog; - use crate::ledger::storage::{Storage, WlStorage}; + use crate::ledger::storage::{State, WlStorage}; use crate::types::address::{nam, wnam, InternalAddress}; use crate::types::chain::ChainId; use crate::types::eth_bridge_pool::{GasFee, TransferToEthereum}; @@ -914,7 +914,7 @@ mod test_bridge_pool_vp { }, }; let mut wl_storage = WlStorage { - storage: Storage::::open( + storage: State::::open( std::path::Path::new(""), ChainId::default(), address::nam(), @@ -933,7 +933,7 @@ mod test_bridge_pool_vp { /// Setup a ctx for running native vps fn setup_ctx<'a>( tx: &'a Tx, - storage: &'a Storage, + storage: &'a State, write_log: &'a WriteLog, keys_changed: &'a BTreeSet, verifiers: &'a BTreeSet
, diff --git a/shared/src/ledger/native_vp/ethereum_bridge/vp.rs b/shared/src/ledger/native_vp/ethereum_bridge/vp.rs index 320f6b25e4..01c7f63a87 100644 --- a/shared/src/ledger/native_vp/ethereum_bridge/vp.rs +++ b/shared/src/ledger/native_vp/ethereum_bridge/vp.rs @@ -183,7 +183,7 @@ mod tests { use crate::ledger::storage::mockdb::MockDB; use crate::ledger::storage::traits::Sha256Hasher; use crate::ledger::storage::write_log::WriteLog; - use crate::ledger::storage::{Storage, WlStorage}; + use crate::ledger::storage::{State, WlStorage}; use crate::types::address::testing::established_address_1; use crate::types::address::{nam, wnam}; use crate::types::ethereum_events; @@ -247,7 +247,7 @@ mod tests { /// Setup a ctx for running native vps fn setup_ctx<'a>( tx: &'a Tx, - storage: &'a Storage, + storage: &'a State, write_log: &'a WriteLog, keys_changed: &'a BTreeSet, verifiers: &'a BTreeSet
, diff --git a/shared/src/ledger/native_vp/mod.rs b/shared/src/ledger/native_vp/mod.rs index 12e191d31d..9654aec2ce 100644 --- a/shared/src/ledger/native_vp/mod.rs +++ b/shared/src/ledger/native_vp/mod.rs @@ -23,7 +23,7 @@ use super::vp_host_fns; use crate::ledger::gas::VpGasMeter; use crate::ledger::storage; use crate::ledger::storage::write_log::WriteLog; -use crate::ledger::storage::{Storage, StorageHasher}; +use crate::ledger::storage::{State, StorageHasher}; use crate::types::address::Address; use crate::types::hash::Hash; use crate::types::ibc::IbcEvent; @@ -73,7 +73,7 @@ where /// Errors sentinel pub sentinel: RefCell, /// Read-only access to the storage. - pub storage: &'a Storage, + pub storage: &'a State, /// Read-only access to the write log. pub write_log: &'a WriteLog, /// The transaction code is used for signature verification @@ -128,7 +128,7 @@ where #[allow(clippy::too_many_arguments)] pub fn new( address: &'a Address, - storage: &'a Storage, + storage: &'a State, write_log: &'a WriteLog, tx: &'a Tx, tx_index: &'a TxIndex, diff --git a/shared/src/ledger/protocol/mod.rs b/shared/src/ledger/protocol/mod.rs index fa0d53a952..e2a00bf4a8 100644 --- a/shared/src/ledger/protocol/mod.rs +++ b/shared/src/ledger/protocol/mod.rs @@ -31,7 +31,7 @@ use crate::ledger::native_vp::{self, NativeVp}; use crate::ledger::pgf::PgfVp; use crate::ledger::pos::{self, PosVP}; use crate::ledger::storage::write_log::WriteLog; -use crate::ledger::storage::{DBIter, Storage, StorageHasher, WlStorage, DB}; +use crate::ledger::storage::{DBIter, State, StorageHasher, WlStorage, DB}; use crate::types::address::{Address, InternalAddress}; use crate::types::storage; use crate::types::storage::TxIndex; @@ -690,7 +690,7 @@ where fn execute_tx( tx: &Tx, tx_index: &TxIndex, - storage: &Storage, + storage: &State, tx_gas_meter: &mut TxGasMeter, write_log: &mut WriteLog, vp_wasm_cache: &mut VpCache, @@ -726,7 +726,7 @@ where { tx: &'a Tx, tx_index: &'a TxIndex, - storage: &'a Storage, + storage: &'a State, tx_gas_meter: &'a mut TxGasMeter, write_log: &'a WriteLog, verifiers_from_tx: &'a BTreeSet
, @@ -779,7 +779,7 @@ fn execute_vps( keys_changed: BTreeSet, tx: &Tx, tx_index: &TxIndex, - storage: &Storage, + storage: &State, write_log: &WriteLog, tx_gas_meter: &TxGasMeter, vp_wasm_cache: &mut VpCache, diff --git a/shared/src/ledger/vp_host_fns.rs b/shared/src/ledger/vp_host_fns.rs index d0c60ed4fc..6b56c68433 100644 --- a/shared/src/ledger/vp_host_fns.rs +++ b/shared/src/ledger/vp_host_fns.rs @@ -11,7 +11,7 @@ use namada_core::types::storage::{ use namada_core::types::validity_predicate::VpSentinel; use namada_gas::MEMORY_ACCESS_GAS_PER_BYTE; use namada_state::write_log::WriteLog; -use namada_state::{write_log, Storage, StorageHasher}; +use namada_state::{write_log, State, StorageHasher}; use namada_tx::{Section, Tx}; use thiserror::Error; @@ -63,7 +63,7 @@ pub fn add_gas( /// storage. pub fn read_pre( gas_meter: &mut VpGasMeter, - storage: &Storage, + storage: &State, write_log: &WriteLog, key: &Key, sentinel: &mut VpSentinel, @@ -105,7 +105,7 @@ where /// the write log first and if no entry found then from the storage. pub fn read_post( gas_meter: &mut VpGasMeter, - storage: &Storage, + storage: &State, write_log: &WriteLog, key: &Key, sentinel: &mut VpSentinel, @@ -168,7 +168,7 @@ pub fn read_temp( /// from the storage. pub fn has_key_pre( gas_meter: &mut VpGasMeter, - storage: &Storage, + storage: &State, write_log: &WriteLog, key: &Key, sentinel: &mut VpSentinel, @@ -202,7 +202,7 @@ where /// check the write log first and if no entry found then the storage. pub fn has_key_post( gas_meter: &mut VpGasMeter, - storage: &Storage, + storage: &State, write_log: &WriteLog, key: &Key, sentinel: &mut VpSentinel, @@ -235,7 +235,7 @@ where /// Getting the chain ID. pub fn get_chain_id( gas_meter: &mut VpGasMeter, - storage: &Storage, + storage: &State, sentinel: &mut VpSentinel, ) -> EnvResult where @@ -251,7 +251,7 @@ where /// current transaction is being applied. pub fn get_block_height( gas_meter: &mut VpGasMeter, - storage: &Storage, + storage: &State, sentinel: &mut VpSentinel, ) -> EnvResult where @@ -266,7 +266,7 @@ where /// Getting the block header. pub fn get_block_header( gas_meter: &mut VpGasMeter, - storage: &Storage, + storage: &State, height: BlockHeight, sentinel: &mut VpSentinel, ) -> EnvResult> @@ -285,7 +285,7 @@ where /// current transaction is being applied. pub fn get_block_hash( gas_meter: &mut VpGasMeter, - storage: &Storage, + storage: &State, sentinel: &mut VpSentinel, ) -> EnvResult where @@ -320,7 +320,7 @@ pub fn get_tx_code_hash( /// current transaction is being applied. pub fn get_block_epoch( gas_meter: &mut VpGasMeter, - storage: &Storage, + storage: &State, sentinel: &mut VpSentinel, ) -> EnvResult where @@ -350,7 +350,7 @@ pub fn get_tx_index( /// Getting the native token's address. pub fn get_native_token( gas_meter: &mut VpGasMeter, - storage: &Storage, + storage: &State, sentinel: &mut VpSentinel, ) -> EnvResult
where @@ -368,7 +368,7 @@ where /// Given the information about predecessor block epochs pub fn get_pred_epochs( gas_meter: &mut VpGasMeter, - storage: &Storage, + storage: &State, sentinel: &mut VpSentinel, ) -> EnvResult where @@ -404,7 +404,7 @@ pub fn get_ibc_events( pub fn iter_prefix_pre<'a, DB, H>( gas_meter: &mut VpGasMeter, write_log: &'a WriteLog, - storage: &'a Storage, + storage: &'a State, prefix: &Key, sentinel: &mut VpSentinel, ) -> EnvResult> @@ -422,7 +422,7 @@ where pub fn iter_prefix_post<'a, DB, H>( gas_meter: &mut VpGasMeter, write_log: &'a WriteLog, - storage: &'a Storage, + storage: &'a State, prefix: &Key, sentinel: &mut VpSentinel, ) -> EnvResult> diff --git a/shared/src/vm/host_env.rs b/shared/src/vm/host_env.rs index 1d53591a76..ee56968bd3 100644 --- a/shared/src/vm/host_env.rs +++ b/shared/src/vm/host_env.rs @@ -16,7 +16,7 @@ use namada_gas::{ MEMORY_ACCESS_GAS_PER_BYTE, }; use namada_state::write_log::{self, WriteLog}; -use namada_state::{Storage, StorageHasher}; +use namada_state::{State, StorageHasher}; use namada_storage::{self, ResultExt}; use namada_token::storage_key::{ balance_key, is_any_minted_balance_key, is_any_minter_key, @@ -100,7 +100,7 @@ where CA: WasmCacheAccess, { /// Read-only access to the storage. - pub storage: HostRef<'a, &'a Storage>, + pub storage: HostRef<'a, &'a State>, /// Read/write access to the write log. pub write_log: MutHostRef<'a, &'a WriteLog>, /// Storage prefix iterators. @@ -147,7 +147,7 @@ where #[allow(clippy::too_many_arguments)] pub fn new( memory: MEM, - storage: &Storage, + storage: &State, write_log: &mut WriteLog, iterators: &mut PrefixIterators<'a, DB>, gas_meter: &mut TxGasMeter, @@ -262,7 +262,7 @@ where /// The address of the account that owns the VP pub address: HostRef<'a, &'a Address>, /// Read-only access to the storage. - pub storage: HostRef<'a, &'a Storage>, + pub storage: HostRef<'a, &'a State>, /// Read-only access to the write log. pub write_log: HostRef<'a, &'a WriteLog>, /// Storage prefix iterators. @@ -336,7 +336,7 @@ where pub fn new( memory: MEM, address: &Address, - storage: &Storage, + storage: &State, write_log: &WriteLog, gas_meter: &mut VpGasMeter, sentinel: &mut VpSentinel, @@ -403,7 +403,7 @@ where #[allow(clippy::too_many_arguments)] pub fn new( address: &Address, - storage: &Storage, + storage: &State, write_log: &WriteLog, gas_meter: &mut VpGasMeter, sentinel: &mut VpSentinel, @@ -2685,7 +2685,7 @@ pub mod testing { /// Setup a transaction environment #[allow(clippy::too_many_arguments)] pub fn tx_env( - storage: &Storage, + storage: &State, write_log: &mut WriteLog, iterators: &mut PrefixIterators<'static, DB>, verifiers: &mut BTreeSet
, @@ -2724,7 +2724,7 @@ pub mod testing { #[allow(clippy::too_many_arguments)] pub fn vp_env( address: &Address, - storage: &Storage, + storage: &State, write_log: &WriteLog, iterators: &mut PrefixIterators<'static, DB>, gas_meter: &mut VpGasMeter, diff --git a/shared/src/vm/wasm/run.rs b/shared/src/vm/wasm/run.rs index ac72aeee81..b4360b2905 100644 --- a/shared/src/vm/wasm/run.rs +++ b/shared/src/vm/wasm/run.rs @@ -7,7 +7,7 @@ use borsh::BorshDeserialize; use namada_core::types::validity_predicate::VpSentinel; use namada_gas::{GasMetering, TxGasMeter, WASM_MEMORY_PAGE_GAS}; use namada_state::write_log::StorageModification; -use namada_state::{Storage, StorageHasher}; +use namada_state::{State, StorageHasher}; use namada_tx::data::TxSentinel; use namada_tx::{Commitment, Section, Tx}; use parity_wasm::elements; @@ -92,7 +92,7 @@ pub type Result = std::result::Result; /// the transaction. #[allow(clippy::too_many_arguments)] pub fn tx( - storage: &Storage, + storage: &State, write_log: &mut WriteLog, gas_meter: &mut TxGasMeter, tx_index: &TxIndex, @@ -224,7 +224,7 @@ pub fn vp( tx: &Tx, tx_index: &TxIndex, address: &Address, - storage: &Storage, + storage: &State, write_log: &WriteLog, gas_meter: &mut VpGasMeter, keys_changed: &BTreeSet, @@ -508,7 +508,7 @@ fn fetch_or_compile( wasm_cache: &mut Cache, code_or_hash: &Commitment, write_log: &WriteLog, - storage: &Storage, + storage: &State, gas_meter: &mut dyn GasMetering, ) -> Result<(Module, Store)> where diff --git a/state/src/lib.rs b/state/src/lib.rs index 448fcfbe01..bcb429fefd 100644 --- a/state/src/lib.rs +++ b/state/src/lib.rs @@ -49,9 +49,9 @@ pub type Result = std::result::Result; /// it has 2 blocks delay on validator set update. pub const EPOCH_SWITCH_BLOCKS_DELAY: u32 = 2; -/// The storage data +/// The ledger's state #[derive(Debug)] -pub struct Storage +pub struct State where D: DB + for<'iter> DBIter<'iter>, H: StorageHasher, @@ -166,7 +166,7 @@ pub enum Error { InvalidCodeHash(HashError), } -impl Storage +impl State where D: DB + for<'iter> DBIter<'iter>, H: StorageHasher, @@ -187,7 +187,7 @@ where pred_epochs: Epochs::default(), results: BlockResults::default(), }; - Storage:: { + State:: { db: D::open(db_path, cache), chain_id, block, @@ -1060,7 +1060,7 @@ pub mod testing { /// Prefer to use [`TestWlStorage`], which implements /// `storage_api::StorageRead + StorageWrite` with properly working /// `prefix_iter`. - pub type TestStorage = Storage; + pub type TestStorage = State; impl Default for TestStorage { fn default() -> Self { diff --git a/state/src/wl_storage.rs b/state/src/wl_storage.rs index 5aed144914..1a91a69b62 100644 --- a/state/src/wl_storage.rs +++ b/state/src/wl_storage.rs @@ -11,7 +11,7 @@ use namada_storage::{ResultExt, StorageRead, StorageWrite}; use super::EPOCH_SWITCH_BLOCKS_DELAY; use crate::write_log::{self, WriteLog}; -use crate::{DBIter, Storage, DB}; +use crate::{DBIter, State, DB}; /// Storage with write log that allows to implement prefix iterator that works /// with changes not yet committed to the DB. @@ -24,7 +24,7 @@ where /// Write log pub write_log: WriteLog, /// Storage provides access to DB - pub storage: Storage, + pub storage: State, } /// Temporary storage that can be used for changes that will never be committed @@ -40,7 +40,7 @@ where /// Write log pub write_log: WriteLog, /// Storage provides access to DB - pub storage: &'a Storage, + pub storage: &'a State, } impl<'a, D, H> TempWlStorage<'a, D, H> @@ -50,7 +50,7 @@ where { /// Create a temp storage that can mutated in memory, but never committed to /// DB. - pub fn new(storage: &'a Storage) -> Self { + pub fn new(storage: &'a State) -> Self { Self { write_log: WriteLog::default(), storage, @@ -94,12 +94,12 @@ pub trait WriteLogAndStorage { fn write_log_mut(&mut self) -> &mut WriteLog; /// Borrow `Storage` - fn storage(&self) -> &Storage; + fn storage(&self) -> &State; /// Splitting borrow to get immutable reference to the `Storage` and mutable /// reference to `WriteLog` when in need of both (avoids complain from the /// borrow checker) - fn split_borrow(&mut self) -> (&mut WriteLog, &Storage); + fn split_borrow(&mut self) -> (&mut WriteLog, &State); /// Write the provided tx hash to storage. fn write_tx_hash(&mut self, hash: Hash) -> write_log::Result<()>; @@ -121,11 +121,11 @@ where &mut self.write_log } - fn storage(&self) -> &Storage { + fn storage(&self) -> &State { &self.storage } - fn split_borrow(&mut self) -> (&mut WriteLog, &Storage) { + fn split_borrow(&mut self) -> (&mut WriteLog, &State) { (&mut self.write_log, &self.storage) } @@ -150,11 +150,11 @@ where &mut self.write_log } - fn storage(&self) -> &Storage { + fn storage(&self) -> &State { self.storage } - fn split_borrow(&mut self) -> (&mut WriteLog, &Storage) { + fn split_borrow(&mut self) -> (&mut WriteLog, &State) { (&mut self.write_log, (self.storage)) } @@ -169,7 +169,7 @@ where H: 'static + StorageHasher, { /// Combine storage with write-log - pub fn new(write_log: WriteLog, storage: Storage) -> Self { + pub fn new(write_log: WriteLog, storage: State) -> Self { Self { write_log, storage } } @@ -280,7 +280,7 @@ pub fn iter_prefix_pre<'iter, D, H>( // 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, + storage: &'iter State, prefix: &storage::Key, ) -> (PrefixIter<'iter, D>, u64) where @@ -305,7 +305,7 @@ pub fn iter_prefix_post<'iter, D, H>( // 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, + storage: &'iter State, prefix: &storage::Key, ) -> (PrefixIter<'iter, D>, u64) where diff --git a/state/src/write_log.rs b/state/src/write_log.rs index e2b91c16bd..041108f7cd 100644 --- a/state/src/write_log.rs +++ b/state/src/write_log.rs @@ -17,7 +17,7 @@ use namada_trans_token::storage_key::{ }; use thiserror::Error; -use crate::{DBIter, Storage, DB}; +use crate::{DBIter, State, DB}; #[allow(missing_docs)] #[derive(Error, Debug)] @@ -496,7 +496,7 @@ impl WriteLog { /// write log. pub fn commit_block( &mut self, - storage: &mut Storage, + storage: &mut State, batch: &mut D::WriteBatch, ) -> Result<()> where From 781496fd0f30b1ba8dc13c85b53eca85eab67c88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 5 Jan 2024 18:31:19 +0000 Subject: [PATCH 085/118] merkle_tree: fix tests --- Cargo.lock | 3 +++ merkle_tree/Cargo.toml | 5 +++++ merkle_tree/src/eth_bridge_pool.rs | 14 +++++++------- merkle_tree/src/lib.rs | 6 ++---- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f63e26ccab..7fd69cf464 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4556,10 +4556,13 @@ dependencies = [ name = "namada_merkle_tree" version = "0.29.0" dependencies = [ + "assert_matches", "borsh", "eyre", "ics23", + "itertools 0.10.5", "namada_core", + "proptest", "prost 0.12.3", "sparse-merkle-tree", "thiserror", diff --git a/merkle_tree/Cargo.toml b/merkle_tree/Cargo.toml index 6fc7e1bcc8..79587aef18 100644 --- a/merkle_tree/Cargo.toml +++ b/merkle_tree/Cargo.toml @@ -21,3 +21,8 @@ eyre.workspace = true ics23.workspace = true prost.workspace = true thiserror.workspace = true + +[dev-dependencies] +assert_matches.workspace = true +proptest.workspace = true +itertools.workspace = true \ No newline at end of file diff --git a/merkle_tree/src/eth_bridge_pool.rs b/merkle_tree/src/eth_bridge_pool.rs index 7082489307..b8c30a36aa 100644 --- a/merkle_tree/src/eth_bridge_pool.rs +++ b/merkle_tree/src/eth_bridge_pool.rs @@ -371,17 +371,17 @@ impl Encode<3> for BridgePoolProof { #[cfg(test)] mod test_bridge_pool_tree { - use std::assert_matches; - + use assert_matches::assert_matches; use itertools::Itertools; + use namada_core::types::address::{nam, Address}; + use namada_core::types::eth_bridge_pool::{ + GasFee, TransferToEthereum, TransferToEthereumKind, + }; + use namada_core::types::ethereum_events::EthAddress; + use namada_core::types::storage::Key; use proptest::prelude::*; use super::*; - use crate::types::address::nam; - use crate::types::eth_bridge_pool::{ - GasFee, TransferToEthereum, TransferToEthereumKind, - }; - use crate::types::ethereum_events::EthAddress; /// An established user address for testing & development fn bertha_address() -> Address { diff --git a/merkle_tree/src/lib.rs b/merkle_tree/src/lib.rs index fd4a301896..eb55878a82 100644 --- a/merkle_tree/src/lib.rs +++ b/merkle_tree/src/lib.rs @@ -1004,13 +1004,11 @@ impl<'a> SubTreeWrite for &'a mut BridgePoolTree { #[cfg(test)] mod test { use ics23::HostFunctionsManager; - use namada_core::ledger::storage::ics23_specs::{ - ibc_proof_specs, proof_specs, - }; - use namada_core::ledger::storage::traits::Sha256Hasher; + use namada_core::types::hash::Sha256Hasher; use namada_core::types::storage::KeySeg; use super::*; + use crate::ics23_specs::{ibc_proof_specs, proof_specs}; #[test] fn test_crud_value() { From 62148291c3f9ebde3d2ccbb571e6aeaf36424188 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 5 Jan 2024 18:32:36 +0000 Subject: [PATCH 086/118] gas: fix tests --- Cargo.lock | 2 ++ gas/Cargo.toml | 4 ++++ gas/src/lib.rs | 1 + 3 files changed, 7 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 7fd69cf464..45eb7cfcb0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4487,8 +4487,10 @@ dependencies = [ name = "namada_gas" version = "0.29.0" dependencies = [ + "assert_matches", "borsh", "namada_core", + "proptest", "serde 1.0.193", "thiserror", ] diff --git a/gas/Cargo.toml b/gas/Cargo.toml index c998d83ab6..381fa922b0 100644 --- a/gas/Cargo.toml +++ b/gas/Cargo.toml @@ -18,3 +18,7 @@ namada_core = { path = "../core", default-features = false } borsh.workspace = true serde.workspace = true thiserror.workspace = true + +[dev-dependencies] +assert_matches.workspace = true +proptest.workspace = true \ No newline at end of file diff --git a/gas/src/lib.rs b/gas/src/lib.rs index c090a6f782..6b1b94e761 100644 --- a/gas/src/lib.rs +++ b/gas/src/lib.rs @@ -384,6 +384,7 @@ impl VpsGas { #[cfg(test)] mod tests { + use assert_matches::assert_matches; use proptest::prelude::*; use super::*; From 25f45936a2c6da78436dd7cd5aa96d8cb0af528f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 5 Jan 2024 18:37:51 +0000 Subject: [PATCH 087/118] tx: fix tests --- Cargo.lock | 2 ++ tx/Cargo.toml | 3 +++ tx/src/data/mod.rs | 7 ++++--- tx/src/data/pgf.rs | 3 ++- tx/src/lib.rs | 3 ++- 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 45eb7cfcb0..3550d65bdb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4802,6 +4802,7 @@ name = "namada_tx" version = "0.29.0" dependencies = [ "ark-bls12-381", + "assert_matches", "borsh", "data-encoding", "masp_primitives", @@ -4812,6 +4813,7 @@ dependencies = [ "proptest", "prost 0.12.3", "prost-types 0.12.3", + "rand 0.8.5", "serde 1.0.193", "serde_json", "sha2 0.9.9", diff --git a/tx/Cargo.toml b/tx/Cargo.toml index 7f1f4a6522..0bca3f1334 100644 --- a/tx/Cargo.toml +++ b/tx/Cargo.toml @@ -36,7 +36,10 @@ thiserror.workspace = true [dev-dependencies] namada_core = { path = "../core", features = ["testing"] } + +assert_matches.workspace = true proptest.workspace = true +rand.workspace = true [build-dependencies] tonic-build.workspace = true \ No newline at end of file diff --git a/tx/src/data/mod.rs b/tx/src/data/mod.rs index a4923e1de4..f3d93131b7 100644 --- a/tx/src/data/mod.rs +++ b/tx/src/data/mod.rs @@ -340,13 +340,14 @@ impl TxSentinel { #[cfg(test)] mod test_process_tx { + use assert_matches::assert_matches; use namada_core::types::address::nam; use namada_core::types::key::*; use namada_core::types::storage::Epoch; use namada_core::types::token::{Amount, DenominatedAmount}; use super::*; - use crate::proto::{Code, Data, Section, Signature, Tx, TxError}; + use crate::{Code, Data, Section, Signature, Tx, TxError}; fn gen_keypair() -> common::SecretKey { use rand::prelude::ThreadRng; @@ -481,7 +482,7 @@ mod test_process_tx { /// with some unsigned data and returns an identical copy #[test] fn test_process_tx_decrypted_unsigned() { - use crate::proto::{Code, Data, Tx}; + use crate::{Code, Data, Tx}; let mut tx = Tx::from_type(TxType::Decrypted(DecryptedTx::Decrypted)); let code_sec = tx .set_code(Code::new("transaction data".as_bytes().to_owned(), None)) @@ -506,7 +507,7 @@ fn test_process_tx_decrypted_unsigned() { fn test_process_tx_decrypted_signed() { use namada_core::types::key::*; - use crate::proto::{Code, Data, Section, Signature, Tx}; + use crate::{Code, Data, Section, Signature, Tx}; fn gen_keypair() -> common::SecretKey { use rand::prelude::ThreadRng; diff --git a/tx/src/data/pgf.rs b/tx/src/data/pgf.rs index 1151aa18a7..284b930dd1 100644 --- a/tx/src/data/pgf.rs +++ b/tx/src/data/pgf.rs @@ -35,9 +35,10 @@ pub struct UpdateStewardCommission { pub mod tests { use namada_core::types::address::testing::arb_non_internal_address; use namada_core::types::dec::testing::arb_dec; - use namada_core::types::transaction::pgf::UpdateStewardCommission; use proptest::{collection, prop_compose}; + use super::UpdateStewardCommission; + prop_compose! { /// Generate an arbitraary steward commission update pub fn arb_update_steward_commission()( diff --git a/tx/src/lib.rs b/tx/src/lib.rs index 2364b28833..842d06b223 100644 --- a/tx/src/lib.rs +++ b/tx/src/lib.rs @@ -15,13 +15,14 @@ pub use types::{ #[cfg(test)] mod tests { use data_encoding::HEXLOWER; - use generated::types::Tx; use prost::Message; use super::*; #[test] fn encoding_round_trip() { + use proto::Tx; + let tx = Tx { data: "arbitrary data".as_bytes().to_owned(), }; From 51b8d42f8a435050b4d7d3a9986f30a7c9e20a0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 5 Jan 2024 18:40:26 +0000 Subject: [PATCH 088/118] vote_ext: fix tests --- vote_ext/Cargo.toml | 2 ++ vote_ext/src/ethereum_events.rs | 2 +- vote_ext/src/validator_set_update.rs | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/vote_ext/Cargo.toml b/vote_ext/Cargo.toml index ebdba3037b..c89888aa1c 100644 --- a/vote_ext/Cargo.toml +++ b/vote_ext/Cargo.toml @@ -20,4 +20,6 @@ borsh.workspace = true serde.workspace = true [dev-dependencies] +namada_core = { path = "../core", features = ["testing"] } + data-encoding.workspace = true diff --git a/vote_ext/src/ethereum_events.rs b/vote_ext/src/ethereum_events.rs index 3988caa4b3..5fe5b5fcac 100644 --- a/vote_ext/src/ethereum_events.rs +++ b/vote_ext/src/ethereum_events.rs @@ -151,7 +151,7 @@ mod tests { use namada_core::types::ethereum_events::{EthereumEvent, Uint}; use namada_core::types::hash::Hash; use namada_core::types::key; - use namada_tx::proto::Signed; + use namada_tx::Signed; use super::*; diff --git a/vote_ext/src/validator_set_update.rs b/vote_ext/src/validator_set_update.rs index d2231f39e6..2e8814e8dc 100644 --- a/vote_ext/src/validator_set_update.rs +++ b/vote_ext/src/validator_set_update.rs @@ -425,6 +425,8 @@ pub use tag::SerializeWithAbiEncode; #[cfg(test)] mod tests { + use std::str::FromStr; + use data_encoding::HEXLOWER; use namada_core::types::ethereum_events::EthAddress; From 5510672b504d20fa22eae8b625d6709b07ddb0bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 5 Jan 2024 18:41:03 +0000 Subject: [PATCH 089/118] account: fix tests --- Cargo.lock | 1 + account/Cargo.toml | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 3550d65bdb..4fdf4d1d07 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4268,6 +4268,7 @@ dependencies = [ "namada_core", "namada_macros", "namada_storage", + "proptest", "serde 1.0.193", ] diff --git a/account/Cargo.toml b/account/Cargo.toml index 01fa06c504..dac36bb7d6 100644 --- a/account/Cargo.toml +++ b/account/Cargo.toml @@ -19,3 +19,8 @@ namada_storage = { path = "../storage" } borsh.workspace = true serde.workspace = true + +[dev-dependencies] +namada_core = { path = "../core", features = ["testing"] } + +proptest.workspace = true \ No newline at end of file From 1e01a894110eb5b92f54e4c90a5618e49697924b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 5 Jan 2024 18:42:10 +0000 Subject: [PATCH 090/118] parameters: fix warning --- parameters/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/parameters/src/lib.rs b/parameters/src/lib.rs index 71a93a95f4..ab17af45a4 100644 --- a/parameters/src/lib.rs +++ b/parameters/src/lib.rs @@ -3,7 +3,6 @@ pub mod storage; use std::collections::BTreeMap; -use namada_core::borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use namada_core::types::address::{Address, InternalAddress}; use namada_core::types::chain::ProposalBytes; use namada_core::types::dec::Dec; From 399193e06e33996dff8a239dbd00cb23c35a488d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 5 Jan 2024 18:54:01 +0000 Subject: [PATCH 091/118] state: fix tests --- Cargo.lock | 4 ++++ Cargo.toml | 1 + governance/src/storage/proposal.rs | 1 + state/Cargo.toml | 8 ++++++- state/src/lib.rs | 37 +++++++++++++++++++----------- state/src/wl_storage.rs | 5 ++-- state/src/write_log.rs | 11 ++++----- storage/src/lib.rs | 2 +- 8 files changed, 44 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4fdf4d1d07..c6f306999a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4688,7 +4688,9 @@ dependencies = [ name = "namada_state" version = "0.29.0" dependencies = [ + "assert_matches", "borsh", + "chrono", "ics23", "itertools 0.10.5", "namada_core", @@ -4698,9 +4700,11 @@ dependencies = [ "namada_storage", "namada_trans_token", "namada_tx", + "pretty_assertions", "proptest", "sha2 0.9.9", "sparse-merkle-tree", + "test-log", "thiserror", "tiny-keccak", "tracing", diff --git a/Cargo.toml b/Cargo.toml index 3ade8d2f65..4a2bf34532 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -155,6 +155,7 @@ tendermint-rpc = {version = "0.34.0", default-features = false} test-log = {version = "0.2.14", default-features = false, features = ["trace"]} tiny-bip39 = {git = "https://github.com/anoma/tiny-bip39.git", rev = "bf0f6d8713589b83af7a917366ec31f5275c0e57"} tiny-hderive = "0.3.0" +tiny-keccak = { version = "2.0.2", features = ["keccak"] } thiserror = "1.0.38" tokio = {version = "1.8.2", default-features = false} tokio-test = "0.4.2" diff --git a/governance/src/storage/proposal.rs b/governance/src/storage/proposal.rs index 6ff9bd7c30..971c8ee8a5 100644 --- a/governance/src/storage/proposal.rs +++ b/governance/src/storage/proposal.rs @@ -454,6 +454,7 @@ pub mod testing { use proptest::{collection, option, prop_compose}; use super::*; + use crate::storage::vote::testing::arb_proposal_vote; /// Generate an arbitrary add or removal of what's generated by the supplied /// strategy diff --git a/state/Cargo.toml b/state/Cargo.toml index e8beaf298b..48460ae64f 100644 --- a/state/Cargo.toml +++ b/state/Cargo.toml @@ -33,9 +33,15 @@ ics23.workspace = true itertools.workspace = true sha2.workspace = true thiserror.workspace = true -tiny-keccak = { version = "2.0.2", features = ["keccak"] } +tiny-keccak.workspace = true tracing.workspace = true proptest = { workspace = true, optional = true } [dev-dependencies] +namada_core = { path = "../core", features = ["testing"] } + +assert_matches.workspace = true +chrono.workspace = true +pretty_assertions.workspace = true proptest.workspace = true +test-log.workspace = true diff --git a/state/src/lib.rs b/state/src/lib.rs index bcb429fefd..22ea0a10e5 100644 --- a/state/src/lib.rs +++ b/state/src/lib.rs @@ -14,7 +14,6 @@ use namada_core::types::address::{ }; use namada_core::types::chain::{ChainId, CHAIN_ID_LENGTH}; use namada_core::types::eth_bridge_pool::is_pending_transfer_key; -use namada_core::types::ethereum_events::Uint; use namada_core::types::hash::{Error as HashError, Hash}; pub use namada_core::types::hash::{Sha256Hasher, StorageHasher}; use namada_core::types::storage::{ @@ -164,6 +163,8 @@ pub enum Error { NoMerkleTree { height: BlockHeight }, #[error("Code hash error: {0}")] InvalidCodeHash(HashError), + #[error("DB error: {0}")] + DbError(#[from] namada_storage::DbError), } impl State @@ -324,7 +325,8 @@ where // prune old merkle tree stores self.prune_merkle_tree_stores(&mut batch)?; } - self.db.exec_batch(batch) + self.db.exec_batch(batch)?; + Ok(()) } /// Find the root hash of the merkle tree @@ -853,7 +855,7 @@ where /// Execute write batch. pub fn exec_batch(&mut self, batch: D::WriteBatch) -> Result<()> { - self.db.exec_batch(batch) + Ok(self.db.exec_batch(batch)?) } /// Batch write the value with the given height and account subspace key to @@ -875,8 +877,12 @@ where // Update the merkle tree self.block.tree.update(key, value)?; } - self.db - .batch_write_subspace_val(batch, self.block.height, key, value) + Ok(self.db.batch_write_subspace_val( + batch, + self.block.height, + key, + value, + )?) } /// Batch delete the value with the given height and account subspace key @@ -889,8 +895,9 @@ where ) -> Result { // Update the merkle tree self.block.tree.delete(key)?; - self.db - .batch_delete_subspace_val(batch, self.block.height, key) + Ok(self + .db + .batch_delete_subspace_val(batch, self.block.height, key)?) } // Prune merkle tree stores. Use after updating self.block.height in the @@ -1006,7 +1013,7 @@ where /// Check it the given transaction's hash is already present in storage pub fn has_replay_protection_entry(&self, hash: &Hash) -> Result { - self.db.has_replay_protection_entry(hash) + Ok(self.db.has_replay_protection_entry(hash)?) } /// Write the provided tx hash to storage @@ -1015,7 +1022,8 @@ where batch: &mut D::WriteBatch, key: &Key, ) -> Result<()> { - self.db.write_replay_protection_entry(batch, key) + self.db.write_replay_protection_entry(batch, key)?; + Ok(()) } /// Delete the provided tx hash from storage @@ -1024,7 +1032,8 @@ where batch: &mut D::WriteBatch, key: &Key, ) -> Result<()> { - self.db.delete_replay_protection_entry(batch, key) + self.db.delete_replay_protection_entry(batch, key)?; + Ok(()) } /// Iterate the replay protection storage from the last block @@ -1118,12 +1127,12 @@ mod tests { use namada_core::types::dec::Dec; use namada_core::types::time::{self, Duration}; use namada_core::types::token; + use namada_parameters::Parameters; use proptest::prelude::*; use proptest::test_runner::Config; use super::testing::*; use super::*; - use crate::ledger::parameters::{self, Parameters}; prop_compose! { /// Setup test input data with arbitrary epoch duration, epoch start @@ -1210,7 +1219,7 @@ mod tests { fee_unshielding_descriptions_limit: 15, minimum_gas_price: BTreeMap::default(), }; - parameters.init_storage(&mut wl_storage).unwrap(); + namada_parameters::init_storage(¶meters, &mut wl_storage).unwrap(); // Initialize pred_epochs to the current height wl_storage .storage @@ -1280,8 +1289,8 @@ mod tests { Duration::seconds(min_duration + min_duration_delta).into(); parameters.max_expected_time_per_block = Duration::seconds(max_expected_time_per_block + max_time_per_block_delta).into(); - parameters::update_max_expected_time_per_block_parameter(&mut wl_storage, ¶meters.max_expected_time_per_block).unwrap(); - parameters::update_epoch_parameter(&mut wl_storage, ¶meters.epoch_duration).unwrap(); + namada_parameters::update_max_expected_time_per_block_parameter(&mut wl_storage, ¶meters.max_expected_time_per_block).unwrap(); + namada_parameters::update_epoch_parameter(&mut wl_storage, ¶meters.epoch_duration).unwrap(); // Test for 2. let epoch_before = wl_storage.storage.block.epoch; diff --git a/state/src/wl_storage.rs b/state/src/wl_storage.rs index 1a91a69b62..b190d2a29e 100644 --- a/state/src/wl_storage.rs +++ b/state/src/wl_storage.rs @@ -553,8 +553,7 @@ impl_storage_traits!(TempWlStorage<'_, D, H>); mod tests { use std::collections::BTreeMap; - use borsh::BorshDeserialize; - use borsh_ext::BorshSerializeExt; + use namada_core::borsh::{BorshDeserialize, BorshSerializeExt}; use namada_core::types::address::InternalAddress; use namada_core::types::storage::DbKeySeg; use proptest::prelude::*; @@ -564,7 +563,7 @@ mod tests { use test_log::test; use super::*; - use crate::ledger::storage::testing::TestWlStorage; + use crate::testing::TestWlStorage; proptest! { // Generate arb valid input for `test_prefix_iters_aux` diff --git a/state/src/write_log.rs b/state/src/write_log.rs index 041108f7cd..28ca56cd2d 100644 --- a/state/src/write_log.rs +++ b/state/src/write_log.rs @@ -727,6 +727,7 @@ impl WriteLog { #[cfg(test)] mod tests { + use assert_matches::assert_matches; use namada_core::types::hash::Hash; use namada_core::types::{address, storage}; use pretty_assertions::assert_eq; @@ -893,10 +894,9 @@ mod tests { #[test] fn test_commit() { - let mut storage = - crate::ledger::storage::testing::TestStorage::default(); + let mut storage = crate::testing::TestStorage::default(); let mut write_log = WriteLog::default(); - let mut batch = crate::ledger::storage::testing::TestStorage::batch(); + let mut batch = crate::testing::TestStorage::batch(); let address_gen = EstablishedAddressGen::new("test"); let key1 = @@ -954,10 +954,9 @@ mod tests { #[test] fn test_replay_protection_commit() { - let mut storage = - crate::ledger::storage::testing::TestStorage::default(); + let mut storage = crate::testing::TestStorage::default(); let mut write_log = WriteLog::default(); - let mut batch = crate::ledger::storage::testing::TestStorage::batch(); + let mut batch = crate::testing::TestStorage::batch(); // write some replay protection keys write_log diff --git a/storage/src/lib.rs b/storage/src/lib.rs index 2b92068287..934375c318 100644 --- a/storage/src/lib.rs +++ b/storage/src/lib.rs @@ -8,7 +8,7 @@ pub mod mockdb; pub mod tx_queue; pub mod types; -pub use db::*; +pub use db::{Error as DbError, *}; pub use error::{CustomError, Error, OptionExt, Result, ResultExt}; use namada_core::borsh::{BorshDeserialize, BorshSerialize, BorshSerializeExt}; use namada_core::types::address::Address; From c98e9e4e05456f06fce13dfc0dbe1f64ac92c5ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 5 Jan 2024 19:01:37 +0000 Subject: [PATCH 092/118] shielded_token: fix tests --- Cargo.lock | 2 ++ shielded_token/Cargo.toml | 8 ++++++++ shielded_token/src/conversion.rs | 19 +++++++++++++------ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c6f306999a..22c0f6996e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4680,7 +4680,9 @@ dependencies = [ "namada_state", "namada_storage", "namada_trans_token", + "proptest", "rayon", + "test-log", "tracing", ] diff --git a/shielded_token/Cargo.toml b/shielded_token/Cargo.toml index 7eb57e7c19..82a27765b7 100644 --- a/shielded_token/Cargo.toml +++ b/shielded_token/Cargo.toml @@ -27,3 +27,11 @@ namada_trans_token = { path = "../trans_token" } masp_primitives.workspace = true rayon = { workspace = true, optional = true } tracing.workspace = true + +[dev-dependencies] +namada_core = { path = "../core", features = ["testing"] } +namada_state = { path = "../state", features = ["testing"] } + +proptest.workspace = true +rayon.workspace = true +test-log.workspace = true \ No newline at end of file diff --git a/shielded_token/src/conversion.rs b/shielded_token/src/conversion.rs index 3279842513..4d8e5c3cd2 100644 --- a/shielded_token/src/conversion.rs +++ b/shielded_token/src/conversion.rs @@ -488,16 +488,16 @@ mod tests { use namada_core::types::address; use namada_core::types::dec::testing::arb_non_negative_dec; - use namada_core::types::testing::arb_amount; use namada_core::types::time::DurationSecs; + use namada_core::types::token::testing::arb_amount; use namada_parameters::{EpochDuration, Parameters}; - use namada_storage::write_denom; + use namada_state::testing::TestWlStorage; + use namada_trans_token::{write_denom, Denomination}; use proptest::prelude::*; use proptest::test_runner::Config; use test_log::test; use super::*; - use crate::ledger::storage::testing::TestWlStorage; proptest! { #![proptest_config(Config { @@ -544,10 +544,10 @@ mod tests { // Initialize the state { // Parameters - params.init_storage(&mut s).unwrap(); + namada_parameters::init_storage(¶ms, &mut s).unwrap(); // Tokens - let token_params = Parameters { + let token_params = namada_trans_token::Parameters { max_reward_rate: Dec::from_str("0.1").unwrap(), kp_gain_nom: Dec::from_str("0.1").unwrap(), kd_gain_nom: Dec::from_str("0.1").unwrap(), @@ -555,7 +555,14 @@ mod tests { }; for (token_addr, (alias, denom)) in tokens() { - token_params.init_storage(&token_addr, &mut s); + namada_trans_token::write_params( + &token_params, + &mut s, + &token_addr, + ) + .unwrap(); + crate::write_params(&token_params, &mut s, &token_addr) + .unwrap(); write_denom(&mut s, &token_addr, denom).unwrap(); From 47580b28041c391916a596122d15522d82e6a840 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Mon, 8 Jan 2024 09:34:36 +0000 Subject: [PATCH 093/118] proof_of_stake: fix tests --- proof_of_stake/src/rewards.rs | 4 +-- proof_of_stake/src/tests/state_machine.rs | 4 +-- proof_of_stake/src/tests/state_machine_v2.rs | 6 ++--- proof_of_stake/src/tests/test_helper_fns.rs | 16 +++++++----- proof_of_stake/src/tests/test_pos.rs | 25 ++++++++++--------- .../src/tests/test_slash_and_redel.rs | 22 ++++++++++------ proof_of_stake/src/tests/test_validator.rs | 18 ++++++------- 7 files changed, 51 insertions(+), 44 deletions(-) diff --git a/proof_of_stake/src/rewards.rs b/proof_of_stake/src/rewards.rs index 7107254188..edd5df9e10 100644 --- a/proof_of_stake/src/rewards.rs +++ b/proof_of_stake/src/rewards.rs @@ -11,8 +11,6 @@ use namada_core::types::uint::{Uint, I256}; use namada_parameters::storage as params_storage; use namada_storage::collections::lazy_map::NestedSubKey; use namada_storage::{ResultExt, StorageRead, StorageWrite}; -use namada_trans_token::credit_tokens; -use namada_trans_token::storage_key::minted_balance_key; use thiserror::Error; use crate::storage::{ @@ -21,6 +19,8 @@ use crate::storage::{ rewards_accumulator_handle, validator_commission_rate_handle, validator_rewards_products_handle, validator_state_handle, }; +use crate::token::credit_tokens; +use crate::token::storage_key::minted_balance_key; use crate::types::{into_tm_voting_power, BondId, ValidatorState, VoteInfo}; use crate::{ bond_amounts_for_rewards, get_total_consensus_stake, staking_token_address, diff --git a/proof_of_stake/src/tests/state_machine.rs b/proof_of_stake/src/tests/state_machine.rs index 9903e34c00..87562ae26a 100644 --- a/proof_of_stake/src/tests/state_machine.rs +++ b/proof_of_stake/src/tests/state_machine.rs @@ -16,8 +16,7 @@ use namada_state::testing::TestWlStorage; use namada_storage::collections::lazy_map::{ Collectable, NestedSubKey, SubKey, }; -use namada_storage::{token, StorageRead}; -use namada_trans_token::read_balance; +use namada_storage::StorageRead; use proptest::prelude::*; use proptest::test_runner::Config; use proptest_state_machine::{ @@ -37,6 +36,7 @@ use crate::storage::{ read_consensus_validator_set_addresses_with_stake, }; use crate::tests::helpers::{advance_epoch, arb_params_and_genesis_validators}; +use crate::token::{self, read_balance}; use crate::types::{ BondId, EagerRedelegatedBondsMap, GenesisValidator, ReverseOrdTokenAmount, Slash, SlashType, ValidatorState, WeightedValidator, diff --git a/proof_of_stake/src/tests/state_machine_v2.rs b/proof_of_stake/src/tests/state_machine_v2.rs index a349eb5516..bd08bba2bd 100644 --- a/proof_of_stake/src/tests/state_machine_v2.rs +++ b/proof_of_stake/src/tests/state_machine_v2.rs @@ -13,10 +13,9 @@ use namada_core::types::key; use namada_core::types::key::common::PublicKey; use namada_core::types::storage::Epoch; use namada_core::types::token::Change; -use namada_namada_trans_token::read_balance; use namada_state::testing::TestWlStorage; use namada_storage::collections::lazy_map::{NestedSubKey, SubKey}; -use namada_storage::{token, StorageRead}; +use namada_storage::StorageRead; use proptest::prelude::*; use proptest::test_runner::Config; use proptest_state_machine::{ @@ -42,6 +41,7 @@ use crate::storage::{ }; use crate::tests::helpers::arb_params_and_genesis_validators; use crate::tests::utils::pause_for_enter; +use crate::token::read_balance; use crate::types::{ BondId, GenesisValidator, ReverseOrdTokenAmount, Slash, SlashType, ValidatorState, WeightedValidator, @@ -49,7 +49,7 @@ use crate::types::{ use crate::{ below_capacity_validator_set_handle, bond_handle, consensus_validator_set_handle, delegator_redelegated_bonds_handle, - read_pos_params, redelegate_tokens, validator_deltas_handle, + read_pos_params, redelegate_tokens, token, validator_deltas_handle, validator_slashes_handle, validator_state_handle, RedelegationError, }; diff --git a/proof_of_stake/src/tests/test_helper_fns.rs b/proof_of_stake/src/tests/test_helper_fns.rs index f270e6e0dc..213712636c 100644 --- a/proof_of_stake/src/tests/test_helper_fns.rs +++ b/proof_of_stake/src/tests/test_helper_fns.rs @@ -1,7 +1,5 @@ use std::collections::{BTreeMap, BTreeSet}; -use namada_core::ledger::storage_api::collections::lazy_map::NestedMap; -use namada_core::ledger::storage_api::collections::LazyCollection; use namada_core::types::address::testing::{ established_address_1, established_address_2, established_address_3, }; @@ -9,6 +7,8 @@ use namada_core::types::dec::Dec; use namada_core::types::storage::{Epoch, Key}; use namada_core::types::token; use namada_state::testing::TestWlStorage; +use namada_storage::collections::lazy_map::NestedMap; +use namada_storage::collections::LazyCollection; use crate::slashing::{ apply_list_slashes, compute_amount_after_slashing_unbond, @@ -36,7 +36,8 @@ use crate::{ #[test] fn test_find_bonds_to_remove() { let mut storage = TestWlStorage::default(); - let gov_params = namada_core::ledger::governance::parameters::GovernanceParameters::default(); + let gov_params = + namada_governance::parameters::GovernanceParameters::default(); gov_params.init_storage(&mut storage).unwrap(); write_pos_params(&mut storage, &OwnedPosParams::default()).unwrap(); @@ -1102,7 +1103,8 @@ fn test_slash_validator_redelegation() { unbonding_len: 4, ..Default::default() }; - let gov_params = namada_core::ledger::governance::parameters::GovernanceParameters::default(); + let gov_params = + namada_governance::parameters::GovernanceParameters::default(); gov_params.init_storage(&mut storage).unwrap(); write_pos_params(&mut storage, ¶ms).unwrap(); @@ -1283,7 +1285,8 @@ fn test_slash_validator() { unbonding_len: 4, ..Default::default() }; - let gov_params = namada_core::ledger::governance::parameters::GovernanceParameters::default(); + let gov_params = + namada_governance::parameters::GovernanceParameters::default(); gov_params.init_storage(&mut storage).unwrap(); write_pos_params(&mut storage, ¶ms).unwrap(); @@ -1927,7 +1930,8 @@ fn test_from_sm_case_1() { use namada_core::types::address::testing::established_address_4; let mut storage = TestWlStorage::default(); - let gov_params = namada_core::ledger::governance::parameters::GovernanceParameters::default(); + let gov_params = + namada_governance::parameters::GovernanceParameters::default(); gov_params.init_storage(&mut storage).unwrap(); write_pos_params(&mut storage, &OwnedPosParams::default()).unwrap(); diff --git a/proof_of_stake/src/tests/test_pos.rs b/proof_of_stake/src/tests/test_pos.rs index 027d5cdc99..72bc87c3e3 100644 --- a/proof_of_stake/src/tests/test_pos.rs +++ b/proof_of_stake/src/tests/test_pos.rs @@ -2,9 +2,6 @@ use std::collections::{BTreeMap, HashSet}; -use namada_core::ledger::storage_api::collections::lazy_map::Collectable; -use namada_core::ledger::storage_api::token::{credit_tokens, read_balance}; -use namada_core::ledger::storage_api::StorageRead; use namada_core::types::address::Address; use namada_core::types::dec::Dec; use namada_core::types::key::testing::{ @@ -12,8 +9,10 @@ use namada_core::types::key::testing::{ }; use namada_core::types::key::RefTo; use namada_core::types::storage::{BlockHeight, Epoch}; -use namada_core::types::{address, key, token}; +use namada_core::types::{address, key}; use namada_state::testing::TestWlStorage; +use namada_storage::collections::lazy_map::Collectable; +use namada_storage::StorageRead; use proptest::prelude::*; use proptest::test_runner::Config; // Use `RUST_LOG=info` (or another tracing level) and `--nocapture` to see @@ -38,6 +37,7 @@ use crate::test_utils::test_init_genesis; use crate::tests::helpers::{ advance_epoch, arb_genesis_validators, arb_params_and_genesis_validators, }; +use crate::token::{credit_tokens, read_balance}; use crate::types::{ into_tm_voting_power, BondDetails, BondId, BondsAndUnbondsDetails, GenesisValidator, SlashType, UnbondDetails, ValidatorState, VoteInfo, @@ -47,9 +47,9 @@ use crate::{ below_capacity_validator_set_handle, bond_handle, bond_tokens, change_consensus_key, consensus_validator_set_handle, is_delegator, is_validator, read_validator_stake, redelegate_tokens, - staking_token_address, unbond_handle, unbond_tokens, unjail_validator, - validator_consensus_key_handle, validator_set_positions_handle, - validator_state_handle, withdraw_tokens, + staking_token_address, token, unbond_handle, unbond_tokens, + unjail_validator, validator_consensus_key_handle, + validator_set_positions_handle, validator_state_handle, withdraw_tokens, }; proptest! { @@ -298,7 +298,7 @@ fn test_bonds_aux(params: OwnedPosParams, validators: Vec) { let pipeline_epoch = current_epoch + params.pipeline_len; let staking_token = staking_token_address(&s); let pos_balance_pre = s - .read::(&token::balance_key( + .read::(&token::storage_key::balance_key( &staking_token, &crate::ADDRESS, )) @@ -409,7 +409,8 @@ fn test_bonds_aux(params: OwnedPosParams, validators: Vec) { let delegator = address::testing::gen_implicit_address(); let amount_del = token::Amount::from_uint(201_000_000, 0).unwrap(); credit_tokens(&mut s, &staking_token, &delegator, amount_del).unwrap(); - let balance_key = token::balance_key(&staking_token, &delegator); + let balance_key = + token::storage_key::balance_key(&staking_token, &delegator); let balance = s .read::(&balance_key) .unwrap() @@ -744,7 +745,7 @@ fn test_bonds_aux(params: OwnedPosParams, validators: Vec) { } let pos_balance = s - .read::(&token::balance_key( + .read::(&token::storage_key::balance_key( &staking_token, &crate::ADDRESS, )) @@ -762,7 +763,7 @@ fn test_bonds_aux(params: OwnedPosParams, validators: Vec) { assert!(unbond_iter.is_none()); let pos_balance = s - .read::(&token::balance_key( + .read::(&token::storage_key::balance_key( &staking_token, &crate::ADDRESS, )) @@ -788,7 +789,7 @@ fn test_bonds_aux(params: OwnedPosParams, validators: Vec) { assert!(unbond_iter.is_none()); let pos_balance = s - .read::(&token::balance_key( + .read::(&token::storage_key::balance_key( &staking_token, &crate::ADDRESS, )) diff --git a/proof_of_stake/src/tests/test_slash_and_redel.rs b/proof_of_stake/src/tests/test_slash_and_redel.rs index 587c0f12cf..a38912705b 100644 --- a/proof_of_stake/src/tests/test_slash_and_redel.rs +++ b/proof_of_stake/src/tests/test_slash_and_redel.rs @@ -2,14 +2,13 @@ use std::ops::Deref; use std::str::FromStr; use assert_matches::assert_matches; -use namada_core::ledger::storage_api::collections::lazy_map::Collectable; -use namada_core::ledger::storage_api::token::{credit_tokens, read_balance}; -use namada_core::ledger::storage_api::StorageRead; +use namada_core::types::address; use namada_core::types::dec::Dec; use namada_core::types::storage::{BlockHeight, Epoch}; use namada_core::types::token::NATIVE_MAX_DECIMAL_PLACES; -use namada_core::types::{address, token}; use namada_state::testing::TestWlStorage; +use namada_storage::collections::lazy_map::Collectable; +use namada_storage::StorageRead; use proptest::prelude::*; use proptest::test_runner::Config; // Use `RUST_LOG=info` (or another tracing level) and `--nocapture` to see @@ -32,10 +31,11 @@ use crate::tests::helpers::{ advance_epoch, arb_genesis_validators, arb_redelegation_amounts, test_slashes_with_unbonding_params, }; +use crate::token::{credit_tokens, read_balance}; use crate::types::{BondId, GenesisValidator, SlashType}; use crate::{ - bond_tokens, redelegate_tokens, staking_token_address, unbond_tokens, - withdraw_tokens, OwnedPosParams, RedelegationError, + bond_tokens, redelegate_tokens, staking_token_address, token, + unbond_tokens, withdraw_tokens, OwnedPosParams, RedelegationError, }; proptest! { @@ -255,7 +255,10 @@ fn test_simple_redelegation_aux( ); let delegator_balance = storage - .read::(&token::balance_key(&staking_token, &delegator)) + .read::(&token::storage_key::balance_key( + &staking_token, + &delegator, + )) .unwrap() .unwrap_or_default(); assert_eq!( @@ -644,7 +647,10 @@ fn test_redelegation_with_slashing_aux( ); let delegator_balance = storage - .read::(&token::balance_key(&staking_token, &delegator)) + .read::(&token::storage_key::balance_key( + &staking_token, + &delegator, + )) .unwrap() .unwrap_or_default(); assert_eq!(delegator_balance, del_balance - amount_delegate); diff --git a/proof_of_stake/src/tests/test_validator.rs b/proof_of_stake/src/tests/test_validator.rs index c2329ac91e..ebcaec7703 100644 --- a/proof_of_stake/src/tests/test_validator.rs +++ b/proof_of_stake/src/tests/test_validator.rs @@ -1,8 +1,5 @@ use std::cmp::min; -use namada_core::ledger::storage::WlStorage; -use namada_core::ledger::storage_api::collections::lazy_map; -use namada_core::ledger::storage_api::token::credit_tokens; use namada_core::types::address::testing::arb_established_address; use namada_core::types::address::{self, Address, EstablishedAddressGen}; use namada_core::types::dec::Dec; @@ -13,6 +10,7 @@ use namada_core::types::key::{self, common, RefTo}; use namada_core::types::storage::Epoch; use namada_core::types::token; use namada_state::testing::TestWlStorage; +use namada_storage::collections::lazy_map; use proptest::prelude::*; use proptest::test_runner::Config; // Use `RUST_LOG=info` (or another tracing level) and `--nocapture` to see @@ -35,6 +33,7 @@ use crate::tests::helpers::{ advance_epoch, arb_genesis_validators, arb_params_and_genesis_validators, get_tendermint_set_updates, }; +use crate::token::credit_tokens; use crate::types::{ into_tm_voting_power, ConsensusValidator, GenesisValidator, Position, ReverseOrdTokenAmount, ValidatorSetUpdate, WeightedValidator, @@ -1219,11 +1218,10 @@ fn test_purge_validator_information_aux(validators: Vec) { let mut current_epoch = s.storage.block.epoch; // Genesis - let gov_params = - namada_core::ledger::governance::parameters::GovernanceParameters { - max_proposal_period: 5, - ..Default::default() - }; + let gov_params = namada_governance::parameters::GovernanceParameters { + max_proposal_period: 5, + ..Default::default() + }; gov_params.init_storage(&mut s).unwrap(); let params = crate::read_non_pos_owned_params(&s, owned).unwrap(); @@ -1241,9 +1239,7 @@ fn test_purge_validator_information_aux(validators: Vec) { let validator_positions = validator_set_positions_handle(); let all_validator_addresses = validator_addresses_handle(); - let check_is_data = |storage: &WlStorage<_, _>, - start: Epoch, - end: Epoch| { + let check_is_data = |storage: &TestWlStorage, start: Epoch, end: Epoch| { for ep in Epoch::iter_bounds_inclusive(start, end) { assert!(!consensus_val_set.at(&ep).is_empty(storage).unwrap()); // assert!(!below_cap_val_set.at(&ep).is_empty(storage). From ca4e2429f58cc3257600dc08d4877083fd1156b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Mon, 8 Jan 2024 10:32:14 +0000 Subject: [PATCH 094/118] ethereum_bridge: fix tests --- ethereum_bridge/src/lib.rs | 1 + .../transactions/bridge_pool_roots.rs | 2 +- .../transactions/ethereum_events/events.rs | 31 +++++++++++-------- .../transactions/ethereum_events/mod.rs | 6 ++-- .../src/protocol/transactions/read.rs | 2 +- .../src/protocol/transactions/update.rs | 7 +++-- ethereum_bridge/src/storage/parameters.rs | 4 +-- ethereum_bridge/src/storage/proof.rs | 2 +- ethereum_bridge/src/storage/wrapped_erc20s.rs | 11 ++++--- 9 files changed, 38 insertions(+), 28 deletions(-) diff --git a/ethereum_bridge/src/lib.rs b/ethereum_bridge/src/lib.rs index 86b5d5fa37..31d9fc1e62 100644 --- a/ethereum_bridge/src/lib.rs +++ b/ethereum_bridge/src/lib.rs @@ -7,3 +7,4 @@ pub mod storage; pub mod test_utils; pub use namada_core::ledger::eth_bridge::ADDRESS; +pub use namada_trans_token as token; diff --git a/ethereum_bridge/src/protocol/transactions/bridge_pool_roots.rs b/ethereum_bridge/src/protocol/transactions/bridge_pool_roots.rs index 56918a9e43..0a0efc5d32 100644 --- a/ethereum_bridge/src/protocol/transactions/bridge_pool_roots.rs +++ b/ethereum_bridge/src/protocol/transactions/bridge_pool_roots.rs @@ -225,7 +225,6 @@ mod test_apply_bp_roots_to_storage { use assert_matches::assert_matches; use borsh::BorshDeserialize; - use namada_core::proto::{SignableEthMessage, Signed}; use namada_core::types::address; use namada_core::types::ethereum_events::Uint; use namada_core::types::keccak::{keccak_hash, KeccakHash}; @@ -235,6 +234,7 @@ mod test_apply_bp_roots_to_storage { use namada_proof_of_stake::storage::write_pos_params; use namada_state::testing::TestWlStorage; use namada_storage::StorageRead; + use namada_tx::{SignableEthMessage, Signed}; use namada_vote_ext::bridge_pool_roots; use super::*; diff --git a/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs b/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs index 07d820191d..3d9e7cf14f 100644 --- a/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs +++ b/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs @@ -19,7 +19,6 @@ use namada_core::types::ethereum_events::{ }; use namada_core::types::ethereum_structs::EthBridgeEvent; use namada_core::types::storage::{BlockHeight, Key, KeySeg}; -use namada_core::types::token; use namada_parameters::read_epoch_duration_parameter; use namada_state::{DBIter, StorageHasher, WlStorage, DB}; use namada_storage::{StorageRead, StorageWrite}; @@ -32,6 +31,7 @@ use crate::storage::bridge_pool::{ use crate::storage::eth_bridge_queries::{EthAssetMint, EthBridgeQueries}; use crate::storage::parameters::read_native_erc20_address; use crate::storage::{self as bridge_storage}; +use crate::token; /// Updates storage based on the given confirmed `event`. For example, for a /// confirmed [`EthereumEvent::TransfersToNamada`], mint the corresponding @@ -580,10 +580,8 @@ mod tests { use std::collections::HashMap; use assert_matches::assert_matches; - use borsh_ext::BorshSerializeExt; use eyre::Result; - use namada_core::ledger::storage::mockdb::MockDBWriteBatch; - use namada_core::ledger::storage::types::encode; + use namada_core::borsh::BorshSerializeExt; use namada_core::types::address::testing::gen_implicit_address; use namada_core::types::address::{gen_established_address, nam, wnam}; use namada_core::types::eth_bridge_pool::GasFee; @@ -592,12 +590,14 @@ mod tests { }; use namada_core::types::time::DurationSecs; use namada_core::types::token::Amount; - use namada_core::types::{address, eth_bridge_pool}; + use namada_core::types::{address, encode, eth_bridge_pool}; use namada_parameters::{update_epoch_parameter, EpochDuration}; use namada_state::testing::TestWlStorage; + use namada_storage::mockdb::MockDBWriteBatch; use super::*; use crate::storage::bridge_pool::get_pending_key; + use crate::storage::wrapped_erc20s; use crate::test_utils::{self, stored_keys_count}; fn init_storage(wl_storage: &mut TestWlStorage) { @@ -1251,8 +1251,10 @@ mod tests { let amount = Amount::from(100); // pre wNAM balance - 0 - let receiver_wnam_balance_key = - token::balance_key(&wrapped_erc20s::token(&wnam()), &receiver); + let receiver_wnam_balance_key = token::storage_key::balance_key( + &wrapped_erc20s::token(&wnam()), + &receiver, + ); assert!( wl_storage .read_bytes(&receiver_wnam_balance_key) @@ -1261,10 +1263,11 @@ mod tests { ); let bridge_pool_initial_balance = Amount::from(100_000_000); - let bridge_pool_native_token_balance_key = token::balance_key( - &wl_storage.storage.native_token, - &BRIDGE_ADDRESS, - ); + let bridge_pool_native_token_balance_key = + token::storage_key::balance_key( + &wl_storage.storage.native_token, + &BRIDGE_ADDRESS, + ); let bridge_pool_native_erc20_supply_key = minted_balance_key(&wrapped_erc20s::token(&wnam())); StorageWrite::write( @@ -1277,8 +1280,10 @@ mod tests { &bridge_pool_native_erc20_supply_key, amount, )?; - let receiver_native_token_balance_key = - token::balance_key(&wl_storage.storage.native_token, &receiver); + let receiver_native_token_balance_key = token::storage_key::balance_key( + &wl_storage.storage.native_token, + &receiver, + ); let changed_keys = redeem_native_token(&mut wl_storage, &wnam(), &receiver, &amount)?; diff --git a/ethereum_bridge/src/protocol/transactions/ethereum_events/mod.rs b/ethereum_bridge/src/protocol/transactions/ethereum_events/mod.rs index 1193381106..e1451849a3 100644 --- a/ethereum_bridge/src/protocol/transactions/ethereum_events/mod.rs +++ b/ethereum_bridge/src/protocol/transactions/ethereum_events/mod.rs @@ -300,8 +300,6 @@ mod tests { use std::collections::{BTreeSet, HashMap, HashSet}; use borsh::BorshDeserialize; - use namada_core::ledger::storage::mockdb::MockDBWriteBatch; - use namada_state::testing::TestWlStorage; use namada_core::types::address; use namada_core::types::ethereum_events::testing::{ arbitrary_amount, arbitrary_eth_address, arbitrary_nonce, @@ -310,8 +308,9 @@ mod tests { use namada_core::types::ethereum_events::{ EthereumEvent, TransferToNamada, }; - use namada_core::types::token::{balance_key, minted_balance_key}; use namada_core::types::voting_power::FractionalVotingPower; + use namada_state::testing::TestWlStorage; + use namada_storage::mockdb::MockDBWriteBatch; use namada_storage::StorageRead; use super::*; @@ -321,6 +320,7 @@ mod tests { }; use crate::storage::wrapped_erc20s; use crate::test_utils; + use crate::token::storage_key::{balance_key, minted_balance_key}; /// All kinds of [`Keys`]. enum KeyKind { diff --git a/ethereum_bridge/src/protocol/transactions/read.rs b/ethereum_bridge/src/protocol/transactions/read.rs index a17339a4bb..d8da8f6520 100644 --- a/ethereum_bridge/src/protocol/transactions/read.rs +++ b/ethereum_bridge/src/protocol/transactions/read.rs @@ -54,7 +54,7 @@ where #[cfg(test)] mod tests { use assert_matches::assert_matches; - use borsh_ext::BorshSerializeExt; + use namada_core::borsh::BorshSerializeExt; use namada_core::types::storage; use namada_core::types::token::Amount; use namada_state::testing::TestWlStorage; diff --git a/ethereum_bridge/src/protocol/transactions/update.rs b/ethereum_bridge/src/protocol/transactions/update.rs index ea9f1a5877..6d1e1b3e7b 100644 --- a/ethereum_bridge/src/protocol/transactions/update.rs +++ b/ethereum_bridge/src/protocol/transactions/update.rs @@ -1,6 +1,6 @@ //! Helpers for writing to storage -use borsh::{BorshDeserialize, BorshSerialize}; use eyre::Result; +use namada_core::borsh::{BorshDeserialize, BorshSerialize}; use namada_core::types::hash::StorageHasher; use namada_core::types::storage; use namada_core::types::token::Amount; @@ -42,13 +42,14 @@ where #[cfg(test)] mod tests { - use borsh::BorshDeserialize; - use borsh_ext::BorshSerializeExt; use eyre::{eyre, Result}; + use namada_core::borsh::BorshSerializeExt; use namada_core::types::storage; use namada_state::testing::TestWlStorage; use namada_storage::{StorageRead, StorageWrite}; + use super::*; + #[test] /// Test updating a value fn test_value() -> Result<()> { diff --git a/ethereum_bridge/src/storage/parameters.rs b/ethereum_bridge/src/storage/parameters.rs index e9e1ae65a4..dd420de67b 100644 --- a/ethereum_bridge/src/storage/parameters.rs +++ b/ethereum_bridge/src/storage/parameters.rs @@ -1,8 +1,8 @@ //! Parameters for configuring the Ethereum bridge use std::num::NonZeroU64; -use borsh::{BorshDeserialize, BorshSerialize}; use eyre::{eyre, Result}; +use namada_core::borsh::{BorshDeserialize, BorshSerialize}; use namada_core::types::ethereum_events::EthAddress; use namada_core::types::storage::Key; use namada_core::types::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; @@ -367,8 +367,8 @@ where #[cfg(test)] mod tests { - use borsh_ext::BorshSerializeExt; use eyre::Result; + use namada_core::borsh::BorshSerializeExt; use namada_core::types::ethereum_events::EthAddress; use namada_state::testing::TestWlStorage; diff --git a/ethereum_bridge/src/storage/proof.rs b/ethereum_bridge/src/storage/proof.rs index 5c3d28386a..92b377f70f 100644 --- a/ethereum_bridge/src/storage/proof.rs +++ b/ethereum_bridge/src/storage/proof.rs @@ -123,9 +123,9 @@ mod test_ethbridge_proofs { //! Test ethereum bridge proofs. use assert_matches::assert_matches; - use namada_core::proto::Signed; use namada_core::types::ethereum_events::EthAddress; use namada_core::types::key; + use namada_tx::Signed; use super::*; diff --git a/ethereum_bridge/src/storage/wrapped_erc20s.rs b/ethereum_bridge/src/storage/wrapped_erc20s.rs index 085d30a218..4976caea8f 100644 --- a/ethereum_bridge/src/storage/wrapped_erc20s.rs +++ b/ethereum_bridge/src/storage/wrapped_erc20s.rs @@ -2,7 +2,9 @@ use eyre::eyre; use namada_core::types::address::{Address, InternalAddress}; -use namada_core::types::eth_bridge_pool::erc20_token_address; +pub use namada_core::types::eth_bridge_pool::{ + erc20_nut_address as nut, erc20_token_address as token, +}; use namada_core::types::ethereum_events::EthAddress; use namada_core::types::storage::{self, DbKeySeg}; use namada_trans_token::storage_key::{ @@ -32,7 +34,7 @@ pub struct Key { impl From<&Key> for storage::Key { fn from(mt_key: &Key) -> Self { - let token = erc20_token_address(&mt_key.asset); + let token = token(&mt_key.asset); match &mt_key.suffix { KeyType::Balance { owner } => balance_key(&token, owner), KeyType::Supply => minted_balance_key(&token), @@ -107,13 +109,14 @@ mod test { use std::result::Result; use std::str::FromStr; + use assert_matches::assert_matches; use namada_core::types::address::{nam, Address}; use namada_core::types::ethereum_events::testing::DAI_ERC20_ETH_ADDRESS; use namada_core::types::storage::DbKeySeg; - use namada_core::types::token::BALANCE_STORAGE_KEY; use super::*; - use crate::ledger::eth_bridge::ADDRESS; + use crate::token::storage_key::BALANCE_STORAGE_KEY; + use crate::ADDRESS; const MULTITOKEN_ADDRESS: Address = Address::Internal(InternalAddress::Multitoken); From 2d91fa46cdb9ea45d0d157d6a98b80d415791486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Mon, 8 Jan 2024 10:33:21 +0000 Subject: [PATCH 095/118] ibc: fix tests --- Cargo.lock | 1 + ibc/Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 22c0f6996e..98bccd8580 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4526,6 +4526,7 @@ dependencies = [ "namada_storage", "namada_trans_token", "primitive-types", + "proptest", "prost 0.12.3", "sha2 0.9.9", "thiserror", diff --git a/ibc/Cargo.toml b/ibc/Cargo.toml index 92a25f8d59..72d19b90c8 100644 --- a/ibc/Cargo.toml +++ b/ibc/Cargo.toml @@ -27,6 +27,7 @@ ibc-derive.workspace = true ibc.workspace = true ics23.workspace = true primitive-types.workspace = true +proptest.workspace = true prost.workspace = true sha2.workspace = true thiserror.workspace = true From c42917fff8229617de3f7744e41b82a0152cd9f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Mon, 8 Jan 2024 10:59:44 +0000 Subject: [PATCH 096/118] sdk: fix tests --- Cargo.lock | 3 +-- account/Cargo.toml | 5 +++++ core/Cargo.toml | 3 --- ethereum_bridge/src/storage/bridge_pool.rs | 1 + governance/Cargo.toml | 3 ++- ibc/Cargo.toml | 11 ++++++++--- sdk/Cargo.toml | 6 ++++++ sdk/src/lib.rs | 18 ++++++++---------- sdk/src/queries/mod.rs | 7 ++----- sdk/src/queries/shell.rs | 2 +- sdk/src/queries/shell/eth_bridge.rs | 22 +++++++++++----------- 11 files changed, 45 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 98bccd8580..d7a74454e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4392,8 +4392,6 @@ dependencies = [ "ethbridge-structs", "eyre", "ibc 0.48.1", - "ibc-derive 0.4.0", - "ibc-testkit", "ics23", "impl-num-traits", "index-set", @@ -4520,6 +4518,7 @@ dependencies = [ "borsh", "ibc 0.48.1", "ibc-derive 0.4.0", + "ibc-testkit", "ics23", "namada_core", "namada_parameters", diff --git a/account/Cargo.toml b/account/Cargo.toml index dac36bb7d6..201c828861 100644 --- a/account/Cargo.toml +++ b/account/Cargo.toml @@ -12,12 +12,17 @@ readme.workspace = true repository.workspace = true version.workspace = true +[features] +default = [] +testing = ["namada_core/testing", "proptest"] + [dependencies] namada_core = { path = "../core", default-features = false } namada_macros = { path = "../macros" } namada_storage = { path = "../storage" } borsh.workspace = true +proptest = { workspace = true, optional = true } serde.workspace = true [dev-dependencies] diff --git a/core/Cargo.toml b/core/Cargo.toml index 39c80a7fea..799b5dfee1 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -22,7 +22,6 @@ ethers-derive = [ ] # for integration tests and test utilities testing = [ - "ibc-testkit", "rand", "proptest", ] @@ -41,8 +40,6 @@ ethabi.workspace = true ethbridge-structs.workspace = true eyre.workspace = true ibc.workspace = true -ibc-derive.workspace = true -ibc-testkit = {workspace = true, optional = true} ics23.workspace = true impl-num-traits = "0.1.2" index-set.workspace = true diff --git a/ethereum_bridge/src/storage/bridge_pool.rs b/ethereum_bridge/src/storage/bridge_pool.rs index c65205f5dd..b43a6025ac 100644 --- a/ethereum_bridge/src/storage/bridge_pool.rs +++ b/ethereum_bridge/src/storage/bridge_pool.rs @@ -8,6 +8,7 @@ pub use namada_core::types::eth_bridge_pool::{ use namada_core::types::eth_bridge_pool::{PendingTransfer, Segments}; use namada_core::types::keccak::KeccakHash; use namada_core::types::storage::{DbKeySeg, Key, KeySeg}; +pub use namada_state::merkle_tree::eth_bridge_pool::BridgePoolTree; /// Get the storage key for the transfers in the pool pub fn get_pending_key(transfer: &PendingTransfer) -> Key { diff --git a/governance/Cargo.toml b/governance/Cargo.toml index d501ae6777..b89c305e5c 100644 --- a/governance/Cargo.toml +++ b/governance/Cargo.toml @@ -13,7 +13,7 @@ repository.workspace = true version.workspace = true [features] -testing = [] +testing = ["proptest"] [dependencies] namada_core = {path = "../core", default-features = false} @@ -23,6 +23,7 @@ namada_storage = {path = "../storage"} namada_trans_token = {path = "../trans_token"} borsh.workspace = true +proptest = { workspace = true, optional = true } serde_json.workspace = true serde.workspace = true thiserror.workspace = true diff --git a/ibc/Cargo.toml b/ibc/Cargo.toml index 72d19b90c8..f131dd0754 100644 --- a/ibc/Cargo.toml +++ b/ibc/Cargo.toml @@ -14,7 +14,7 @@ version.workspace = true [features] default = [] -testing = ["namada_core/testing"] +testing = ["namada_core/testing", "ibc-testkit", "proptest"] [dependencies] namada_core = { path = "../core", default-features = false } @@ -23,12 +23,17 @@ namada_storage = { path = "../storage" } namada_trans_token = { path = "../trans_token" } borsh.workspace = true -ibc-derive.workspace = true ibc.workspace = true +ibc-derive.workspace = true +ibc-testkit = {workspace = true, optional = true} ics23.workspace = true primitive-types.workspace = true -proptest.workspace = true +proptest = { workspace = true, optional = true } prost.workspace = true sha2.workspace = true thiserror.workspace = true tracing.workspace = true + +[dev-dependencies] +ibc-testkit.workspace = true +proptest.workspace = true \ No newline at end of file diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index e8fb80ae02..f2ab25d818 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -104,6 +104,7 @@ tokio = { workspace = true, default-features = false, features = ["sync"] } wasmtimer = "0.2.0" [dev-dependencies] +namada_account = { path = "../account", features = ["testing"]} namada_core = { path = "../core", default-features = false, features = [ "rand", "testing", @@ -111,11 +112,16 @@ namada_core = { path = "../core", default-features = false, features = [ namada_ethereum_bridge = { path = "../ethereum_bridge", default-features = false, features = [ "testing", ] } +namada_governance = { path = "../governance", features = ["testing"] } +namada_ibc = { path = "../ibc", features = ["testing"] } +namada_parameters = {path = "../parameters"} namada_proof_of_stake = { path = "../proof_of_stake", default-features = false, features = [ "testing", ] } namada_state = { path = "../state", features = ["testing"] } namada_test_utils = { path = "../test_utils" } +namada_tx = { path = "../tx", features = ["testing"]} +namada_vote_ext = {path = "../vote_ext"} assert_matches.workspace = true base58.workspace = true diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index b70d0d2bc8..2ab380f7b9 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -765,7 +765,7 @@ where pub mod testing { use governance::ProposalType; use ibc::primitives::proto::Any; - use namada_core::ledger::ibc::testing::arb_ibc_any; + use namada_account::{InitAccount, UpdateAccount}; use namada_core::types::address::testing::{ arb_established_address, arb_non_internal_address, }; @@ -776,24 +776,23 @@ pub mod testing { arb_denominated_amount, arb_transfer, }; use namada_core::types::token::Transfer; - use namada_core::types::transaction::account::{ - InitAccount, UpdateAccount, + use namada_governance::storage::proposal::testing::{ + arb_init_proposal, arb_vote_proposal, }; - use namada_core::types::transaction::governance::{ - InitProposalData, VoteProposalData, - }; - use namada_core::types::transaction::pgf::UpdateStewardCommission; - use namada_core::types::transaction::pos::{ + use namada_governance::{InitProposalData, VoteProposalData}; + use namada_ibc::testing::arb_ibc_any; + use namada_tx::data::pgf::UpdateStewardCommission; + use namada_tx::data::pos::{ BecomeValidator, Bond, CommissionChange, ConsensusKeyChange, MetaDataChange, Redelegation, Unbond, Withdraw, }; + use namada_tx::data::{DecryptedTx, Fee, TxType, WrapperTx}; use proptest::prelude::{Just, Strategy}; use proptest::{option, prop_compose}; use prost::Message; use super::*; use crate::account::tests::{arb_init_account, arb_update_account}; - use crate::governance::tests::{arb_init_proposal, arb_vote_proposal}; use crate::tx::data::pgf::tests::arb_update_steward_commission; use crate::tx::data::pos::tests::{ arb_become_validator, arb_bond, arb_commission_change, @@ -805,7 +804,6 @@ pub mod testing { use crate::types::eth_bridge_pool::testing::arb_pending_transfer; use crate::types::key::testing::arb_common_pk; use crate::types::time::{DateTime, DateTimeUtc, Utc}; - use crate::types::transaction::{DecryptedTx, Fee, TxType, WrapperTx}; #[derive(Debug)] #[allow(clippy::large_enum_variant)] diff --git a/sdk/src/queries/mod.rs b/sdk/src/queries/mod.rs index 8e69a1d653..82e02e1d25 100644 --- a/sdk/src/queries/mod.rs +++ b/sdk/src/queries/mod.rs @@ -130,15 +130,12 @@ mod testing { // Initialize mock gas limit let max_block_gas_key = - namada_core::ledger::parameters::storage::get_max_block_gas_key( - ); + namada_parameters::storage::get_max_block_gas_key(); wl_storage .storage .write( &max_block_gas_key, - namada_core::ledger::storage::types::encode( - &20_000_000_u64, - ), + namada_core::types::encode(&20_000_000_u64), ) .expect( "Max block gas parameter must be initialized in storage", diff --git a/sdk/src/queries/shell.rs b/sdk/src/queries/shell.rs index a3c64b9b38..34f84ffa4d 100644 --- a/sdk/src/queries/shell.rs +++ b/sdk/src/queries/shell.rs @@ -539,7 +539,7 @@ where #[cfg(test)] mod test { - use namada_core::types::{address, token}; + use namada_core::types::address; use namada_token::storage_key::balance_key; use crate::queries::RPC; diff --git a/sdk/src/queries/shell/eth_bridge.rs b/sdk/src/queries/shell/eth_bridge.rs index 4d59564159..334ddbb97f 100644 --- a/sdk/src/queries/shell/eth_bridge.rs +++ b/sdk/src/queries/shell/eth_bridge.rs @@ -837,11 +837,6 @@ mod test_ethbridge_router { use std::collections::BTreeMap; use assert_matches::assert_matches; - use namada_core::ledger::eth_bridge::storage::bridge_pool::{ - get_pending_key, get_signed_root_key, BridgePoolTree, - }; - use namada_core::ledger::eth_bridge::storage::whitelist; - use namada_core::ledger::storage::mockdb::MockDBWriteBatch; use namada_core::types::address::nam; use namada_core::types::address::testing::established_address_1; use namada_core::types::eth_abi::Encode; @@ -850,17 +845,22 @@ mod test_ethbridge_router { }; use namada_core::types::ethereum_events::EthAddress; use namada_core::types::storage::BlockHeight; - use namada_core::types::vote_extensions::validator_set_update; - use namada_core::types::vote_extensions::validator_set_update::{ - EthAddrBook, VotingPowersMapExt, - }; use namada_core::types::voting_power::{ EthBridgeVotingPower, FractionalVotingPower, }; use namada_ethereum_bridge::protocol::transactions::validator_set_update::aggregate_votes; + use namada_ethereum_bridge::storage::bridge_pool::{ + get_pending_key, get_signed_root_key, BridgePoolTree, + }; use namada_ethereum_bridge::storage::proof::BridgePoolRootProof; + use namada_ethereum_bridge::storage::whitelist; use namada_proof_of_stake::pos_queries::PosQueries; + use namada_state::mockdb::MockDBWriteBatch; use namada_storage::StorageWrite; + use namada_vote_ext::validator_set_update; + use namada_vote_ext::validator_set_update::{ + EthAddrBook, VotingPowersMapExt, + }; use super::test_utils::bertha_address; use super::*; @@ -1006,14 +1006,14 @@ mod test_ethbridge_router { .unwrap(); let expected = { let mut proof = - EthereumProof::new((1.into(), vext.data.voting_powers)); + EthereumProof::new((1.into(), vext.0.data.voting_powers)); proof.attach_signature( client .wl_storage .ethbridge_queries() .get_eth_addr_book(&established_address_1(), Some(0.into())) .expect("Test failed"), - vext.sig, + vext.0.sig, ); proof.encode() }; From 94ff8a8dbdda0fa0b216929b061d4e4fbce03cbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Mon, 8 Jan 2024 11:37:28 +0000 Subject: [PATCH 097/118] shared: fix tests --- Cargo.lock | 1 - ibc/src/lib.rs | 1 + shared/Cargo.toml | 1 - shared/src/ledger/governance/mod.rs | 4 +- shared/src/ledger/ibc/mod.rs | 3 +- shared/src/ledger/mod.rs | 34 ++--- .../ethereum_bridge/bridge_pool_vp.rs | 22 +-- .../ledger/native_vp/ethereum_bridge/nut.rs | 12 +- .../ledger/native_vp/ethereum_bridge/vp.rs | 48 +++--- shared/src/ledger/native_vp/ibc/context.rs | 20 +-- shared/src/ledger/native_vp/ibc/mod.rs | 33 +++-- shared/src/ledger/native_vp/masp.rs | 20 +-- shared/src/ledger/native_vp/mod.rs | 140 +++++++++--------- shared/src/ledger/native_vp/multitoken.rs | 23 +-- shared/src/ledger/native_vp/slash_fund.rs | 6 +- shared/src/ledger/pgf/utils.rs | 2 +- shared/src/ledger/pos/mod.rs | 4 +- shared/src/ledger/pos/vp.rs | 2 +- shared/src/ledger/protocol/mod.rs | 40 ++--- shared/src/lib.rs | 12 +- shared/src/vm/host_env.rs | 79 +++++----- shared/src/vm/wasm/run.rs | 10 +- state/src/lib.rs | 2 +- 23 files changed, 259 insertions(+), 260 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d7a74454e2..6b9e7c6549 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4213,7 +4213,6 @@ dependencies = [ "namada_proof_of_stake", "namada_sdk", "namada_state", - "namada_storage", "namada_test_utils", "namada_token", "namada_tx", diff --git a/ibc/src/lib.rs b/ibc/src/lib.rs index cb48bc70e3..2306b3ee8e 100644 --- a/ibc/src/lib.rs +++ b/ibc/src/lib.rs @@ -33,6 +33,7 @@ use namada_core::ibc::core::host::types::identifiers::{ChannelId, PortId}; use namada_core::ibc::core::router::types::error::RouterError; use namada_core::ibc::core::router::types::module::ModuleId; use namada_core::ibc::primitives::proto::Any; +pub use namada_core::ibc::*; use namada_core::types::address::{Address, MASP}; use namada_core::types::ibc::{ get_shielded_transfer, is_ibc_denom, MsgShieldedTransfer, diff --git a/shared/Cargo.toml b/shared/Cargo.toml index 9e2bc82c48..b3560debfb 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -81,7 +81,6 @@ namada_parameters = { path = "../parameters" } namada_proof_of_stake = { path = "../proof_of_stake" } namada_sdk = { path = "../sdk", default-features = false } namada_state = { path = "../state" } -namada_storage = { path = "../storage" } namada_token = { path = "../token" } namada_tx = { path = "../tx" } namada_tx_env = { path = "../tx_env" } diff --git a/shared/src/ledger/governance/mod.rs b/shared/src/ledger/governance/mod.rs index b6b93116e6..e2fa7017d5 100644 --- a/shared/src/ledger/governance/mod.rs +++ b/shared/src/ledger/governance/mod.rs @@ -10,8 +10,7 @@ use namada_governance::storage::vote::StorageProposalVote; use namada_governance::storage::{is_proposal_accepted, keys as gov_storage}; use namada_governance::utils::is_valid_validator_voting_period; use namada_proof_of_stake::is_validator; -use namada_storage::StorageRead; -use namada_token as token; +use namada_state::StorageRead; use namada_tx::Tx; use namada_vp_env::VpEnv; use thiserror::Error; @@ -19,6 +18,7 @@ use thiserror::Error; use self::utils::ReadType; use crate::ledger::native_vp::{Ctx, NativeVp}; use crate::ledger::{native_vp, pos}; +use crate::token; use crate::types::address::{Address, InternalAddress}; use crate::types::storage::{Epoch, Key}; use crate::vm::WasmCacheAccess; diff --git a/shared/src/ledger/ibc/mod.rs b/shared/src/ledger/ibc/mod.rs index 78b520813e..4f0a9970be 100644 --- a/shared/src/ledger/ibc/mod.rs +++ b/shared/src/ledger/ibc/mod.rs @@ -4,8 +4,7 @@ pub use namada_ibc::storage; use namada_ibc::storage::{ channel_counter_key, client_counter_key, connection_counter_key, }; -use namada_state::{StorageHasher, WlStorage}; -use namada_storage::StorageWrite; +use namada_state::{StorageHasher, StorageWrite, WlStorage}; /// Initialize storage in the genesis block. pub fn init_genesis_storage(storage: &mut WlStorage) diff --git a/shared/src/ledger/mod.rs b/shared/src/ledger/mod.rs index 3e95cbd38a..cd716bbeb7 100644 --- a/shared/src/ledger/mod.rs +++ b/shared/src/ledger/mod.rs @@ -23,8 +23,7 @@ pub use { #[cfg(feature = "wasm-runtime")] mod dry_run_tx { use namada_sdk::queries::{EncodedResponseQuery, RequestCtx, RequestQuery}; - use namada_state::{DBIter, StorageHasher, DB}; - use namada_storage::ResultExt; + use namada_state::{DBIter, ResultExt, StorageHasher, DB}; use super::protocol; use crate::vm::wasm::{TxCache, VpCache}; @@ -34,7 +33,7 @@ mod dry_run_tx { pub fn dry_run_tx( mut ctx: RequestCtx<'_, D, H, VpCache, TxCache>, request: &RequestQuery, - ) -> namada_storage::Result + ) -> namada_state::StorageResult where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -115,7 +114,9 @@ mod dry_run_tx { .into_storage_result()?; cumulated_gas = cumulated_gas .checked_add(tx_gas_meter.get_tx_consumed_gas()) - .ok_or(namada_storage::Error::SimpleMessage("Overflow in gas"))?; + .ok_or(namada_state::StorageError::SimpleMessage( + "Overflow in gas", + ))?; // Account gas for both inner and wrapper (if available) data.gas_used = cumulated_gas; // NOTE: the keys changed by the wrapper transaction (if any) are not @@ -133,23 +134,24 @@ mod dry_run_tx { mod test { use borsh::BorshDeserialize; use borsh_ext::BorshSerializeExt; + use namada_core::types::address; use namada_core::types::hash::Hash; use namada_core::types::storage::{BlockHeight, Key}; - use namada_core::types::transaction::decrypted::DecryptedTx; - use namada_core::types::transaction::TxType; - use namada_core::types::{address, token}; use namada_sdk::queries::{ EncodedResponseQuery, RequestCtx, RequestQuery, Router, RPC, }; use namada_state::testing::TestWlStorage; - use namada_storage::StorageWrite; + use namada_state::StorageWrite; use namada_test_utils::TestWasms; + use namada_tx::data::decrypted::DecryptedTx; + use namada_tx::data::TxType; + use namada_tx::{Code, Data, Tx}; use tempfile::TempDir; use tendermint_rpc::{Error as RpcError, Response}; use crate::ledger::events::log::EventLog; use crate::ledger::queries::Client; - use crate::proto::{Code, Data, Tx}; + use crate::token; use crate::vm::wasm::{TxCache, VpCache}; use crate::vm::{wasm, WasmCacheRoAccess}; @@ -191,9 +193,7 @@ mod test { .storage .write( &max_block_gas_key, - namada_core::ledger::storage::types::encode( - &20_000_000_u64, - ), + namada_core::types::encode(&20_000_000_u64), ) .expect( "Max block gas parameter must be initialized in storage", @@ -268,8 +268,8 @@ mod test { } #[tokio::test] - async fn test_shell_queries_router_with_client() -> namada_storageResult<()> - { + async fn test_shell_queries_router_with_client() + -> namada_state::StorageResult<()> { // Initialize the `TestClient` let mut client = TestClient::new(RPC); // store the wasm code @@ -306,7 +306,7 @@ mod test { // Request storage value for a balance key ... let token_addr = address::testing::established_address_1(); let owner = address::testing::established_address_2(); - let balance_key = token::balance_key(&token_addr, &owner); + let balance_key = token::storage_key::balance_key(&token_addr, &owner); // ... there should be no value yet. let read_balance = RPC .shell() @@ -316,7 +316,7 @@ mod test { assert!(read_balance.data.is_empty()); // Request storage prefix iterator - let balance_prefix = token::balance_prefix(&token_addr); + let balance_prefix = token::storage_key::balance_prefix(&token_addr); let read_balances = RPC .shell() .storage_prefix(&client, None, None, false, &balance_prefix) @@ -350,7 +350,7 @@ mod test { ); // Request storage prefix iterator - let balance_prefix = token::balance_prefix(&token_addr); + let balance_prefix = token::storage_key::balance_prefix(&token_addr); let read_balances = RPC .shell() .storage_prefix(&client, None, None, false, &balance_prefix) diff --git a/shared/src/ledger/native_vp/ethereum_bridge/bridge_pool_vp.rs b/shared/src/ledger/native_vp/ethereum_bridge/bridge_pool_vp.rs index 4d152ba5ef..f1d50cad81 100644 --- a/shared/src/ledger/native_vp/ethereum_bridge/bridge_pool_vp.rs +++ b/shared/src/ledger/native_vp/ethereum_bridge/bridge_pool_vp.rs @@ -18,19 +18,19 @@ use std::marker::PhantomData; use borsh::BorshDeserialize; use eyre::eyre; use namada_core::hints; -use namada_core::ledger::eth_bridge::ADDRESS as BRIDGE_ADDRESS; use namada_core::types::eth_bridge_pool::erc20_token_address; use namada_ethereum_bridge::storage::bridge_pool::{ get_pending_key, is_bridge_pool_key, BRIDGE_POOL_ADDRESS, }; use namada_ethereum_bridge::storage::parameters::read_native_erc20_address; use namada_ethereum_bridge::storage::whitelist; +use namada_ethereum_bridge::ADDRESS as BRIDGE_ADDRESS; use namada_state::{DBIter, StorageHasher, DB}; -use namada_token::storage_key::balance_key; -use namada_token::Amount; use namada_tx::Tx; use crate::ledger::native_vp::{Ctx, NativeVp, StorageReader}; +use crate::token::storage_key::balance_key; +use crate::token::Amount; use crate::types::address::{Address, InternalAddress}; use crate::types::eth_bridge_pool::{PendingTransfer, TransferToEthereumKind}; use crate::types::ethereum_events::EthAddress; @@ -642,27 +642,27 @@ mod test_bridge_pool_vp { use std::env::temp_dir; use borsh::BorshDeserialize; - use borsh_ext::BorshSerializeExt; - use namada_core::ledger::eth_bridge::storage::bridge_pool::get_signed_root_key; + use namada_core::borsh::BorshSerializeExt; use namada_core::types::address; + use namada_ethereum_bridge::storage::bridge_pool::get_signed_root_key; use namada_ethereum_bridge::storage::parameters::{ Contracts, EthereumBridgeParams, UpgradeableContract, }; + use namada_ethereum_bridge::storage::wrapped_erc20s; use namada_gas::TxGasMeter; - use namada_storage::StorageWrite; + use namada_state::StorageWrite; + use namada_tx::data::TxType; use super::*; use crate::ledger::gas::VpGasMeter; - use crate::ledger::storage::mockdb::MockDB; - use crate::ledger::storage::traits::Sha256Hasher; - use crate::ledger::storage::write_log::WriteLog; - use crate::ledger::storage::{State, WlStorage}; + use crate::state::mockdb::MockDB; + use crate::state::write_log::WriteLog; + use crate::state::{Sha256Hasher, State, WlStorage}; use crate::types::address::{nam, wnam, InternalAddress}; use crate::types::chain::ChainId; use crate::types::eth_bridge_pool::{GasFee, TransferToEthereum}; use crate::types::hash::Hash; use crate::types::storage::TxIndex; - use crate::types::transaction::TxType; use crate::vm::wasm::VpCache; use crate::vm::WasmCacheRwAccess; diff --git a/shared/src/ledger/native_vp/ethereum_bridge/nut.rs b/shared/src/ledger/native_vp/ethereum_bridge/nut.rs index c8e02873a6..5acd2927f2 100644 --- a/shared/src/ledger/native_vp/ethereum_bridge/nut.rs +++ b/shared/src/ledger/native_vp/ethereum_bridge/nut.rs @@ -5,13 +5,13 @@ use std::collections::BTreeSet; use eyre::WrapErr; use namada_core::types::address::{Address, InternalAddress}; use namada_core::types::storage::Key; -use namada_core::types::token::Amount; use namada_state::StorageHasher; -use namada_token::storage_key::is_any_token_balance_key; use namada_tx::Tx; use namada_vp_env::VpEnv; use crate::ledger::native_vp::{Ctx, NativeVp}; +use crate::token::storage_key::is_any_token_balance_key; +use crate::token::Amount; use crate::vm::WasmCacheAccess; /// Generic error that may be returned by the validity predicate @@ -121,19 +121,19 @@ mod test_nuts { use std::env::temp_dir; use assert_matches::assert_matches; - use borsh_ext::BorshSerializeExt; + use namada_core::borsh::BorshSerializeExt; use namada_core::types::address::testing::arb_non_internal_address; use namada_core::types::ethereum_events::testing::DAI_ERC20_ETH_ADDRESS; use namada_core::types::storage::TxIndex; - use namada_core::types::token::balance_key; - use namada_core::types::transaction::TxType; use namada_ethereum_bridge::storage::wrapped_erc20s; use namada_state::testing::TestWlStorage; - use namada_storage::StorageWrite; + use namada_state::StorageWrite; + use namada_tx::data::TxType; use proptest::prelude::*; use super::*; use crate::ledger::gas::{TxGasMeter, VpGasMeter}; + use crate::token::storage_key::balance_key; use crate::vm::wasm::VpCache; use crate::vm::WasmCacheRwAccess; diff --git a/shared/src/ledger/native_vp/ethereum_bridge/vp.rs b/shared/src/ledger/native_vp/ethereum_bridge/vp.rs index 01c7f63a87..34b204e2e4 100644 --- a/shared/src/ledger/native_vp/ethereum_bridge/vp.rs +++ b/shared/src/ledger/native_vp/ethereum_bridge/vp.rs @@ -2,17 +2,17 @@ use std::collections::{BTreeSet, HashSet}; use eyre::{eyre, Result}; -use namada_core::ledger::eth_bridge; use namada_core::types::address::Address; use namada_core::types::hash::StorageHasher; use namada_core::types::storage::Key; +use namada_ethereum_bridge; use namada_ethereum_bridge::storage; use namada_ethereum_bridge::storage::escrow_key; -use namada_token::storage_key::{balance_key, is_balance_key}; -use namada_token::Amount; use namada_tx::Tx; use crate::ledger::native_vp::{Ctx, NativeVp, StorageReader}; +use crate::token::storage_key::{balance_key, is_balance_key}; +use crate::token::Amount; use crate::vm::WasmCacheAccess; /// Generic error that may be returned by the validity predicate @@ -44,8 +44,10 @@ where &self, verifiers: &BTreeSet
, ) -> Result { - let escrow_key = - balance_key(&self.ctx.storage.native_token, ð_bridge::ADDRESS); + let escrow_key = balance_key( + &self.ctx.storage.native_token, + &crate::ethereum_bridge::ADDRESS, + ); let escrow_pre: Amount = if let Ok(Some(value)) = (&self.ctx).read_pre_value(&escrow_key) { @@ -166,31 +168,29 @@ mod tests { use std::default::Default; use std::env::temp_dir; - use borsh_ext::BorshSerializeExt; - use namada_core::ledger::eth_bridge; - use namada_core::ledger::eth_bridge::storage::bridge_pool::BRIDGE_POOL_ADDRESS; - use namada_core::ledger::eth_bridge::storage::wrapped_erc20s; - use namada_ethereum_bridge::storage::parameters::{ - Contracts, EthereumBridgeParams, UpgradeableContract, - }; + use namada_core::borsh::BorshSerializeExt; use namada_gas::TxGasMeter; - use namada_storage::StorageWrite; + use namada_state::StorageWrite; + use namada_tx::data::TxType; use namada_tx::Tx; use rand::Rng; use super::*; + use crate::ethereum_bridge::storage::bridge_pool::BRIDGE_POOL_ADDRESS; + use crate::ethereum_bridge::storage::parameters::{ + Contracts, EthereumBridgeParams, UpgradeableContract, + }; + use crate::ethereum_bridge::storage::wrapped_erc20s; use crate::ledger::gas::VpGasMeter; - use crate::ledger::storage::mockdb::MockDB; - use crate::ledger::storage::traits::Sha256Hasher; - use crate::ledger::storage::write_log::WriteLog; - use crate::ledger::storage::{State, WlStorage}; + use crate::state::mockdb::MockDB; + use crate::state::write_log::WriteLog; + use crate::state::{Sha256Hasher, State, WlStorage}; + use crate::token::storage_key::minted_balance_key; use crate::types::address::testing::established_address_1; use crate::types::address::{nam, wnam}; use crate::types::ethereum_events; use crate::types::ethereum_events::EthAddress; use crate::types::storage::TxIndex; - use crate::types::token::minted_balance_key; - use crate::types::transaction::TxType; use crate::vm::wasm::VpCache; use crate::vm::WasmCacheRwAccess; @@ -253,7 +253,7 @@ mod tests { verifiers: &'a BTreeSet
, ) -> Ctx<'a, MockDB, Sha256Hasher, WasmCacheRwAccess> { Ctx::new( - ð_bridge::ADDRESS, + &crate::ethereum_bridge::ADDRESS, storage, write_log, tx, @@ -271,7 +271,7 @@ mod tests { fn test_accepts_expected_keys_changed() { let keys_changed = BTreeSet::from([ balance_key(&nam(), &established_address_1()), - balance_key(&nam(), ð_bridge::ADDRESS), + balance_key(&nam(), &crate::ethereum_bridge::ADDRESS), ]); let result = validate_changed_keys(&nam(), &keys_changed); @@ -371,7 +371,7 @@ mod tests { .expect("Test failed"); // credit the balance to the escrow - let escrow_key = balance_key(&nam(), ð_bridge::ADDRESS); + let escrow_key = balance_key(&nam(), &crate::ethereum_bridge::ADDRESS); wl_storage .write_log .write( @@ -421,7 +421,7 @@ mod tests { .expect("Test failed"); // do not credit the balance to the escrow - let escrow_key = balance_key(&nam(), ð_bridge::ADDRESS); + let escrow_key = balance_key(&nam(), &crate::ethereum_bridge::ADDRESS); wl_storage .write_log .write( @@ -470,7 +470,7 @@ mod tests { .expect("Test failed"); // credit the balance to the escrow - let escrow_key = balance_key(&nam(), ð_bridge::ADDRESS); + let escrow_key = balance_key(&nam(), &crate::ethereum_bridge::ADDRESS); wl_storage .write_log .write( diff --git a/shared/src/ledger/native_vp/ibc/context.rs b/shared/src/ledger/native_vp/ibc/context.rs index 25d70273a3..bb63d66b30 100644 --- a/shared/src/ledger/native_vp/ibc/context.rs +++ b/shared/src/ledger/native_vp/ibc/context.rs @@ -5,13 +5,13 @@ use std::collections::{BTreeSet, HashMap, HashSet}; use borsh_ext::BorshSerializeExt; use namada_core::types::storage::Epochs; use namada_ibc::{IbcCommonContext, IbcStorageContext}; -use namada_storage::{StorageRead, StorageWrite}; -use namada_token::{self as token, Amount, DenominatedAmount}; +use namada_state::{StorageRead, StorageWrite}; use crate::ledger::ibc::storage::is_ibc_key; use crate::ledger::native_vp::CtxPreStorageRead; -use crate::ledger::storage::write_log::StorageModification; -use crate::ledger::storage::{self as ledger_storage, StorageHasher}; +use crate::state::write_log::StorageModification; +use crate::state::{self as ledger_storage, StorageHasher}; +use crate::token::{self as token, Amount, DenominatedAmount}; use crate::types::address::{Address, InternalAddress}; use crate::types::ibc::{IbcEvent, IbcShieldedTransfer}; use crate::types::storage::{ @@ -20,7 +20,7 @@ use crate::types::storage::{ use crate::vm::WasmCacheAccess; /// Result of a storage API call. -pub type Result = std::result::Result; +pub type Result = std::result::Result; /// Pseudo execution environment context for ibc native vp #[derive(Debug)] @@ -203,7 +203,7 @@ where token: &Address, amount: DenominatedAmount, ) -> Result<()> { - let amount = namada_token::denom_to_amount(amount, token, self)?; + let amount = crate::token::denom_to_amount(amount, token, self)?; let src_key = token::storage_key::balance_key(token, src); let dest_key = token::storage_key::balance_key(token, dest); let src_bal: Option = self.ctx.read(&src_key)?; @@ -218,12 +218,12 @@ where } fn handle_masp_tx(&mut self, shielded: &IbcShieldedTransfer) -> Result<()> { - namada_token::utils::handle_masp_tx( + crate::token::utils::handle_masp_tx( self, &shielded.transfer, &shielded.masp_tx, )?; - namada_token::utils::update_note_commitment_tree( + crate::token::utils::update_note_commitment_tree( self, &shielded.masp_tx, ) @@ -235,7 +235,7 @@ where token: &Address, amount: DenominatedAmount, ) -> Result<()> { - let amount = namada_token::denom_to_amount(amount, token, self)?; + let amount = crate::token::denom_to_amount(amount, token, self)?; let target_key = token::storage_key::balance_key(token, target); let mut target_bal: Amount = self.ctx.read(&target_key)?.unwrap_or_default(); @@ -262,7 +262,7 @@ where token: &Address, amount: DenominatedAmount, ) -> Result<()> { - let amount = namada_token::denom_to_amount(amount, token, self)?; + let amount = crate::token::denom_to_amount(amount, token, self)?; let target_key = token::storage_key::balance_key(token, target); let mut target_bal: Amount = self.ctx.read(&target_key)?.unwrap_or_default(); diff --git a/shared/src/ledger/native_vp/ibc/mod.rs b/shared/src/ledger/native_vp/ibc/mod.rs index 7097259296..7774c41cf1 100644 --- a/shared/src/ledger/native_vp/ibc/mod.rs +++ b/shared/src/ledger/native_vp/ibc/mod.rs @@ -263,8 +263,8 @@ pub fn get_dummy_genesis_validator() use crate::core::types::address::testing::established_address_1; use crate::core::types::dec::Dec; use crate::core::types::key::testing::common_sk_from_simple_seed; + use crate::token::Amount; use crate::types::key; - use crate::types::token::Amount; let address = established_address_1(); let tokens = Amount::native_whole(1); @@ -317,22 +317,16 @@ mod tests { }; use ibc_testkit::testapp::ibc::clients::mock::consensus_state::MockConsensusState; use ibc_testkit::testapp::ibc::clients::mock::header::MockHeader; - use namada_core::ledger::governance::parameters::GovernanceParameters; use namada_gas::TxGasMeter; - use namada_storage::StorageRead; + use namada_governance::parameters::GovernanceParameters; + use namada_state::testing::TestWlStorage; + use namada_state::StorageRead; + use namada_tx::data::TxType; + use namada_tx::{Code, Data, Section, Signature, Tx}; use prost::Message; use sha2::Digest; use super::*; - use crate::core::ledger::ibc::storage::{ - ack_key, calc_hash, channel_counter_key, channel_key, - client_connections_key, client_counter_key, client_state_key, - client_update_height_key, client_update_timestamp_key, commitment_key, - connection_counter_key, connection_key, consensus_state_key, - ibc_denom_key, next_sequence_ack_key, next_sequence_recv_key, - next_sequence_send_key, receipt_key, - }; - use crate::core::ledger::storage::testing::TestWlStorage; use crate::core::types::address::testing::{ established_address_1, established_address_2, }; @@ -396,19 +390,26 @@ mod tests { use crate::ibc::core::router::types::event::ModuleEvent; use crate::ibc::primitives::proto::{Any, Protobuf}; use crate::ibc::primitives::{Msg, Timestamp}; + use crate::ibc::storage::{ + ack_key, calc_hash, channel_counter_key, channel_key, + client_connections_key, client_counter_key, client_state_key, + client_update_height_key, client_update_timestamp_key, commitment_key, + connection_counter_key, connection_key, consensus_state_key, + ibc_denom_key, next_sequence_ack_key, next_sequence_recv_key, + next_sequence_send_key, receipt_key, + }; use crate::ledger::gas::VpGasMeter; use crate::ledger::parameters::storage::{ get_epoch_duration_storage_key, get_max_expected_time_per_block_key, }; use crate::ledger::parameters::EpochDuration; use crate::ledger::{ibc, pos}; - use crate::proto::{Code, Data, Section, Signature, Tx}; use crate::tendermint::time::Time as TmTime; + use crate::token::storage_key::balance_key; + use crate::token::Amount; use crate::types::key::testing::keypair_1; use crate::types::storage::{BlockHash, BlockHeight, TxIndex}; use crate::types::time::DurationSecs; - use crate::types::token::{balance_key, Amount}; - use crate::types::transaction::TxType; use crate::vm::wasm; const ADDRESS: Address = Address::Internal(InternalAddress::Ibc); @@ -449,7 +450,7 @@ mod tests { let time_key = get_max_expected_time_per_block_key(); wl_storage .write_log - .write(&time_key, crate::ledger::storage::types::encode(&time)) + .write(&time_key, namada_core::types::encode(&time)) .expect("write failed"); // set a dummy header wl_storage diff --git a/shared/src/ledger/native_vp/masp.rs b/shared/src/ledger/native_vp/masp.rs index 81c7659b4a..80249dc38e 100644 --- a/shared/src/ledger/native_vp/masp.rs +++ b/shared/src/ledger/native_vp/masp.rs @@ -14,15 +14,7 @@ use namada_core::types::address::{Address, MASP}; use namada_core::types::storage::{BlockHeight, Epoch, Key, KeySeg, TxIndex}; use namada_gas::MASP_VERIFY_SHIELDED_TX_GAS; use namada_sdk::masp::verify_shielded_tx; -use namada_storage::OptionExt; -use namada_token::storage_key::{ - is_masp_allowed_key, is_masp_key, is_masp_nullifier_key, - is_masp_tx_pin_key, is_masp_tx_prefix_key, HEAD_TX_KEY, - MASP_CONVERT_ANCHOR_KEY, MASP_NOTE_COMMITMENT_ANCHOR_PREFIX, - MASP_NOTE_COMMITMENT_TREE_KEY, MASP_NULLIFIERS_KEY, PIN_KEY_PREFIX, - TX_KEY_PREFIX, -}; -use namada_token::{self as token, Transfer}; +use namada_state::OptionExt; use namada_tx::Tx; use namada_vp_env::VpEnv; use ripemd::Digest as RipemdDigest; @@ -31,6 +23,14 @@ use thiserror::Error; use crate::ledger::native_vp; use crate::ledger::native_vp::{Ctx, NativeVp}; +use crate::token::storage_key::{ + is_masp_allowed_key, is_masp_key, is_masp_nullifier_key, + is_masp_tx_pin_key, is_masp_tx_prefix_key, HEAD_TX_KEY, + MASP_CONVERT_ANCHOR_KEY, MASP_NOTE_COMMITMENT_ANCHOR_PREFIX, + MASP_NOTE_COMMITMENT_TREE_KEY, MASP_NULLIFIERS_KEY, PIN_KEY_PREFIX, + TX_KEY_PREFIX, +}; +use crate::token::{self as token, Transfer}; use crate::vm::WasmCacheAccess; #[allow(missing_docs)] @@ -384,7 +384,7 @@ where ) -> Result { let epoch = self.ctx.get_block_epoch()?; let (transfer, shielded_tx) = self.ctx.get_shielded_action(tx_data)?; - let transfer_amount = namada_token::denom_to_amount( + let transfer_amount = crate::token::denom_to_amount( transfer.amount, &transfer.token, &self.ctx.pre(), diff --git a/shared/src/ledger/native_vp/mod.rs b/shared/src/ledger/native_vp/mod.rs index 9654aec2ce..6692a514b8 100644 --- a/shared/src/ledger/native_vp/mod.rs +++ b/shared/src/ledger/native_vp/mod.rs @@ -12,18 +12,18 @@ use std::collections::BTreeSet; use borsh::BorshDeserialize; use eyre::WrapErr; +use namada_core::types::storage; use namada_core::types::storage::Epochs; use namada_core::types::validity_predicate::VpSentinel; use namada_gas::GasMetering; -use namada_storage::{ResultExt, StorageRead}; use namada_tx::Tx; pub use namada_vp_env::VpEnv; use super::vp_host_fns; use crate::ledger::gas::VpGasMeter; -use crate::ledger::storage; -use crate::ledger::storage::write_log::WriteLog; -use crate::ledger::storage::{State, StorageHasher}; +use crate::state; +use crate::state::write_log::WriteLog; +use crate::state::{ResultExt, State, StorageHasher, StorageRead}; use crate::types::address::Address; use crate::types::hash::Hash; use crate::types::ibc::IbcEvent; @@ -34,9 +34,9 @@ use crate::vm::prefix_iter::PrefixIterators; use crate::vm::WasmCacheAccess; /// Possible error in a native VP host function call -/// The `namada_storage::Error` may wrap the `vp_host_fns::RuntimeError` and can -/// be extended with other custom errors when using `trait VpEnv`. -pub type Error = namada_storage::Error; +/// The `state::StorageError` may wrap the `vp_host_fns::RuntimeError` +/// and can be extended with other custom errors when using `trait VpEnv`. +pub type Error = state::StorageError; /// A native VP module should implement its validation logic using this trait. pub trait NativeVp { @@ -60,7 +60,7 @@ pub trait NativeVp { #[derive(Debug)] pub struct Ctx<'a, DB, H, CA> where - DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, + DB: state::DB + for<'iter> state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -99,7 +99,7 @@ where #[derive(Debug)] pub struct CtxPreStorageRead<'view, 'a: 'view, DB, H, CA> where - DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, + DB: state::DB + for<'iter> state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -111,7 +111,7 @@ where #[derive(Debug)] pub struct CtxPostStorageRead<'view, 'a: 'view, DB, H, CA> where - DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, + DB: state::DB + for<'iter> state::DBIter<'iter>, H: StorageHasher, CA: WasmCacheAccess, { @@ -120,7 +120,7 @@ where impl<'a, DB, H, CA> Ctx<'a, DB, H, CA> where - DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, + DB: 'static + state::DB + for<'iter> state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { @@ -174,16 +174,16 @@ where impl<'view, 'a: 'view, DB, H, CA> StorageRead for CtxPreStorageRead<'view, 'a, DB, H, CA> where - DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, + DB: 'static + state::DB + for<'iter> state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { - type PrefixIter<'iter> = storage::PrefixIter<'iter, DB> where Self: 'iter; + type PrefixIter<'iter> = state::PrefixIter<'iter, DB> where Self: 'iter; fn read_bytes( &self, - key: &crate::types::storage::Key, - ) -> Result>, namada_storage::Error> { + key: &storage::Key, + ) -> Result>, state::StorageError> { vp_host_fns::read_pre( &mut self.ctx.gas_meter.borrow_mut(), self.ctx.storage, @@ -194,10 +194,7 @@ where .into_storage_result() } - fn has_key( - &self, - key: &crate::types::storage::Key, - ) -> Result { + fn has_key(&self, key: &storage::Key) -> Result { vp_host_fns::has_key_pre( &mut self.ctx.gas_meter.borrow_mut(), self.ctx.storage, @@ -210,8 +207,8 @@ where fn iter_prefix<'iter>( &'iter self, - prefix: &crate::types::storage::Key, - ) -> Result, namada_storage::Error> { + prefix: &storage::Key, + ) -> Result, state::StorageError> { vp_host_fns::iter_prefix_pre( &mut self.ctx.gas_meter.borrow_mut(), self.ctx.write_log, @@ -228,7 +225,7 @@ where fn iter_next<'iter>( &'iter self, iter: &mut Self::PrefixIter<'iter>, - ) -> Result)>, namada_storage::Error> { + ) -> Result)>, state::StorageError> { vp_host_fns::iter_next::( &mut self.ctx.gas_meter.borrow_mut(), iter, @@ -237,38 +234,38 @@ where .into_storage_result() } - fn get_chain_id(&self) -> Result { + fn get_chain_id(&self) -> Result { self.ctx.get_chain_id() } - fn get_block_height(&self) -> Result { + fn get_block_height(&self) -> Result { self.ctx.get_block_height() } fn get_block_header( &self, height: BlockHeight, - ) -> Result, namada_storage::Error> { + ) -> Result, state::StorageError> { self.ctx.get_block_header(height) } - fn get_block_hash(&self) -> Result { + fn get_block_hash(&self) -> Result { self.ctx.get_block_hash() } - fn get_block_epoch(&self) -> Result { + fn get_block_epoch(&self) -> Result { self.ctx.get_block_epoch() } - fn get_tx_index(&self) -> Result { + fn get_tx_index(&self) -> Result { self.ctx.get_tx_index().into_storage_result() } - fn get_native_token(&self) -> Result { + fn get_native_token(&self) -> Result { self.ctx.get_native_token() } - fn get_pred_epochs(&self) -> namada_storage::Result { + fn get_pred_epochs(&self) -> state::StorageResult { self.ctx.get_pred_epochs() } } @@ -276,16 +273,16 @@ where impl<'view, 'a: 'view, DB, H, CA> StorageRead for CtxPostStorageRead<'view, 'a, DB, H, CA> where - DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, + DB: 'static + state::DB + for<'iter> state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { - type PrefixIter<'iter> = storage::PrefixIter<'iter, DB> where Self: 'iter; + type PrefixIter<'iter> = state::PrefixIter<'iter, DB> where Self: 'iter; fn read_bytes( &self, - key: &crate::types::storage::Key, - ) -> Result>, namada_storage::Error> { + key: &storage::Key, + ) -> Result>, state::StorageError> { vp_host_fns::read_post( &mut self.ctx.gas_meter.borrow_mut(), self.ctx.storage, @@ -296,10 +293,7 @@ where .into_storage_result() } - fn has_key( - &self, - key: &crate::types::storage::Key, - ) -> Result { + fn has_key(&self, key: &storage::Key) -> Result { vp_host_fns::has_key_post( &mut self.ctx.gas_meter.borrow_mut(), self.ctx.storage, @@ -312,8 +306,8 @@ where fn iter_prefix<'iter>( &'iter self, - prefix: &crate::types::storage::Key, - ) -> Result, namada_storage::Error> { + prefix: &storage::Key, + ) -> Result, state::StorageError> { vp_host_fns::iter_prefix_post( &mut self.ctx.gas_meter.borrow_mut(), self.ctx.write_log, @@ -330,7 +324,7 @@ where fn iter_next<'iter>( &'iter self, iter: &mut Self::PrefixIter<'iter>, - ) -> Result)>, namada_storage::Error> { + ) -> Result)>, state::StorageError> { vp_host_fns::iter_next::( &mut self.ctx.gas_meter.borrow_mut(), iter, @@ -339,51 +333,51 @@ where .into_storage_result() } - fn get_chain_id(&self) -> Result { + fn get_chain_id(&self) -> Result { self.ctx.get_chain_id() } - fn get_block_height(&self) -> Result { + fn get_block_height(&self) -> Result { self.ctx.get_block_height() } fn get_block_header( &self, height: BlockHeight, - ) -> Result, namada_storage::Error> { + ) -> Result, state::StorageError> { self.ctx.get_block_header(height) } - fn get_block_hash(&self) -> Result { + fn get_block_hash(&self) -> Result { self.ctx.get_block_hash() } - fn get_block_epoch(&self) -> Result { + fn get_block_epoch(&self) -> Result { self.ctx.get_block_epoch() } - fn get_tx_index(&self) -> Result { + fn get_tx_index(&self) -> Result { self.ctx.get_tx_index().into_storage_result() } - fn get_native_token(&self) -> Result { + fn get_native_token(&self) -> Result { Ok(self.ctx.storage.native_token.clone()) } - fn get_pred_epochs(&self) -> namada_storage::Result { + fn get_pred_epochs(&self) -> state::StorageResult { self.ctx.get_pred_epochs() } } impl<'view, 'a: 'view, DB, H, CA> VpEnv<'view> for Ctx<'a, DB, H, CA> where - DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, + DB: 'static + state::DB + for<'iter> state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { type Post = CtxPostStorageRead<'view, 'a, DB, H, CA>; type Pre = CtxPreStorageRead<'view, 'a, DB, H, CA>; - type PrefixIter<'iter> = storage::PrefixIter<'iter, DB> where Self: 'iter; + type PrefixIter<'iter> = state::PrefixIter<'iter, DB> where Self: 'iter; fn pre(&'view self) -> Self::Pre { CtxPreStorageRead { ctx: self } @@ -396,7 +390,7 @@ where fn read_temp( &self, key: &Key, - ) -> Result, namada_storage::Error> { + ) -> Result, state::StorageError> { vp_host_fns::read_temp( &mut self.gas_meter.borrow_mut(), self.write_log, @@ -410,7 +404,7 @@ where fn read_bytes_temp( &self, key: &Key, - ) -> Result>, namada_storage::Error> { + ) -> Result>, state::StorageError> { vp_host_fns::read_temp( &mut self.gas_meter.borrow_mut(), self.write_log, @@ -420,7 +414,7 @@ where .into_storage_result() } - fn get_chain_id(&self) -> Result { + fn get_chain_id(&self) -> Result { vp_host_fns::get_chain_id( &mut self.gas_meter.borrow_mut(), self.storage, @@ -429,7 +423,7 @@ where .into_storage_result() } - fn get_block_height(&self) -> Result { + fn get_block_height(&self) -> Result { vp_host_fns::get_block_height( &mut self.gas_meter.borrow_mut(), self.storage, @@ -441,7 +435,7 @@ where fn get_block_header( &self, height: BlockHeight, - ) -> Result, namada_storage::Error> { + ) -> Result, state::StorageError> { vp_host_fns::get_block_header( &mut self.gas_meter.borrow_mut(), self.storage, @@ -451,7 +445,7 @@ where .into_storage_result() } - fn get_block_hash(&self) -> Result { + fn get_block_hash(&self) -> Result { vp_host_fns::get_block_hash( &mut self.gas_meter.borrow_mut(), self.storage, @@ -460,7 +454,7 @@ where .into_storage_result() } - fn get_block_epoch(&self) -> Result { + fn get_block_epoch(&self) -> Result { vp_host_fns::get_block_epoch( &mut self.gas_meter.borrow_mut(), self.storage, @@ -469,7 +463,7 @@ where .into_storage_result() } - fn get_tx_index(&self) -> Result { + fn get_tx_index(&self) -> Result { vp_host_fns::get_tx_index( &mut self.gas_meter.borrow_mut(), self.tx_index, @@ -478,7 +472,7 @@ where .into_storage_result() } - fn get_native_token(&self) -> Result { + fn get_native_token(&self) -> Result { vp_host_fns::get_native_token( &mut self.gas_meter.borrow_mut(), self.storage, @@ -487,7 +481,7 @@ where .into_storage_result() } - fn get_pred_epochs(&self) -> namada_storage::Result { + fn get_pred_epochs(&self) -> state::StorageResult { vp_host_fns::get_pred_epochs( &mut self.gas_meter.borrow_mut(), self.storage, @@ -499,7 +493,7 @@ where fn get_ibc_events( &self, event_type: String, - ) -> Result, namada_storage::Error> { + ) -> Result, state::StorageError> { vp_host_fns::get_ibc_events( &mut self.gas_meter.borrow_mut(), self.write_log, @@ -511,7 +505,7 @@ where fn iter_prefix<'iter>( &'iter self, prefix: &Key, - ) -> Result, namada_storage::Error> { + ) -> Result, state::StorageError> { vp_host_fns::iter_prefix_pre( &mut self.gas_meter.borrow_mut(), self.write_log, @@ -526,7 +520,7 @@ where &self, vp_code_hash: Hash, input_data: Tx, - ) -> Result { + ) -> Result { #[cfg(feature = "wasm-runtime")] { use std::marker::PhantomData; @@ -583,13 +577,13 @@ where } } - fn charge_gas(&self, used_gas: u64) -> Result<(), namada_storage::Error> { + fn charge_gas(&self, used_gas: u64) -> Result<(), state::StorageError> { self.gas_meter.borrow_mut().consume(used_gas).map_err(|_| { Error::SimpleMessage("Gas limit exceeded in native vp") }) } - fn get_tx_code_hash(&self) -> Result, namada_storage::Error> { + fn get_tx_code_hash(&self) -> Result, state::StorageError> { vp_host_fns::get_tx_code_hash( &mut self.gas_meter.borrow_mut(), self.tx, @@ -601,36 +595,36 @@ where fn read_pre( &self, key: &Key, - ) -> Result, namada_storage::Error> { + ) -> Result, state::StorageError> { self.pre().read(key).map_err(Into::into) } fn read_bytes_pre( &self, key: &Key, - ) -> Result>, namada_storage::Error> { + ) -> Result>, state::StorageError> { self.pre().read_bytes(key).map_err(Into::into) } fn read_post( &self, key: &Key, - ) -> Result, namada_storage::Error> { + ) -> Result, state::StorageError> { self.post().read(key).map_err(Into::into) } fn read_bytes_post( &self, key: &Key, - ) -> Result>, namada_storage::Error> { + ) -> Result>, state::StorageError> { self.post().read_bytes(key).map_err(Into::into) } - fn has_key_pre(&self, key: &Key) -> Result { + fn has_key_pre(&self, key: &Key) -> Result { self.pre().has_key(key).map_err(Into::into) } - fn has_key_post(&self, key: &Key) -> Result { + fn has_key_post(&self, key: &Key) -> Result { self.post().has_key(key).map_err(Into::into) } } @@ -669,7 +663,7 @@ pub trait StorageReader { impl<'a, DB, H, CA> StorageReader for &Ctx<'a, DB, H, CA> where - DB: 'static + namada_state::DB + for<'iter> namada_state::DBIter<'iter>, + DB: 'static + state::DB + for<'iter> state::DBIter<'iter>, H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { diff --git a/shared/src/ledger/native_vp/multitoken.rs b/shared/src/ledger/native_vp/multitoken.rs index 78c345a56e..5537ca7541 100644 --- a/shared/src/ledger/native_vp/multitoken.rs +++ b/shared/src/ledger/native_vp/multitoken.rs @@ -2,16 +2,16 @@ use std::collections::{BTreeSet, HashMap}; -use namada_token::storage_key::{ - is_any_minted_balance_key, is_any_minter_key, is_any_token_balance_key, - minter_key, -}; -use namada_token::{Amount, Change}; use namada_tx::Tx; use namada_vp_env::VpEnv; use thiserror::Error; use crate::ledger::native_vp::{self, Ctx, NativeVp}; +use crate::token::storage_key::{ + is_any_minted_balance_key, is_any_minter_key, is_any_token_balance_key, + minter_key, +}; +use crate::token::{Amount, Change}; use crate::types::address::{Address, InternalAddress}; use crate::types::storage::{Key, KeySeg}; use crate::vm::WasmCacheAccess; @@ -141,23 +141,24 @@ mod tests { use borsh_ext::BorshSerializeExt; use namada_gas::TxGasMeter; + use namada_state::testing::TestWlStorage; + use namada_tx::data::TxType; + use namada_tx::{Code, Data, Section, Signature, Tx}; use super::*; - use crate::core::ledger::storage::testing::TestWlStorage; use crate::core::types::address::nam; use crate::core::types::address::testing::{ established_address_1, established_address_2, }; use crate::ledger::gas::VpGasMeter; use crate::ledger::ibc::storage::ibc_token; - use crate::proto::{Code, Data, Section, Signature, Tx}; + use crate::token::storage_key::{ + balance_key, minted_balance_key, minter_key, + }; + use crate::token::Amount; use crate::types::address::{Address, InternalAddress}; use crate::types::key::testing::keypair_1; use crate::types::storage::TxIndex; - use crate::types::token::{ - balance_key, minted_balance_key, minter_key, Amount, - }; - use crate::types::transaction::TxType; use crate::vm::wasm::compilation_cache::common::testing::cache as wasm_cache; const ADDRESS: Address = Address::Internal(InternalAddress::Multitoken); diff --git a/shared/src/ledger/native_vp/slash_fund.rs b/shared/src/ledger/native_vp/slash_fund.rs index e282c92936..6cd463ea1c 100644 --- a/shared/src/ledger/native_vp/slash_fund.rs +++ b/shared/src/ledger/native_vp/slash_fund.rs @@ -5,15 +5,15 @@ use std::collections::BTreeSet; use namada_core::ledger::slash_fund; /// SlashFund storage pub use namada_core::ledger::slash_fund::storage; -use namada_storage::StorageRead; +use namada_state::StorageRead; use namada_tx::Tx; use thiserror::Error; use crate::ledger::native_vp::{self, governance, Ctx, NativeVp}; -use crate::ledger::storage::{self as ledger_storage, StorageHasher}; +use crate::state::{self as ledger_storage, StorageHasher}; +use crate::token; use crate::types::address::Address; use crate::types::storage::Key; -use crate::types::token; use crate::vm::WasmCacheAccess; #[allow(missing_docs)] diff --git a/shared/src/ledger/pgf/utils.rs b/shared/src/ledger/pgf/utils.rs index 3d320de3e4..8132de32af 100644 --- a/shared/src/ledger/pgf/utils.rs +++ b/shared/src/ledger/pgf/utils.rs @@ -1,9 +1,9 @@ use std::collections::HashMap; use namada_core::types::address::Address; -use namada_core::types::token; use crate::ledger::events::EventType; +use crate::token; /// Proposal event definition pub struct ProposalEvent { diff --git a/shared/src/ledger/pos/mod.rs b/shared/src/ledger/pos/mod.rs index ccc64fd63a..a7e8fa0d22 100644 --- a/shared/src/ledger/pos/mod.rs +++ b/shared/src/ledger/pos/mod.rs @@ -7,7 +7,6 @@ use std::convert::TryFrom; use namada_core::types::address; pub use namada_core::types::dec::Dec; pub use namada_core::types::key::common; -pub use namada_core::types::token; pub use namada_proof_of_stake::parameters::{OwnedPosParams, PosParams}; pub use namada_proof_of_stake::pos_queries::*; pub use namada_proof_of_stake::storage::*; @@ -15,8 +14,9 @@ pub use namada_proof_of_stake::storage::*; pub use namada_proof_of_stake::test_utils; pub use namada_proof_of_stake::{staking_token_address, types}; pub use vp::PosVP; -pub use {namada_proof_of_stake, namada_storage}; +pub use {namada_proof_of_stake, namada_state}; +pub use crate::token; use crate::types::address::{Address, InternalAddress}; /// Address of the PoS account implemented as a native VP diff --git a/shared/src/ledger/pos/vp.rs b/shared/src/ledger/pos/vp.rs index f9700fefce..69ec002654 100644 --- a/shared/src/ledger/pos/vp.rs +++ b/shared/src/ledger/pos/vp.rs @@ -14,7 +14,7 @@ pub use namada_proof_of_stake::types; // is_validator_max_commission_rate_change_key, // }; use namada_state::StorageHasher; -use namada_storage::StorageRead; +use namada_state::StorageRead; use namada_tx::Tx; use thiserror::Error; diff --git a/shared/src/ledger/protocol/mod.rs b/shared/src/ledger/protocol/mod.rs index e2a00bf4a8..36746a974d 100644 --- a/shared/src/ledger/protocol/mod.rs +++ b/shared/src/ledger/protocol/mod.rs @@ -6,11 +6,10 @@ use eyre::{eyre, WrapErr}; use masp_primitives::transaction::Transaction; use namada_core::types::hash::Hash; use namada_core::types::storage::Key; -use namada_core::types::token::Amount; use namada_gas::TxGasMeter; use namada_sdk::tx::TX_TRANSFER_WASM; use namada_state::wl_storage::WriteLogAndStorage; -use namada_storage::StorageRead; +use namada_state::StorageRead; use namada_tx::data::protocol::ProtocolTxType; use namada_tx::data::{DecryptedTx, TxResult, TxType, VpsResult, WrapperTx}; use namada_tx::{Section, Tx}; @@ -30,8 +29,9 @@ use crate::ledger::native_vp::parameters::{self, ParametersVp}; use crate::ledger::native_vp::{self, NativeVp}; use crate::ledger::pgf::PgfVp; use crate::ledger::pos::{self, PosVP}; -use crate::ledger::storage::write_log::WriteLog; -use crate::ledger::storage::{DBIter, State, StorageHasher, WlStorage, DB}; +use crate::state::write_log::WriteLog; +use crate::state::{DBIter, State, StorageHasher, WlStorage, DB}; +use crate::token::Amount; use crate::types::address::{Address, InternalAddress}; use crate::types::storage; use crate::types::storage::TxIndex; @@ -393,7 +393,7 @@ pub fn transfer_fee( where WLS: WriteLogAndStorage + StorageRead, { - let balance = namada_token::read_balance( + let balance = crate::token::read_balance( wl_storage, &wrapper.fee.token, &wrapper.fee_payer(), @@ -402,7 +402,7 @@ where match wrapper.get_tx_fee() { Ok(fees) => { - let fees = namada_token::denom_to_amount( + let fees = crate::token::denom_to_amount( fees, &wrapper.fee.token, wl_storage, @@ -461,7 +461,7 @@ where /// Transfer `token` from `src` to `dest`. Returns an `Err` if `src` has /// insufficient balance or if the transfer the `dest` would overflow (This can /// only happen if the total supply doesn't fit in `token::Amount`). Contrary to -/// `namada_token::transfer` this function updates the tx write log and +/// `crate::token::transfer` this function updates the tx write log and /// not the block write log. fn token_transfer( wl_storage: &mut WLS, @@ -473,17 +473,17 @@ fn token_transfer( where WLS: WriteLogAndStorage + StorageRead, { - let src_key = namada_token::storage_key::balance_key(token, src); - let src_balance = namada_token::read_balance(wl_storage, token, src) + let src_key = crate::token::storage_key::balance_key(token, src); + let src_balance = crate::token::read_balance(wl_storage, token, src) .expect("Token balance read in protocol must not fail"); match src_balance.checked_sub(amount) { Some(new_src_balance) => { if src == dest { return Ok(()); } - let dest_key = namada_token::storage_key::balance_key(token, dest); + let dest_key = crate::token::storage_key::balance_key(token, dest); let dest_balance = - namada_token::read_balance(wl_storage, token, dest) + crate::token::read_balance(wl_storage, token, dest) .expect("Token balance read in protocol must not fail"); match dest_balance.checked_add(amount) { Some(new_dest_balance) => { @@ -514,7 +514,7 @@ pub fn check_fees(wl_storage: &WLS, wrapper: &WrapperTx) -> Result<()> where WLS: WriteLogAndStorage + StorageRead, { - let balance = namada_token::read_balance( + let balance = crate::token::read_balance( wl_storage, &wrapper.fee.token, &wrapper.fee_payer(), @@ -526,7 +526,7 @@ where .map_err(|e| Error::FeeError(e.to_string()))?; let fees = - namada_token::denom_to_amount(fees, &wrapper.fee.token, wl_storage) + crate::token::denom_to_amount(fees, &wrapper.fee.token, wl_storage) .map_err(|e| Error::FeeError(e.to_string()))?; if balance.checked_sub(fees).is_some() { Ok(()) @@ -1097,16 +1097,12 @@ mod tests { use borsh::BorshDeserialize; use eyre::Result; - use namada_core::proto::{SignableEthMessage, Signed}; use namada_core::types::ethereum_events::testing::DAI_ERC20_ETH_ADDRESS; use namada_core::types::ethereum_events::{ EthereumEvent, TransferToNamada, }; use namada_core::types::keccak::keccak_hash; use namada_core::types::storage::BlockHeight; - use namada_core::types::token::Amount; - use namada_core::types::vote_extensions::bridge_pool_roots::BridgePoolRootVext; - use namada_core::types::vote_extensions::ethereum_events::EthereumEventsVext; use namada_core::types::voting_power::FractionalVotingPower; use namada_core::types::{address, key}; use namada_ethereum_bridge::protocol::transactions::votes::{ @@ -1116,7 +1112,11 @@ mod tests { use namada_ethereum_bridge::storage::proof::EthereumProof; use namada_ethereum_bridge::storage::{vote_tallies, vp}; use namada_ethereum_bridge::test_utils; - use namada_storage::StorageRead; + use namada_state::StorageRead; + use namada_token::Amount; + use namada_tx::{SignableEthMessage, Signed}; + use namada_vote_ext::bridge_pool_roots::BridgePoolRootVext; + use namada_vote_ext::ethereum_events::EthereumEventsVext; use super::*; @@ -1164,7 +1164,9 @@ mod tests { }; let signing_key = key::testing::keypair_1(); let signed = vext.sign(&signing_key); - let tx = EthereumTxData::EthEventsVext(signed); + let tx = EthereumTxData::EthEventsVext( + namada_vote_ext::ethereum_events::SignedVext(signed), + ); apply_eth_tx(tx.clone(), &mut wl_storage)?; apply_eth_tx(tx, &mut wl_storage)?; diff --git a/shared/src/lib.rs b/shared/src/lib.rs index 1812400f0e..a01dba6ebb 100644 --- a/shared/src/lib.rs +++ b/shared/src/lib.rs @@ -6,16 +6,18 @@ #![deny(rustdoc::broken_intra_doc_links)] #![deny(rustdoc::private_intra_doc_links)] -pub use namada_core::{ibc, tendermint, tendermint_proto}; +pub use namada_core::{tendermint, tendermint_proto}; #[cfg(feature = "tendermint-rpc")] pub use tendermint_rpc; pub use { - bip39, namada_account as account, namada_core as core, namada_gas as gas, - namada_governance as governance, namada_parameters as parameters, - namada_proof_of_stake as proof_of_stake, namada_sdk as sdk, - namada_state as state, namada_storage as storage, namada_token as token, + bip39, namada_account as account, namada_core as core, + namada_ethereum_bridge as ethereum_bridge, namada_gas as gas, + namada_governance as governance, namada_ibc as ibc, + namada_parameters as parameters, namada_proof_of_stake as proof_of_stake, + namada_sdk as sdk, namada_state as state, namada_token as token, namada_tx as tx, namada_vote_ext as vote_ext, }; + pub mod ledger; pub use namada_tx::proto; pub mod types; diff --git a/shared/src/vm/host_env.rs b/shared/src/vm/host_env.rs index ee56968bd3..8bdbd8b587 100644 --- a/shared/src/vm/host_env.rs +++ b/shared/src/vm/host_env.rs @@ -16,12 +16,7 @@ use namada_gas::{ MEMORY_ACCESS_GAS_PER_BYTE, }; use namada_state::write_log::{self, WriteLog}; -use namada_state::{State, StorageHasher}; -use namada_storage::{self, ResultExt}; -use namada_token::storage_key::{ - balance_key, is_any_minted_balance_key, is_any_minter_key, - is_any_token_balance_key, minted_balance_key, minter_key, -}; +use namada_state::{self, ResultExt, State, StorageHasher}; use namada_tx::data::TxSentinel; use namada_tx::Tx; use thiserror::Error; @@ -32,6 +27,10 @@ use super::wasm::TxCache; use super::wasm::VpCache; use super::WasmCacheAccess; use crate::ledger::vp_host_fns; +use crate::token::storage_key::{ + balance_key, is_any_minted_balance_key, is_any_minter_key, + is_any_token_balance_key, minted_balance_key, minter_key, +}; use crate::types::address::{self, Address}; use crate::types::hash::Hash; use crate::types::ibc::{IbcEvent, IbcShieldedTransfer}; @@ -58,7 +57,7 @@ pub enum TxRuntimeError { #[error("Storage modification error: {0}")] StorageModificationError(write_log::Error), #[error("Storage error: {0}")] - StorageError(namada_state::Error), + StorageError(#[from] namada_state::Error), #[error("Storage data error: {0}")] StorageDataError(crate::types::storage::Error), #[error("Encoding error: {0}")] @@ -2190,7 +2189,7 @@ where .map_err(TxRuntimeError::EncodingError)?; let mut ctx = env.ctx.clone(); - match namada_token::utils::update_note_commitment_tree( + match crate::token::utils::update_note_commitment_tree( &mut ctx, &transaction, ) { @@ -2296,7 +2295,7 @@ where } // Temp. workaround for -use namada_storage::StorageRead; +use namada_state::StorageRead; use crate::types::storage::BlockHash; impl<'a, DB, H, CA> StorageRead for TxCtx<'a, DB, H, CA> @@ -2311,7 +2310,7 @@ where fn read_bytes( &self, key: &Key, - ) -> std::result::Result>, namada_storage::Error> { + ) -> std::result::Result>, namada_state::StorageError> { let write_log = unsafe { self.write_log.get() }; let (log_val, gas) = write_log.read(key); ibc_tx_charge_gas(self, gas)?; @@ -2336,7 +2335,7 @@ where }) } - fn has_key(&self, key: &Key) -> Result { + fn has_key(&self, key: &Key) -> Result { // try to read from the write log first let write_log = unsafe { self.write_log.get() }; let (log_val, gas) = write_log.read(key); @@ -2360,7 +2359,7 @@ where fn iter_prefix<'iter>( &'iter self, prefix: &Key, - ) -> Result, namada_storage::Error> { + ) -> Result, namada_state::StorageError> { let write_log = unsafe { self.write_log.get() }; let storage = unsafe { self.storage.get() }; let (iter, gas) = @@ -2374,7 +2373,7 @@ where fn iter_next<'iter>( &'iter self, iter_id: &mut Self::PrefixIter<'iter>, - ) -> Result)>, namada_storage::Error> { + ) -> Result)>, namada_state::StorageError> { let write_log = unsafe { self.write_log.get() }; let iterators = unsafe { self.iterators.get() }; let iter_id = PrefixIteratorId::new(*iter_id); @@ -2407,14 +2406,16 @@ where Ok(None) } - fn get_chain_id(&self) -> Result { + fn get_chain_id(&self) -> Result { let storage = unsafe { self.storage.get() }; let (chain_id, gas) = storage.get_chain_id(); ibc_tx_charge_gas(self, gas)?; Ok(chain_id) } - fn get_block_height(&self) -> Result { + fn get_block_height( + &self, + ) -> Result { let storage = unsafe { self.storage.get() }; let (height, gas) = storage.get_block_height(); ibc_tx_charge_gas(self, gas)?; @@ -2426,7 +2427,7 @@ where height: BlockHeight, ) -> Result< Option, - namada_storage::Error, + namada_state::StorageError, > { let storage = unsafe { self.storage.get() }; let (header, gas) = storage @@ -2436,21 +2437,21 @@ where Ok(header) } - fn get_block_hash(&self) -> Result { + fn get_block_hash(&self) -> Result { let storage = unsafe { self.storage.get() }; let (hash, gas) = storage.get_block_hash(); ibc_tx_charge_gas(self, gas)?; Ok(hash) } - fn get_block_epoch(&self) -> Result { + fn get_block_epoch(&self) -> Result { let storage = unsafe { self.storage.get() }; let (epoch, gas) = storage.get_current_epoch(); ibc_tx_charge_gas(self, gas)?; Ok(epoch) } - fn get_tx_index(&self) -> Result { + fn get_tx_index(&self) -> Result { let tx_index = unsafe { self.tx_index.get() }; ibc_tx_charge_gas( self, @@ -2459,7 +2460,7 @@ where Ok(TxIndex(tx_index.0)) } - fn get_native_token(&self) -> Result { + fn get_native_token(&self) -> Result { let storage = unsafe { self.storage.get() }; let native_token = storage.native_token.clone(); ibc_tx_charge_gas( @@ -2469,7 +2470,7 @@ where Ok(native_token) } - fn get_pred_epochs(&self) -> namada_storage::Result { + fn get_pred_epochs(&self) -> namada_state::StorageResult { let storage = unsafe { self.storage.get() }; ibc_tx_charge_gas( self, @@ -2480,7 +2481,7 @@ where } // Temp. workaround for -use namada_storage::StorageWrite; +use namada_state::StorageWrite; impl<'a, DB, H, CA> StorageWrite for TxCtx<'a, DB, H, CA> where DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, @@ -2491,7 +2492,7 @@ where &mut self, key: &Key, data: impl AsRef<[u8]>, - ) -> Result<(), namada_storage::Error> { + ) -> Result<(), namada_state::StorageError> { let write_log = unsafe { self.write_log.get() }; let (gas, _size_diff) = write_log .write(key, data.as_ref().to_vec()) @@ -2499,7 +2500,7 @@ where ibc_tx_charge_gas(self, gas) } - fn delete(&mut self, key: &Key) -> Result<(), namada_storage::Error> { + fn delete(&mut self, key: &Key) -> Result<(), namada_state::StorageError> { if key.is_validity_predicate().is_some() { return Err(TxRuntimeError::CannotDeleteVp).into_storage_result(); } @@ -2520,7 +2521,7 @@ where fn emit_ibc_event( &mut self, event: IbcEvent, - ) -> Result<(), namada_storage::Error> { + ) -> Result<(), namada_state::StorageError> { let write_log = unsafe { self.write_log.get() }; let gas = write_log.emit_ibc_event(event); ibc_tx_charge_gas(self, gas) @@ -2529,7 +2530,7 @@ where fn get_ibc_events( &self, event_type: impl AsRef, - ) -> Result, namada_storage::Error> { + ) -> Result, namada_state::StorageError> { let write_log = unsafe { self.write_log.get() }; Ok(write_log .get_ibc_events() @@ -2544,9 +2545,9 @@ where src: &Address, dest: &Address, token: &Address, - amount: namada_core::types::token::DenominatedAmount, - ) -> Result<(), namada_storage::Error> { - use namada_token as token; + amount: crate::token::DenominatedAmount, + ) -> Result<(), namada_state::StorageError> { + use crate::token; let amount = token::denom_to_amount(amount, token, self)?; if amount != token::Amount::default() && src != dest { @@ -2570,13 +2571,13 @@ where fn handle_masp_tx( &mut self, shielded: &IbcShieldedTransfer, - ) -> Result<(), namada_storage::Error> { - namada_token::utils::handle_masp_tx( + ) -> Result<(), namada_state::StorageError> { + crate::token::utils::handle_masp_tx( self, &shielded.transfer, &shielded.masp_tx, )?; - namada_token::utils::update_note_commitment_tree( + crate::token::utils::update_note_commitment_tree( self, &shielded.masp_tx, ) @@ -2586,9 +2587,9 @@ where &mut self, target: &Address, token: &Address, - amount: namada_core::types::token::DenominatedAmount, - ) -> Result<(), namada_storage::Error> { - use namada_token as token; + amount: crate::token::DenominatedAmount, + ) -> Result<(), namada_state::StorageError> { + use crate::token; let amount = token::denom_to_amount(amount, token, self)?; let target_key = balance_key(token, target); @@ -2615,9 +2616,9 @@ where &mut self, target: &Address, token: &Address, - amount: namada_core::types::token::DenominatedAmount, - ) -> Result<(), namada_storage::Error> { - use namada_token as token; + amount: crate::token::DenominatedAmount, + ) -> Result<(), namada_state::StorageError> { + use crate::token; let amount = token::denom_to_amount(amount, token, self)?; let target_key = balance_key(token, target); @@ -2645,7 +2646,7 @@ where fn ibc_tx_charge_gas<'a, DB, H, CA>( ctx: &TxCtx<'a, DB, H, CA>, used_gas: u64, -) -> Result<(), namada_storage::Error> +) -> Result<(), namada_state::StorageError> where DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>, H: StorageHasher, diff --git a/shared/src/vm/wasm/run.rs b/shared/src/vm/wasm/run.rs index b4360b2905..3c8fe307c0 100644 --- a/shared/src/vm/wasm/run.rs +++ b/shared/src/vm/wasm/run.rs @@ -17,7 +17,7 @@ use wasmer::{BaseTunables, Module, Store}; use super::memory::{Limit, WasmMemory}; use super::TxCache; use crate::ledger::gas::VpGasMeter; -use crate::ledger::storage::write_log::WriteLog; +use crate::state::write_log::WriteLog; use crate::types::address::Address; use crate::types::hash::{Error as TxHashError, Hash}; use crate::types::internal::HostEnvResult; @@ -633,15 +633,15 @@ mod tests { use borsh_ext::BorshSerializeExt; use itertools::Either; use namada_test_utils::TestWasms; + use namada_tx::data::TxType; + use namada_tx::{Code, Data}; use test_log::test; use wasmer_vm::TrapCode; use super::*; - use crate::ledger::storage::testing::TestStorage; - use crate::proto::{Code, Data}; + use crate::state::testing::TestStorage; + use crate::tx::data::eval_vp::EvalVp; use crate::types::hash::Hash; - use crate::types::transaction::TxType; - use crate::types::validity_predicate::EvalVp; use crate::vm::wasm; const TX_GAS_LIMIT: u64 = 10_000_000_000; diff --git a/state/src/lib.rs b/state/src/lib.rs index 22ea0a10e5..2281023230 100644 --- a/state/src/lib.rs +++ b/state/src/lib.rs @@ -34,7 +34,7 @@ pub use namada_merkle_tree::{ }; use namada_merkle_tree::{Error as MerkleTreeError, MerkleRoot}; use namada_parameters::{self, EpochDuration, Parameters}; -pub use namada_storage::*; +pub use namada_storage::{Error as StorageError, Result as StorageResult, *}; use thiserror::Error; use tx_queue::{ExpiredTxsQueue, TxQueue}; pub use wl_storage::{ From 41c176dc12706fbfc18e9f580ab95e898b04a80f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Mon, 8 Jan 2024 13:07:34 +0000 Subject: [PATCH 098/118] tidy up feature flags to avoid wasm-bindgen in wasm crates --- Cargo.lock | 1 - account/Cargo.toml | 2 +- apps/Cargo.toml | 13 ++++++------- core/Cargo.toml | 2 +- gas/Cargo.toml | 2 +- governance/Cargo.toml | 2 +- ibc/Cargo.toml | 2 +- merkle_tree/Cargo.toml | 2 +- parameters/Cargo.toml | 2 +- proof_of_stake/Cargo.toml | 2 +- sdk/Cargo.toml | 12 ++++++++---- shared/Cargo.toml | 5 +++-- shared/src/ledger/mod.rs | 2 +- shielded_token/Cargo.toml | 2 +- state/Cargo.toml | 2 +- storage/Cargo.toml | 2 +- test_utils/Cargo.toml | 2 +- token/Cargo.toml | 2 +- trans_token/Cargo.toml | 2 +- tx/Cargo.toml | 2 +- tx_env/Cargo.toml | 2 +- tx_prelude/Cargo.toml | 7 +++---- tx_prelude/src/lib.rs | 6 ++---- vm_env/Cargo.toml | 2 +- vote_ext/Cargo.toml | 2 +- vp_env/Cargo.toml | 2 +- vp_prelude/Cargo.toml | 6 +++--- wasm/Cargo.lock | 28 ++++++++++++--------------- wasm/wasm_source/Cargo.toml | 4 ---- wasm_for_tests/wasm_source/Cargo.lock | 26 ++++++++++++------------- wasm_for_tests/wasm_source/Cargo.toml | 1 - wasm_for_tests/wasm_source/src/lib.rs | 5 ++--- 32 files changed, 71 insertions(+), 83 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6b9e7c6549..1170ee3bcb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4843,7 +4843,6 @@ dependencies = [ "masp_primitives", "namada_account", "namada_core", - "namada_ethereum_bridge", "namada_governance", "namada_ibc", "namada_macros", diff --git a/account/Cargo.toml b/account/Cargo.toml index 201c828861..43b2693622 100644 --- a/account/Cargo.toml +++ b/account/Cargo.toml @@ -17,7 +17,7 @@ default = [] testing = ["namada_core/testing", "proptest"] [dependencies] -namada_core = { path = "../core", default-features = false } +namada_core = { path = "../core" } namada_macros = { path = "../macros" } namada_storage = { path = "../storage" } diff --git a/apps/Cargo.toml b/apps/Cargo.toml index cfc173c25d..83605c5b3d 100644 --- a/apps/Cargo.toml +++ b/apps/Cargo.toml @@ -50,11 +50,10 @@ name = "namadar" path = "src/bin/namada-relayer/main.rs" [features] -default = ["std"] +default = [] mainnet = [ "namada/mainnet", ] -std = ["ed25519-consensus/std", "rand/std", "rand_core/std", "namada/std", "namada_sdk/std"] # for integration tests and test utilities testing = ["namada_test_utils"] benches = ["testing", "namada_test_utils"] @@ -62,8 +61,8 @@ integration = [] [dependencies] -namada = {path = "../shared", features = ["multicore", "http-client", "tendermint-rpc"]} -namada_sdk = {path = "../sdk", default-features = false, features = ["wasm-runtime", "download-params"]} +namada = {path = "../shared", features = ["multicore", "http-client", "tendermint-rpc", "std"]} +namada_sdk = {path = "../sdk", default-features = false, features = ["wasm-runtime", "download-params", "std"]} namada_test_utils = {path = "../test_utils", optional = true} ark-serialize.workspace = true @@ -85,7 +84,7 @@ config.workspace = true data-encoding.workspace = true derivative.workspace = true directories.workspace = true -ed25519-consensus.workspace = true +ed25519-consensus = { workspace = true, features = ["std"] } ethabi.workspace = true ethbridge-bridge-events.workspace = true ethbridge-events.workspace = true @@ -110,8 +109,8 @@ orion.workspace = true pretty_assertions.workspace = true prost-types.workspace = true prost.workspace = true -rand_core.workspace = true -rand.workspace = true +rand_core = { workspace = true, features = ["std"] } +rand = { workspace = true, features = ["std"] } rayon.workspace = true regex.workspace = true reqwest.workspace = true diff --git a/core/Cargo.toml b/core/Cargo.toml index 799b5dfee1..67271d6728 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -13,7 +13,7 @@ repository.workspace = true version.workspace = true [features] -default = ["rand"] +default = [] mainnet = [] wasm-runtime = ["rayon"] rand = ["dep:rand", "rand_core"] diff --git a/gas/Cargo.toml b/gas/Cargo.toml index 381fa922b0..db53d236b2 100644 --- a/gas/Cargo.toml +++ b/gas/Cargo.toml @@ -13,7 +13,7 @@ repository.workspace = true version.workspace = true [dependencies] -namada_core = { path = "../core", default-features = false } +namada_core = { path = "../core" } borsh.workspace = true serde.workspace = true diff --git a/governance/Cargo.toml b/governance/Cargo.toml index b89c305e5c..de47365d44 100644 --- a/governance/Cargo.toml +++ b/governance/Cargo.toml @@ -16,7 +16,7 @@ version.workspace = true testing = ["proptest"] [dependencies] -namada_core = {path = "../core", default-features = false} +namada_core = { path = "../core" } namada_macros = {path = "../macros"} namada_parameters = {path = "../parameters"} namada_storage = {path = "../storage"} diff --git a/ibc/Cargo.toml b/ibc/Cargo.toml index f131dd0754..1246ff11cb 100644 --- a/ibc/Cargo.toml +++ b/ibc/Cargo.toml @@ -17,7 +17,7 @@ default = [] testing = ["namada_core/testing", "ibc-testkit", "proptest"] [dependencies] -namada_core = { path = "../core", default-features = false } +namada_core = { path = "../core" } namada_parameters = { path = "../parameters" } namada_storage = { path = "../storage" } namada_trans_token = { path = "../trans_token" } diff --git a/merkle_tree/Cargo.toml b/merkle_tree/Cargo.toml index 79587aef18..8546434ad2 100644 --- a/merkle_tree/Cargo.toml +++ b/merkle_tree/Cargo.toml @@ -13,7 +13,7 @@ repository.workspace = true version.workspace = true [dependencies] -namada_core = { path = "../core", default-features = false } +namada_core = { path = "../core" } arse-merkle-tree.workspace = true borsh.workspace = true diff --git a/parameters/Cargo.toml b/parameters/Cargo.toml index 1880fddcc7..fc212110af 100644 --- a/parameters/Cargo.toml +++ b/parameters/Cargo.toml @@ -13,7 +13,7 @@ repository.workspace = true version.workspace = true [dependencies] -namada_core = { path = "../core", default-features = false } +namada_core = { path = "../core" } namada_macros = { path = "../macros" } namada_storage = { path = "../storage" } diff --git a/proof_of_stake/Cargo.toml b/proof_of_stake/Cargo.toml index 9b03792421..4b4d75ad2f 100644 --- a/proof_of_stake/Cargo.toml +++ b/proof_of_stake/Cargo.toml @@ -19,7 +19,7 @@ testing = ["proptest"] [dependencies] namada_account = { path = "../account" } -namada_core = { path = "../core", default-features = false } +namada_core = { path = "../core" } namada_governance = { path = "../governance" } namada_storage = { path = "../storage" } namada_parameters = { path = "../parameters" } diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index f2ab25d818..a567c4bdd6 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -21,7 +21,7 @@ multicore = ["masp_proofs/multicore"] namada-sdk = ["tendermint-rpc", "masp_primitives/transparent-inputs"] -std = ["fd-lock"] +std = ["fd-lock", "dep:rand", "rand_core", "namada_core/rand"] # tendermint-rpc support tendermint-rpc = ["async-client", "dep:tendermint-rpc"] @@ -35,9 +35,13 @@ async-send = [] # for integration tests and test utilities testing = [ + "namada_account/testing", "namada_core/testing", "namada_ethereum_bridge/testing", + "namada_governance/testing", + "namada_ibc/testing", "namada_proof_of_stake/testing", + "namada_tx/testing", "async-client", "proptest", ] @@ -52,7 +56,7 @@ namada_ethereum_bridge = { path = "../ethereum_bridge", default-features = false namada_governance = { path = "../governance" } namada_ibc = { path = "../ibc" } namada_parameters = { path = "../parameters" } -namada_proof_of_stake = { path = "../proof_of_stake", default-features = false } +namada_proof_of_stake = { path = "../proof_of_stake" } namada_state = { path = "../state" } namada_storage = { path = "../storage" } namada_token = { path = "../token" } @@ -81,8 +85,8 @@ parse_duration = "2.1.1" paste.workspace = true proptest = { workspace = true, optional = true } prost.workspace = true -rand.workspace = true -rand_core.workspace = true +rand = { workspace = true, optional = true } +rand_core = { workspace = true, optional = true } ripemd.workspace = true serde.workspace = true serde_json.workspace = true diff --git a/shared/Cargo.toml b/shared/Cargo.toml index b3560debfb..5cc94ca0e6 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -15,9 +15,9 @@ version.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -default = ["namada-sdk", "wasm-runtime"] +default = ["wasm-runtime"] mainnet = ["namada_core/mainnet"] -std = ["fd-lock"] +std = ["fd-lock", "namada_sdk/std"] wasm-runtime = [ "namada_core/wasm-runtime", "loupe", @@ -159,6 +159,7 @@ namada_ethereum_bridge = { path = "../ethereum_bridge", default-features = false namada_proof_of_stake = { path = "../proof_of_stake", default-features = false, features = [ "testing", ] } +namada_sdk = { path = "../sdk", features = ["std", "testing"] } namada_state = { path = "../state", features = ["testing"] } namada_test_utils = { path = "../test_utils" } diff --git a/shared/src/ledger/mod.rs b/shared/src/ledger/mod.rs index cd716bbeb7..cf63816869 100644 --- a/shared/src/ledger/mod.rs +++ b/shared/src/ledger/mod.rs @@ -140,6 +140,7 @@ mod test { use namada_sdk::queries::{ EncodedResponseQuery, RequestCtx, RequestQuery, Router, RPC, }; + use namada_sdk::tendermint_rpc::{self, Error as RpcError, Response}; use namada_state::testing::TestWlStorage; use namada_state::StorageWrite; use namada_test_utils::TestWasms; @@ -147,7 +148,6 @@ mod test { use namada_tx::data::TxType; use namada_tx::{Code, Data, Tx}; use tempfile::TempDir; - use tendermint_rpc::{Error as RpcError, Response}; use crate::ledger::events::log::EventLog; use crate::ledger::queries::Client; diff --git a/shielded_token/Cargo.toml b/shielded_token/Cargo.toml index 82a27765b7..1fb8816aba 100644 --- a/shielded_token/Cargo.toml +++ b/shielded_token/Cargo.toml @@ -18,7 +18,7 @@ multicore = ["dep:rayon"] testing = ["multicore", "namada_core/testing"] [dependencies] -namada_core = { path = "../core", default-features = false } +namada_core = { path = "../core" } namada_parameters = { path = "../parameters" } namada_state = { path = "../state" } namada_storage = { path = "../storage" } diff --git a/state/Cargo.toml b/state/Cargo.toml index 48460ae64f..740c63083e 100644 --- a/state/Cargo.toml +++ b/state/Cargo.toml @@ -19,7 +19,7 @@ default = [] testing = ["proptest", "namada_core/testing"] [dependencies] -namada_core = { path = "../core", default-features = false } +namada_core = { path = "../core" } namada_gas = { path = "../gas" } namada_merkle_tree = { path = "../merkle_tree" } namada_parameters = { path = "../parameters" } diff --git a/storage/Cargo.toml b/storage/Cargo.toml index a9c542a950..17ee879e6b 100644 --- a/storage/Cargo.toml +++ b/storage/Cargo.toml @@ -19,7 +19,7 @@ testing = [ ] [dependencies] -namada_core = { path = "../core", default-features = false } +namada_core = { path = "../core" } namada_gas = { path = "../gas" } namada_merkle_tree = { path = "../merkle_tree" } namada_tx = { path = "../tx" } diff --git a/test_utils/Cargo.toml b/test_utils/Cargo.toml index 9e2da00a8a..3dc882e09f 100644 --- a/test_utils/Cargo.toml +++ b/test_utils/Cargo.toml @@ -13,6 +13,6 @@ repository.workspace = true version.workspace = true [dependencies] -namada_core = { path = "../core", default-features = false } +namada_core = { path = "../core" } borsh.workspace = true strum = {version = "0.24", features = ["derive"]} diff --git a/token/Cargo.toml b/token/Cargo.toml index 9d11800361..1029be2b38 100644 --- a/token/Cargo.toml +++ b/token/Cargo.toml @@ -17,7 +17,7 @@ default = [] multicore = ["namada_shielded_token/multicore"] [dependencies] -namada_core = { path = "../core", default-features = false } +namada_core = { path = "../core" } namada_shielded_token = { path = "../shielded_token" } namada_storage = { path = "../storage" } namada_trans_token = { path = "../trans_token" } diff --git a/trans_token/Cargo.toml b/trans_token/Cargo.toml index 2f56b73bb4..6a1fa96a9f 100644 --- a/trans_token/Cargo.toml +++ b/trans_token/Cargo.toml @@ -13,5 +13,5 @@ repository.workspace = true version.workspace = true [dependencies] -namada_core = { path = "../core", default-features = false } +namada_core = { path = "../core" } namada_storage = { path = "../storage" } diff --git a/tx/Cargo.toml b/tx/Cargo.toml index 0bca3f1334..c92a26460f 100644 --- a/tx/Cargo.toml +++ b/tx/Cargo.toml @@ -17,7 +17,7 @@ default = [] testing = ["proptest", "namada_core/testing"] [dependencies] -namada_core = { path = "../core", default-features = false } +namada_core = { path = "../core" } namada_gas = { path = "../gas" } ark-bls12-381.workspace = true diff --git a/tx_env/Cargo.toml b/tx_env/Cargo.toml index 5c67646ebc..cc26e87cf1 100644 --- a/tx_env/Cargo.toml +++ b/tx_env/Cargo.toml @@ -13,5 +13,5 @@ repository.workspace = true version.workspace = true [dependencies] -namada_core = { path = "../core", default-features = false } +namada_core = { path = "../core" } namada_storage = { path = "../storage" } diff --git a/tx_prelude/Cargo.toml b/tx_prelude/Cargo.toml index a7dcb6b9de..83bc1eac73 100644 --- a/tx_prelude/Cargo.toml +++ b/tx_prelude/Cargo.toml @@ -17,18 +17,17 @@ default = [] [dependencies] namada_account = { path = "../account" } -namada_core = { path = "../core", default-features = false } -namada_ethereum_bridge = { path = "../ethereum_bridge" } +namada_core = { path = "../core" } namada_governance = { path = "../governance" } namada_ibc = { path = "../ibc" } namada_macros = { path = "../macros" } namada_parameters = { path = "../parameters" } -namada_proof_of_stake = { path = "../proof_of_stake", default-features = false } +namada_proof_of_stake = { path = "../proof_of_stake" } namada_storage = { path = "../storage" } namada_token = { path = "../token" } namada_tx = { path = "../tx" } namada_tx_env = { path = "../tx_env" } -namada_vm_env = { path = "../vm_env", default-features = false } +namada_vm_env = { path = "../vm_env" } borsh.workspace = true masp_primitives.workspace = true diff --git a/tx_prelude/src/lib.rs b/tx_prelude/src/lib.rs index 75d4e8e364..0da9bf3045 100644 --- a/tx_prelude/src/lib.rs +++ b/tx_prelude/src/lib.rs @@ -20,6 +20,7 @@ use masp_primitives::transaction::Transaction; pub use namada_core::borsh::{ BorshDeserialize, BorshSerialize, BorshSerializeExt, }; +pub use namada_core::ledger::eth_bridge; use namada_core::types::account::AccountPublicKeysMap; pub use namada_core::types::address::Address; use namada_core::types::chain::CHAIN_ID_LENGTH; @@ -42,10 +43,7 @@ pub use namada_tx::{data as transaction, Section, Tx}; pub use namada_tx_env::TxEnv; use namada_vm_env::tx::*; use namada_vm_env::{read_from_buffer, read_key_val_bytes_from_buffer}; -pub use { - namada_ethereum_bridge as eth_bridge, namada_governance as governance, - namada_parameters as parameters, -}; +pub use {namada_governance as governance, namada_parameters as parameters}; /// Log a string. The message will be printed at the `tracing::Level::Info`. pub fn log_string>(msg: T) { diff --git a/vm_env/Cargo.toml b/vm_env/Cargo.toml index 332b5007c9..29f5882eb2 100644 --- a/vm_env/Cargo.toml +++ b/vm_env/Cargo.toml @@ -16,6 +16,6 @@ version.workspace = true default = [] [dependencies] -namada_core = {path = "../core", default-features = false} +namada_core = { path = "../core" } borsh.workspace = true masp_primitives.workspace = true diff --git a/vote_ext/Cargo.toml b/vote_ext/Cargo.toml index c89888aa1c..d20c591fc3 100644 --- a/vote_ext/Cargo.toml +++ b/vote_ext/Cargo.toml @@ -13,7 +13,7 @@ repository.workspace = true version.workspace = true [dependencies] -namada_core = { path = "../core", default-features = false } +namada_core = { path = "../core" } namada_tx = { path = "../tx" } borsh.workspace = true diff --git a/vp_env/Cargo.toml b/vp_env/Cargo.toml index ad2a6ba325..c3a39302f8 100644 --- a/vp_env/Cargo.toml +++ b/vp_env/Cargo.toml @@ -13,7 +13,7 @@ repository.workspace = true version.workspace = true [dependencies] -namada_core = { path = "../core", default-features = false } +namada_core = { path = "../core" } namada_storage = { path = "../storage" } namada_tx = { path = "../tx" } diff --git a/vp_prelude/Cargo.toml b/vp_prelude/Cargo.toml index 227197c51a..ae0cbd7d43 100644 --- a/vp_prelude/Cargo.toml +++ b/vp_prelude/Cargo.toml @@ -17,16 +17,16 @@ default = [] [dependencies] namada_account = { path = "../account" } -namada_core = { path = "../core", default-features = false } +namada_core = { path = "../core" } namada_governance = { path = "../governance" } namada_ibc = { path = "../ibc" } namada_macros = { path = "../macros" } namada_parameters = { path = "../parameters" } -namada_proof_of_stake = { path = "../proof_of_stake", default-features = false } +namada_proof_of_stake = { path = "../proof_of_stake" } namada_storage = { path = "../storage" } namada_token = { path = "../token" } namada_tx = { path = "../tx" } -namada_vm_env = { path = "../vm_env", default-features = false } +namada_vm_env = { path = "../vm_env" } namada_vp_env = { path = "../vp_env" } borsh.workspace = true diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index cb73f96200..12b7a875f8 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -3333,7 +3333,6 @@ dependencies = [ "namada_proof_of_stake", "namada_sdk", "namada_state", - "namada_storage", "namada_token", "namada_tx", "namada_tx_env", @@ -3356,7 +3355,6 @@ dependencies = [ "sha2 0.9.9", "slip10_ed25519", "tempfile", - "tendermint-rpc", "thiserror", "tiny-bip39", "tiny-hderive", @@ -3384,6 +3382,7 @@ dependencies = [ "namada_core", "namada_macros", "namada_storage", + "proptest", "serde", ] @@ -3391,25 +3390,19 @@ dependencies = [ name = "namada_core" version = "0.29.0" dependencies = [ - "ark-bls12-381", - "ark-serialize", "bech32 0.8.1", "borsh", "borsh-ext", "chrono", "data-encoding", - "derivative", "ed25519-consensus 1.2.1", "ethabi", "ethbridge-structs", "eyre", "ibc", - "ibc-derive", - "ibc-testkit", "ics23", "impl-num-traits", "index-set", - "itertools 0.10.5", "k256", "masp_primitives", "namada_macros", @@ -3448,6 +3441,7 @@ dependencies = [ "ethers", "eyre", "itertools 0.10.5", + "namada_account", "namada_core", "namada_macros", "namada_parameters", @@ -3473,8 +3467,6 @@ version = "0.29.0" dependencies = [ "borsh", "namada_core", - "namada_parameters", - "namada_storage", "serde", "thiserror", ] @@ -3489,6 +3481,7 @@ dependencies = [ "namada_parameters", "namada_storage", "namada_trans_token", + "proptest", "serde", "serde_json", "thiserror", @@ -3502,12 +3495,14 @@ dependencies = [ "borsh", "ibc", "ibc-derive", + "ibc-testkit", "ics23", "namada_core", "namada_parameters", "namada_storage", "namada_trans_token", "primitive-types", + "proptest", "prost 0.12.3", "sha2 0.9.9", "thiserror", @@ -3604,8 +3599,6 @@ dependencies = [ "paste", "proptest", "prost 0.12.3", - "rand 0.8.5", - "rand_core 0.6.4", "ripemd", "serde", "serde_json", @@ -3649,6 +3642,7 @@ dependencies = [ "namada_storage", "namada_trans_token", "namada_tx", + "proptest", "sha2 0.9.9", "sparse-merkle-tree", "thiserror", @@ -3660,9 +3654,14 @@ dependencies = [ name = "namada_storage" version = "0.29.0" dependencies = [ + "borsh", "itertools 0.10.5", "namada_core", + "namada_gas", + "namada_merkle_tree", + "namada_tx", "thiserror", + "tracing", ] [[package]] @@ -3738,6 +3737,7 @@ dependencies = [ "namada_gas", "num-derive", "num-traits", + "proptest", "prost 0.12.3", "prost-types 0.12.3", "serde", @@ -3763,7 +3763,6 @@ dependencies = [ "masp_primitives", "namada_account", "namada_core", - "namada_ethereum_bridge", "namada_governance", "namada_ibc", "namada_macros", @@ -3834,9 +3833,7 @@ dependencies = [ name = "namada_wasm" version = "0.29.0" dependencies = [ - "borsh", "getrandom 0.2.11", - "masp_primitives", "namada", "namada_test_utils", "namada_tests", @@ -3844,7 +3841,6 @@ dependencies = [ "namada_vp_prelude", "once_cell", "proptest", - "ripemd", "test-log", "tracing", "tracing-subscriber", diff --git a/wasm/wasm_source/Cargo.toml b/wasm/wasm_source/Cargo.toml index f894cf6160..abff08cff0 100644 --- a/wasm/wasm_source/Cargo.toml +++ b/wasm/wasm_source/Cargo.toml @@ -41,13 +41,9 @@ vp_user = ["namada_vp_prelude", "once_cell"] [dependencies] namada_tx_prelude = {path = "../../tx_prelude", optional = true} namada_vp_prelude = {path = "../../vp_prelude", optional = true} -borsh = "1.2.0" once_cell = {version = "1.8.0", optional = true} wee_alloc = "0.4.5" getrandom = { version = "0.2", features = ["custom"] } -# branch = "murisi/namada-integration" -masp_primitives = { git = "https://github.com/anoma/masp", tag = "v1.0.0", optional = true } -ripemd = "0.1" [dev-dependencies] namada = {path = "../../shared"} diff --git a/wasm_for_tests/wasm_source/Cargo.lock b/wasm_for_tests/wasm_source/Cargo.lock index b5ac0953fd..2702a6d803 100644 --- a/wasm_for_tests/wasm_source/Cargo.lock +++ b/wasm_for_tests/wasm_source/Cargo.lock @@ -3333,7 +3333,6 @@ dependencies = [ "namada_proof_of_stake", "namada_sdk", "namada_state", - "namada_storage", "namada_token", "namada_tx", "namada_tx_env", @@ -3356,7 +3355,6 @@ dependencies = [ "sha2 0.9.9", "slip10_ed25519", "tempfile", - "tendermint-rpc", "thiserror", "tiny-bip39", "tiny-hderive", @@ -3384,6 +3382,7 @@ dependencies = [ "namada_core", "namada_macros", "namada_storage", + "proptest", "serde", ] @@ -3391,25 +3390,19 @@ dependencies = [ name = "namada_core" version = "0.29.0" dependencies = [ - "ark-bls12-381", - "ark-serialize", "bech32 0.8.1", "borsh", "borsh-ext", "chrono", "data-encoding", - "derivative", "ed25519-consensus 1.2.1", "ethabi", "ethbridge-structs", "eyre", "ibc", - "ibc-derive", - "ibc-testkit", "ics23", "impl-num-traits", "index-set", - "itertools 0.10.5", "k256", "masp_primitives", "namada_macros", @@ -3448,6 +3441,7 @@ dependencies = [ "ethers", "eyre", "itertools 0.10.5", + "namada_account", "namada_core", "namada_macros", "namada_parameters", @@ -3473,8 +3467,6 @@ version = "0.29.0" dependencies = [ "borsh", "namada_core", - "namada_parameters", - "namada_storage", "serde", "thiserror", ] @@ -3489,6 +3481,7 @@ dependencies = [ "namada_parameters", "namada_storage", "namada_trans_token", + "proptest", "serde", "serde_json", "thiserror", @@ -3502,12 +3495,14 @@ dependencies = [ "borsh", "ibc", "ibc-derive", + "ibc-testkit", "ics23", "namada_core", "namada_parameters", "namada_storage", "namada_trans_token", "primitive-types", + "proptest", "prost 0.12.3", "sha2 0.9.9", "thiserror", @@ -3604,8 +3599,6 @@ dependencies = [ "paste", "proptest", "prost 0.12.3", - "rand 0.8.5", - "rand_core 0.6.4", "ripemd", "serde", "serde_json", @@ -3649,6 +3642,7 @@ dependencies = [ "namada_storage", "namada_trans_token", "namada_tx", + "proptest", "sha2 0.9.9", "sparse-merkle-tree", "thiserror", @@ -3660,9 +3654,14 @@ dependencies = [ name = "namada_storage" version = "0.29.0" dependencies = [ + "borsh", "itertools 0.10.5", "namada_core", + "namada_gas", + "namada_merkle_tree", + "namada_tx", "thiserror", + "tracing", ] [[package]] @@ -3738,6 +3737,7 @@ dependencies = [ "namada_gas", "num-derive", "num-traits", + "proptest", "prost 0.12.3", "prost-types 0.12.3", "serde", @@ -3763,7 +3763,6 @@ dependencies = [ "masp_primitives", "namada_account", "namada_core", - "namada_ethereum_bridge", "namada_governance", "namada_ibc", "namada_macros", @@ -3834,7 +3833,6 @@ dependencies = [ name = "namada_wasm_for_tests" version = "0.29.0" dependencies = [ - "borsh", "getrandom 0.2.11", "namada_test_utils", "namada_tests", diff --git a/wasm_for_tests/wasm_source/Cargo.toml b/wasm_for_tests/wasm_source/Cargo.toml index da411e40b0..158c24734c 100644 --- a/wasm_for_tests/wasm_source/Cargo.toml +++ b/wasm_for_tests/wasm_source/Cargo.toml @@ -28,7 +28,6 @@ tx_proposal_code = [] namada_test_utils = {path = "../../test_utils"} namada_tx_prelude = {path = "../../tx_prelude"} namada_vp_prelude = {path = "../../vp_prelude"} -borsh = "1.0.0-alpha.4" wee_alloc = "0.4.5" getrandom = { version = "0.2", features = ["custom"] } diff --git a/wasm_for_tests/wasm_source/src/lib.rs b/wasm_for_tests/wasm_source/src/lib.rs index aa352a6bc6..3cb6b6ec3c 100644 --- a/wasm_for_tests/wasm_source/src/lib.rs +++ b/wasm_for_tests/wasm_source/src/lib.rs @@ -75,11 +75,10 @@ pub mod main { /// A tx that attempts to write arbitrary data to the given key #[cfg(feature = "tx_write")] pub mod main { - use borsh::BorshDeserialize; use namada_test_utils::tx_data::TxWriteData; use namada_tx_prelude::{ - log_string, transaction, Ctx, StorageRead, StorageWrite, Tx, TxEnv, - TxResult, + log_string, transaction, BorshDeserialize, Ctx, StorageRead, + StorageWrite, Tx, TxEnv, TxResult, }; const TX_NAME: &str = "tx_write"; From 9539587fc1da656b5c6bd197b93136d4921ca8f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Mon, 8 Jan 2024 13:42:22 +0000 Subject: [PATCH 099/118] rm ethereum_bridge dep from tx_prelude --- core/src/types/eth_bridge_pool.rs | 18 +++++++++++++++- ethereum_bridge/src/storage/bridge_pool.rs | 24 ++++------------------ ethereum_bridge/src/storage/mod.rs | 6 +----- parameters/src/lib.rs | 6 ++++++ wasm/wasm_source/src/tx_bridge_pool.rs | 11 ++++++---- 5 files changed, 35 insertions(+), 30 deletions(-) diff --git a/core/src/types/eth_bridge_pool.rs b/core/src/types/eth_bridge_pool.rs index d3fa9c563b..83bcef6c6a 100644 --- a/core/src/types/eth_bridge_pool.rs +++ b/core/src/types/eth_bridge_pool.rs @@ -10,7 +10,8 @@ use namada_macros::StorageKeys; use serde::{Deserialize, Serialize}; use super::address::InternalAddress; -use super::storage; +use super::keccak::KeccakHash; +use super::storage::{self, KeySeg}; use crate as namada_core; // This is needed for `StorageKeys` macro use crate::types::address::Address; use crate::types::eth_abi::Encode; @@ -47,6 +48,21 @@ pub fn is_pending_transfer_key(key: &storage::Key) -> bool { !Segments::ALL.iter().any(|s| s == &segment) } +/// Get the storage key for the transfers in the pool +pub fn get_pending_key(transfer: &PendingTransfer) -> Key { + get_key_from_hash(&transfer.keccak256()) +} + +/// Get the storage key for the transfers using the hash +pub fn get_key_from_hash(hash: &KeccakHash) -> Key { + Key { + segments: vec![ + DbKeySeg::AddressSeg(BRIDGE_POOL_ADDRESS), + hash.to_db_key(), + ], + } +} + /// A version used in our Ethereuem smart contracts const VERSION: u8 = 1; diff --git a/ethereum_bridge/src/storage/bridge_pool.rs b/ethereum_bridge/src/storage/bridge_pool.rs index b43a6025ac..9fe34e7611 100644 --- a/ethereum_bridge/src/storage/bridge_pool.rs +++ b/ethereum_bridge/src/storage/bridge_pool.rs @@ -1,30 +1,14 @@ //! Tools for accessing the storage subspaces of the Ethereum //! bridge pool -use namada_core::types::eth_abi::Encode; +use namada_core::types::eth_bridge_pool::Segments; pub use namada_core::types::eth_bridge_pool::{ - is_pending_transfer_key, BRIDGE_POOL_ADDRESS, + get_key_from_hash, get_pending_key, is_pending_transfer_key, + BRIDGE_POOL_ADDRESS, }; -use namada_core::types::eth_bridge_pool::{PendingTransfer, Segments}; -use namada_core::types::keccak::KeccakHash; -use namada_core::types::storage::{DbKeySeg, Key, KeySeg}; +use namada_core::types::storage::{DbKeySeg, Key}; pub use namada_state::merkle_tree::eth_bridge_pool::BridgePoolTree; -/// Get the storage key for the transfers in the pool -pub fn get_pending_key(transfer: &PendingTransfer) -> Key { - get_key_from_hash(&transfer.keccak256()) -} - -/// Get the storage key for the transfers using the hash -pub fn get_key_from_hash(hash: &KeccakHash) -> Key { - Key { - segments: vec![ - DbKeySeg::AddressSeg(BRIDGE_POOL_ADDRESS), - hash.to_db_key(), - ], - } -} - /// Get the storage key for the root of the Merkle tree /// containing the transfers in the pool pub fn get_signed_root_key() -> Key { diff --git a/ethereum_bridge/src/storage/mod.rs b/ethereum_bridge/src/storage/mod.rs index bd4b24e374..96c2b8dd84 100644 --- a/ethereum_bridge/src/storage/mod.rs +++ b/ethereum_bridge/src/storage/mod.rs @@ -12,6 +12,7 @@ pub mod wrapped_erc20s; use namada_core::ledger::eth_bridge::ADDRESS; use namada_core::types::address::Address; use namada_core::types::storage::{DbKeySeg, Key, KeySeg}; +pub use namada_parameters::native_erc20_key; use namada_parameters::storage::*; use namada_parameters::ADDRESS as PARAM_ADDRESS; use namada_trans_token::storage_key::balance_key; @@ -59,11 +60,6 @@ pub fn min_confirmations_key() -> Key { get_min_confirmations_key_at_addr(PARAM_ADDRESS) } -/// Storage key for the Ethereum address of wNam. -pub fn native_erc20_key() -> Key { - get_native_erc20_key_at_addr(PARAM_ADDRESS) -} - /// Storage key for the Ethereum address of the bridge contract. pub fn bridge_contract_key() -> Key { get_bridge_contract_address_key_at_addr(PARAM_ADDRESS) diff --git a/parameters/src/lib.rs b/parameters/src/lib.rs index ab17af45a4..9caaec48a6 100644 --- a/parameters/src/lib.rs +++ b/parameters/src/lib.rs @@ -8,6 +8,7 @@ use namada_core::types::chain::ProposalBytes; use namada_core::types::dec::Dec; use namada_core::types::hash::Hash; pub use namada_core::types::parameters::*; +use namada_core::types::storage::Key; use namada_core::types::time::DurationSecs; use namada_core::types::token; use namada_storage::{self, ResultExt, StorageRead, StorageWrite}; @@ -469,3 +470,8 @@ where .expect("The max tx bytes param should be present in storage"); Ok(tx_size <= max_tx_bytes as usize) } + +/// Storage key for the Ethereum address of wNam. +pub fn native_erc20_key() -> Key { + storage::get_native_erc20_key_at_addr(ADDRESS) +} diff --git a/wasm/wasm_source/src/tx_bridge_pool.rs b/wasm/wasm_source/src/tx_bridge_pool.rs index 6b93bf672e..f4826b3d41 100644 --- a/wasm/wasm_source/src/tx_bridge_pool.rs +++ b/wasm/wasm_source/src/tx_bridge_pool.rs @@ -1,7 +1,10 @@ //! A tx for adding a transfer request across the Ethereum bridge //! into the bridge pool. -use eth_bridge::storage::{bridge_pool, native_erc20_key}; use eth_bridge_pool::{GasFee, PendingTransfer, TransferToEthereum}; +use namada_tx_prelude::eth_bridge_pool::{ + get_pending_key, BRIDGE_POOL_ADDRESS, +}; +use namada_tx_prelude::parameters::native_erc20_key; use namada_tx_prelude::*; #[transaction(gas = 1038546)] @@ -22,7 +25,7 @@ fn apply_tx(ctx: &mut Ctx, signed: Tx) -> TxResult { token::undenominated_transfer( ctx, payer, - &bridge_pool::BRIDGE_POOL_ADDRESS, + &BRIDGE_POOL_ADDRESS, fee_token_addr, amount, )?; @@ -49,14 +52,14 @@ fn apply_tx(ctx: &mut Ctx, signed: Tx) -> TxResult { token::undenominated_transfer( ctx, sender, - &bridge_pool::BRIDGE_POOL_ADDRESS, + &BRIDGE_POOL_ADDRESS, &token, amount, )?; } log_string("Escrow succeeded"); // add transfer into the pool - let pending_key = bridge_pool::get_pending_key(&transfer); + let pending_key = get_pending_key(&transfer); ctx.write_bytes(&pending_key, transfer.serialize_to_vec()) .wrap_err("Could not write transfer to bridge pool")?; Ok(()) From 653fd18dcb1a69e918b2f667a0725a7e97df1155 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Mon, 8 Jan 2024 13:44:37 +0000 Subject: [PATCH 100/118] make -C wasm_for_tests --- wasm_for_tests/tx_fail.wasm | Bin 388778 -> 373867 bytes wasm_for_tests/tx_memory_limit.wasm | Bin 440700 -> 426629 bytes wasm_for_tests/tx_mint_tokens.wasm | Bin 594890 -> 594890 bytes wasm_for_tests/tx_no_op.wasm | Bin 388757 -> 373657 bytes wasm_for_tests/tx_proposal_code.wasm | Bin 440631 -> 425314 bytes wasm_for_tests/tx_read_storage_key.wasm | Bin 447332 -> 435118 bytes wasm_for_tests/tx_write.wasm | Bin 450471 -> 437266 bytes wasm_for_tests/tx_write_storage_key.wasm | Bin 465107 -> 465107 bytes wasm_for_tests/vp_always_false.wasm | Bin 414509 -> 396467 bytes wasm_for_tests/vp_always_true.wasm | Bin 414485 -> 396341 bytes wasm_for_tests/vp_eval.wasm | Bin 488116 -> 472965 bytes wasm_for_tests/vp_memory_limit.wasm | Bin 464547 -> 447674 bytes wasm_for_tests/vp_read_storage_key.wasm | Bin 472315 -> 457411 bytes 13 files changed, 0 insertions(+), 0 deletions(-) diff --git a/wasm_for_tests/tx_fail.wasm b/wasm_for_tests/tx_fail.wasm index 0748dc04de6610dfb3268f83e1a08738d797ebc6..3b253779dfa2c9d3b62ee20481903fedf3325aa1 100755 GIT binary patch literal 373867 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

O#&u868~Pe6?r&yPo+j*W$yv;QeY}Iy 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

ntl`xs60S%#w} zB6A&ZsL82Y-1r9Ho8#1I%i9hXWI1&`LU*+)GqW3-^>}M-%>$9`c2keFVHuiOB-3jE zN3qgK5URFD#G$?B+@7MOw!q0+wFjupO-?#^hv8ox^To&Y^%2;O@?wC|VBuL6<^-

TX}vpj?R9Af-StmuVE&jv}0 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%0AFE4#yb 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 CD5($t diff --git a/wasm_for_tests/tx_memory_limit.wasm b/wasm_for_tests/tx_memory_limit.wasm index 87667b88cf7d7b0cf11d8dbc0e9ddc7e04929f9a..34fdb10a0bdc00f6e5f5a82bd4ab421ae6e39951 100755 GIT binary patch literal 426629 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

pc^;$Xuy7G{DdD4}(jCtEvVp%^L$ z%yS5C)th)t24M{z*@B}e`&#&;O3YYQBhG?1J>g;6%8t+zKKdi{RB5xILnWKXQQ$hk zBGuaF4Xjp{WUK4xW}zNsSdvY2YLixR*Xaw^+2>dyFq2Z%&1!-oH!JwI1)^C2*=C;x zOouNa$S0fAF{`93HQ2ecK0NxCMa_Lo%^mD+uDmGn=Gv|kjO(T5h91b1;yMqPi<_%u zj?mm0-&{K`&ceRWsJYhg89pvG_fT)%+yy=l#ymtgx2pZIFqr8e$AJe`=u)4jFoG?3 zu?`drf}b%TnYWNQ&x1Q1Jc~O$40j6rse}Mp2&)u2PHigs9={l9bmA>>PEP`dy1SGB zZLVeCh&nl-IJN9+r3A*KmVKd=VC3Vor35CumOW7R!9x#)~Ys%AVTAzYbDkygBm<77}R8%5`QZh z)B>Lq>DIcNkzj{!AKG-_9-`^+*z5AS6~{6iw0O_ti#r$3%)P0(p8H6C=egiUw&_)h zc#)rp7pD%-Wj;NG*pUncC5}n#LC@)QN4B``?r2!d2q#+{WI`Pl(GbnY8p7CI`hZri zJpJN?Xol8fb3<3zvsHvQNIbU-Q@03HIc8A&o~$TzSD*9iZCzid`g+>~$z{dYH=pb4 zbn<~@Jz3YPeP$IILEk4pR5D5)daL?Td$A0IUrz>IOci7 zfP^>+ry~4FZgx6ndhO0gRt;#bIqHpYW-U}Sl4M6v28)_IAo)|RNu6)K&Bt~2Va*;+y|=q%}4g3ITnB2xmNcmy+U zy5ADEs*uEgwG(3OyCwn)gc770N#TVaQa0CA7m7v32nI~u==q=)TeOe@&! zdAwhb@LnlXC%L!C5E$U5Zc1+8%2u!BR<6gnuF!KE^Pk@H zyou{+T(aA`l7C3=an-(&>v3Xe4C{)Qc7rSOMsgE+$V#{S`K;;eeEwcw_9nMe@B&QV zBs#ydL+5W2>h6?!lD`LccXsIfP13=3@>9Hr&hPBd`I`W8o(nopxPCnsbe;y|t~cOz zGY95A*MKM?e#n!KcQ}9bxLcUCkN~;DzMGw_+4BG>7;fnx&XYX)oYUf%NKz_xCfV(7 zbuWP4h-lm@M)U$TFn^|jFHi&XXBvnLO8%TTVmNQ$4H}^vHA4P_8#F>UYJ~DO1lRI|A{gDF4EL|$$T*8~#;$K7cIKa<|#*m98 zA+eN*jU*77)@@{nEMba-F%qUsnrOMqU82q-)MskcCHycyz`Nja7>116j~CbdKmTZy zZOM)k^U;oes0c$umPD_N99`|UX*F8) z=4pkU%~H!g_2ExdL@O}qR0~>B%l?vNqZODE%d~<=Xa#m*)J}6V7<3z-Plm`2;tiCF zVZ=djDm%*5jd8_A3DPi?Dcnv|SDnqGPaNUP4doMs;TvM+0%G7O zv4&4#9~YrERI@c*5Nb@uZ$qRp!JkQV`7`im5?%hxRGUQSpH-CVYUY)%p!5LCT8EgE z`z*imy;EQ*nyq%uK+)AaMWi0|aOe#!(q=isoxT0MHL$Z?Xtp#HbIu^?0UTea=wwaz zI)}q~jf+HQ4V!H9U66NjdC0rjyNs#G9P(}skjKUy}`91ydMaV z$MZpg3nT+T+(I&&;-tD1#CdpQ`AYD%WtI|OExAm5%Q)S5nt6C5zP+vNOnZ2(X9g;;Ug!S9NHz`q=(-o+2D<$%J9Y`@FtgD<4xY@V+?jQ;H4ss(@ow5@Ja-#CA(!JQYq6_ z0Odqfsm4$KEQP{=e5!DDoKl{Rojg-2V`Ha`Db=x&pYSE&2D8t5HbrDY6j4JX1*^CA ztF_YXJfN%pGkD;s*E-vM!~^ zBe(c@Zc|2TZAoe^=)L)L>4-*?7G?Y~H&MV^Djf>Vsa~LjD`z~bN%G2kQTg*1*(dV0 z6vVlomVk3e09AuVCcFa=d3IJd% zECnIF=-m-ZK~67H5WOf3QNBpwEN@ayc2YFPI$Lp}sU;=)wNQ4NS}$Ai&5)|~`8-~x zl&eX(EKlK-7&Kf`*x_fMtvJ8PbQ%L+M&Gn-;(fIm=3k}V5wDdQ)C|mz#81WS+mE0F_R(Jotmg44Q)BP69r02)$d@YMhUB?U8le!{p=u}y$R~&K^ zC1dPp&c)jP zVw4fnPp_UOhqF!;&v>1&#$wfs4?;{-7`%^JE|~t6`H#;1;?I5YkFUlJTF)Q-^S^(` zKY#rG|M4$mII4tSJMoPtem`vJI--P6?*FB)e)G@X^|PNO2WP|7roZ;(C(nE!?C?6I zgpZv5izk2ME06s4jo^-+fA>#6{J|4p$Jaq69D3*fbKkc=@r`@_fg(8YqNYZDPS=IX z4bf+HgQx8&Y@>30heh^gwf0-%T$+2jtBhp_8s4L@=6#V%rG-@whRq7iGs`Wb=3R5o zC$j=n02oCZmn{`rmk9cD8R61htwo?!uaTH!vHrBcnh}xT6uIs_x ziN`Q-%T1L$xH##Ac8mqKs z(qdgxyKp>?AV!sOMD)~S2#XMYOqalpdq4bJC*QRgJKpp8fBGt)Jd4-?e3)rkVssCz zB^jv#M}{m0hy&UB85X|Y%J6$sxOsp{HB%i3$GF=zg8;LjSk?@APveNoS;gIDG0h`g zW%VKYpkqpd--S-EJw668Hr9gRXA^cu0@PxUhn_sUjS)v?X#znLZ~Gh&8Ql_Q2aGk$>yG}L&P-5lsmHY)+#xKdYe;}yDs8$Y2dxbaiEf*Y@9sc=M$*`q=~lT~68 zY#Yv@zj_Ynf6Ji{oy{Ab_`pAZ@!5y|I)t+W{o)9Q0gxmcZUD4|0jHW!Q-$6sa9}BN zoa4ZOhUEsepn8+hyBgOLJ+2_o{^kC4|AVstV9D-2O|I^|rP9iNAU z%dOA)`K4y3&tKra(`U;7q*a$;1!v#~ffYQ<{S*4bjWp5*6&;*g3gaM;hH*cwE4=76 zx`J1kKd3AC_Cr~WY^bxz;Ym@7A6lQ)4=9z`&OxN&g6|-X zRSU`NRJAOb5stn#ecypc7vAl85BtP|WcI}Sz9!ytObKPl?5GmTlGza@lqIvnN+?NY zhxA;Q%odbTmdp;4Aj#~Dx=J$p0!IXAEGkW4U^2fVBW`R>B?O5KLPl`dEKG>^W;(pr zj)?YtNbAaQzH8R4D}(uU>}4-Q`F1@QCRg(}TG!FdYrNupq5sP?z;g-0*BPv{qZxY} zk!BTe-XvwHaMx^@Y?;$Rs1mV zAhzmUBROnwu}qFV!p5}DNMnNYBYmHTd#t|QBaP*ud!z};&^^-gN+^%?oDxbSeFpy9 zHPS@J=pN})l7DE_@A9RM^v89>#>_JKXqmO>(79DCK<8BGP)EheK?k1H4LVBb0v#ok zJM^!kVqvqbzY2k^E-Hj{04O0;_HaE#NFms(=c%w+Fc@R9r=qwg%oc*>F?Pyf>*AG- z4ZQATJln=bz!wB)F!GB^5=6&ixTPvaZYTGynf7;`E3e2-C^?jB?SQD4O0_m1+S(e3 zB+@w!&l^E&(H1sSTaMLdv-Y zr6;g=Iy7gI(E{t#rP~X2+m*NkrPW z-6atnw1g7<(su4HiD=#uO7x3`6mpd4m-bsPArYMh+_ptW`b_O5R6W2avL(y_Vl-}| zPT^E2W&NE#ugmKG<9tT^Xwb(p5)fl9+%IsC<;>C#a(_D9AK?B}xS!|#WVpwjuzDig zpFzqx9_}^F$LhfOqtS~^w(uw>9_A5rpp0i?2RTqL3H!%7qt1#G#JR*Zbi{}p#Bu_! zWoZ&hqizS3l(~lDrOJ7^_~e8olt%ryC6u^^4;+7tVJeIbe$;~r!TPtX(5A0Gi;3bn z5eI5pSLd)!918bmz~zN-f0}!?@>`x$+#d+{99E;P{g!@$`?+v`oclAtLTC6&gDxw3 zu4fA>@UohA}w6?R7qNp4;vIRXmu!IuQvY39>{s{L6!u?_H=fnLW?&re&0{3S? zR3YL)?oWq%&us%Du@9gNk&LZ%8T6`BlE%$9Q&y_QijwB0B<&ul7m5$=1K1gtDS^Pzhye_ka@0((b$xO5*#Rp392T89LvkD4ixjjru>y zY2y2%x=~!9aWy)pr>BaEY7KvzU{I7pUutF%zW2aA5}tmtiMxAX_t;ISSxks z+?wCpP88i)K-TK&&aDXSndJEg!6at1EB>L;;PD z9X+P;$xbF?!jR`P{aZ`6mz0Nz zr`=T?l4v=@0&T+aqg-JF^%98)FUC5?SmTlX7 zSGHx_cINz)TAnvQMl3khMDW6*S(gfEoH4Ffl( zEPiWXiNpZKUS8s6$syM-rRwDRrBt0z7h>a{W@OPOe`{)yefssXDoS=>Y)J zCjC;#=Ml+^WndXTW*!v`o1RwHIZ@1@jrxkkqrPJCsIOQ&>MP33R->*xkzM+wHcb`@ z8g=ce%o9S0NMqUAN zmyuV)S&Co;)LFu!;atMV`^?q#?*I&UY$S_E^8(fN0*1FmN9%>U{wxZPLEA8OU6@%? z*Cl&rNajIZ|9ynYTwQ;({_LvjZ;%ktQP&lmW2=^?Q#Pj9LM#j!7Z+3VLC(T0rw!67 zEEpj*P){~-jDR^&uL%3A<&!qahxnOqLIdInBZ8hlZmO8HDoE9qcrL^vZQ-WK-ZaIP zvLxp(ZBt#L3X;b;`iqrb)~{?^9e0=(cp;+U+o&_y3!^qG%e3)k2&RrcOd<*_OpB(A z*}@24no?NmV=j8I*sM;P3&l08FT2{6(YqVd+u!VJS4a0Xh`oN#J+{t#O~iimE2CB6 z)u?~Nj(e!aIZV1I3hsAJ^ac3KmC;w5p@=Uxt&EnndgLLtz;hl1=9jZs=&_;@!dh1i z5wv;h*;v#I7=UxVpABJvzz0uFF)h|I0s{O)HTN|7g0+~_UGUgb#$Cw|>vCQ4a$UA3 z&(md+xOQ9|!9BT*Cs*mxPNFPU2 zALjOX{`Ns`SNq%hx!vk--^=Y){`OsXUG=aVPi1H;-lyohPSlo5iJ}l|EZk4;jDJqo z*X)crT&?Ez?2P{pE-&60@8$B6ofKDdFWVXa7?&U18UGNMAKDpzFPE|BMBl^Z7btl* z*LQKvxZ>w|J(nwYMmVUrSL}?YxctPDs;UbQS0c^5wnctpb(Gg(hUrjF}^j?s*5Pz$k4<6AmE!90qW_9J^b|A~Y5KXLNOcYo#W zzsac&$AG!%k3RCIM~@u&+_(Npwsj>u3%07xTEcvTbw7VVM2-%gWlJ~9#YS(rjsxO! z+$cj%l%z6fv)GN`Bca7(*jWTEHK+b_J4FQRsX6_8$4Hl)Ud$Dvo~lkCR$2+k=&OjKpe} zQJB;&oA6OieTAl(ex)-=36o*sapuATqL092>xVw_(gGwUL?N=(Lx146Egt%nOIthi z2VT!9ucxf}%Asfx9)fHY&cO-!Hpd_k>u}BB?ZpEo$#^}*gOSPg5ME%1eA2cNfhk2R zg=V4yj!GsBS+2X-#=pI1xV$ONGn|SSxejuq6CGdDNqqj(xkrNu!y+rjaF(@uu)y04 zT%-ZYIZ4@2Mx0&E0UT#nL>6FAY(8b)wx}GXh zY5>hjCVz;Z)4m(qDy5AzKj(K%DOSA&Dj(M7m&1f3*Y!vZIhORJH5~Q0K2~F+J?Y14 z$)v8wYl>x0Z2KBI4zUJ4Q%jtlpR6U{ulHwa_~!FGS6B4gsmeWdeDry~w+;{G{ee1D z!Slg-ax<-yFUJ0^Jze{K>p+!?3X8hc5$wc*OObO8_8x&w0c}9 z?b{+DTd;EyTJ8s)hpn1JW!(?tTl8M46f`*Z(tKa)s8YsA8O8GL_g`rbDqaV+De6Ha zS>?C}9Mx8=Yr;mayJMxkgVA88QAn%k!!NO&P3q{Wip6a*pO;FT)d6**mMVNRBTdNp z)zdw60QlsEBGB_0cZb;qTPmWbK1^69Z%sf0Cuf)1w@=<|C+fC+j({MJBT+#=p*!UwNk)( zvOzg)kNJ$QgrPXmF#qgI_AQoD*gGXPh$tC6^m_O6iY}+?X!`WA#)_CzTy~4oM3~FY z5%UT`i51X?GhjUJh)UdU^K_k?XhYOq=1n4cTFy>m1AFWpK)Z84<+4c>Vy-y=sN; zwd>(DD0b=jIS6|xSFx>CcG<3r$>4HYl`3wT>f^s-n%>0lix17;zx1g1G`gEM@oYNRAO~s zb8D?zc6xFJ)tP(Q=~=JFJaUFV@WMzM(3#1RMao9u`! z#hT>g1%mA}7C?8PSoJ`$*mzr_Sj}w2DTd*Q(W_$w92iN5pEn@14g?G7)(Dmj4>6Aq z_635)V7N+qKwu@Btf_%Y6a7|u-~EVvSANJa@Ay98EsgI!a5=|Yon-a22QJe_u(mgQ z>wkKCeRo#v;BM)_W%9`6`EPOHvLY@H707$AkG=hLZlTYRmwFxB{6SkE_|QW7RTT%) z#-kVw|4G162AH7!vuQKns1CnS1}2ql)3HpPHhdmO?FFn;$2J?!^?RgsQUx5*C~`Bo zO!zwOjll-BOw=Px%C_iGO_Os)^dS>2K7xhtj=jkTloUTwOFqaAqC_oufSX)cG9Jr0 ztrUovc%yQN4OK6Ioo>s4vu2!K2JR{{jzlle#Runw6olZsItHPPG}jnQy#@%aW}k=z zp;ONrAb6Dk+~hw2I>hdNU8e|3(X7JpbB1ZV!6};&hV4o=p#usY=E;SkKbM)`(~z&C zCpJD&1DCNwsP6d&J-?NasA;kNsuKjo03%Dxn!@N z))Ene%Qy^*%LqGoxU|`v%0=7e$k`%T#@eZy309 zKu&uh_T|FP!Je6PGzdG{0Un0@!)fD3WJjUI zxA7*uO3%+X-8^;v9Wt0nVCi%1*|RA{kdx#q`#O6(DsCGp9@nYp3%!!EvOhpFYapow z$xLgnNNQe>*ZU(p*D`NeG z^2TsPn-1d&%z6nSoiTbN_-B%OX9P8B1ivVq$XLndcy5#=&@&j%IUlx=Ku`1B<6cxe zpW>N=S&HYAJaO1L(G_dukxH|NR0np2?91|_P|<< zRcsGPY-TI52NZA

  • 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 z
  • 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 osf!-iw?U|C&Dq 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�?S)`SK_shRSb2m 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%0DK{U3T^Xp2Nil%7kO3TqEjo}P6gRA4hO3$ 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⁣kBvNI05DSQV9_ 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≦V(fG>Y-)&8EW8dM%uttoC` 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 J+X!3we*togj70zd diff --git a/wasm_for_tests/tx_mint_tokens.wasm b/wasm_for_tests/tx_mint_tokens.wasm index 761e1a14e90d92dd9527555320a3569913e76ea7..e79d206dfd61c87bbf7707ac7a33db09682c92f7 100755 GIT binary patch delta 95 zcmX@LT;m=Fk@0>WNl9~V*+AkAZ7t#5T6Z**|(>eairV@0H6OD2LJ#7 delta 95 zcmX@LT; o7V)x3DX>jWNS||V*+AkAZ7t#Rv-q6v2RZ^<4Cy+0H5F(2LJ#7 diff --git a/wasm_for_tests/tx_no_op.wasm b/wasm_for_tests/tx_no_op.wasm index e6bc331403102fc5c24498501555e7250e05c419..8ba96dca5dc13eabc96d4bd41f6e4f8b729943ef 100755 GIT binary patch literal 373657 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 diff --git a/wasm_for_tests/tx_proposal_code.wasm b/wasm_for_tests/tx_proposal_code.wasm index 568e7457eaa9d74f2a00d0ed4acd5da6e6ddf413..9afba4a48a29dc64a469eef2e5fc404fa0aa3286 100755 GIT binary patch literal 425314 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

    Ub!8!^1vntufNkqvK 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 Rq&!u>=tT8juOo%={{y{MVK)E( literal 440631 zcmeFa51d|CS?|05y?^J8M3qNi5K+=;hv{pSI_~ zwRfhJCaD3>O_M2YAt+pfq8GJlu5fO9gh~+9s`bMZ5u=tv6x67QQF^38)f@$_oT z_pH75`_5!il86WJB-6fo@3q%n>sinL=UHp79o@0}wQ&?h@yFt|x2OB}?~nK2o|M0l zl|;A4`=i@4?s14!p{8>#!jS?>_o>FdebnIpY#;xk+w0WYw~vZZ@jO)c1}el$*?$(l&vt+E6Wb|kMhF5b)<0jS9I8A^m8A{S2 z!K~Aw`3zF8iK5zg8a1<8d{6w{-z}h))K{nbV?R4P7q#|B{@DE=w&QUf8&_xb*S_Yp zuiN!~Ni=lZ9rxUG*Y|qT%e#>`FU-jB+_I&T_zwbZ(UwiMn>)XEjb@zTM{?pif zI{vfxo$-&w563?d|496^@%`~f;tOZ~B>v<04g3DrOD^4T`!`?co{Epg7koT^#jEc4 z_y6J5ulYazD1LwZqw(E89{)e_Z^geI|8)F`_>=LW`1j&(dD&C(vH0eX$FuSNkH!Bb z{{49PuOAxyTztGSD$bqvw{P(3qR1bLXYyoyluWvKB8l=;hwPS4q+_>NbFoW(Jbi6q zHGF&8D)AHk%T+tiyt^f%0msSAwMoXAH5PlPCtc$IGMdgQ_Q~|7D7rpU(NT&T6fK+H z5Jg*375zR%O^U{>NVQyiWfa{IzglM-qSlslyG#3>bDHHte%;NTxaDfJ?9-coqo!g3 zU}NF9Bnzi`>N6WaCTLY5V{}awb<-9dw|%CQ#Q%0Q&F@E}KDZ~Wq&vMKEr5N_3IMe% zn2q&}>lYlbf{A{?K`YqYFZdBFcyT%0+SD6o`Sn0X(m{Mg=B=0?AoLhqnPk>={`3z(ZEh>=yu~ z_+pSOK=$ez;+F=TA@%)1qC1f~N_&aeHu1^M{uP}dq(0s0 z(p^#*YAd(alC7Q4H4XFtSe-KN#MMNCQim78;oDP5<^{4dB}3-8#;G{zEOo))PWVuSwT?ADv$y~1wN|Ik8m&XkH*to_`)TdqGuAg zY5#6;+O3P#!~i^TeRP6fAKwH`5;57ql;ep+PajC9yA6<>_;9VL6t6J=r zUmwW~9eG+k8vz7W{MeLxQ?EXi_8Q^_IP*x{%M1+g;Im!+;kf%>V-O&;#NlcUOpslh zG~rYfgWW1_OvSw#?MzAIOXbsYLd$VWj4i%2TL!Ct1zXmXFf$6*v{7+3&z5Db?ivr8 zJt@d#P_8lV3}*LGDpm5QsaP|xrJRI)u;z)#SkuSddanV#p24z|cEFh9xD^EBVFkym zAQ+DUZn>3*4dYmS*XX_l8S#()%9D{P*hwyqMAQCoSrAb`MgRC<0WJHvfdaAH_8ita zL}OD%o|Ewd_hG4^vU?!Uwxn+W`CN>-^ntv#CA}wHc6C{o&9b|o?SUNQdOMdqGsc0s zT@KWNZjO5Z!~iB|0C(zdzyBS7`Qz_-?+=YV#DfD@>hPU_LdEN3;ita%)Bo+ie)&s3 z{LvrVZ#4%l12`Y>p0NvW)3_v!GW6&Xm9`^?~2%zO;?&74{X%Mo1)Ryh(;Fs!=HIF>i$*S>T)5? zOeuWVUDx`(v|id59bt*hGkGR=F@fs}cMz4$a}xNf>UL#D7neK~^5r5CbEA9Ex1(RNOwUMjJ# zgP5(9b$4LS%0d+My*+Nq6oMF@cm;h?+8xei(0H&r9wFIkP!AQhR_ui$$BwyFDhB^q z0){*#=4EF1TwEhg#Wl)TXI+`TYm+t)w5R>U_s{9k?sRuOAU)5Oe-YOcNYooSKgLh@ zVrdn>S)A}weV+k(!VtmzH)9?5%udR=$y1-~>1Bx=3GDqg{oqJM!I6k6M`6HaNh& zT;3r-eA*Os+7EEhzz}1(x&5ZfKV}81t2jWhSbRhLU5s0jzd>FA&>&Ep_Uhh{TJvaR zu-WQ~@S7C%C7za4I|3@C`rlItm$IuK8reg8F%?bMjeHKo0q0vI=o@|qh}^*TD^U{V!}iZUCL z8Aarl4mc%34a1YF+6;Q7)x<)&zE0Pb7m=aB2_VxV;8cx3jdX&#@Y{M_>Uys3mx>s5;U1?+SEXB6 zw7WuXVOqI`JVKSJ6mIj!{^m)v`V?$GjIx3UU*b?_av0t4gN#v@BeRqTeC%)Nk6bB_ zE`eqckIY}f;5J2gBYaZMeOig{Szad(yGi4>BOBwNnqRAztGO771 zbqzs77+dqiv>HJHKd7;@rWAXh6&Q0(22T{6h@xxl8igQS5_7NPu%y6c%|qL`Lbc)l z+9(SJYKHhhs-&IBoj%jrqS3)bS{PL^8-qH0%qL>VU>pc5Mo_IFQg9-e z((5E&DoC#j+8Zyk>W2(Z5iL|jh1v(`s>qS*p)J6!ipcFKhlB556^$r)G%pn=;*fIc zO8r}cOxYwf7+Ixe^_a3t2t{@MPOcH{>Y88?lTsy+6vF~@KW9Z;j8PH=13iW@qQ(Gb zp9DhJTyKM+eM3w}4JN_*N3J8CR9ZrHFo}IXqx7N?W1m?kH+}o|V9g!Ors1 zInGP|n#HGIbC>0;WmBfQLmsYPUcdx-v$)XV&SZbG&rHN*vad)}4p@j7#k6Q%LeY0+ z9_n25Z37pl@6qy)PFbsmie4FhpKW22Qp!=Nq(4MTjYVJH;88HUot4;qG| zT{8>|^92KjA;vI-^udEN1R?h+$LQ~sNa~L?@k+I7yq!{2hSWJiu_>Akt`Wk4aqQA}N(Tv%9hc~-R{+J{RbsMAgyYmx8UDNvJfLJ<| zS}EYkwgit(LP#$7I>fQDCAxt_NWNarFpP$%(U{6T2EHM^PW>`+DUwnX<&?x%#^m`O zca(riVERf_m7+=pRH2%2NvkS!#8oj5Ca#zw&?b&I6*)@ z-PCvRa>e!XX#unX8VzYqEu*Q*)(3C6K5b(~LkP4-5&yEe+XRXDV$iXu5Jvs!d)il)?Tk$D&bATrDymAPxZk6E#Dn#eRm-82JZ zLSdJ}jiN43;lc70N4`9roGH()tI9T4XF+TH<@-PU<)@Fl`*%oK0KaANYf~rR{mTb_ z=n@_##*dHAI{0DxO+fo3t zb9m8YRm3V_(RHuu>EcLDyjohoIj%}nz-nBTSTbUO*TY37^I%L2`ebCv5N`nNQR{#~3V=grJlXCC_Bnwj~9R=YAY!Q9v!n96WR505NxM^S({i&e@3zX&JW z)*_&Zjtoi5hxG_?cWLQp$|fEevJ}@ua&$=ZGyYN2d7m62Q(7udV*KZ&)!z7dIS$VS zPd{*RZp#N!?{=ViiBt;@wPt=dHlbY7Acu1xMUkLURZw}Vi6`cupz>qMB+F+~=0AXM zeqeW*Y{)Z1<>Z*awA4}JKgDS=ANCG|^XL#y0`CzWx%1ZGOC4z7qaCA+(&uWNA5ipl z(A*}_)Ga5$ND9`;7+{7D#+ndPU`j>6WaNZ7{Yfynpr=fNS@Oyz!KTvS$8JcT8X_>H z4SWNBfxSpno8v$UL#Rp=!6yFcp(=C)5*pZ&-kMm(sjoyVi$t)yBV<#L43T^ySSu(| zBr*DwU=)LBR**H14wh0F2pR$zaGFl(@#)OEH0H8QNz*@_nY~r_PiI9!M5X2y540dw zg*!eTX)#K*jzied{O!?bzE_O{72?XHbs=RSMn7f+A$4M|!f`9;$N8ckTy#XSb4BA4 zOk)6ucQh)z1bBmbNI;+WXRQco1gdlUIqPCDHa}q{1EINky>(0ajMBVGe|*Y!)vL6K z=0^TSX_0zJi^MI9GlSeRf1DnC!|H!hcF990>ku~Y&mugVy+ zEk*(BRT-GEwJ0FIEYs+pVOH&);Kd!`lo?I+S(^^H#?S_h1W8I_q?QgqY?}C8Hv2n3 z2a;6RCXdGQ-8ZE7Y)RhBssA7{YbgPwGxASxWf=;)`UqD_*oklKpd3OblxiX^}bg}4xiVr3Z8I$r`xHHxs#^HC7ifp9-T-Euu3yb2}Foi-mD$?2lU zH$`%y2NxbF%9R&=H0E*hq8GZpU{1JfXPS#6P|P@(wv9X-r{p08kXMlsXGoAmndB5L zg#Q9`E|l~0{nM~m;RKzl@n>|T($fM4l_=ZwoSk2r9M?UOj-&fJTcPR6Wmfk`pJB%u*X<#zg`u(!iv$0hr8;GXNNWN5 z>j4xMP7+pVt^-_Hp|hF1mQZ zBLp~6D?B7KU82FDe}oe=;2+Hqua*8$hJQpFPQKAxR`^CovYr&=adbf9pUNs7t88}o zFDxOdGnvzg=NUB2_D3vlt#r|8#QLYK`+?=22drRVvFF>YAV@Y6rWBBtdOnPUB5r}8 zvsn_f$^cBZ9!4-JNx30PF%+7t2~aQ_0_Z4$Qn@^8ee^vrf7l8Je6+_0ppe!xM%|Jg zlHVv4;?`w_iaB1hINh)Hn$|Ct3XM(sPyNM{(L|W8uoUAbxt5Y*P||Gvsln@yR2PM} zg;SP5wzaYn5Y#4bbv59P0o19IE^lm#;Y)wv!__1umeV^JPKPNy5Kb-eHe2f{PyQUU zwN%tv%M;;LD=eQ0rbK%gw z{r|MFE6jz@5z?Wk9?=T+qH8IMgn1@Qy}@7w84&p^VXPbCPnq~Lw?q0#-0!5fhWt;L zVF47U&w9V$W9Dzv%69w_u5I2cbS3Cn9yAv_mI+mMzG@KFxFvdzZZ4UE86IPC(gRzP zCteyoYN!WbOon#;dCXZkB8G$oE{FRW1K#!)5gFur1^`Iwj_sC zVfY0WBW+3EsVew%r(J}!B{`@n?NintZAlL3M&||kpH+pC7wjLC7`f3G>>sNYx$YwU zV`7EO)u7^dI9o4MsBe za9^2I$1Q5KYptoImxg&>O@@`Zu_I!>zg22b>cSQ>T8QM*2j#?`NzS>PG8|aQx7d^+ zA&@-0QZ13Xuh1TeQRqs~Kh*So6q= z9u`2^Y-~@9&NR6@-bM3AnQAlkE=`N_2D2}$7#i|*mVmN)K&Lfuf&yom62!;8w6=Ce z7!4Nj7nyjJH+h(2fx|9{*}t3Yp54u~#4li(zy*}uMNos~G2j*E6Adx%J21{!_@mwT zsxu-64fIZV47ZRZQ2S`Q(HV#9JnGsSa#sh35vg-CyO*W;>H>AY#MaLDmI5Vob{#h= z2xuz5%Ysl*V^mL2l-)~F55K|H3efe44WLlpnTejkT$PBOGY`;Zi8A`i)vKY{4T%|O zhnEE!>KcX-`|z5;ka1jx#qDHr!fFNLE;oFA)Z@^UTUKKfG#+Fn;;zyM;tcg=*Z7&@ zoNz5RHGalUgwA5`7brvs_oT988*#YCKs_aq9hAD%uin1Zs zDMp492K^2*D|e6=MS{p{L6RG;9>1wiLFAdE97T3j+L{<;QTT(x}xezlB?xW`O2zDvaW9PRb`O?o{;oru6up-7NP*x zzCJnt&*x05qH@Q4i)1FM351$zs`ph>eNZ*krJCx1JK9%GqlId!xBT%JR866;HasHL zG~AbOjCx|HtcI5U2lZp0C>}lIfA&F*3_V$bZ3Z8Ei3_q!ejw{VqkP21#6>W zCh2wVD!dsHWFL%?zhN^9C^F^9Nl?4y4hcY0FZ0OU9Ve4j8-P?9u%=s6LaKE(7>s4* zwICP!ZfEerZgd>|5SH7Kk8q9d${-G@TTxA+uB;ZYtQvTgie|QJt`@6avbu}&LL6*? zwN{rS8PL((gnG$Y1XV^o#)a`Mj2}lEXB46%E#}hj2^ETBUOTdEDz5xCFdX=>=7(?Y z3>hmxpSsbqoWwK;?AI3C66MC!wwxqza6`??B6bWoqbaKEGiX;E0GVM8Koh>QWAjigKMmA~bqFikq~!`7ohzmQb{s)q zZ#VyiUX45%IXAs#o0qI&-||oY9_w2*^*cOxF+#NZh2oBX`~+*cFZahkPR+5X<)1&w z^=ALx-z~0BaKk(QM0s6!G!Qptq^7Hh@&ZK)+G?$$))GD*<9mY^4bWoYNEJk*V28Xg zjQJK?g<(v%P6Meb8*Z=JfY8c_lk}u=---)slxR^e@V}RV`3e6?jgRsCK~}v}2Qb(Y zOud{L{Ly5RGqID4f@t52%SrB8-V=Z@Lm@}pO&3c~wLFjOpvLSzCO;`Oon6}mOz_l5 zESkx$n{H}27UDyH=a`ly*XK)Cwsqn#m!t&^t<~XgYOd%$WhozRXF0o2g%uj(DjzWb;=eD-7PLcK1 z_-tmsz#parg~JL{2B{1AHLKi+Y``ivDgjlnX7)>6P6W$Dm*zx9ML=WTid_J`8GO4y zT`di3!Q2vAcci!>4#dAQmd0^5Dwzw@l&TPESl%H9M<x`r7?v4+`Anv;Q(m4weE&FVhI!__VjQsjb(w*+_Q+MXe^_^9a8ATUjj0?ia( zs8=k;jBk*1p<}Bk_)hn~%_#FmU@#XEOCdhxlGT{l11AJ+DFE1PtU@yb zPaP(c@>AuRj!W-F0dqz_N%;(JfhK5qU10t@^TSpVtBc{}zTSJ&9-?j;2|BFG`_FL3mh1>jBh zV5Qu;Xzi%XY_uMxmvJ>S8|mFF2_I!o-#^D0FZ~tHFNUB`_z^uTlCT+jgT_sElYKt2wylhO_&`UTR74W|F=SdjU|d# z8)*~eptnPT4-7M_WiO=`0$WV&jU-WoP{Guylx>Q--Q%&)J#f+H9+}KUOKz(-d}$jn zc*FPIBd1jr$)r`vhjr-Y>8?zw!c<4>5@mFSOi>?v<0JR7R25JWSPD6>V)E2AZ|Wqx zii6!l`uE5e#ZdV+1!^+2ikV-k_exJhY%#GZN~>pf0mJO8*gKS&261XH^d<-ec`+Ym6EJNu-Y9WrM%(kj&y~NT~d5vqy6xtT&nl^z$ z#A?#2kvl~av77?*L;JslN&Ssbu{=wvv((NYs&?No-rz0Oq2RTHh53zGHQ0%%6R}Xu zs^Q*I0yvCV9Mu2<;`O3q2sdnV0pN(T0D=|T3prL8pwUJmSf4#|FTt?*)uq&gQI#M> zHx6;QWjwd>TdZ?$uT8&A|2*%r4@+iA$OsAQ6EoH;GBlD3r6f>H_R{ih-`K+l>U(zi z```FrbEj536fcH>&qZ$9D`K$bqBp}T$Hi_M!!re%x{d`I41vf87tOT;4x4-Y$@`yg z>;xD)PW$_3v%7qEkAoJO!x2SW)Wg71OKyjODP6~b9teclQaWPb0HUjzj*-C9N678xwZKNW4w86$2&Vdab*U`G zAlk$a>?cQ72(Q!VcVclURmrcgp_e zw`6^|`i{|-#b@;GuwnwDXv#Kl(5^kF-fE2p`>J~+F04WrO}E$j^+soLXIrI)x5Vet z4BdQ0qmp(`_Jc(^fom8r*co+95eO3rZ{Wi0rJ=-*PWSWztL&w1oiQ}uRElh7D(i)* z)OrSNGb^p}^@1rv7K5=I!X#L0h0P=%g3T;;9HntEzw-KBGt_E^T1|pop#uQ3#dBge;|Uj zdJQ22M+vt4zf(zgV~;rhMV(Wgajw1?n{@!<8n`r~EAJOczQClPjdx%MiO)GdIeIgL zgg8u#xyCrpZ}xvE(L}A^yT^|bqxs<-qGlrrsPSB z6dp5V7~_fzs1?&A7>3-yvaPvj0_Wzy2SXRX5tnORLpn}Gp^+FrH;->dZQ|@A_Ibii zFL;c)quo$A`X+mY*xJ)|wp|Jlv+1*tpu>M(j&3S+9|3|mCsk}Q#!;0k z0PkeDdEmfYf7>6XFd0&xM11$NVbmLBT%{p#>DgI^Mx2&&ycc1!q?lawFot3;8Wfk* zt6I-3UgS)UC!sg14T!zvgMvSs5Ht1Nl|LX1S5=JZZRM}Rq5?pTMOxPYMzl7j25J_Bm_7LbQhVB>B8m+Cv*Z3 z!a^_vXm(I255hX(fVe0rP(udHo(uD$7(LYPMGcoA5GyPWK^(ZDfWT;1LV%J&wK?8O zXX#*A3n7Ba$Z+Z+2|HlPeXyYJmg9R*j0%6IV--}ct$7ZEH zjzH|_PF#->_pu@ljBMa%u+APt3|4phZH7myHnq2p+EnU8omrc5EbS?^$;6hCP$}&p zNet>!HB+rU;G{q?s1tsX3gI`xFR8)rrL&TRv4n(^kxaxyRjC#AWk!CGfan9wws>AN z0Ij5diOu8Io8u7PPtr04Ho?d|v+))O)>bd)0RJoFy1sZ#r{>w$#iH~Yn_7A0Hn@B6 z{mKD2&NwjTD}KL*hq0RE>nH3?Z&qJtXWETpGxta(X38^c0}j~}K(FA(hn`VF4U~}U z&&?CDeWbvROQrA<6WvH+OnVT#C0^6;31$PnTXgZfPx_g5gIRoP;nKQ)62GC zFyIuB5G(X-v2U7o-s*lXZv6&sj;SwH7xtstQ~|tMs;{&jAo43&52YRxv5OK*#&Y6% zlb^zRB*s&Q9n_Kl!g|2*!Lx)!XmubqJ!_lU7%GFT=8Id8Bv_9ee(@Li&)5BtBXdlB z{w6Zp(K~;WfsQ)s#-s{sQfn;uVPGq#u~d z6h0KvNQ6mM@WVDnW*RBmg5N0LAjR z;h_U+NC4VvI~WAjTEgg24DhgTKwLJ?i!8oj87Qhk~UU=xNKK5%vVNlvwd zo(gE~;_M^+!ehTI1&(@_ut_&lTNGrR$-o$+9Pwz47ZOg6zFenrpH&x^vxWIHTO=bQuDCYY2q+A~ zrrK*>gD6g)?T>c>7CK?*!oo;6eebWF;ehY7!D+x@=o6fUp|9sG3|;SEY!`_TcCqoe z**pPhFDL~di_--}DMn$;B&=uO)L1oZ~X4j z;*l-|=X?1^#aLQWUu-7or@o-R3Pk=%{RT1F=s&Rt?YSq{jIqfjT84G|E;h|IHE--bd z#Y5;nOIgr>oPjZ5#_}l46O>B+60MH;4+Q`@UmE~yX`@MoX<-*Oo;6IuTO$<04bZ4T(UTRu1 zwcW-d6m}1lNny4J>ymo%f@Xte@mmvre|!r;Ir$;~To{A3$2|{VGu>C3hkcsk22#n1H?%ia!WE5|7M_< zH_e$9n=71FZX~*)Vsl@W%)@;(=iUEVEN9sA^4Ts-6-=q7BqnriN4h zq@CK-Nis#IyjasgDkzPy{&8mmanhdf=ASjs_wH=H5mfCiK!NuaoyS=`hy2QipDDvkuMPRXRkytKDUUhqH0_GjZ=lHUu>XcgOBB zCKG1&@64~^Ja*UIJf2@M-McKA5%{0h-Pp>5o|LX>}zyUbmoHw!K!)uuPmd)MNE z)OXqG7KC>)opf7z>)f@xBYOoTxr|V6gS+OY{5mCtH_#Q!D0E~YGYM&h_#0TWFctOI z@&9Udgip9uFD*{yPKRcGk-fs0U+OlwtM~5B*Y9=f(Mh$(_U12sXzF(#`tiU0=a0YR z&A%HxH09osUov&zhyMO2Pd@ce4}LD%n~%Fq4^2(HCGYLcFW9SA#`5(#bn^-NxQfOu z+{?*@@N4Q`%&YWy_R!Q%AN!|2`mk-|W-X>uzw!Rx|D#9V_l~DN65W@twe~LDn_ukG zho&~aC2za6dzqEF@S&;rEjg#T+TWh1YS+%M%WX89^9$YO?n=6B?j=3A?xCrF`tWl< z@%DFp;KZK<$k$E%;1B)4oB#OO8;|`l{WRU>))VjUFHvQB>KHg$!5K~D>vVARw$W{- z&DLJma;qPj`usn?>5o733;*yWPk@ZoQ|~$Pi7&kGZ=U$2mr%uRv=*1`WsZWXwAghm zJ(@4I8_W0dsu```lds;(I?4;&C06MIH@=spnmrrf_+GbqZ{Ab2H{_S>b>pt9JM=Z~ zhPh!4ak9jX7d?Rb6>iub=9Cu>H$=DQG}B|nWy>wI8!g(om{DA;yRU*;bbhh5dI6~F za09HlmNm{*%UxiN^DrF`Bd}Bk`%IT@h;Gj>a6`1k;(-m(ojl3yA=kF5ZMSM~-mqra z?O>Hkje;PV|5n#R#&Km8gQB+v20h^ zEuRz`;KAj*b^<^Jyi07@OB`ItN=>)Kpwyaqh|~m&*5t@uR{jDtpz_OzSsdYD21YFF zy$X0b4q8y?x>lhnn5h9gT>;;;hYgdUF8~TARyZ)d3_7f_`>cRfPh-~dO1F-7#~i}O z%RWV`HlBbcs*>d{+AYh0i1IZ;&YA!WCA>)KE~Txmo7hWV0YVXNiD7+-h611f))k*H zN`4vTs8CYiX(^EI<&B}W!c$wtRA>uJftrS*66=AQOI-!%8aNmpSYeG1IY_kJt+JL^ z!M+z5Y?tn(-2hwLq(U_U{`!1k5L+4)y4yx)9$ElmhG11dQS92nbGI~*R6st1SNdS+^2x2J-vxk(6j^Z zrNSUZmjSCVg`^Eq6uvz@MXgo%c8##p6<3-LGkQW?pj`nlf^10b_NCs&Lk-4>+^?wQ z-pYe4Qa+~P4Dn`LpPnlVzJt(`&%hs&PsTYYp9|^Pm(OJ;;T?&7Gm!ZV>1hH6;!mMx z;M`H;v{e$dVX^8W^jvjXdcy4U>A9ldI^^V>=_$Q9f~21%J>lCmL6GLrGjQ!nhA(*- z3Y=Z)TccYLZ|KGgeZ~oe+z!ywm|&2etI!035^Z_T^qioDj^ya<=n3DBiEmG#XW-mX zadIr8Nxb7U~^FqTE{B?HnX$Ao7ERfGq-wq*U zqNi!(dGrkWcAi~cD4!r_zI?8L|3y!%)H%~rwy7gacy{zOeGB4Gp=YUYFQQ#usW}&Y zyKW&pSDCi#APeU%pTRPjChJ?!Gt{?8^#XeK^{vJ^B#AnEeQV}wP(Ed!L{GTmoawpR z=n1Qw9X(Co8iOscpQXMPr{M;li@xQd`SQ8Kq@W|dJx6_;;`|6xXq2;)Pt&*X(E@t* z^{wnO0m3QtJOh0To6MKb;3teo3eK6H^3OW5=VwPx)3;{O7tpiRx6?=S)v| z@&)Ic+k1xokm*~q8Vl%I>RVZMMJ>>iqDAcI>HS;AIiH@Z3aTJ4 z^a==9<~&xK7e5|+w$;is(b{V0LR3bsDg?@wI16bmeyPwQfTJpHjuU0es#u&pUgZ+Qi*s3ix=5ZCV|*Ber`Db|6+ zDODK9LnO?bKu$uq;jZTLY8Z3#LyW4onM7@`s0Ippmje=jtacObN-ADS&b0viAzJQj zq~og|a+f|tw!O@9vl4cLd$CP_Fk|B`*LO`5w^83UP25^dswHldCe;#mW%$;{=J1`3 ztMo>E;;z=~+AmU;TyH{?H4iCsKX)Lk1SvD!!)*Gr{Rtk3dr2&oSMkw|@0fU7ZO4<^fqc$||uAlat)NSu0j4V@e<`780r*Eq>x_ zO?RqTtA&|Yo6#Bt-UhT)88pju>5Sv+xVNig7AHz+m7?cl5ro*VG-XG zn@|6m%~$76n5Di{vqj9s!iXHkdY9?2yvLTMtm%QLIkbCM=+K0vIb<+ChX^*ll+7jV zGcKH!pT4=&PgG^DIvcCby46{;JPS=|#thPKwCSdeOgBN=tD#lwt_HW8rh6L`%{FbC z$v62MHQ6*5^)_j;=~B1RZPH}ZMlFD23YX8py4o(CT_b*Gs_7cI_(eVEHuWxW8<}pJ za97`yzgSaE6Mj-NO&n&*iKYoQGEn0ez=TeW*dnP}n_wE{LLD-68HWgcdbzu5dO9DU zMu(^Fg2(pe=_9+nKJQc8?Ypnfd;YUeJ;MI$WOOdy?U%nkUpJlS`l4CXM!#`rqSx2B z&9rgRG$*_j!KR%w@z&q{kz?OyO=d>($`;(2$+4C1FO<7^-`NyMjL02%lFVcl#gv@dy4a0GRoY{>a?#vqM+IZLs!7 zwL?~B?YFJ}rD|e1OwFN7AD6i+X=#{!ADr9m-}8YVd_J~%&g=U{Ncyn5%vxd{d{7;PfDE@$@$5-KjYO1Wbb?d5Kr4TmKItas-Nh!fFE^4>JLRwlQs zpE2H_$vFj?oOIk+(F2%W>00(Mr|`i9lV3Emj_DTyLc1|6uNLTsbax}f;F``MR4!+Y z8`|-Qx-`z!Z~z+8R|`*W~Pos<%+7Kx<4Lu*%h%0iS&C(qQC1 zrHs*TtkRZmYuS&)Ewj5z9ozV>a3j_OTb;S}G|qIP+65wOb)`zE0eFVQvRzPXaWd~{HJIsAU#qO`<(La?@N$7# zrigJu0gWf1iK?rrmdkQrq8#%-jeTV{##&;Q$6ZWYU57ZfR7hJ)K08PN*4fq5Km{~r zGXo%2Wf@gE)1Nq(S+3_qPjsNX^OEZbq6|j(R#Pby8+TL`Edhm74N!imz`S>br=>tD zdLn0o^aNfo0yK-#vjUWeL5PPd;cMpa#MBTMXxRtQU~8&n0>y$)Y&_JUWkicP345}j zGg^cvDPQFnkajcb!-?86I3YBJ1%F6Fq3584E~IB4SG9tkh<+oG`3&i40tVvw?CE@Z zf(GCnF-}`1QDYK*ke&m2XdXQa4JLZR>@+$+(Tu&5#C6EYIa5@6v7@HXn4(4y_!j5< z6nX}(MGK(}ZL`Qz=o$17bX$H}dKwc9(v!(?gA#ptj`Um#?+H`L(b>@xL1OZ15qbvB z4KlTocj2KYMSc0K=xJsYj%UM-d3I&B?8anAH~Mo(Df?C5Fw))?$G`c|CA;*N9Cw>&hTo=i6wl;&NXqXm^c z??|JZoqU?Ug^w1{v(&e2%S*dWY2*d;JOh0To6MI_(sTwTQgF`nlz+w=y|bmK>02}C z3+P$uTj}wY;Tv&y3q-W4%77Lt>e44&R4=tc)U*Ad|n$klH=y?YE z*38wQe6BQ4p2>oL0X^kWwWYDoPCiZFn$;K+^38)I`qbD0?=GR;5ot(vgden@1V#9L zROB-T_Ecd%DaVhZe7S^ZIp75UcAU6u8ErM>O^;JdB#SlHF4TB^#KOTvmHX$$twDaN z(8fWeDs7GtR7oo`Nu~(7q$bPlh-3{K^KV0h%zcY*@zzT~mr0whuPC}+9&WGjaJvMG z;uI*rawqHfLiqz@a!wg1AQGG!SQo2YwkROQqae8|PZaDQ&Svj`LdZPAY^7B$O$$hc6M}RK{yrOI5{t zYXlI05)QG9(ryju)~Kkn`t`d3h3+#!1Bm0K&1JYQkSn8$Fysq}F5zFAy--Ny0jkp| z9p~->yK%v8GG(kkF7hzW4I=}V&T(`>f;x0T=9D0KTr`@+v5IYOm3Y z$E2(EmPq2R(0k4#sCk)(!)9CW@2=8&mY3SA93-YK&o<+qe@l^E4e7RUO-A)MN0wA8 z+r#?yMSe+F9^$>>MEOcTn~+sy<$9h@kvC-RdY(;1-KWXy`h($CH?MK4Dm@fRdCg|9 zbdFM0qm}S59|(7`u!!p_05Y6~BpvxJ$3c3JUZr9PD2NMnqpBp94~I1Izp1nbUhnmB~VKNTpJ#j zEkpAXM)1y1goW1-+1O!2dMMGLIho_6B}_B}CJ;Fa869rB8-Aq_a4d|G#6v)U(figx z6g|2CMXcThOZuQlft-W$ki#eIPs60VOT0Lb;@B;U8g_(N`9jPXMK+Tb6n0W`KxAxzMocpDb`mmQG4;g!F#sYuKp-~I36&w_0|odfoyN=e+-^L;fgF&*!Y@}y-X?CHVYa|X<@ihGo{>W zVk+SBU`$dkQ!4V9DXmIn7bzV>PY=|Ufi*i#z0zdz@*kxtpjjD2nk-CaVIkSuyC|Ts zP~&K5Xr5IE_M%$V(BXv*9bedx%ry{=4Y~)6bYVkt3mZDIupt?7_EE=>Rz@7ZV9&R<8m33tK1XJnl7~&_Pcvj} zA#*S`;=<$%2X%%y1E#|UC9_+oAjTaGN~6&AeCPoS5>QeYzQX$RLXI58VyK5w`2*-p z>!#4MMBG7D8P;a0BCHkGgl{hy)&q0U4lAg5AIdO|iDS)eY24@s$K;IghIL<@Q!wv~ zVQo>2p?i2AmAE)998-rqhQb&UZX9J8s6pX}3XcYj%|f#U?AzERgX` zut!Xj6v$|}Wk!d>g0m(}yk!J{10mHC0!=KcfD5EzPBB!0)mVmi)IQfQ&5i#d~oM%K@!MVUihksA0p#B+Kf{u}Xa~$!)hh;H9on zYm7wAd^@%OrRiLrN#QjtA>PFb@_Yf3G6iAi&nzY7b0LTxDe)6SCovT zN=h;X!%p5N3k4aOTsKLWR5%!ce5iDUGN^Pk zU=`K{m5>(ky+MQZV6zm-saQEEE{mlql)FJ_f*~pVpZQ8k^BM zNa&#iVOZ2hlWR-WfU%&-D+mU()}WR_S@ks|pHrcIQY?%hC|1@D3t7^6+~5?(?Mu(1 z0TZeSq-}{5FyXw+(|U&Tpw08fmPi59yl@Fz7a>)po#}f^v}nLohE`i5sH zJVK##p7Q!-p-9N>IaMa9>p09&ja?hNH*h{V!*Ykj`oWXco3nQQI8~s&ST66CP3hZ2 zXaYWwO=(`0K1!+5Z}Jf40dSbBO$$%FoN(AIb_!CA z;>T%zLp8pV5Ug}8#@IfE5Js^QlC1g&YX5Vo)v&Dtgo7!Dax{myTJOefG?qoKKG`c| z68Kl&>lHGIU#gGwTC0cn1}6R2nJ4KOcV4G?82n8bL`=U~lSX_Hv$&klaOyT-2koN>*SUoox_V;vl)gH2TjmOjh--5{CV?{~&9 zxt`bM@qpsg^#|A=+Tw#Eb&_9E_1W*O-_PPbl;~&fs2Ft3oq}>3)ggMgvg!dTu+fnL=`UOjNpsSn)F1F~f|xXY>sF0US7H{OLk%<*{=b3V=jBKa!kt1rZ) zXmz%jIa^*h+xE{B>NG<2uZ&33Dd$Dxt05v^S1fGC}*^1QD3+9;2&D<(T{a`Xbh*TRN;U9rLcM8LQW=M~?1j!$f6 z?7R#2_3y&Di_gpJd3ik_>Ymn3v2_RMHQ6_SCL1}g$-Xw4jE~x#PpthLjB?z0i8`%W z___+|&fBPOkTk=2i8@cHb6LLlb){{-u7Ei&QQs&MrS;qA z>f-}L=e5mgLec@;87pLhmthBO`P!Y68`pwql-61Y|_MMO-9lvPWUPb^{3kjM?T5|eEJqjaoVD) zzxbvOCE6Y_iMAvWx06Y~hCYUKKAEF8FUO|+(|`43bY&FXz&qG_?bv^c>kUz)ce3@y zvH#@Y^;6Y_Z~KgPQnizM_Jid0ZI@{4N4tuO`$a!1uIK%2UPt?ZxhU)onQC`PdU$Ng zy=f|a5a{?2PP&cn-$8@<*?QsSw89sJc61Ux55sH!+qkN2+o?~&@a7zUe9)(tCaRR_ zJ4NhLHN6p}erW%<*cHz*qG#U5hL)QmVYwbQ_0rdkG{*1*!)(_;JCE0RrCS6X_Seto z(?R|pVtpq_TQ9J;+nc9`!#+>E%*zK2#H2%%ervYR4g1YAk4>G7AJBeVucSKf%=7c@ zySF~Z9$KwOKw9L%D`?X9lCpN4ty)}-0UGo=(b!-H-D!qFysT532Xw6fx+#dMR<)R_ zMe6cl9X^ZGj=-<~$Xn+kzt$h;&>ds&vDyb4$0}@;@bKLy__P6I@vi-oS%ck0M3iEe z5y-Nc4@w2H@chXi{`{v>z=c!&?4y71%WQZE)%@%`D87fU_7tagKgK)q>31;5>{~s1 zjg9XLAF2{bvipEE+VMj#X1`-IDWK0>5XsxHb|5{ z;`r!P(uP%2C1cc0JR|vMcO?>nUaZW)MiKgMNMAD3bufmxg3=ke>gc@IV zAE=2#1F=8(GqsoVc%}~b+6_*4h|!0_P@6(P5m)l!rM7!8b%Gp)A#N56{aRWt7|7Tt zh_im_E!*QyraQHL0vjv%ze#5N58rnSfJNH{(e~Y2O}Jy<`Ce4X!SuB~_LJfGIEbhOKu&(&uFN5Y}1Y~4DFETpZj6B2Nl4RZ%_U6j|^UYs<`s|b?@2o z-aIO{UW3^SLZ52uL6KJbq`;q;55Klx=d@yMx_rNwSbG$4U>`Lx{6-$p?o(oOoi#a| z;E1hG#N4|Q@kl}3M2Ht@Ay!f#DDI;n+wUW(t`ZZzy9>g?B!&apG#s?xg?S^6FPMTZt2qaziz1t`ka9ANuAoI8E?j$YsCz;%67(mu1k_j2~WKyTsvEvDc&3RYj zrP@oZ+AwF=*jdMFroutjaKP?4Td~E$!SH+>3gXcTx5fQY|+M5|HaA;kj;f38+g zw=C!%g^tO*i?QV7&U|=WL+A8%Y5s|1yRJ4xhu9&|H@0;!V-()nN!#rp3a_KQkTgh; zA!#>v@QwXPqQG}(FW?3MK--@qL<*1p?i1jqRo>p zQUmO4>WBVjExIkvGSE<8C<$j9SBmSOVT9+d3T^uOY!d6-qNX_JkB{*-txn zmvwI4+UfA0iXUoT<1pdDfz14iZ`MGw+pV z+z0Q|UR1Q3fsD@g3e6FeOS{{*b`V5PAYC}w(P_1Z%`3e=!s9?ajIhD8G(OF&%{8pX zT|V~z(FiGhBo~{HQ7nWPnoSRa(A^3NXk&Q=0!$k~04V{4Z~|KiYe|q`JKo(}CM6{! zfAYqidB=Ztlk9It?D_0Z1k(z8qG~$RerOMLs`2?kfwXRCk8Smr!anTZux-3kFT@l5 z2r0Llr^5M&TZV41qFrF3iSlkkJSp0(s5qDP_(@!ITT!LKo;1d|LFycWO3|8fRgaM%8tff(B-C10vk0<02Z;v z(#QT&K@DUugnyGl#nK+<$sU4de+`CbM`4%5O!A^6;fpoKfVch+XZOmaNM$>lQzsk@KZH}dXr)t zd>{s@MgdjYY7dokF=1)6+7PPX>mXx%wAV}|O zqSuNm-QgTT_YP-epDO z26Q#`DZ>u&!$yDm)e(9X*kjPtcjFzp4Nt2$tAokH2xJ z!J-cyW5y#S6XG>)+SO_uO6G-Cj2ALh5u98;;X+4-3nQ7jOA2)t#B83zOHADfbDRZr zS7HbKWY{rx)MblcXUwn@9M=jvBHjRYbZOXW1njs9JFdV^aDfZ#G`1NDcZn3j&SU{Z zp(g7AFKI8<%{w#5SU4tdC;V|!tM&G{)_1{atl(brCQw9_6!tKdHxbL5sF^nb5`v#l zGpmGu5eFuahY<~U7!s@CW<{lk5d)W%-nx~lg-21pX_ql=>pfYBrttdNSFp?yCX?l# z=p2nS;v~&#^#*C1BcCMGFyX)OXpw0^Q)fN2fno(Hr3i~}ycBr@ZIMW<#b;jpTi>IU zg1*VXLB8`rzw_X(3 zL7XV=!Kp(}JWC?U{9qWfJQZ{}giE5R^@(gC_!TEkF8ni2#7qt_BSNCAw*aCk8h-Cw zYtkf-FS- z3Ei{)`fb(%92#@nAM^WElUSkO$_tJ86F-mi%6nkULoYC9#3||ote(+$RbOLTY`j6L zBLB03)Z(K4n1ZlG{&zlLa1D$iE#@R5|C6VTFqOFzE&LDZUSRXUNYZMWf>36t^|5je zgtW#i#yq1`VD6z{; zHL}z7xaD7?!xj)`0zkOI^Ae`~Gr-pU4#1vw^%k2EDRW3{N()*i$p@|nTfGLzL^3j> zFAlZdkPRcR3%>+VhF>ls|D?wK>-@N(agT4!!EvMD3DB=v4q-gC)IX*)_ktU8uCgUD z0jzL0B*C?Qo=`vBT+sAbkJy8Gi0dOZ!t;lyB!k7bT{OQ%#<)i5D#S~f_oUflL+A}j zx5p!{zA19oN7w0$aPivY8qS#M*vz4|C1n>sH@qcv9BOEnoI89(Pvhv`0Zvp_fN$4I z&=XV(aNPw<{zC#7w~fXNUbeUhNNWxki%AfknVb=wTF<71Mzy;ro4CiqVXD}C zTl}D9tBLF3bjfW_RtP5>r?=NcQmlteJt$ydSkpwcQ?y2`yk;sPwwQ`(I@`fLP!lUP zj!QiHa#Cr{HL&E#XeY5qgA^(=IGQv-_U7ckMXf1tY)$3~aIO)O%?8NV08=G=${h0d z)MG`8Iq{eJ6FEk61_iE`Cqza8kQ zuMcX=XJm&Q>bBO=DpV=6-b^7%k6miBwb9hU{}R1Y9)Jadpt9zNK$cRH*^IN>vrMF( zcz?RjMSCA%dhsN)Mv83Eb@9FapAqHhVnRIg$qb?X!~5@Y4J1hQ7StegoRPVhI{D-u zUiqCMz7Ew;q(EJNgf7W~0}IJ9O&19gNHMR^>;+nHfs)J*YnFIk3#91g!71gLDhp9| zkwFk_8koae3EN&;%+TO3Wq!K*%AV$VG^2)V_ye^CEJ_#~6Chnq!r>!GL@)#KPQ~2H ztk!Iyfr?3FZ=y(3!!5HSg|(2y1t`E*{yc!k%-pc?UN43yFsf3wOr{gG%ce5(N5Y&j z3Tj8XzDLM`&+;Drw@N-w$u8i2$JvT0Ws?~u?Y>p_W>RY_)}r4B?k$GWV+G1I+}#| z`2^}11p*MLBR+BG&_!}4P_XO>Ec?`j#W~<>_=AU~s>Tu|Gq5FPB;0*n=BuXJj`Ebb z18Gu~l6v)~u512;J%2leH1jGB^!XBcsi8NoP41MeNHCH6?zFizdVWIJd`3cU4%e(< z(wXDVf=-J5$Q1Z?=z4M3o_b*@3?2tOx5{~{0ZLHs@`R+PRW`;M4wZK2_a{F0q^&Ag z9%u#qAeniEanypMvPSwL`0TE4{m!YbeUMvPrqT=e^u5T^lqOBmF>wD^#jBSlIGtBd zpTs3%!CrJ~pIZ==a|$HBLYE3&PVddb4TaZFoHUS^D$%GT5|K{(y|x|#rvbjl1~Jnt z5x99=9-oGTG-#qkO0icj(1|xEg;E!zA?`UjI!p=j;G48?Gt;zYZ<;gA72#w^?P)pF z{cn7*d6ybd-x!I!A*$DRV^;U?Myu)m%uYYc@eMlxs1Y=bYBW@XMEQam=8OXb>3i%c z#?5lp7JT2tNGh^`a}D-aRas{aNGZiP7`8))}~(<9&5|VY5ILT z{oN4Onq|7LW~=Q~S0!9d@?<$iQvVG776m(Dj8tY-n`*LtpdKbXZ1C1fP3)k-lHzH! zjAi5$!lNZi5{m|w>=)=edIW^b)51QM#3`UCPzMYZ>L@Bur~bevK-fTRA%mzwe+Dtu zX#ip(-J+sl#{e`}#F!Jap_CoI8|z0=uX5&DRHrj6ht0RRl=X?lfaFvuQC<=@Fg^`K zXbnO(kmuB-CU|8dctD)Lnj1pKXB4Lorg~ccKYQ;3XV-DndEP&--~aA@{ZDGCW$V6c z*={8fb_lWjQB2aeA%8p}#6XtK4$Q7un9nDp&d#_Mjn~Ucb|-BqZ7@Uuvm;msn!ytZ zu!%D95D`r15HAR9fYz=RH%pg=OC*#x41iTC?ERrkL8`j0HfVSw4FKYF)rRo$vn zr%s(Zbu(bi25@VdiNq4{wamf9_k<}M`bR{r zn-?VDp#Ed9cmC%D#?kJ=Aedm)^??{~BGVv3Ko-)10ga`sX+n>h0tE-gq)^>5(`O-a zKoZf>VxyA%mBlR&L@~5Eu|;dvzqk&TyAFH-w#cDAI>$Lm zCau<|LnoVnT{Noja{5T+N657j&*A?<8_$k^E>skd!2b%EYDRb}^a5knTy z?nOwq8fK%nW4mCG^9e4r4hg+dijP(;!`d>jbqmYodR?j1yW)zp`GO28K5TWqhpzQ% zzQCkAKzQ_hu;fpNEp?4%4d}P?J+066Bf-tV|Np?nUxY1aFUY#t$B{2T2E9G{yZk(^ zpCmiOqjNrNeJTv1nS>Z;8ow)JaG-qQZ99R51x)6lWxxkb)DdK-exp5?o&CTIZzlZM zy9p26j2VrWpZr2=F8h;~;y?9qf zRxo&GAN&xHN5kxGALjXMapzx2eZr?7_|MHbOkz1)j|Owug~fw6n@paa`DHRsXU8Am z@l#Ru{txnePvpLB{b97mGD-i32lq=ynJs*o1$q5;6n(TBA*L-x>IC1=0PN-I+HPTGD}d<7c;6fX;QBg#43gF{8F!$ zw8ePfA(3=yYf7{tvy-&1vsY6m6O6q`i3P~$6DWx_9lVM0AH@LABnPX4#Tl?G@JFNt z?2*zV#*%qR&;*W(EvutoBO;UcC-b|+5L6`ck^|PaNE?VD*;dAdm+@)ABA0D)%(uSZ z?=ygn=SIXVq@u9%FVt0%*qw$@Qbi;lw4r_QVN>7a_CZRJm>I#*r3exwR`#n%sjDMr zj|y7vi24|u8{6V+Fn#@W4v3pMwkO;h+oQ{k?eR3WXEg5EUQlp3vAsY{&2cn)Tf+p= z4wRT+&hvUji04_Jg6Cm*DSzR4UhfODQYlYA&y(RwJa4CDPff2vt}TtU>~{t_GkAop z9}jz-A%jN@+eb|SVL@C0M5u|ZXTONF>_9nWzL#Dem=l7D#B?Edf?-Ygw1cGr`eLjX zhS7zRiJ-LVvC_$zQLFSryz4^&A+8)|$B~+|sIxP-8V+Y8*=K(pYQk#x_9(XidNKl9 z8f&U*ueT1epMLm5nl)YXwE$LfV1Cb6kU36*6#@eVaskMzsus3aS3bHN%G` zSY?N|l=ljHOc6JrwG>=lG0R>iXu zgkb)J1_`04A~ZkLf6dBFFfJ~#7DJ3*wq3wipUt}~p zN@&Vxrj{gs!mAI3d(_#I*@cS*St? zHd_?;G$6i+xScMw2m2}s*QuUut>tD1L4Jd~etU7u3dfMp#fTVJA}+4>c2vejq|Q@} zGF_zTh_FTydFpAM?$1-ty3|R?%ThaOi`2A}r*l1-zV4(Amp+*eDt#zV*Ns_5yG@s# zq{B*Y7wKHwt0drZ;J^z2Eve`4%K#wAiW*mm5HLj*%T%G-i!xU!QxQd(tK?86MWGC5 zRnp2UIpr%U%Dmt!DavGVY_%3;p7OO8WnS==6osM!R3(FXB`dy?qD+{xHLWOffg;qj zqRbUvNm1rP>{?P3Ix}aL47*C`X1kZzu4d%^S!VIw@dd6zN8hqr7oOt{$AyYqAWx4W->P|2n-_i=?-*v1u&I580-RG!QddoWMLp(bRgNC@TB@nq+`^=Ye1s^U$R7_ORQej zLF?Fs(A~K?~FswsY2v~Xq*CoJedz0&(Kr`dtXash>7sEZU(OxwFjsTCN9(ec z#jFA~nE#~{%-!;YB5KlsB6dmv8cs)u<|gcSLly6qxqh+XydCnzE#IIt6-!l4t#D01 z_u~H_g#FdnDyX!qS?4Nu8Oxd$%Npv^vIZYt)(63>4PqIQ12*#~f!5fzt_h+U=_ShR z8di{QU89V&Ghf*dF1)s!OLUO=rA;rX&w@Y+=oyKFxkiYcpKt|eDpSGwpOef{EEz)sHX|C?H(*IO`kIlYZkOXVf339Sof|aTktZs>#eNMSq zGN22|Mmo1-Uvghdv5}4D-kjqirai%g)~)JjVA>*0G%KaS39I$8JUW5Z`g$Xe&IwnZ z+S0e;{*>SW;G)f!L7HPF(sa;;CCDd$Vh|)~#m#ULU5@$nHp?7OmxMN?%2y7Kwa0yT zd{m%yN}z!j_FV}ry=GXKtQ%i8IDMD!=6;J7WNSwbdZzMR}A;4;2ybrxRrl1xJP_nyl)+(al5l&o8GOQeZ%8YZ91jgzAq*& z(H9-HY54Nnr2Lz;N&Y8F`-u(OgmKl<-k~%L_gIMA3L0c@SK1o(BKxVz{5#QBU+ZI8 zMJ?@DV2f+%8DpoVebG626YkZ=RxV5XjMCW2T~5QiYH6QP+L|$fLz2daoa{Zz`k-?4 zIh+gS>sgi?TWHp@$MgkC7MMl01-4ZSeqJ8j%D)-h*aOx~8TSc_zHz=W{(%$RFDtiP+5D7o zLhsr?{VY?4-)!*J+yv00Bby>n3^{!EXz?;TOF7Wtu6CEfiO&KQoprS>5@l(cT zf(&^u;%qc6ED6}MN&BlZEnqMTv>)JdNnH6iBNf@Hm9|a=unEcRZw?|r8g9p<_lLr>UHjklJc0CU~SYFJd-O5IJV1^&#(QIW5Rou?A z2_86rY~fLBWm|c`91x7w@Hu8~p1XGuouYwNg>l>njJnnn{hghQA=HlOMpvf2S z4h!i3ls6@OOEMZGzZ5}@V5W$F>y{!oNB2mZ83JV17r6>|@wo3j z@^?%K@sJHhtVzeMxOyk^YR#88X#H*93P|e}>>Sxvd1Yy!tTzIQyx}eenalE(*nY;# ztmPkUiS&nIzP8ETHf-dkZVoon7m~rRudLlVCgi$}ZV-*xz>1A-M}LFdFo0}|$@iUl z(FDMRumd3pPHDT|@?{}!gXKk`8Noc;G{Iawux`s+!C=Su^OntrSg>{gljxYb>+7#9qMp|D`N?9N%77*u^Ql55UNbZ}blwuv@ z#VvT0OZk{mOnA;yauN7<)_sGJX%PHBO6h|j7cqZ%T}m!szE3G-AyV#iR@y10xcxcK z(MTt)Sjx{Rr7Xr3qUC)`G4VKuIu|V8zV4e`to+h{;+wKqDW5&7=YmqoyBv%CkDpOW zU;pI0AOAopeGte&RD}22O6h|d#%w8%uB%6L<(>c!F7bvv0j@*ZNHmT!mGNu%IETKh zvP}2f`3lEB(CZLk*z99zFjcWMn5tOXgH*SMrMVNNdMwQx$Nt^`xcf2y(5(OW2EfB$ z?pZM4#=hSn^46%V*AlytT%jMcu&a01S@%6-_sb72Hhv|%ee@p%Z@(Vmpq*|$vt7~m zf2mM(-J1u=;s;*q!)tJs^F~(!EHFlXa+)ecBrDtm06JzvC!3{TQlAxi$dCy*G6keO zmsIcN6ta4HvqF$SFSn4>n-&7mUTz`xZ&pa3(lK{RJx1gEIC$&*O?Sj+VZp{)y?}?i z&Od8N>C2wKFTLwe{`QCe^881q%$_p){!cvl{?9%3;m2-qCXPlXGfHzdn0?z%FMs-x zA6a_%-FM|FvW6&S;jho$b8Ph^@AF2B2I8bk_}M@H$j`p}U7!1)xA`{AoYRmI6eAx7 zwFn?CIyHHi5Pu;|!$V2Rc@mEWIBOFtK_fSKMoCN1VHR=FdlLwsoC5ZwdHK-G*0eG7vH>Y&9rQDE8v~CS z3jl7#G{j>i#BPc!hrG*V$!&N$2(Q>B1Uhc?GAqf@nzzoIJ2*GVWoi4q@iySy@omqo zami+F!y2|x(>7`s6pD>dX5yrRk)@G&6OEZ(X$IEeLqJ8Q1=>>a=Icgb=T_^B=3+g= zQ918R*Q`ipn^IriXlB)>O1wU$CowS@9scIKf9A}OJp9PR*U30WPltbN0)h{No@0 z@aMkx*e3#_DFJ9WJC(ka8)enZ()d}EJH@-G2bVMd0&^KIil~4U8JBeenwh0@u&5R4 zSFLP@>CxihH11WZUCLPF`P@~R)LE5zL^xwuQ6I&HuDEcv7=4GSUZY(Y4SNH8k-mZg zu~#vZKpMRPzKAitDcUsS9XNAFtgr_k$N!pF6mSSGath07A+C`;J88#+z0V4ItTV5iHlQ@3^ucL%5u7`X5K&p-B_-+bcJr~f=~oYK7Sl<@A0 z@A&h3KKZ_LA1x9XC`$MbAN#-G`rtDUeeN?w0%KYUOP~AA^QYhOV}I){#t}|#n8$-U zX4#GE5$jOO`ijJEl(N1iu|B1&tIC+=?w})2q7B+4CaaY7b%`-6Wqn- za&6kEF3mJbSzp(xPg!5r1)s96F1Y~7QA5OOOXfuMd`+IkM3l2Nl zI<8O5*+k=t_=Evy11R%@3u~|u8v_sDSYySw*_sI-i9{TFgo51Yu8J4=g0R6;keo{R zo*d3#%xgnrX#*M9GS6D-^7bLNF4q}KwYiEXbJwWeZ^O>7{pAAJ)uL()HsVo%D8bC$ zzHC)ow~D>Vx*{XE5~)HJd&b*v{LW}G+WI%wz#P)_B+m{Gqq;Iy0wNFcfyD3}a!}M#WOd?gHW$!me$8VLUJi$#n;!F;|3P zjU#47AyG3$=tv*Bk)MyKgGn25kSt{$PKWSw`11R|^vrt}KJ&?s1&1ZakX*uV$ev#+ zU>c?brcCWtmm^5k&Gjh)0VKya#8hXGoO=6jFTC}y?|;eR6i<)598Hv=A0Cu4Nu`o{ z_;oV{WfZ`i`J2Cb{6GH1Xa4&00KrQ#whF zOAOpvA>DB}y}}$!cQ8=#s6Awqz?e<8ep2AMeo9(oMnD%30KmH@@pQ}KbcZpYEmSs=1F%JuS?r6U1PkZI8aWL7m2 zRl=xLM#X_W`Bd}!Up?VQI`2p^~VYUJCgv`_Xvr|5X?w}MV>g=?S0a=QaGD3X8OuQso;d9 zzT&VxGNtFmVQyjSESH7X6?1Z5L1T)l_mwiPs30!|M>#L9cefA(S0r|~5_BtsE_h(Y z(%pt>dLY)>F$2+ra=Pn-kBjbpU zJl+oRR{1cftlEnY^ZeSz%9$g1ek+8iI@?z>_DOUwO)L?#<|BMCv$TaH)o}XY)N3U(KOX+{X%}Y zk+YARJtU3f{*ZxjJG7k5&E1G0X|b4g=^?Bp^?#;ATSeZOlPQ@q9`(@S6DSJB+ZQ;} z5!{#jSD%Yhj238soCBG|f zV$dBIRIR}T6{1rBHh2V?bB5JrJek!=#d46BPPP!gQ^jD%eMoWZziI&hC2@8<54qE{ zK0k5IWiIpGWS>v@Jz+o-e*UJDG*_r!)4rN&8mZJ4)%BD`4d~Mnl6gy0yw*&}toc@~ zYSPXH)f(Tr1v%5BR)q-J5}Oh35ZYKBdwEm&Odo{^OuosKRfF;&1e0&fi^aLP+|g2+ zcm$jU4X0Y4pg1M1{{Z5s)-`Fz50_e57@m-dxCu9yLYgG2NsDe_t znhYZU=L;*?m0MU1(qhmm0oYW4?Au;5<{Z*8%2dQFNljetI_mJp>Ci(kI8lgMj7m`af6eQRkzUpLWC3Qo}P zSsr7Nm?z5JL>oEk!L%P}I<1qCdL3Ua)x8K}Xos|Y2C6zqd{|QX77joGuM!Z`T@abm zKf2yQQcSNV&Pg^d4v{gvhBznUxGsYrZ#7E0f&>bJ;SieKnuW8-glmA!c9K9msk-xNu32H=!8iaVxSdk2j(g<#7v=8IKv1r957P zNDF~WgJfIE>beKe_Q57BLWvJj^m^&gVRR#K4;FEaWQ-2B<+bIz-t9N*I8IPO=^{>G zdMaC?TBaMd#uvLt#V#tzRFaa(>7#l|xE=5Utr4&C?4n-_hkipEnjOhiqQo7ZUG(YU zMx}!pmL>!eBdbv&Yc7_8jMzR(96uzz4=oy2W){lV`pLa1jmdg_8#i^ZWeZhC(=;Y# zEfAmSQx)n8MXy@a;#}XD#$>?0DXSw(8k0V)oIaWC)0nuC(g(e?UcMSOnwQt!BjT5g z=6qnYgI4@gnSll^)iC@8YlSpt+r8$@aVP80QIt#r<$;YVZkVwzeSsgbln!Uo?U3!m zC}GfNj4Ii>dW4V5EE$FDux$pcJ7qs*!6v0}vq=bLPGVX+8sQlc@S}=5lumkHw4D;m z?L$d9PWMiV0!F;J)(7ykgJ?Y24#?=jfQ(N(FY1RbIfQa%nryV%C}7mMQQ*fvb^>W) z+%`S12q=!lcc4-Rfo41Gz``sYOI|`4-+Is*HLj_XPQGg0IZMzGilj|W~3T~Chw`n=vl+TS`4U1@D40IJK zU3#Mgy$rWBpBy9(Y24?1G=L5X<&SjpsWV@hbv_;Q1BjKrjW?&nK}YCEGlW+{Cntu7BW0HUK8Iu{6bfL36lP=2Y+&2vxJT^{f5g+xT)2!WGbPGD`{1tO=O=vdD zJ0sWV(9HTWG}sy?pGeV#xz&jc4bx<~i3&l;W1g&rd9q@oV4dMH?+81V%E{*BeB`)M z7MStD<}9bpq9X%THB6bg)`s~qp*xB7y%}g~OkkPH$h?>+tm|A?0bnKT7Fd|6IHYVdl@KLmqpFSn04wfvxo zMb4SA)lw4i`&a9Nn&gi-wCy*+cCWswc)5v4KOB%~jX)M9j z%>Bgk#u4~Dja61d0O*lD9$SN^LRl6x;Chg;DC%(n88bnLd|eph{L18OpCTUV)AEQH zW9@MQkFCgN2{#g;nf^ryW|<+IW;xe`(vWvW+x?6Idw}dB&b=jfgAs!$WQ0XoPSiTO z&37_iI7@OFK4b7G(|Qe&A52B$!x%hr<=Ta3S-adDnQm!WEHM32v-j?opN!U1u|EJp zI<=kYwK}71ABXTD&~}g}(YVAf4iYa#`a(o(5si(lzI9Vf+ahiaQS?3Y~vLwU(;OYT&K#&dme$PHKx9$R4? zk7ax|{&A}sRqg?%V@5QweWIN2#azD8{jF5C91Hn262Y^zy4*F0M4Bokc`)Kj1EkL@ zK+9puItiu>G2Rf}>oAirh;=>RL#CF->pGIA&Cs(nS_|T#g-E2W?dE9(m?nc(h z2C?KZQ03-#kiG)a*K{O2$obzDjsa&vn1`t=I^UR&rjrOEwxU`N87Q>TI_Wq%I$oFk zD?)%?kNa18Sg*%;)pW(L4qs}ihvf^KwsK>RBal^fe{S({QRj&22+~*(v&=c?Z^ZV}FydgN7`VyK+UIWKKtuOT-vLSDM;BPf`KWEDa`P%h}tLMIFn;UP&w-km zL+Dt`ISXix!6a;E@T`KvsnR67_ya4l7CR=eLR2@qP>$ljtTfHem!nqmms5Da@NIWA z5yaiGL?Cv@GYQ-VV|R+Mjw)y5yV@gINv2aFw`3uzDmK}2s3#k48MnLRGxGT3xe_^j z*XhXVwoXMDd5J$SPe7ilQP+-mI~R5BRHd_c45|_Y6fabK8>dl@l*9(!u?{A~B`c4Q zs`cg(7n5%FRwd@Gq;_PH|6X~4*5e4AmtVg=YbPTXF+zkb)L8dh!~hXXVHPA?b*%4P z0*83Ze8P#gDCaac^9xnLRINEDY5{69h}D|2ae&?Wt}oVCR_MPxA@lVpfy&@Vp2T!oKLWbji7TD= z_7TwDNLgnTFmZk#qNK zAg`wd<_Xd?RMry(gOs!_N-M-dM;!%i*m;0UaXJ8n3>=ZzCq(=!kqJVBmVU4}*jPW*coI9n_zGrekQ={s&>NF2 zwO~!}YqD4GHzoQcqptrYp`$9d?ibGWcAM)w1sAMgcl078Udv?QV$#9FrYy)zAj4;R zQEgi$C!=T_Rhk#Zs>u|a8Vs{d)<`$We9gEI=aXx3Rd@?*j?EXhK!^kU%=4`HXWczP zsGM(T#>|}W0lDY!ZZFeLp>kz=sWNgNDfG5=QC{P4ysD+aW1>ggEF`lVbI8V zGKa)h^xvIqhTcH*owW7qR=W7CYltXH{IbqM+Uq1yaEMDOrAi7&ps|B$Eci`$?hD}d zELXJ*UIk8{LDzMR&w+bz8FUyW` zBavNmfoY1>s&);_@r$yCVT#ek4(wkeilk;-P|qJ>l>i)+*_ev^fais}q(RY8h3*bE8Bfd^f)%dOQY zooj`OMRFWHP$&}? zP#?lP?L!9RjH@$Cz!;T*N3L3v#?8hm7Rs9l2k1J~B@6fm9OMcK83SQPkk;p`8}Br7 zotYd-@g~1>zmkOnZwytjAws_I%0dQnN+X7~yRe9xx91CJR@Lb>2#t ztefnZ&@rC{L`M?Nc<#N4%5LQAMhQsE(u4VksPcO9^f}e3m|i=Mh?`H72aArhPF92q zr5HF8QewP-q$6eVt(V54EK-PddZ{mJhG+WGOKVZex&^u?aoMB9<%|-Si8;ro8+uSh zZs6M5Yoq!gDm71i5S3uJ<5(QnHsUmtZlrQ+HR?e%%sgEa9)@MBvBMh!8^Uq(YLrR5 z$F09ZI|h@nYzgkrIifZh;fR46<#=9c)YA)}4twDh+ODm_u*O8x2|1p(sonER5h-!3 ze+c+E(JFCH1aNWle6HzhU$9Jp3?dD>ryEmdTG-H}aH$lvz+a3d9RXj<#3I__p5VOC+ z(H5FT5p_(5y3d}$Y17f`fOT@kz6v3yT>^7<=qR6Tf!gBPk3w3WMA48Jr3d8O;Y;@< z&UaO6sZe4GNND!uQC+1L-&R_G;0>#nBmecU!aXc_A%RHTfoazLm?EAyTqUgTxjE5;@*JH`{so{kDn z6ij}!3YJs(aUryHRuP8Cj%8!EQn3}wz!;}KRuGn9(BX``6NG;oIIt~W2+ADCEJm!q z+I$CNZ{7(dh?UqO>GXKVQb&9bc3eruF;M-u0){QiuuHh;UEiku*2q>O7S$_PEJI~bF8t1MCG3IjYY+)-F>TwctAJf~ zt^p1|>q``Cz~UW9dMw1gkISP}OkZPrRI<{}MMj;* zb_C|#XT7nlk;oA48{3h_c5Guiva#(cCeNqRHMTt^<}^q78r$L^B_Ju@jqRv3wolxs z4Z)Z5myiBDuXzPm3a{Xfl#MK!Qv8;+B%V&Mz|~pK)^!6?>I4>Yyt6&U$7sQawvl}m zd6ZsY8Kkq{MlWy`Ud(cfQ@6d`5ZgTylcUT`LcM|nvo3(`MY#8*PAfSrxmS4o%1dxK^BQ=*!dx> zqEM3s(9mmzXUecGt~#wAi8vOfwp9yLnTt3c$|p?#;S!=8%VE0BBGiz(@f-hc*IKO# zS$*SwpPi1gU z2&P$XxSU7$)R`E@Xr-CD(?K;_6yRiO)jGhNleYCn*L!(fZ}fV$YLd9YCy>s$f}a;m z==bn6K|IZ&L7iGOjm|3G1PwaJqKUb>uz+R(z3%hF(DP`>^Y|2$W1blS2cwP*N7TbK z76RxgB4{q}c+TWKn=SA@T=Y5JNGZ4$=IQDn_520qgT#vL71k@JSHM^OQ~X2o7JbDu z`@GO9%u=@PReYzo~pyXZ}qkvo54}~Bv zT#r0kO!d`GTMQMw`e}*9^kAS&i`f7UD5eYt5FnVlC8L%hckYsfx9-K|k`KdM|@ z=UcKC>rgMW8%EOJYSoGEhMBZj)8^f>49Zc@8k+as{DdIHdyoF;G~PFutIlZhwj$j$mzqM?736fut`Mh2b_Em~WGV2{ zEBD3}5;{%rfYT0FoyNH4wa5v17MucmLXAN6jM{lRy1z1~2WExo=#k1?_VhE))NlaL zw%nc$X4Qj8SJTo+Pe;eeIGBC3_U8TBBcH-29=%z%{r3IY)9mo!g`FA)l_4 zNoPJ8olnN*lkxdviZAA^h*urJB@?f6Y#q2uHTB?3aXr`xqzAtL)6tn&?&Z_b<8c?S z|LN$77!P-zPsX^o^L#4q;?J9Lx4C(DOX!}CJ{x23_$;diOj$z&&%59_!KYlXL2%Ut z#|S>@f=zFDX0_V8S& z&^(@ZS6qvhDy~KMR$PmYRn#KoxUZ6q8F?jRbLse7XcUnbVj|_3lSKHi=FX+Au@NYJ z%MDN^-;e6cXE32Un(x#;}Nm*7(#f^>xds@R%8l>E> zln8pYVI*Wk5K=@6voZx#Rgjn_kXZcn_ zv@>U60?nQXIL!)=nk&o)O4hQKid>)`s5Cb+6JmS}_XHsfK+h4TO2lA>7YaA!DC%7_4%oT>1YL1gCtaiV#reDRv{93v#9~>!1Ldm_b#s~`CNRi1@ zP9tXWvzTgoR*CRIyn z`=mj(O!=f8N!yZaFzlR9x9?9im#D6U&Jh@O|J4q*VJRurJF`mNQaR+?y@q0j_+ZR~ z_@p|I|9Mg)lP4*4GGD!F9_B;%w0QZ`K)acDJOf_Eeb0uN)L}5BD=CM%lyR{_8?E z$Rx4*;Poh|*zK?68UO9SFXWEI3~5CbLF~G7IfL>U|KCZjWUboaKF~+aXLAZ z`lM;<(hSKsG}p3Lto(XbyLmA1AR$ql+VsC+ukI9Q_&X%r=gv7lLJdBWLX^~@RoI~L ztWb)KcRJj!qw2sbr0KepE5QP8p!67A`h>&a(MrAOSiiqNLW2p?!VDEyw;9dkf8uv% zLynGAr@3Q4rRmi4$Q+i`Zk|f$3_Ox};q|psxILI=XyE2S*ce$~>&~fUZ$dJ_gvN=S zuFPZruLzs>NN~*|3+l0_NQ z`mC;$!S$JsY&w-Mk#@>6AG5z!y%uM}UFCIC*}`4(jx2Z;l)_|xjarfRY82$ImLz7| zyQ_sg@yb-xqEgfv8lu)1`npveC&-S$?CjD?(Af(p)og(?P4u>vH&N!Qe<6WXSWe2W z0g%bx0BEH=xs+6d)&cUomJZRgR|qW@k}G{kuIv|zb;DwX;|RI0FXpQpiXnL{h1r7| zSf2@7ZC=IV!_NN|wmz};KE#=vB=lT+%NIL~7<@K-hZ$65@q8$U=={cbnPvn?VnWz?0sMpNS5SxPI4P7O=Ler z@22`t2S7%gEE^_)j&Vf}4xatyXMH4;uD>h2C6Ry|;NcR9TEp08wxtPSC+4Wr?Sm+XaxbmK0x8c{{NDQV0!Q<~;0X*wy*Qw;;Mp}#T_i99WN#ibXNl_ zdzye%9q)k5d)NAn-pxBB$hvt?2r9t2x&*deq%5#)g>o64KvNx*8*~F{j7&$pDcEz@ z#b)pSuQUVh(xcz!(u`lx*L0}5AiYFO>^5h$1k|W==<&&{GH-1a!q;78>Y&}2ph$~a zuo6pMf6ub07ne9TkA-_>Uce!a!azKQ?@d>o3o8h+2yn+;O6Xxz!csT z+JsIU+_CJydzZjtD@Fkxc;lu}+R%TK<1yvKFjM5ajArb*WHY$l*)@YLH>GBgv*UpdJyZ);!N_pEsynLG3_=F_u7uPZ+4kiXK!#n}Ax;U8?Iq{o-!` zYWXhK=M5^?WIIs7V+m9ya|0C!Jg9^=0X5>1ZATpcMc^UJ6`c-o_*?EPczG5yzN&oX zOJJXOGx~Y4n}3YTjr~F8LD3LG8#ZJRNG)Z9!Cx@IjnZI%n|_BdziC=Nw{1`+UC$(B z%;wixm&a_JR6)zqcu&AQ)6Zez{?WPg5jx;GGMTFxorcXxO?p<5IBlrO&@N^VNe&0; zWYLi=a9+(_#g2|35Kl**RQzPvBb8SUwse6pth^u8r+@pa4Jwz%J5Z^u1Zs~T=Afc{ zDN#xp)P`p~y@xp|26##QZi#ukmo9*Yj|XUfXmEb+c#0 zqnFSoFxTMW)^Nccmf`+g!OaB-#jw3J+y?G7fNPSDsKI7m6OBAIXtrQFDWjM9+&GG< zfo!@lF{coVdbK0WxhZ80C45j|;w%4Lz`Qh8ZAuYV{gmRdYV886ae-Arn_x9|Q_31p z|KnFcwAWse0BlMTsD4WEpoSi+VF4PX+H?2ZHlDrFaQefbX%v2TLEU)36Pvd;6=#DiI6B0Z=OPa9FPF%B-TpG z6(JSoTK6?2M{`4!02vcxsn#^KbxU3Kk;)>;;ye0o0NHK4m%tj=ec?(>5CAG!taXjn z8GN6r#cDRJ6cu{{z5tv{;REUNN@Zd{xMQATzi)|Q_& z^QU$3Z%|2Q>zjN{9%<}b+G(Yg1sYUeISK!~(#n!BY3dcmgcFKAsTGw~DlRw;M}A->HQsIF&) zHb((gh%Ihy((vH^%;(`5&klB5^{R2fuj8=%sNLJZvu)JQ^_CH{yo zS(cK)cs{`IQJPKcywl;ziiqFwehA$%efi}F&~ z;6XG|@MsiKQz_4^{yLtrbL_m`bg>#g40LIznW)wIAi<3-`rfoCq5`g_0V(LVv`X&$ zr0adjV^9IwV#tdk7!)d^qLCH(!5d^coaPR$i%IBThDK*fYfx)R>$gH1CK1I{W^6#* z<%R`-2H#5QbXwitf>TK=lo3kbVd636n_^XjufCP$n$r-PQV~ikiSErbYf{NYjt_*I z5)8%4*iugL?O>+SI>y5qVdaVi=p71C3JTi5)I+^E%cfq@SFItnKFl6{4CUOX!@k$Q z2wT4wR&o{YW_@O)xM826{G`vS2c@5sRlIX^79Z>mfHv2Ab>DkrxpW7jn*8c^;Kw&@ z7LZB>{91_7H;f1oqx8-qMxL1(hGF&1G%U`3^fwO_*( zH_wGKf89~0>#Nx5T%a!~j1i_r`4Ps@dA1P-Y12ArWKwgK+M%hrOr`>jFx?QQLB8<_i`@u&Pkl0{J9~Qb0bv*Fy~}cBH`mbSvjM8? zT+sxu{sJGFYVfw9Ut3x0b#BB0vTsaUwe*Mt_AJ6J(A~d`FKANBJ?**|Rt&O!V1a1z z?0gCfgUA}w2+t-@i*k#Gw5=PoAe^~ZGp0GN)tAt=5+Z)nQ3g4V96H{Q`JO@$3=XE_ zid7smc5pw!0tYI^0nQ>EbRxeHFM;56q+^(b#Vsn5j2LCoVHDE1R>-i<7bmaaIHV08Wz~Wh)|nMRP_QN4o?t9&Zz|thgh&xU+da3gX-u%ZCpX zRxa{EE=lcW#BrUNJj88F>Hsyf;Sg(v;wQ1Ay0e1^fRcNJL3zO8`&(SQ)!GqCvE$)| zh14U!?rEd|oq3~j@OwzM$;4*g4`2(nv;N%q;REJ2P_8gk;M)8b>$($%D zPQsUk4>NvsH{K}?k9r!mP*~1qV)FIaT?4;;RK1sMVxgbtWH$*pT ziC`HP6kEL^!U<1Zj}M=W(zyBQ4NpLcF?b}>bq~9R6z_Qbr~$s=`?Hql+>W|s1@~@S zFsVu(g%P!L!t60t5gv@H0lz2JaEp7qh6m#ZBD*2Fl`xgy5}AnddNz99>-H@hR}EXD z>Ic-8#tqSooAwL>OK}irE=&*=X7mIE5GN3BD|qm@j+2j| zC}P3%(1{)^@f(S3Rc!y>O_;D+d4{|=e4t5&TkI(ms9=S$A1uUXv08o>N6hngWB zsWKLcIo{hkr2E>4DQ)YKSDen>_9O~jb`;duvPUHy7_u>$sG;3l_{IA@3XUXfbV< z1h+Yb|BIp{bs(cW%vRxCrohldIL+(?GHud@Tp?*D6XsqQp*b3=4~V-7hUh}fyqc(O z5`{Mxhr-JCUYsdy3mTpD=2$Zfcvs2mdmrQ<#o8-gt1*2$Jm_AJSkxj5-%%$l+8sz&QvDiJWn?My<3 z82>tfZy0Q?7x+5NF33qvyc|UWK(NF^O|zEdF#Lr1NO|Nb>;ykNbX5m&9OSl<;3sLn z=>#4|uoDJF-Ch_3=`N0fxcVm$5isrb1ivu}P<84^JI>9#F2Qfi-P&B&yt~Z_fpyKT zcsOXfD8o9PW@4s z^9!fprJALOVU|ve-b4cQ5-tPDp}}EDK2j#8=R%;Jr`%5LYFkiZ(*H0B(z<~I)=Vsu zD#E66{HGML7P&OQAz4HzQ;lEddnRlXEm?puOc|qS08F%e^!H3k{45WX662C+ z`LvJB7L@7hvxoEdb)qE@`B@E8(uO=IlyNw!0A!%b<44|UUfhhBdXg661yIKGX~6Wt zb;l;e!P{-8r9=|kh$0i>5Pm$HP%5<{f=F8f;RhzfwX+z9H4<=X3Nwz!oa^e%6)ABw zif4Ftaw>x@i|6_ZX4sfKs-$Rbk`q;x6s}2fFgigqhf#VWw`w((w?C^mQMH!c#l6a4 z0dhgli|C9{t#=cut0q*lWRy@{0a)&EHHc~n;vhmbYe0_z2PyHCv?j?xsw6K|7hefd z#e1Q;h!?P0sA?&j1`blYyp|#kCRHa?hk{2Xkk4vlfm!YkK|)jL7FMm+A9XKO|4n^- z4X^AMtMfNFC&Kmh_~D8{!WiMP zX9z$h*fKnH)i1ZeCL_i(Tb#oy7d5gwme{bt%f*<5_}vn3(Ov1VD|nq)MoX**y>v;G zZ;3NuDbp02=i*Hqp=s~96Z0;)9ltx1*o(D)&9Y4x&t+<)K8a(2n4iFMvzfhU0SUhm ztCYiPGM>NaupL`aPOyhVEQ0${sLB=v8h9L7W$Z<}i0lq_H#cB`v?QQ3_R~{7aJ{Sh zKr3#r=QjP+C|$f{h?3zZl*|}sCx%;5N@`~CQxyb*90h<{SZa|$PqI+aFmGy@Hzh?X zS7pCMxZW^lbz@}3qNPk$EKuZ@YqC<)kyX-|V|=>7&PC{Mu-E6%LFD{HgI%{M4EXUw zEAqk4l}=VXN(CEQO7erfm*j?!lKfyVl0;X;8>B^~#t%nekzMl5e%WudF2xyA6li7q zVhA4Ch$3fEp)%)`L*kU6)Nw^&byZkp*RwD-$gq@~uMjt2qz>Zn+85%)A7m5Kt_1^! zlgO-P6`(-gr(TU}x4<)aaQzp51Qn!bI;>7A{{4~q*k$mZg7Y+4)x#$c(v9Vq@}tLx+7^jS zGHeP!5LIS0%(mh%TBQ#ghiQt#$hS7Y@=XIqKR=BoX&e3#%@{(L=|utopoGrn34P?X?hOB zJ}cueZR0S~b9h-q0tSVP!{Cu2$&e5%k+$1Z=VTS}AqNxC*ay1xko9ETAqa}Lus4x`OQd}~-;t*|oS7++ybS|K4#YI?55IEZt5KU+raYoKtYLK$hX zxQmR=rPEhcP92?ouR)yAW?+ z7LnpEoY1-!V{N#K<1S!U?4%=h(#+Y(8t(DOY+iGBAoN)b_zVe}W6R-8Hx_$Jh`K3l zpe4aj#?hoShKUNc2W?{TK{!QNnUxhStB5_n6l)+D%(xSrwEzL`^6mGi~robSybH@H~J zE%Q_%@X%xFioqBFkLadMlraK}uQ!CGraONGEwx}*(ZxemtsFr;^PP|fKGY%<_JY_B zl4m|KsKGWe<8084ZTk+%97a#S-h>5IkkL&fRa&$xWDlVB>OiO@SYN>$ioSw19O$Br zQwii#H(5UQRFO|T}c3vR^+}S1vY%>wFXGW_+(=~?T!>GtA&R=FQlRDbnuGBcO6vcajmQ<|F?@)Wu8n#o(0Y}kAZK(q?J8inbk$m|R zWh)l3tru~|BD(u&jcAc}T^F#dUy{*_deNwy0>(Xn9cBZs%|OG!>_zjhJRpn$tY$1y z>%eTN%qj=6AVcK)Q&u=!*w93s#PeZJ6?8YpMA<5Fl%73UiaSSKleh;;aTkb_cIA;$ z+!u*M?T~$#xZI|BuA#FPqU>W)Zk>q$0L?OeOut6=bH3JD3MU*qrZ5U#vV$5}ooSnR z126tf;DVGKAK5~qr@M@@xASI3AK@xHujQ2gj+-cW%WsR}4hkVQPZNEeg64%yWQvAJYdU(g!Fb?#vcY7+^Hift$sFX+ zkR>@=hAY7$qp#aSJ7oMb%Vk`e!YjCbUA+aRs>31qTV<=YrZ%eXk2@OZD9@s9o3#1gE1fc)&mF0e{K^{$vB=JLzW|U3{B3 z_@U9=rsstQB0A6WzTvBei0l-Qfp9hKVj~^*4Lg@N>@>*|)CG+ivcfw{5H=FK_cOe+ zjNy=qykXl2J?Ma)BzV#Vw-S8V1%-o0Tu?}O)CGlv#|i4nxI$we=?c5j2w3K=Hv*3H z);9u9qLa-zFZWOyv7ob|l%^f8A}ll=c0Sv5-F&|3y8Wr9BZJkZ!`hQghqZG}hqbfK zuJ!mAn%xP*Y7>=;ie3N#=wNm5$(2M{z}3UJ;6#mPcb8$k+1;Th^+55{5%r{cQqQ$| zQqQ!W)bk=esb`O#Pc*w&SjY}rB&_lOn zd^DNLnMnp`Op^RfEMx)1!dkvA*)o^xNhb2MTt>9%o~>4_&R&Gy3j@~|~Zu zTha4FT;L1`l4*{lAd^M2-#mb{4kXtm+c4;qBbx`3G?|=3I4|Yo44Cc7kPeC|MGhTE zb|k~=PlVZ(46Hc{X5fG{=yPaAN}sp#Gg(HowVmDbItPG(%b6@J%sG<mj&Yqr(zWw3o?`xHsGX}b-Bn+o zePa^d&LJhQJfNMGd*FhONpWW=JqDAC#H4g)F0z45fls)gIN@0r)O7W@3yKwxZ;9cf=qWRY)bX5Mj8#-lP=2G1=o}syLd2ignu$(Jr5`kM2_&6BM6`O`y)e*o-JXR=&h)sN);nFoHBb?ffI{X7!Fzwi%;9(mlXD(?iY?&SazUR= zgf^ykbJ~o}Pnv8nl?t`S?m2pf_>#2MFc;{i{2XOva#JAOOO`7#G7dlhRr+kqXHue! z?+dhOJoA)lmON(ubKF9h9GzuJ=5~s00`9`6*MU2|A-Fn)e{*QH0J;ol1?I%FL5uHm z6Sl6+`SWUn@$83a2Z7=Fhxi?`_`5LX=#}STx*=y9!GptO*E}mDy*)#o5ZY+Y+(DLaWYyFxuJ| zh%9v*nHK=!S~LFeerlVC_eh`*=k?> zf{PEob2jdazk^mEXAwxUE|ow=<1nFYl^A3NUAQ-Ufg1Vu3T_AL!KJiQ<+SSTOO|wa z0Uw{^<8kgXa^KMiWj?t{W|@O0)@EMbB(vEe;kT*02?PnSu8>T z$~mU*Y=x4qWsy#yI`=i*!R!{>+ga0O?y#?WrCcNyUqoN#GDl6sOlSl4#Tni-Jlyve zcr%^8bijL8Np?L2RM0F|RxK^g#w~3L#fi@F zaP^(y4I{1pPC`!aJESSqo_)Q2hwLrL@^EOr#lsqTxnFL_4(K=_Mv2)V~cDS0;|q{qpDsy*F3Vw9lY+IX}BBt$IXn2s*&W? z=8>eb$PMZFWwGbUFyXGI#?;J>?Jh(6NZNK(c!3OCj5JbqUvm4m%h0epJmG-}dQkK| z4WQ6d)mM(BJ54nJ&2g;XmZSm1NMOezlaUIj8$I%998McO3hKqT4|BgEbaG@->Q|he zxd6;v1t)Uj!qA*$C$+|#}fIGlxVm|`8z$;7kSZ~ zZa|0$7nFk7om7oZWHi~Kg9{NFv-h)b)4<|B>OfP*dwBOZf!q5D-ZePr5PSbA-pw7S zZSNkrC@KD3lyI<*u&~u#us!K+?cJ8Ro|2~!>z2`6;)HuM;G2>R(LQBJ7$qSPNj(<= zkN?RgLZFnUP9ULL_9LJdopK%dZ@4SFM}56M(5fm0xf>#Vt2I*n4CWzGBU9)^?NLu{ z*#EkWUM1Q`)&=Pm`h)|;_}lOkb8UheN}pV%PqwYArOe$>SoT*V#W~ciYD8C>UQ5+E z($O8`pgc^h8F6Bu4J1|_2aH+?T8Gy@ zZXq@n;0n4(hVfZn>IGgV@|W|xZ1FE80WQ96Z+x2hg0sBhRi4=r{!00R0;$Nj3?5i*SD6xfFM=u=luom7d4r?$vspjJq$;b2;u_L-7cw zKSCvvPT#2~;_0!dN2~Pq3pe5oa2(?!EEP5+4xdtwr=wuu$P3$9zTK8x%5Wz zLQhwve22P~9*8eEoW5Wq5U)9$UZX+OePMFVM(I}{POrA~Ym%!sO26uGdX=SLom^E) zFNVVvp6P<#7kCiu_D#of4+;ATi+~^HcXoQ|MAFqMl`L;|vobm1G>P=p2x|!LS+@gl z7$DItEOV-Rqu21L?Tu!5wD-!}6~4t4c9>!IM$o2f+69-fH;nC7L3DB1g=;UzSAX|kvBitE9g^`v{=PkmW@M#ecI63eu&$A>i+#4Y$2gx;i z<#!(>SMQCm(FTm+2#;p29o-u(>si`h#@mQFYiKoLx8;_(dk~^FCd*<`hmfA#%ZXscqVBia?jXRztb_4JmbISorZCz> zGtrF9EqHsZHQcaTlP@TvpK)(yo0)#A4N3CBN4W!<#Y<*vr@V8wp`*0E-pGH?VhXV$Q%>FN6-s#%H*>CsWd4c5>@tD+08(M<8ZQh8aD{-`4r__G;$Q-T z@XVOQ8609Cu6!1-Ys+T^u|fH$AvP=@^ALsgRf8!KRIW3BJu4P%9O(E8s*j51$*isk z#>_$SzTg!(vXXdcQ2%27M=iO8Z3V)Y1I<#02hRpgtj=Ph?u+6h@=*hI;5*{lu(flQOPbJS*C^%pU9ClObAWI zhRDKdsA`@CSqzG2(^yq?_LV6i_@z5@`8%s-LZN!%V zoy)&sL);K`mX%cY&2v*yIllXxTFQ=dd6Cq+;N0#2eQuain`xhYTA-I^ii~LAo8&8@ z28(i_rW>K58(mT&sqT6;{>AQ)$jyPpdaasagTHt67Dxg`k*zR zIm}^OGiOEiKZfbPASB-S8>c|fMr|LV>>xusSal)XEh{JqiDNl2AWz#Mn{ZQ+!-nv~ zT(m1_{V!q2Wa0J0<`B)|Kr|@!BrURB-PUphG?wd$+%Fb#H8KSZGyC83P~~x|*z~WM z!^q*>8C>38xEJb`vQb>_+|YaWause41sqCKZ2s5#>Qc4jx$~0OKHay<)hlK5kjs^q zxp}@zrCh3}91d4Quf0Wi$PK3=Zn(m|WDx$*-9yAG=efs3Fk!wN=_^ixmm_`Iy`B6{ z?(e)mlyr_*eju93|L|XkE=KA5!Yq2Vycsf?FS&0O-vYNLU`Lt7ur4WLkZZ`ZT8de+ z7?wRno&}3JTZ*}e2*oMxr5Kc)#M~WwG6iJdTVZW+0Z0i)PZM4xoPE-A{$QN5TyB@J znB(PduM6(M9q>u_ZF~&Bsp6uN!g@3Aj?L6I;>8k$PSJ3k!| zB7Pdr?`75fRJ_0Y(+Zp+@b4{vc9@14-)k~ zJePU?uRQPLd64I$JlWxzogPHeJJ9_(eSTkifA`<=e4O%h1p0@`^L8cNPeq2(V-3sw z^NM^gk^d9VF9F~`=XrW)fA>KpoFxK1efAS%xJMCB5b-TMkI<(7-P|<0-^J@EiGE1w z_YwaK3S1=S4xTH~{_cNL1Y@SlPGZ0(@B_GO;;+>G-*`Uv5tYTef;-^E(@b+g=iBGC-V z*JGpdGBsKpp>zAb;Fk$h#q&r`cgXaa*9qS#Bo^yKBle- zPISAoRbb{!sS-SHE~M9SK%I`3>g+S}6X0<;`n7(gj~Wc>wuqi@T(&^O9SBzi`@Ddy z_ZNEN7BF6GX-fj^4hvpHGe#a;Bb+xa8HDlz5d;MCO@iUxdiO>>kJWW$41N{$?v;9; zs&`+m=Y#bwqYLM+diR_4e4viHjOTK_o6h22WerTGOiegtqlYCJT=iw)#U`WTF2+&U z4h>-APq0<7KtvqJ$+)pq$>g6o$*~MLWB%)ev9$$~=P}W}j?FyyWbV>q5V|L_4K(D8 z0iN`-WIc=m955N&5}xMp71hWKqmXNM&4?sfd%#s8P`{}m*C z6VI1p(^j8qNM3BdEc}RIhm)KYg1?FDpLp(ORCoUmo4aiu7bl>7a%}Gd$vBn+so+?E zR7~4g)3sCsvy&;!#e>=Kl+V;~OT$vcX?e_jT6^$dKEt+o(5AD-D+h;x8E`eFEl`vW zNO;=NeM5Ay83T`(8(>J{?qWYWgFE}RmK?*s3*o<*UXO9-Mm_l!$gMbb8XiU6biRGy?x+f0N`C!wM0e{|<=% z4(tm~waLYK`k+eMjwk4j%;`APLyCG*PtaiypzXM~t@ z9IBAxgr&73Vr>^d;e4ldY0~6275r52yf(~3?JJzmWB^r-B7o^u%1!FtMfO&Gx@5=r z*cnA16U^V6)6Z`SvEQq#p{hj`Yj@#jgo5A3Z1rREJR@;wmpcwVE!SCG{RL}>dI$rRYf42W`{*D(X|fltgR}QNdwBWf+4~7HKK0Tn6j0% zc2fwRN`9IhDrvw(`&MOTr1cQjbv5~t=R#{BskEp*!8(dmF4Kml3Yss226*|g1{h*8 z!YHygR3Zd1K?E$!HlKko6=TS-!5x`&B4wZ9&kZE;46AjFaj|gcycmjRGtEa@30UtU zw}{#;=Hpy$qSix@vZl_5TC{*#j?ON8 z`I%>i7n9+;lM!=yn$Ss2_3juWJmP;vOm!g9fEnQOlX`S#cKWTqroAt>RA#a>1ZP=( z1+yVzW<_tv@quNr(5#N@vjn~%i#{Nv+8I8gC`)AE$Z3Vh?4+Hg^Pr{#I{G9}>86)? zswa-CZ^!&16oHbLxoqHe&P@n9A7EPHYLf%qhA8osgF!OpwwN+GrL3KFgkeSN;A-*^ zq21vYbsmyGGqw6oTrvofYlWbE+I~nAHW63q`WknhQM51kBMliHOynmO39~jnk2lg6 zP@^;1szdk{Be#!k0MPi$LCM`oW17H8l3w!4>(Ew3zu zcXp@1|5YNq)mrxk(F!i$*hWsIbaFHk&m`lJUS~cX66k4=OdVlGdDjt;Srl@3adshp zxofdIEHOP9rt1=Qe|WSn8J#Z9bjejTIlYPvwxYn{@9%!AdgpL5%;#_}+`W=-J(0z? zCqwM#=5(cl?D^S4{q4MsI1#IjYz)!tYBMZbDCdTv5dZn)yTAY&a>hGv9hHWlG|h0G8wi_1L5)?zn{_c^vKm?goUr zGLBMVhXGIlzYJ_`!4o|&Q|ma6O}=1ci-FNa4>ZU({QZU?GKK-wx>^y0_>udWum(=G)&V6TULqb~$|4`viH zIm}t`8o<~PUTx^pv2V%93ASqiiPWFEJ*+;*g@m?w-l8B-5r`%TW0!G;tCo!$@lPzw zFD_mfW=&k_Ii>@g-P=y*GN?1&vmy|U6aR{%^KPr38NLi?!MmR z?M6)wukTJ6$9$>wMtr|g?r&(lh3{`e>o2U!jr4UVBsup)uSaq!E{tya;7m;g#xKvIVf_ZR;aia`{P$8lS&M$i$c-lb8xP^X zbi^$USEUeVOhd&LodCw|R48I3`*|Ie;kl#+Gl6Ek2A~k}8?ZXN?Q;jD<%Ygz02}Dk zo~^K93L7&B3PlZMdnO|UgrcvrT5akUKob^LpVzlk z22cSCL~#;M-mT{BiBh=MeSP4n90*BoBZdupjnuHa2J+z`6I?_x2}jd|;JPW(!zKGD zs!=qM?n`)Vih3#XrfyG_DXLQBjonrRcEZgvCj_6yLq<4D$rqYBVDo=dHdA}qZ*Xyu z?*E#2BBqs-Nt}@P45enh-1=h!zx{nN#E+v%oG5@Dek`u+lF_9bLn72h|vsCAD1_kUZ6lORH=~y|q zJNuSc1|MvxEN7R+{?|>zfiYBsC8Ee^c-Gm#^sz=J5>*1z2j|XRjD?rRn^cXlnOKha zB+2mQFKxI@JLB^puRlFFTI6urT-L6nf+)cZf_wq_lzib3jnIAx(j8UgqO zX%!D?%tZ=m6+v1vK$@b!N)N-<&mgUmL0Tn)G>g@0B8UZXML6n$w63Vcj%ir6XE4gkc51-U6&;w?}j{~!jOuxmI01k--|?#-tCK=-MGX(xz& z`TkuDG{I2wFN%>1K|x}yvS<4_ zUxtIXbS7f_S%_3oqBCZkeSeK%(OC-t_Zkxe9>?lSLf{GBw=!4=!d|qw@J8V@qQjUf z%lB5QEXdA=Xa?>VhE}u{oNTk`CpMV^-Q~b~GY=gE8n7kE(dJ4z5?FTksvvV{z5c8) zRHzIgP0e$^;Wn#x8b&e==6yZASNrXg(qf@P^dgA&nlf*$nnTP9Lgz`>&qU0TU;IH6 z{Bt-Ps81L%r^&~vU$Z9KEA6yOhzlRknq*+(GlGs^h)p`lWv&ToW2^+rF#UdVH@;`Y zf4W=LwC017OC_-yJ;IhNP)NYlu2z>6ja^J5ca68|#?T7HEr^K%Piv=v8lxQDoq|u$ zuofvA>Yd?%=o^y_qrZ1a35T!>glv|~%2FuV*T;Mp6DPh2Rajl&bp|6`=!laQMKPz; z4Fw1Y^(GGsI}k}tf-M*f&_rh85T>xv3wh^xmqRd7$w8nKLrs5Br5@~;Xz7;*?Md81 znOe2uwdF*Yy9htcg5`9l&;6=B>R>=j|C?VqP& zzgwRtMNBnx*EC`2&Mkp*qYtU?^UsPts1(iFUZuBT+xGfY(}&$w$SK73_G{Nl9Ag?% zi3J*z-&q~?*d#o*r!kXQ^J+&^kF81a>M@B@#Ki1Uj}cOz;!a3?l=E}dV?Lz*vDIT7 zoBJ=S$M7G?smJbYz;OZnQ(ir`gWALnTu6n2|_GG?N2}!yn5nrwkw9&JcVX z4|Sr?K>;l+rSV#R!}t#b9*X@&WITd`1|Dj%k&!d{L4WFO7Ael4R`Z8OUVg0wz4bb7 zlxah+JTac{4IZ_vk)RX&SsOhE5VfouJ*W6#Woc{@Cse@!r@{K;`D_e|8oFOt2m!p| zWlGydFqM4Fv}RIck>|fFuchsvN?W;mH{Ufa8prH1Et(^?$~tQdDQe40;#Qzu`Uqz3 zUR^ZH0jjHnrB3lS0>p(8;w54IUaC90>dBTJ*b7O9I|0tXm$OOYNG))7&lbL|8UIl|Zh(2k{ z71F?t0*L3 z+4@j;GLN9Cf$;cv9_ikx;Zebka^KRN<-QX0m}`GMkGYlyc+9nYDv!CAfleds&0q|S z-1f`?Kh7NP*Sl4rCj%KOIyFN@whx7hSPB6PYbRhKsF3zim3<_QTa5`l_>UYJ)Wygg z81oO@i|iqiHPJe#+9ejfq(}%J6_21ET&C0?af1CA7$EW(UxTZ~W+5ie;)V zIl={3AyxX378T@>;)~dUMH~fTmaRiO*kiPzN^A-=Y)`g6E2a__Bdb@1O!GUxR)#O> ztBR)?glDp6h9pFjJu@WZiqiJkx9Y=yEg$DmE>&a&x1M{S ztfc5xDKr=ZYOZO3Dxiegh}zORh-(kJ>by6GI;x>2AptF*!qNwKeOvnK zSo-R^vgp-1hGAqC3MPqRsZdinIg?TGtBw+bCy4xKq>xLb=1~U^efcV&;38EpKXx5* zQG(m-tmLD*H!Iuwf_nILlba|v|BrRDE$dqj_ql)~UcnNJjVu{9a&^Kv(BeT@NX4-X z%X1PB*;;MpA%i_WYIWezjWBZu&W6a*t2&+%#?o3_C+FKbIp5aF`L;ZFskT`BUy=<4VY3KkslR+a)h6M%{K1a`tPu5`menSn$bSun_SS!2#@jEZE-IF72W0 z@Is+xULh<0dNg;1c>%oVp_4-5Rlt`R^QGm(dk`0qMtGa=pvV$uh#2h@N zP^Qb1(L(`fiuADT0g5@^14AvXBy&v^P!IPdW3v~yRBSyhlSnGblHm=J1f~jyIwU&P z$+d>1^g^W4ERu`dvrMEAhxsf)wMkXaq8@P)tqiM0T|U##qCJO2AJ$%cpG0HROk#1} z+nZ5BL_xs?o`Swg6eJ_pC zwmu;4Sq1w(8eH)+z8j3vaGQ1BzF|CxNuOsR-!**4<;I}V+puii^w60VaghX=U`(t^ zQL|TvGzxsp2k>M=s3P_hyNV?5$CiG$^rHv?X~Ck2LJZvp)~ym2A+7w6G`#U=;c zXph7Ao8s?8Cvnotp(v{ySh-@MY3<${9k(#*uSl>EXl4`vKNWQ3dF#CNBbVDC63N## z$V3OpBBP3byPquTqrJp6ix6W<+kJoituEllVZc6g8l$G}uc*bn+I7<&XX0YCi0ulK zv{wDc0-t>5spsnU2!E-NYVY`@(?Qtu^y4jyr5|av3BXm!oGk zCNMl>Y)L&z;5f!n+zxRiUT#@MEw6TIDO|r8IyfE+_#GPa?=1dIGUZNKC!fm{|;6@7pslD1Xy`%cKQ-VH?60bXE zYnI&m3ueAD%=rFHbMz6ZYO%oH$btTkl9$>#E}{U^b$k{Vl`<4sZF$6L_KW5_BM_6= z$ndjplco}>Bw0L4GfZ(p%sM6HFCvGl4ci2slYa;kDBm}T!i7YnLVZnsur#0=MYUb# z&|AQ%c6LfgI(vq285J-R>{ZGjWA%!IduyVU7y>!qPdmL(d`4eny13|8&_M|2c^af9 z#}~0GxZ`?jCA#l}1l>h>ZFALI0&0{qT2{^Yx%CWG9MZ>_eA*L!m<- z)C1vroC$3~70D=G&0owPJ2~!QzNc3r>~{AXy4r3?MheR4>d-aP+gOZKx>QYO7fPgH zb#PBdZkb(}cTX3wk}Ih$IJASBVgS+jNQDHqnwSTXe3SK{Fiz&3Y|&;li?YU{y>N6u zSkL1$xsA4$imj`oCbnj3!$TYw7Jl@$jid}my%dwXJm$u#VwdsjlA0POR_CPQzjRI- zPEco-t9A^5(&XWJ!|@W*{VNb=NGFzaf5*;vJ=tp)4aX;uz7mD`0O@thhU0a`?IoB- z`yZo<`dMx-06-zcfg#MgUt|k{VIc2|hKjf-9s0Ke9OVZVfCp+dR9|5af8 zq+Z7rO>a_-De%<>q5?j5sz#h?#^9?dc(DN;6`jNZJPj)Gpw%19)f>csBV<=iW)=>& zPvHaE{;4L1DLR#+INdVL)nEi0G%a5?-z-sEMb!EH-BDPgB3UCj)np9j?s4MBV0YMZ z)nwEP;K1<#8Y^5Xp@nuR!`{T=%j`hb+RXW)hzd-3!U;K2X2m9g|Djn~SU!-)RKYp>& zJ^e8NZTieH-+})44VIbgkKbsSss8v)WLS{u$Z!fZvM5oCuUU@E+6{NJHP$aAN}x6pL#2fZq&#~$j( zKfe7Fha#(zdn7KFsyurL_3e)d>OuoP2UzHbOLAYF`~5?zur>d|Bffu7RnC6Y?@y`9 z&{4iWsUPM$cddB;2_Cd*Td%dNo)vsh%7Ek$BFlR2BQ!al0^%?B>$k`GEel+mu2%qV zMwe{YHlYD+yLX}mZ_|NTGeI^bWu!1I!MB-%dRTA$ElS<26r;Uv_u zYl)m`0OX0@0-|QxBWnO>n?veh4e0RvTjSN%>uGT^X}70SOvw>9cS_(Us>vRnQgt~{ zOuWO#Ip%OkiS#E~0R z+|wTuxIZS#&FG7WW+Xoc78Y*47eSNCX0w3<&Pab8@f23i%*ah2H>sJO3SdE5z@btK zkPU2FD`iY|X8HM1>Dt|{DUZ6{*nH5rnz%ckb|LE!3>4zrX`>m*W=9d|px6m>mX>M2 zQbJF zK_MIypCb+~r;W&OjBetr%){i9kI=QEKYI>;v8}KVo(E}%4J?3dbTcsxqmpo$sohj9 zbI2#Wr$awZQWIt|{=4#8dAC0r(*+0N)H5+&skIu_KsEUb7-ndDr}rD_7{ZO1Hd%)o zt-mp927#pu8={d``&8CJXeXJNkJ^VrjRD+0&sG^o?)7nP*LhQYHGzt^Ww#dvS2DZQ z7@=1j1*dw*H#UylVlyEAIy2a><00Fb#&BT0haI5zQs}{e7;0md3_vnK1lR@P)5e32 z6XV%<(CLM^Lgdasfe_^r-at~SCU>z|XS#LS-k#(+$-*bwBF`>u4}rNA?s!P(@+7vQ zO9UGBz}%ggzAol=8jew_p+ibBpWnY^bMynPRE5wY*JY0 z&!!8#(s2#!oqg>yMRVqUYzmN1GL2C+Imk0Q|LijYG9-veK;GD>t!fOmY8&6-^f3Rj z2Rpk@0W#0e6jC(D^8!-=d9ILi#_YtwwE5&?JP*3H9eCw)J$#O1Kf3k!;ht@U{UQJ1 zzHJ47-koBV+kBbGF8mAFc7S$JB|vTpw{W|*r-=;VVK$lO`IO8WYD+({oONV?T<63r zlhmY#lgT9YNGMA_0$fq~AW;k;YIF|lTp&Gwy8t7eOm;f3LoCti6*1+>YaY0vU?+B- z!|S~?imW3y9I2S7qa!;sg^XibA=9IkC9H5{r|d3p&!N6QvZI^7w0xxg$PVNXlAaye zkxVOjsn|KPqmD+xMRsIIgNwm1q=XXF#|30VAY*s-ZmkUr68tuEFDaR=pOPpOuGfa? zQ)8+A1xcDWCV4|L%BHYc>|qOBQTE369df@_ojYy4fSMFu%E>)|5a)sPE1=~LGO4eeOq1Oz0kE7rK=w@EGGOPn`798}zO)Ma$ey3|gNUvfV3w0T zJei}5b$Eg&4W{G=^Eo|k1oEW%E+_Z$I2b^*oE+AVg1r>#P^ocR^;AEK50I@UgBbvp zlWU<-L5q2f$km3Z?W;n8^cAO*GUenC^)wKzl-|9Z+@PmqRfi0DNwS52i$Za!v_5t9 z7ztQnHXtKmx~Z2?@>`s1S?@B$Qk1eMeG*+xMRN+p4B(cS`exu;2rK|5!kpR?72u9Y zLM2RWrS0@B+-2pVF+#N$Ujb_f<0G+KcDT%!*T9XS$`Zmw{Jd07O|bV$bS0kf(|H_# z*~rV)eJK^HbQVPi`X=)?;oyO(IyrcNhv)z*BUlu#(2#vfUL9w8Y#*;D9geNT@J{hEM|&=M@@N zDKxmE))X35DKy};Ic{ytD<6$er9=@-7TV>Y+(3#CdW}U=X^4=rerh#SX>1o+O&015 zOtR3N^9~lgN@KugQ%imslRBW=#Lc*WAp^JvGj>9{m!mkVN%8GK++n+e%5C<8^b_cvEb)1gAJDoK2Smq+Z!C~u*7Vy= z%HKs(r+S&B1zW60^oKE7EtP%a&Dit75b;}-4h+ZRCJ+^EjDVD_OoP+EOcBa%W1V_c z8nppauir4Qb5`Vs1#}$#O{VjQnL4c@w*hY6)WK=cBJGqyJ9Ug3+8(NXDfWuxCdZ)f z=M%t1@{Yed5QWX%%jQYm%yXdhy{t&``uy{&vWLJ4BWh_~mrIWE=-59+^0R74!-H{C znj7CK9nAsxv;9oHL*0=P)H`UPg}7U=kJ`441P3SXD@SVyo2J3^X78E4C*8Hr=qq)` zgV*7I%Xq3s9}m0<0bk|T&eFmGja59{-7EkC8plb=0Xk@ueV)CHR@vv_$I&dSE)d1P zT*|Vrnkgh6l|#rzYY8u;Koby7Sbe$0gVoJp9uFg0G1* zr5If|M)SM8n6_oVL)40ie3(+2Ac>eFf}6B2@U<{zN%h7ipRW7SJwTcu)*Mngg>IYJ z)OJa?KuV*TXr??FqeoUCDBr&RWJ_EJu+y;;b7am~9iP$lA3AGik*wj&-$mB029Y-3 z|BOD8`58miFWV2zk5zC_#d!}Aixa5GY%RCyyuLcqwJLZBSU@!)l$*^H1!MtwY|t4r zjQIR8O8|TOX~y((%w`EQ$=KV-zA|l+bGt8NEuwfyV)%IpN_6vmc0v&sF&%V)3M@LqSTn5@o>*j!FDxf^cQY#a`pdj9Py5H3#elbxJzrdP7{K`{I+Y3y*hg=^oQxR%X@rz!?26Ym@*vN__ z#GIcRu&Up2xIu-TX#?EX-##06d)Rw-wg!klOjs9tK>RZ3;ek0u878E{*geu2jNVF& z=>O1M5dh`im15C5Od62QVXnfg6q$RF4PIhEi6D$KdrY&qw|324nW~Uz9|)_!eAhxz z5klp3k+IX3lXk7ZYa>j6yVccNc5%vXZ{_o=yStawHJjYN!`S53C1WF#-VtbNSU0(I zq*+|0(=4HqEGytZ^|a?BkRokPt@T=FB-tBEoG*b~?F%K$p6Wo*CPT?ql?-Z@NHm@H z(x8}ot2`(qX_ekw@Ev$dNV#3X-dYOWq`75Tu9fBBSFE0aA)B(sp2z#+=Fm!K&>00IFa8ga}-Vwf|2!Xn|QtdnK;tc$YeDwO~7k2)gl&!BM{byKJafh{n_33eQn>r@X&ftKmdx+5AU75;a^_!4<9=8`RoTK zSwFnzi*I}7Z9l&Kn_o|Vu+q_Q-aWJLhMRtXj>0!H%k@ose-kZk3Fw$d_p-L5#1OP& z5YM>%W#DOkX!#%5y)1412X6M%8#ey%1D(*fuTgIqDv(YCvkV+AmraDt#Qq%vqvWdU zhkgB~$+qn|NggKM@>AreLjEB6gXo&wlGdocz%4sG@FlqT>p;sE_;lzMOg9Z4Y_wmX zbgI!lTPaTCTkS&)h|ccaL(x;EozQB*3yoa)jN7knU*fi2-G2JekN)y|KmN0kum8gr z8KA5plk}gNv}(>>P02n=((fFuw_l`oXX@=|E1j;lVU=|wK>PCaJC5&M-ucc`-DOv| zU)=Q_nP-d(2;Fg)>pQ}I?&suVx@;U){cZ=(@eH{@lnbzkBnk zFKa*DMbyVFzvy1#nJI@sh94y{88*8!X&PP8GVwLUBOThiJF zgq)=IQc8=?O)b4u@y>;;nx=ef|55r}44z`vmb940Wdd^B)X}}nm@I~ubK90O_Bg3W zj6Et59+B>q+w%)OT%=KC}%Z`JxaqY94KUY<3hJUr<>R?5hqR1OJHfNnJy7m0bKRY z>)V7b(NyFV>}n6VI{y!#OC&X2Vm(3bk10>?f75r%rc2z$M9JTPDUmMG4k#2%mk5jB zrAsuwUv!BzHvrdO1E^Z>VBQD3E|IjXyJF-B9Fbu0G{;KX6(+#_UHJ8P&xct;INub} z4)kt~&DN9X663q9;<)0I9y-o-)I2kx*Zu6IQ&SVaF~x_)%ipPmmpk7oeYIv+%iFH6 zC5pZ2{EkxFnj!jh89I>l7a$HJLJXEJH0_jEcvYqFV!6{W-9GU4o&5v?QQN3P$eL;l zZf){_^i0A=AD&f~LwQC0=#dH)N2$>|E@~v7`WQUhrev0+^#n!@xXL!;H>^OJ&6MI! zqjW4*&6B@CU=6=6arG9?QcD=4w?>*IU4mt}K;srq>YQfkq;%^XCaBx}9 z?}~a5L=Zt4m6xd~^X9)9*+38Bi;X!0wTaog4lNR1YxqN*Qc<##rJ|(&nt2(unHR63 zTp$%?Bd93xGSWk)qGbOwD{cG$GY1F?OkPBB6}6X0Mack!-_>fjin3@*JXDpOe?a>0 zK~-5#4wt>Ea-HTb^S)Z=y|2JF^1hnErD`?lXPF*Z?J@hJs;nj-z#B`hSBE)xBiAcM zgq7>nIO!>*MDL4FB^`m6IFoV3Ny)n4ma+_9|5)#`o&LnhlMaUOalpxX6{v7E`Hq!* z(;MF-1|o&~bPFC0Gj80JqJ={o0vCpq%^l9LHg}vM*}&`(j`ZWbnj{w!(7t}2x)$4Vu2;qe4I7kL=&T>9Yiu&l=`4xHx7P6zN6iJ*IwGJl5_!Bxu_51Z-x>bUPj2QcJIY@h4}F>FDl zHHa22R^7>7Nock-?QPC8KAJKX1|y?=Ec;ZpG(ht@JTtUH0UG>5QEUtZizW=fqhF?E ziYbuD$dO3;4L^s~(u(}TTLG7iQO9dbtYF#-IIxty@-J5Kc<)9pSV4J9YYd7d(Dglf zMk`)TzHc=<9tO!Q1x*=&aA|^7?{xzn98U3xG(Pst(Z1m{JJQL{Hbd!2;YTHcd8LSH z)uFF_;1fT3aO%NNo@*i;S9{%Z$LL*e_}gpV_6n2lszd+&wU2(~-M9Ym*7s1bS8sg! zo_8Pmn}gr@&_^lQBh$>tBq~tkJi|Z9{{0Y06dGH227r=AYl{KMVK9{O&&AN=87p8O z(*l6qbIzdv&RIF&6|>mEK33&Gq@!C*7i}BH6X&j9ztERNtuqBU8=}wmp#cg2br^se z5BVALVhGwLBBC{Cb2*!_ruJCSt> z^LV|rTPR~1p{35*ID5diB>kl+Pc(eI4EbpgtIVwt|LeuMZy_1lw>m9Z^C@Rh4~@VP zz=)j)G)rL!&M|y?SR0Za`u??UENG*M00408CW{39DW6p&=uh~pB0+1r`K@J6CcHIhk(^~X({NlHc_9`%&5L+cv+NoJaYZO&iJYhNJMGTd4+ ziz)P^B=aYEACK!hc6sI{iIVS>T3;!w@I=JBkSt9`EYODErP4*(-jOQ^#+@iOhcD)| zo-~H_`d14p3=NNTXjq*lw7KWHjR*(nbPNBoTEciIGhY@zgUOX!u7U`z)f59Lw)pC0 z_y`D{NT!-*+&)-55ZK)f`CSMO=#H<(r_H_uOzC_it|Uru3Q;J)wcD6Ob=>8Uz!sJ^TnZa)WU5_DoKfN$dYnV4xfb%tv?xB@SkEU95uC|w^O3MyhH zI_q$L`pF2Y+PBWF4N?m2nv~K?GATtJWgp0W6Dh?^*z1(EDvo11eBG_n9(6*edYu6? zW$9!!P5!W>Urm!rZ1&Q5LyG4NRf+e$XR~~AHh<0J%Pxv_K1l+)Sr0Km;HcmQAm z_e@{62SBwUw2(a99QM~2R+C4_dcHY1{4I$=hDb<_OX2{+19n-YP%jaOKr+1kahV*2 zq75agLE9ZIteEWo+reL`+M2Q40|D;ID7o#m5CdNH?>6!y?In6#`IQH+_3x> zm(|;{>T;bV#CFQUGAjhE;9qd*&f8tx29IAOD99$-7;S>dK1*CF2(D~3Jjn_0@+Dw-iuTD~7)xlrMb)t8=SvHRe3r@*_A8x0NWgEe>V6~ep4=$FbHlwp z)qrt*h*6^(ItAnKzdF{jr$1HO*~!drX__bPQbC@5gQ&^UCR=_6U%nX* z9qHR+d>g$f`GM#)#128hPd>Sxp6k%z7whi4+aVtd$i2#z!mz6x^wv@0&T`MH$(7|L@WyX88TK*9E8GLF4 z2nXv}FMXb+IV1Xt^5<@iKBV`4Z*%m1rB663`V*z+Y%A=s_BIkh-e2sXHuiF}z{jne zvlUfj3f(X8F%?AIlX!Mvf7NZ#Xx;Dcj?ZwH8a5cTU%7H%{yD{cu)??E^rNc=Aj#*^ z-{_;dd*HdqggfF*?m&TPrTYul20eMEA);`~>%kxa~#?@d@sg1)`JQ zTp+Ce+Z9d!rO20mRe+Cp8Rz)$l@GSLiD~paI;2m(f9e=+gcoInJJCfO?kU;2Kc96^ zgOL6vwSL?EX7=oZi1JN^NaXeogBD_v2B&_cmzHOWj{ffvH|lVvTxWkcCRLCUf-lPr zgS(1=dJYwF4){0#7*7+cQ<47^zi^HS;;cxdWlqbfK4BNlH5?v}`mg=#oBzCcK7mXU zA{^kEfg{&Ss-i>s%2y9WaAP8G@)k_~j^{wG>;SDa81=b zwx{%3Ufj!9g7z0}jcO!m(0S}spqiw_nOh)RUL;LJJ#xan7udoJn;`5AhLBs_^r2l! z;WEZ05Y-C`gr})vzw2bCv__NPc|F%}B#OLkQ_`2k-%^xoO$l?_o4ofMKfL$lXjziu z(}_5af&XbXFdi87U*w;em5=407Y`?&>^zrr{0w)^<309d4iq+0kNDTv!ACx|vh{9| zm zSLQgD1oS|3Fj};0&ng~;gem}m#%_yqv>O4I*^K~*A_qkZ=AGkj0hotKa#r+ZD`JP7 z9eqg`04(E18YUim%u$>oYeU2HZPo>z^TS&3a{%1F_##^+-HqwNR(E6C8qHToVPuAa_6sRjKq7&WE}gYX!6d@JE~QU8bIWsP1>=*t&9AhD zcDQ;G2RaD1AEl1twiao0`n+bB&JPp^q{!0=-?-PyFcp(w@Ff%PT)o6>EoN2fmlR{n zt!pC1h{=`Tc9lCp-|Vaye4 zL$(%e9d(ejOw49vZ|&1wUE^!E7DeLF{_gEaF>s%A^`M6M~2cq>F2ahp@w$17GAHwOPW={BNc1S-ksp&o;M)H1HAQF++s7I21TurKXS zUV`rlu-`*a`4!anu-|pfh<00DOO6^Pz_`2a_9@f=_HpbL>{kT)isnI%1_46lBsmH8aif+5YOrsP?ocMX zpbBaO=VGwWMOEFfPqac0`!Yj&yx$G`&jhKYu>Y$9aeY>2F2Vc4@^t684(#hpijcbD zW^%eYXpNQOWNXC0?A=1pEh1G8mYylRKYd&m>`w#x3zKmLLrYWbHJ}*ibk}EN8`UQylTL|$s>Lo%?^>~ zO}7HzSLjmA^iHvsU^0shl`Bp`r)lcsY+gtASD-vF&8MMw(#bj>6FIlvzlLx?{G{w)AM z66KM=|NjZ`r@;R#A^sHjf7QVC7{G6RqOR7&`t;KRf2{-jcDe(gx7*r4_!oOZ&_2oa z?`&_h$i?iLhw6#E>&0Wi`RjfrasIk%Dj{tBL2veHsLx)WW#;cuh;EB@I zoDsU|qk8C^LkQ@RjEqoG3_sZ44nDv&>n)on4VZ3SM1XXgIK^Bh(z!6H_X83qV}3x0 z&US$58K5KY>A;woCP}Heqa8onm_^X-Xh-*$QRILH3>gTU>IN>5+Zwhm;OThkF|iXK zi<~s6>n%5~#UG#uJjuUYQf@-N>)8;E5EmzR1i~+y>lrLPPkJ~D35os*@|8MGD|#Yr zj8e2PAWm_N6ru8OugHpU@$J!JwRS5jLKWMid&JvIvm((X-TmG^Ar!%Ip8<3;k7pFm z;x~r3g+xfW2X9!`NU+vp$h^mUsH&rk7q`{Tr3*O#A-+YPyXkO2^BQk%w4g(Hge}6} zJ+Snc)!s#Jpm>&Nc=a3HIJ2|NpMRk3W$2QmIz}%b*HUc&9~e#ODGzv1i zuuT~Scmm4StKl!)8liy%IJUB+qy(Jfm*>%#sGOgQ$Svl?1Eaf8ACtF=&BJ94D3FDN zgHk(mBM=h-88hG(N>d_;;U9t*eOW$K#1O5Se)1?Y!9LUR&J=z$-pt+JVj!h3BLU2% z(4w)1bH$n?;3S0*jnQSW0RaTY=$bU-9PjdcR9Gl++kPP=2IrTof$|T9C)+p*Do<<% zEE~}l1vHQ|+~x&Vz2cSxRUXW!6WpWN7Qz7C?oo!la_&);HVb$*Nktm%c8lfMB+dY? zb&Wt3Ho@3M=9(0W3EQ|Kf@0#KX9_BYqnOSN=lZW^>LSB|yYtWB{YM_m{r+(F5R^UW zzLyB_4F0_0z@Ab~UHHyosS+ybFxZyH;d0nA$XVRwr^4UV3R#dJDVai0is$12+v{$>71f?h?_V|KR?+URczWT?ke|W z>B+9r1~C_@tF&+C`!cpucoUeLY7B9)hX%o2DU%I%&YYf3sHgqiJ>6%0@IAE~R!}Qh zk?(2f{M0de%0Tv-EDeBE-0%2a{f>G%PyPEH_0;RxLdeCs0{V z4zCc-kq(uE=t;qtQnyg?WO~Aq_lm&~mytaK0%B#dXCS~h84n`8p4ht1@2tPrx~x9S zFJUixX7z=^!gF5Jwh*zEJJur5cEtHW(_iHk2f4v$Ug};+WykqVUncJ~;ob;OJWXNQ z(*Z9gPgfpszsKwdHVz(a;xUZnAn1D)_!ENH84R0 zU@6XC(^ygoK33A#_=&a>9A)KHo$+NbJE3;xOG}eg7AqbLoK!Y!MM<}1beuk9kL+WT z8dU}nR46T+GBo=&p!^t}1A>-_Z45mFfHvvTg8v#nE)J>f??JPR7YIchNhOWT(LCWD z88_0{wFEl|xCp(s29P!@MlsAl+BA3|odVxH!N07?=Aiuq&6MsM_brx_!R#wI`-Cr_ zIn1@*^r0g6B}a8+XR#3*BJ3fs56K5P2lpohq)22^P1bLC8tK;oOL9tg(gOL?i9Uw8 zhpbwG-!XIuqG|;Lof<#1f(_{&De*lS5&fkyXIVp%y**8#Zz2hgU=ACCN~8ZIjChq66RlnpZ#mIlg2jo@W2jMs_X*%Ge>Nujf6ZFIX6 zBu+s!Rw;?%)g2vx98=CyX}R6rfl~e@ZP>$s;+pnvD{<}C4tnzva2ZdhwV@>FgW5ei zlAf#DvMBU}!0}Iay`(kp02+BaSX@g!^tF44NdI_1XQUD?bJD|yDK-UfJzVKj=Sn9W z3z3?D|6Q82n%gDOqf89W16jjujq!V2K(+_!Tx!=a}?AD*Z69mIyq z3%zbhL8o65O%8qcBQ5akVGZEzpM$>p#o^@8cRvV)=L~)KGcKMp^i78?-a7HHyg%fb zNSY%)G#0<%fZTNL)T$yWbh?bv6CI5lgTs^bi3HftqkmBzbP_YvsNzv`PX!1=oA(5D zZ>cs@`b_ths(a}(-CL^e`DcAWahv`i?VjW?1PA8FSFo4~y&`YvGxO7{`AMJQZ$#s= zXF8zMA=xt>P&kb2nGW=JIsmwZUruFwLBqaDqp;Wj{v(?-$vP|_s&3LGTZDk{dv;~Z z0O!3!BXtgujp!y5{zajSyD)S0IMxOl+GBG*+y)pq!i_+%%UJ-y)zF_+ZZ-29)S!zf zh;Aix3fovLfkaOo-IkGNEp2&5L5v2`qmZbIBFw=bWaM8`e-P~ip%tMILo0~kLMvD~ zp0Ad{0>Unm&hJrrI!wM&YXZa_MOZoQsfTy_E&K~AI6ablos zYh^KSvYl3+jtN|?qJ z6P{MMO=0`gU4H;5x#TmF3~F`l1kV}38RMj_9qOwo#UDE-{rD1E51;J;(njP?%)gjL zfokB6NN7|Q#xjc$h`Hqf@7G0gYOd|!tgU7C3iuA3odBF=;AaSO4xk|IZA$<&j0py4+yEi#L<(pW8=ze*Kof_Qu1Pj4Z;giY!Jv3rU-7% zJPk+t+(zl;pQ!?zRHMi}Q z#Y)mBU*8CGNpMb~Wf)-vnx_V=t}(faK`#edFrA5_*5d@vTHrG)cprk2je+63j|fG8 zKsLBvMHOt~+TXLIICi?qzJ=x`m@<`%{plRwjB4{p091)Z|5v>!rah?K3 zrRo&8u{6sOfHVto4q3NF(Xv(;>=}VGA7iJ@#Q8_MaARM3 zC(gKH{lXdg73Qaj?5A7or;`5(T408`+p?ec>SuPmz1eW5X%#pVd#4AvJvL!-IF8&N zo3Ol>@~!qOF2PLat>IT@JeyL~P!A%rO5UAT3d{b0QoK)Y)^uJ(I3T5WqLa8$nZ0B< z^iy*0AalJkPduYIs&vg6#obB~nn#o(nQ<)!(UAD7qcJ4bM0_HD^Y~lI-xK(I3V)aK zM|_BQhhQYlfbqhqt z3~>&$y^9~0PYVjDgh>%uOXnlRArqKFiMr#VKPiCYR-(?vtD*2RU4p-w(TZX$X z_{f6fVLR;KUcqst1u|kLkz28{-47Q7lSaaQH0)Zxv*zxr>;L4>r*AyUzIy9}-~CpY z|NrrQ6=Em1oIGN^DT&$7}^EPl0&ls!E&u5gL+t2 zOo^<9VA(W#>3+cZh=Mt=Y}&C<1J#*wz;aK3V$Sb@g?_%B5rxrY3cDkd9l1|r#y?`8 z&~e)*Cn{x|SWGH*3BK~SNuCh)$WxTEMJ`p!4slA^AVaX;i;M$63}n)vsjSrz)qq76upMRl{fGGF7p6Kqbn`pDp2v%5rqhL)$rgsPxor1d%I31&dnh|%VJMrsFcclJFqEYW zlUl2c?N)%R?u8L|t%Xr)3!@BY1EpY>LC|BEoK_g#UmGGn=c38I$sGPNrx^D9Wll5f z`OBPY*z=b;-7v^8&}5r=JS%Mj=BIn2*H8CNoqeFtmnP-h|E_Gh5t)vdZoE1^u3#Kz-nkiAlv##l$kr2c0A2rm?KDa{}c%&Q{NqPf} zW^&`oq_^C-{>Qn@|Elzc!nSe+TwOf@Ilx1hq+%1rYaCHuH?*w9I&7XIWwO~379bZF=e4ccRr?SGmov>>|?7o znh9W!+PBfJA}LNZL7UFDTZ8i%bQCnS4M+Zl)*Ch}erk}t&>E`Yp; zLal-jg#zaXrs5-Lm?Zh?wL5riYcz@gpo$ZO=@<$5aq_e5D_chLheLjp{7lG?ke?3u zS=R7S$R8$uFyytiQ=AA$6gNl5cl{od&ZU@WXHQpQ+9nfj9l0y8Hj!>Z3E#5at7VBgWysQ173yWb8Z{}-MR zd&1jWM9%db9rrbL1JI-uo-e+o70M9AwJrSXv8M}92i_mUqC`90)ELVkigH@aIp8>ldEp;9Od}0_QzXdu+cT^hxfLZXpx?aKi0zC+NU0PBmU;%h(|9PD%Mr zV}aC2hcjI$yxyr{BB1b1z6R_#Dy6g$t0D%)R}msxnA}+&#;>TW3fNIxFI`(GOKK21 zty>`JxxEtO?i=Js+C!Fabv36k=PFFlC)_H$#blB`Nybzl1zaoIJ?HoJb)?H8f^2L2s%{x;$pz_I0&(%#lt# zwrYnSTeX?)YR8(l+3spE?MD?tN4r)*6GHUyR1*Lgm?Gd%HXS}h>s(=|Iae5J=JjaP zh&qZym`XN@grP1GRKie~2r6Ny3E!K(5{~S7VQ9L_nJF>vwh`gavK^xs_5kWJdH0@< z2r6l4B#5ArhEfq!(oiabN*YQp$7Gu-dmcqx=uTuopzdi_M+Q zlx?0gat08f6RP|IN%Wk*gRPb7K`C&Ge6dG)Smhwme+2VXD$(Cj`QMP}Q%nWpkeBF- z+lwzppk)d(cNk<3j=dmzC_Ia~&;T6iYhoW$GGEidQ9~l9N5Ig@5pl&{<%*|9eVh~B zy&Hopr@Z^|M6LNjmK|RF237oQL6A9p=cdd~Raa#bJbv}E&e6xr~ZOy%w za$+Q^tMj<`(Ey?ArP^|5Yv zv$nWGDFn8jK7Iy|FF0eIoKqdek+S7mZnQrTUx&^(k-puQAjshKRk~c#^13Z!QVwh2Gcs;cLagX zIgzCd-Z5pCJsBF&hQbfKeYB>FiFQT4<@1GWwIh$&^1ifX9hgo=UNh;VPg*-IH{_+1+!OLrN=}5ll#=5iFS0ZS9>^UlGq*MS$pZj#vM z<)jYz&x1UCLy^{qdgsvqdk$_O=Wp=8fX61#*3EHQ$}noqWohqG{V4KY;_{zJ9V?FX zqnO1zzBczmhbin$70j7;6jDmN(XI1LoUtRw-0l7&6wL_U?fMa_=7W?r=Q`vdytEh2 zRX2Ajl#ydt0RgK9JQO7b&tW7k*c`n-s+k$W#_Akx=n_IeYJ*;;DGQveLAZ9{Akwg zN3AvtDI7MsT=yi2B1OZr1emu1 zjDTqYXE)t@bwVdCaZj?`D6jK_qVR7|VDt)LLpQ!ZVDq+}5oOMxfSW?_5x7gXE znnL?oX!`f4VFW&ZNbEhnHHDzx5ZzZ(z;^=C*9i7bdQS~yH0e|=zF6sEv7C%wxo?3i@(pzdV5oFyhg{0&!9NDSDo+#YCIt(#TqT(GWnR(9Y^nIL3AQ zdAL30~q)3qi>U zj~?h5YK|w(mu;@99YC|KIbHwPDPhHx13+E_#2qI(*uFT1mb22Za`JEFU9%r zVZ;@Ggy=$pkuGK+@Uf0Rm$`gMcsD-@9AmU&+xnLibv6m3&Gx+X(lq*S7YWUi8lg>6%4lk9{3=xn5z71FW-L`bKi zpd$){tI1Tj6?F!*&u1;@FRs$dIZ09mudZQX2UveW!T{^9Q2#APQrV>=x{Gz0QrT6v zMueH>$~2&XDc0%9&G!DnVYgXxs&dvGH`H|1tx?U#g$7+kqH@|DE#WJ_s&uwar?_uM zK)iTp+($7_ak8k*e^*{hX}HQ`{%%DqD=OH}uL2Qp zzVU@X1T(0sY~sPN&$9hy3NgNP99Jx8HGlH9%OFnI%xjx4BoE9qvy6)ce8g(VoFptn z@Y%XP>MH%kVu7BN6w~QG5rUDg9bh3bAz0A^N_n>3J`fI)TWAyR021OiNDH?Vz<#nU zh7a0B?gL_3bRQ5)G9{!~Lm3xF3P2nKqxjfF8AjRH=Qk60CB-NlyU>|QjWNY2icfAK zsf1A!kE)U^GJG;+=kV)o0!c4s(iO{y!E}XR0HN&b1EE;A`Imsk?^mC7`Nuph_mmEc z5HL~!#uy<~a=)kb{Ki4z)BS!^w4-8vznvcT+iLck-Pv%={YEEkxBcOt-cYe)@>Le5 zrcL7`ZEMd_$8S0=Kk#(2<+o3|>6Swfj7)~=oL-S*SpSL4rE z=7DPbSu%DH^gWijud0vGeu4H4f*RliYOs%!5&@_l^G^2QPE_@{Utu!?Gzt~b8rvBj z1&X9Cpg_9EsUA;)4Q+#_kJwT4180EA>56fkoNjyKp6Ckn=XG5@SXT|k4G|6NaiF)Bz;j-;e8%H?FWbWbtim^MV)IikunFJ~#r_CT~H z-l*iXc#V=xoaiB09}@s#AQ~Wc0?8VtpZ8pR4eZ50bR6D9B+Fu`*g&)heGo}2Uah35 zz)u?(^%AicCC^?I;t%lRMnGr1E#rYG<3tWbHplDTD#4(F0{mg%e89rsTg6rt|09=U zgyZnAhlfV`fXI#*Y`@ImmY8!%iY)JG%s_UDEQ7%!B5Fy?qQq;`2ga4GPaiaEr=<^n zD8}kcdy43e2FK-6Fjh8L#+(~$EgfXKYGrz%Va|mj3pPo3Erv&I9M4`&Haw3hdI03c zi-tI{PpZHO$#-C{%FIwo>6610fd4Yq3em}qqqM_BP4g^ayv@B!kvrK!$+TZr(1YjA z6yHMuDrX~CQv_gPMx(lB>#Isc2YBD)! zY@U4>;=@D3`7duKh^T#(&+Y4DXS8es>h`)Elyhr|fkwaJ_ML^fb7vG=^eXV@Owl_* zKpiICCQy_19xe^)gw`r4_LdDI?esX$`9X~FoF3}YI7WHy&0P{V!gIs3Ug^~NONE%Y zGd6=wN#~sveMV_*TY--Lazdnz{xEC5jK$fn7=ZN4lCFvEXk6Sci;J_#v|l*iWhxat zXkc7XWaVfxOuxbd$eV+(z}p7wT*8qXqJOQjkx9Q%W$%(Aq!I{YL-frmyPovls_bym zZ&lg%q@3wnrS#iXZO#qRzgOb{rT36GfqVzwjT!2Z+*}(Ia zey!%l@lBR_Wi5WQ zWv;2kZ?Vj^wRqezyKC{&^wM>;_^np(s#^Rrb6 z(u0#u^v0`|avl02rQ^MxtaH8M5H4kF0i~@J(?)FxS z!Rup6v8nx)(iK|?#Y{>ta0@uIW^1HKGp>JYbca%1Zco}Z$C|0!33Vj$#!lC}(-n7~*Xj}T#R~Sjg*)Af zoo?w)w`Ql?xYN~kx>ou-v8h>0_c=vUfiDoBU?`dAn_28+@^24NG_EW~w47@q+Y_`D*Vnb7R{eDG7NH^i1Mp4Yb!CY6m0wm?WU8!q3lAT~&ip&G%vp}9xD zZ_Gm`0yu5afF|X7T}i1h)0RgN@^PjSGOdW=cxy4WhzmDF#BJlUes*zakM2jwt*1np z+z=P|A&&LthKK^4&vy-R0YkJX+DE%mEe(N<75bfMuZ*!w{?*9j2b*cUPNsfiw3my- zx$?7>)UFcNS#o>IAai#@{JBD6p>Y>m2z{~L$tF|3FvW1kpyA|37C4|~$>YiO`Ddbu z@6JDSi^L=M=jx-FemMX9Q1;MSabg-In-n??Sy2Hd?J2j^>c>&9meWc})V9kWu1$wE zPo~MdxNNZ4fi+Xmq%x%lpag?f%0ZCd$ln_NmhvZ?@gHZj) ztNG*7C{ypaC7>h8VYOZE8X`3Vfyhh7GH^Szur7)kwa^evN#4bstPLj4;|d!cTrN@7325fmcU?uDfFWczRhAt*;7A;w7J#|Fv_E-c$T=j z_o=D7C6B7&(G8m&70-Q59Eus?p(Y(|s2fgE!`vbt1AOsjAf)U*P}mkBE(+mF1hu=< z%E_mrVQ_lVx=IsRPVTZtAIev;bKeQNS2;dF-pj#mmO~U);aS6Oqt9{pe2veYMrci+ z;{dwFs20zwNN9Qk-o(c}gekTQC@FI=BK}wWh#Eczr(&uuvDI$1FN4>-CiLdB^k#in z^m1}HPb~aIzq$lNxU$X zco@htBhLpHaKs;g#04_nXQR419 zS4SD5w^t0u8-4p|wz)X8Cn-vf*tj8j66;2SSXJcSQkZWkPryBnS1Pn&V!x|Uqg}p4 zo=~)B$9mZO0CCZWp6iJvow;5-&4rj~2r#v#qN6wUWWI5pktr(Pnz+i>YQHi>cM}+W+^>2u;?GSAwAo$)VRc;bf|q_sukL6sQX8rS z+vdu{+VzY;^-8`T&X#I~Un|3x^qGo?_B9jM?@kLb23l4RL?Cab;*NMxXZ{;*(IuK2 zLIyJ`dei1@ba-^Xg(buBPt;zeDX39J#TzMZ8-gtgxfEG(PL-_(V+sV&oK_uGP)6yg zO&9|4I%E-=@XT>d;L`P=U(;tXpZFhb`rE|O20t45i=M;}(UW#EJH2iVr`AdY{-R^x zZ2BVfw*yhUcSFSZ`RAhcvXzu+4GF?c&1Eam_c3o|Sc0l%?-ad6FDh=}65x|LiK}9P zD|DmkwEk@!>jd}m@gBGADLWM+$SH0&ABWjddwh%jn{8p>qV_4_qqIB>CM|-n1}^1+ zS)h}bLS@SKS)NfO6Fwhy~GBd?fv%_8Ik9)789g0KeRc zOe(Td`s!Hv>e904;ZluDRb;SS&Ry{bqFv!0v-gzhwNPB=!;;? zkJByFl|?LwTP6_jILj3`GMaXU`go$^W+seL^gFd|KVePeJV*i{?PzO?%`mVgtyA-_ zYvb^fYwTzfrj_Zz#uHN96A2%Jki#WeT3KMI0p`5|^$@U0#tOy2uw#j`$&EpLim~tl z(VFdTF05&4Gw9_SD{rrJqc}UVrE=cpnh=0l0<^{NUrIeWqhB%1KL&S2_MJ6ndbf>w zLk#BgfXy{l5gF5}YTf~l@S2JDfWI2EcEnjoWgwOUO;&hm1wH|V^Vwk)n?-JmW`^2~ z+W077a@dBcS>hJ6UBjw{TcialMnllU$APPxg;MZQAMc@^y*-96LH%=n_;C6|an27o zj|x>4lhT|Y_NPCT=lp>Gsegmj>U>_ zg>T?tQ3PQxw|ha82Rw$s#ILyT+&FfdO!^+nkQy`?)?tA#?%Q(@Aiqo-23tjjqhJJN zX!CE9T1_M1&GL5oQ%d%}@)MNG{~#G1`Gl@$l-8%XJ^3<{1chGh%uSd{sda$B*-&TI z6Dk>#q-45kA#0Old8_ql5EDT=kEp2IIR}c!Jn6eYg7^&r_a60IF;ThmUvlU&a2kL} z*?g44s7P&8HcA6n+GMu~ZZdI;c}^D={^F?1*hYU1mimbcNewRK4BM5iMC?yq?Q9i) zD|}D9oE0sKDJ#9y+x{D|N8ZWHUK$q#3;`TMV}fymDBL{QFydsy3_0J@QR_38hLO|$ z(8Oaa;zOvxnXx6v4F_BW8nAq zR^77O-EwcTeaTP-W7ub8I=#C9-TG_+{rf$IcnvC@l=etxT&i)$^tG(grja0kbFL+}HO+0@HSEz>jAUeb0m^-evh&+`1#m{l3PZ zFj&1PfxekTVJLZ5sDnpgY8i9O(bv;QHz{2+-Eto%GpfwTu*QujgR{1!l(@F;k z1+ElmdT(`G;jfi{0(J}44bW_^{F^;%ugrD>@!gdJgVFU?SZzvRmn;7AUCX)`5`pd@(V|m7>wa&9~%ELQk&r zysgnh*-gmPkO_~zLe$WRM@{~;rGJ30jne0%Oji07eC(AH_xv`c z7oAbuqm-)!_iNyITimI14QrVx=_3VuArQJ z{`L2j%xMPl<9gU3Y(eQR#V=7u+Dr*Xd8e78kUySkVxn|_#j7zTmP**R^LvKU1tYMl zL5(SMgrUlg(~LTbxKKpPvIcO{_+sy(aa)lMXPrb0H{AcpRwA6o^ zt!%2oM4Um3?*Fjqq!XhG;oJFlmU{|Q+blueDH$ntN{A;x$!M;Gs1uZM<}!PWSO}Di z=SrrmWTI1|WjzcG$he5fsVH|7oSgOEOhFUjcM!FvsWnQi)fqhN9@-_-R~ac-QU&72;I#@f%9@>o1qL7q9QkBJ!h%_2YB;b8F!x{^um?o$_VX= zZzC5CGKRMKcsQG1+Fy=M|Kl6PLhq>#+Z%{RdBmb5d-VIyOF_e zE1=ZZyT7}+`-(U}@u`QarJs{?}M(EoHw;R>-Wqj>AzWPg~VE5l8fg9OH!u>T@ zl6;sSuONLD>5E9&K+h*(A=htdulNfPmRN)1Dy-J@SKyFphC zXj;?DVl|srumZ0tIPY6cXnN^W^gpxGUn)jLSeeP9G(y*@!{#K-25#XZ&2`GMPBn#1 z^n-L!pie05+1|V>B7Baw7RaqGH@OF2Xn6#Y@z%MMcHkNkrbia;Xh!;%mcL<@{=%ZfS|@8z?>xeG9Nj zZoClRTu|o5Li{qNV}d4E2%$(%pg zR)~^$w*Q`sw#hwLw_M(IcedZ2o4h`I2(LoO8jed`u;Ihs!C2zJiOpVDq}SQ&ps%#c z$jy`#P-FJ(&>}ctF#G$De|+w>aUn6W-WC+0RKbE- zWMoS@?-q5-9GqB66#zF7hV%Cz7FqK`gEz|V_^lD!+~Kh)OG;rgVHiwwN1PKG5F>b; zs6jb&r|2P-YlS?(A1^>Y-aR*vmg50^ga7c{2Te^jy5p01t>=LI*-I!t*nTO1xZ~GH z2*Y%iJ|7R9C+7;n%gL&jwvXea%ue96oIFoK^7*yMuMA1WQdC}f5vKVbcU;okwX;I9 zU>7GT4o5Fl)sm{}YJ#0R8_8?tIU1 zr@FUp)zxHcvUTpYfD))Id6E=K;5r315*`p>+z!y#vQ)OEk~Ne{ipi6$5|%-JpOYww z;W^zjtp{$T1gqPCNjr@@$Z01L;3Uo$0(Rr1W$XkI%!|GNFG?8ne*d-iIro;TWE*6E z?|t99cInJ}SbI%-t+m&NP73J-SK`cInmS3r)`k%+R~UODU|qEPL~FuU6W}1m&3QxT zZ0n)(vBK&(*cI@!tL&o6=A&LC{HNI5r&pY_ZrKOUbEoKK*F1(mCZX_ItF?o!q#)82!DHJh< z`eaOX>4NE#1f8VE`N#I2IS*i+C;Msd_SSjW22oV&=|xMTW#B1|U+BuFGF{E*fTlxD zgL_z`p_Rys&EUgaqMO+nRu)__tV%ntiN84-i20-ZD49B(mbayW&J;w=rYCyLWkE&& z1@-XcsyR)~Lg1zWoD-cv`fE`KtR~?2urZug36`2(WVT08T{g$LxS!7aEPjb+g_n3% ze2HhJmv~lwiD#8zD3_HE-ZF{a&~*)_7?$5`q=yxA-xbr{+d-<&efx=f0)>&8Vhv;hZ#0DuyuKG%q?$&VWeN)eKkf#Mjl*bu>(F#z|zB?1=1k!|CDOTx(}RY%F7X9)gQ$fKgL&5i_{MNdbWgG{g%#ObO#N zHkdM>+1GRT^C_snc9lJwe9*5bZ%dIw4lu?U}IMITqU?hsaD5lT(JG13+4V$*wNUM$Ad$U(3b4QwUDMCHax|ML-71qQnfHz`Ka@}sy zJcA-lt*0k=S%Fp^qMDJg3QWtjj*tn&I>ORPx|ddf6j*@I5bBcZK!f4JI?Ni$uq|YM zui#~#LtgDxVm2fi(g~Sar`@0B?n`p8LGaF2`LyR{{~TKf{^~P7SEe(xUo@pj>y;^J zmdZcVbCmE|D*sH+!QxpeU$OHU^wqC|wXcSX!T{7zukaU$Xn=nWR~U}OiXwI-MG3bQ zcgM*jkCUjOxd+(deY4oN`3|}*^YZOZx>~cmbi9_)bBlL+XhXPBGm%+NT59D3($WOt zjUodHJz`FAhRs4P^lzHv)Z*6>dmqUuH+D&q(_W12MW%e22&Hrrq4v!P+lx3!hmV?} zt%>V`ojvKN%hILYQ#^klGq>l zr6sn+CR#m5KnGI}$_R9LJON4mQTYh18W3d4^k4n#$zCB@v3=e7N^kxOWgs2&f+P$e z(K224Pu@>_0$Lzb6EV`6p2nN#lfgj2iBi(gpP)Lxo`D8V)6-bUYk~lf7Xy$^HCytE zgTR1*E+nlS3!5B+ijj~Cot#C(tqJhIXT-o9xMJX^opz?TdzhKpx}OJM>`fY)Dq zG@@nG&`OH|Q!zw6DPgnl z_3lluj{~1$@r@L9r4&!UOT4SNbRm7v-xr*?-RM(?`L_q(i^K2nj7MWFp!78QYRUX$ z*06&2=0n$e%zQ};!O0dR2==98cYATjjWhs5_xl@CN{`AX+?PHpktrW>EE@z%39t+p{{BS>Vu}shg24Ad{E_JgFc`mmb!X} zG1gStv1nre5J~)2s-A9@D=^g zOam-{M)32d`seg`TWZ;$SM;Gm>zNPGzw?q}b1r?HE8bSziB^rWvavZ=ZA}tf0K2W{ zC?Idtt@(Vwp$~PBIUG4PTnlwejYL%SL4dI05~2(#rBVQl&;aA8FQl)@$0`hEKznVu zGSD1+0kn=5Dfm>(YL>7@3Z%-KtVK*~vKB$!_^1Gh-D(%87FA5R+F()4uwuAeqp+jd zXpL_cgJBypyH$@wv9;!kpNBvchrDkeIu6+ojzOsLw(Ts||NMXX-EYO)aaly^tewPW zpy_e<`=9uu&qwZVWH6avR$Fm?G6_!n=6m=AeZNP3#A_|*u5gh z97c`s?xcKJwvF=L%72*jI3G0-k#sazFk_s6{vrzg=Gyvw&GDIL@ehuxsiw`Im$3LL z9vI{i7>H^&@2Yw{8?rEcl(#H(Y$};Gn%<3JWsck`qBG?``oW(dGp0Ab9lB{wv?RC}lq0q}*rOxJbHVb! zSbw-Q__c^Zp1H4y%BAttQ1%Bc_z&9ba8FM(wQUcq_kj4bSwK!GA-IBPVjf%wGt&KE z%RL&*16UfvwKp>{d@POWy#C2o+Yg0GgR}Q`<4q3^f-gPHqO$Wk=RLB@*dgLTB9tNr z?2<7D?2;u8*d@yx@Uv7oU{ zIF4sc%QzlPOrBEOf<`Fjhs=GNmm%%O&K2P|I5pc=I8tk2wOA1-ihJ&oz_Ju9LEgz4 z8pz;*iBx(4B&F%Qg_L!h^;0@=-DwJ>4TYylp30%BQxkEA3c5&Rsuq6N6nyzwvv2 z{YG1Kl%=?k&0?nW(hDTcZ$G)Sey-wwp|r*)LrbV3>NjV-e1v=(aDdZU`Uva;CRGaK zob{+Ulwi(!>zU{t-!_^UBZ5j5s_8UGVagZ_<@Q4?F&v7f3pg-_4>Jss7A(`~67Y~0 z##kehX;zxS`l$3D3I&c<%ag!f$nz;s^&n7(=i6#8JTFoV3*cm8|q09=2XKopF3k*CgR54 zuFEY{H{=^9P*UT+gj7mb6KqehEL@AtvnBNb&#>3JrLe$kPQp@=>>7Y#dZNu=3xglt zyKn!2BV&7_r6Lih0~XS*jhzn!sGbRT=5I{9-H6hxqB~;;ZGVp;o0(RuMGEhvTDm3J z9>o?-KQkyGBNTlS>ifFlM>@K$Pl)FiNy^}XUCPI%kb2sHR%wBGD7pp)Or@DFnS$jF zVp)MspE+d2?GW{pV$n4QZazDYnT)C(58?uL?3RSc6GHb~&*LNku)(3MyBWa7t?w+_97YmxR*4gQSe;`#6 zyg?TQ%kY>rd+_Qe@b+qr4PIS*h(XsNXtIX}!}m}=1iL)AApH+tL06f>EoAJYIzP%k z=FFJT7PTV}Pk>F~`Gvv-?On+yLb>4Yr1`s&JKN58M>>@LaD+R&x0arizl7bKz^YzY%6q*FFF>#-G%@82Q zmS1CvQ0R>3ahB(-f%YH|B%=UHW}~nS{f5a|Q*&}@v*nnFvSgHnaF}n*V#HbxkiKvY zv+fFI^x+_5F|poM%nCc7{B%!;LE;f6+S>42R?+E zi^BQLw~i%O8`Fyb5o63E9KMvPYE8PUSz?q+d{=Ip33&R%*D&TW>eyRG7#MFf=;AGm z27qw9(V#0m&1le-o@O*)ALsE#1NH^PXi&C>m?|%WZ*Hgn2VKiL>=mzm)o1SYJhJH_ zC^gg7G{XjXWwYcs0synXF>K1xRFiiiraRi6RJNZ1V&%-tQU(hJv2yCoEV?0xl~ZqK z(G5YYoO&~hZa9cZ2ZMTD;~btqK+$OW6Zo)1OXI6b%PFKn(^$`76T5p8S5G4ymP8-m zh9TFI=my;kHa0FsEy5&cHa&eK#hY(fl?Q!;vrq$@q7t3zRG{Vj~g8|ZrB>l-= zY^~_Wj`x`gX};{x2XWZe8BC+Zz>KA{G|&_>1UbxtRHn965Q>GA4Y8&8TLLN@w; zH?HQZ0OqjH$)J+2#|I&LjUeqUn04nUGMz17vqsVrn_QMw!#H$o9C{D~bZijfu z{Hk!O3iDH zcw`ICxMbCCc^HxdJaWBh|0-$(>9wPxx~pDk5fiGMWf&Eri+$)&2WuqlB>|W~U`I`G zh>N#akGWFOa4poYq)eSiG>XNB!4g6ZfZFBKt3?eSc!&c*LJVk=hPm4rRH?2N(m=0m z>s7-))uh-nDz93J(l0f?qvcxE36SVygB2`B>Fz|?oq__OrK~Z=KGvn6?KjgCQV)+YyODY*MJ;_+ zC~!gglP8@vH{trx@9=yngKnSe;>qLg^YlI(Drvue^q4N5Kw_;9ji_ zZN}Xp3=eJDbj4atb`Py~vrHES=pb&{GTx*jO$&3fIv`Uh7kwr4$0BUs(EuC|M`aQ!EjvBFjR(|g<=|lUcFu7Z6^7;NsqBHa8q|!`mYjj7uY3^ znQbZunWB;+eWii4Ru!+{7;PJoAp;atn-#<+!DlHNXoZTj)wuc)#>A>mOMas{jow6e zdH7$@H(M&+Ue*{Dh%_~X2Vp8)zyvq4>L|d`;3Q2=}$Tt5AI!x=ganUH@ElS z-#6hoQzHM&?IgyxSt@lUad53Q%yA0CxaaK4)KG32c5Q;hA{n~WJ6YgtSe}hHHe(Ti zkBBStKoSCtpy}2t@JL&&8b|RoAQ(UDI;=wM4O^bT$cVFAP0kG5t_k+*YnneylU}ky zticm1d}3UMPh|@yiCyy*I)ie%!lR_Xu&&X>bj;ZzuP+FlTdOctpgeTNlRb=H1XtembdU?w_H9B##!iQ2~T(h9o-W5rfeQAHI;>MS|y1hnS_lmpfj15-2F>>%ctTN1{B=WyrOLc z?R7Z-kGNHari^owxwO6D@26v$aa3)j)iR#DC+KyI#*&CNUTXD1uFOP>@U43Nmdk6TncUrbZ%mX`FsKqn^J^x79jMA_fYS4T?u&yw!tLTbJ1lI+4XS)mf%qi5u z+KFvXY@JP?Tstw-o2Fkei9yav45iFsPPhtuan!>U!&XfqUToE*Dc&T1E2W?P>)$J` zMjo;U7D>meP?oq2gy|*-W!<&}aBz3S1tthKd9x{mwBafd2sW3(3nSz)+#gXy7o8%@)5I^ET$tzl?n8P2A?#Fy-p)>d20QMNa6Anq z@L9MlwTCu}esJMS+!H?GV3t9VaTGjkOpV(jc|}p8DJvR4GDZc6D@Ag4ZH#%!nC$A z{i0!$X>7dhh#qlq2BP-^ZIq!(H7#rtJ@bDrLkB;s&|+z3j-euR|Fw| zGpePTzJ&%;HOjY#mrF|nV6A0trV#`R-_oe4mjLx+rDD!h?nvFuciqk z))8|>G4g0_f|k=bT2TgPBMa%(-1@0@LDLO)lWP>kD5Tn~M2=#81QeGX)MHGr05k;E zw3=U|A)cjiD7k!-*JF5*f7<5$WuT68{FC2$Ts&5TqXqBj$GP^lT&+A$f4%eiBl(5F z*J4oQ-Ner*uOp}^M3>i5uKaN=#oNb94&PFY!*~^iILZU>S}Kkz?RAV*9t!If@ihqa zFiW(DqV*Z0$!g@SN^g-XG%G2}OHzcKO8P0AYliI(Q~9gXVJSnCbePHDj10E4F0GT~ zCI=VS!17?{^?%GS+OQP#W{iU+SGF_|9;qRsTfhtFNIr~!@m1-Sr2NEQVY7gas?uM; zCR2(*D$8t&qZRfZ}E7B@+jL&@Q(aDHLI3HX=2?!Kn9%reIbws_Eh*6$5^c*|FE8a8vVAcJsRt1? zgZd7|CF~>W%|25vX0ix8J;Ib4X`!gRtoH-Mjk*J^`Y*E=nA{gFmVnmWr5Q6K$#5(5 z0-EWU1QN+EC7`9+BZ*BwW3N0dtwCuN>5L)@$*v|ixY?j0S@}VGiD(6*mu&kT@uW@( zjVR*7*iJkY0UE+t#^h-Ll{ypr1bQ*P{lWW<_6e~gUs>?WT8(Z3Uz8y{ z0ELuV*0&w=X$1)sbo5xSWIgt80Tvo)Br7dKRO<>g<6od*LD)o)sASyNi(R>l+a2Cz z=XZfFCmaDs1O7h@ZW2pHyc8NfItD^nxIw5VFl^wXszk4b_>whHorsfb_HE0q6LdrF zxJ|G021=ElA!tm;g?2YKSgMWXdyt69$gkZ;k=A%3$v*l0Jkrg$@5i~$CEKs(7i|tF zy?&~=F9irNc9pH&_vPB!{V-#F^ibd$6qY&39}Y$}bjmvAQC%Bkuzvfil1*L&Ew6L4s$1auG@(xrM5=3NY`{lT#9Z5tf_Rp$x2A7!U2%Z0j<0=m?vm) z!|7-amMMO)Sj|Kntp3bp3v#S>IGx23+~sjx*Vz-@%kF1q zo!9Z@j*)PFOte_7_zlhKWMhSZQ6O=77l1cS#(o% zBWm;{HCWq`Aa_x!%zFYCK4LaCJ7L);Y#b@ajJ*td;l;-DO!q)?X#OS+XcX)mrmGuV zJL7>!vY}hSu`G(@FC68cU==fXY<2u8M$T|3fz~O-=2}Uy#i}Bxrw@;pizc`3P?Y&( zxc>prlEY0+v&3N=Vt#G&WGu8QZ#vC0kBZtj8-EPuI?Q^!XKWm?BR4pk3-Y2PkZ@zo zS|G8jzsQq5^#iq!ZdBC!4!od{0O2Mjh z|99|2C<>1+{JKLWw57NH3(}_gQDC}fePelFx62W#x!?`0x{G?a4)Uy9kTT1C50WFA z5DXWg3xAO(2#x%O?{@Gl_}H^tdqOcj_Vv!|C-aN87OCVwBmnQw$dwMiI75(c+S#He9q_BocwT+0E(0-PY$6r34I8^0lwFlvg! zD2fE544bNeTlo`C1F}`BK_JW~YTA^&lPb1Sy)$P(8;#4Uq{?5nAi7G_;qrz;9e`ZT z%hJqC+bb#}LCD~?vbqUvrj|@WHVbbIO>WXK8x7zabBP<>2UMWGR<$A;s{p)BW-mU6 zRummh2pZ{6-WnZ`qEg?M(Pj1nDn}AL|l~CND)()`{>?2zGTVoMk%C# zF~wwZH4lw>$$hR<)Z``6Doz;PP42Ls!wZidq4b+L;{d%F3 z(*@JOR7*$kDigu1GC8QshhKS_a`(rkV+&CSw20ffAY@ubJ}XZiPRtw4%Aam}ns*bl z2}naTA&C!7q62-=9X9x!?DFG3$T~1wjV|j89Mv>}(q4*(sb!J@NSo#~VKXsF21f(A zWB~UqOq_in3gEyL@x~CEA7_K%65!GzX%j(f3dltrQ^=|W>M#9EXqmdiHO9sds z0@_Uo>}j%ip{XpE<6o3=Wsf^LWNGSfT5C}@cnPWCCbQzyWew_!AdCY~dg33&aBkwJ z!-6&jWyrvzWgdar5;5C}1nz|l(C}={!yU*hW-X_#_8@8oK0#N5*cnp6e=y$LtP5_g zuTmap>p+5cO6AfUGAQ}p=&cMmiTj|*ou4vYD-W7NV=ZW9yO?n=-r645d}K47IFe3G zZPLn2h8Mg}tia}JnO-P9F=4~ktw{mZ5Fv^#7O`!DZSgwS`jOkYoM{&#G(Z~HMCg+9 zkXXbFZLCXwM+cNvkEJ9k`Ts(5tN(YxcaB#}Xxe~*)U`EsfD z50_e{gV<)Zg4Q%j9J8TYSH&bf9|m*8SSA%#)QfeH=p4BdT3W@wGHX)mBuQfYfYqnpT)jXeq27?;w zR(`I|(E_;<7@aYAJSJYHbKApdUo;kDoM)H;vA}Bq>6#BpwRR(ouivCYF9S>v7IDOt zSBOH>I#4LKuscAI*K^|3n-=312H^oQ8gO$y-GZl8|*y#&Gu;VVbO z+dguIs!LIND5kiRHpON7%5q|2vlUUd1u$BBjR$;5)`GKhu7 zM~Fj?YU$S*W`Es4mV*qu@iMrL0_96 zBMqBo=(b{&;K1B3o!n8*A<+Y7P!T0zllx@)ExohO+DU_yYm09Mu4%JvdeBTeefL}Y zB5N_U&t7tt8cCjl_-?RiY;4!vdlfH!)&r-F#m+o%XxXo1;HqaFI1t-QboCLVCs67} zbD1{S*oWRVO}PsR8A9==4V3DP_*crw(GqL*;V2=40hPD1fDgN1q z)R2EG`df*Eh+G0)gw|V*g-im=2C562jN&8%Q(6LLnf+I8QqvVRio9m_%>+^8TpEH zAVQM%L0L9UCb|}!PaLx@G|v%GZR{+2U6)oYM6f1dAUrL&mwD&`#})32-Rgo*iyI)` zAkv4RHulUcC>AI^p|l|!;WCRtoQ)zH!{P-IdWZlaCV7AYkGk{V)A5qvYn-#)W`Qlp zdap!?O=HMVWF4WuURJVyD3A*PYdY?GGLdHm_wnVD;CWx_Q53slcD{g`ESYiE#?R75 zVb{RsmAq9xDVnAltLt?PjFMG zfERZZCy{CbfjAlUvMVM)8- z9+P8v!96w?bp$&Wct^g#B`GEvd-c zs^J&cFag_RbN-L0^F2oRA9klAdx>4vOySR__kg|!1@5VQ@eu}IX~1AOOU;n{P8%-h zpwVXxTYigqH{>(2nF1%{MS!!N_d+qbf?N?TWePH6Y6^0ngam7>b*Zc{a=Ig6P#9Qd zsL4J4aAzG?Pf*?~^|t!cgK3~%O>YjaO z2A7dU0MEiAxIq)2E^u_C3vZX7LS7+N=(`@J=Q(~ujlOqgU~cJcm_?gaIF=yiC5+%0 zoZ=)UiMc%w2l=s`vvL-$aa=VpotVP`=8(y0E?{j7l3G?|`WdcO&3E$n1br0G3qTugX#!E67r&G*n*Znc-nqyo; zqlAZ)>o1qD*vv3{nE(B|($Z*Dnp;#9``+U;ziyjCsBQ zs4opJ(W{F9`ikJq;P!k_xsRc-MOW#HjmZLyx1s&z%)?<)Mgs{V`#QdSgIl*U&j^}e zm3V+Mp2fU#nvU1IJoq$x7K#i0y^b?h1izyrgh;|6Ys|iAsqz37(uW|PH)>=}oBkYE zb8^C^Q)9G^Z2IiYC?e})cxfvW$0j-sizu9))rJ`|o(@aEqvG4(v%fjy>n%yLnAXE2 zNX}guL>%TyWa}w0RJ_f`fh&U+hv_TA#NAH>OiXDq1_rWZ-o~LSn;<+z=A|HMlF^$3 zc7Qb;@Q_^10prfwIAEV1;PASo!37*zN>C8MQJIC~f6RPT=8v3_QFM@AvN5?Jf4FR8 zGT#AYQmex_p&PPoeYbgqp$UuhB2yv&aYzAp&a`e2Q-KJiS# z6R+l6-wEY&U0M(ZV)43{2Sqd-`V*-?dRCE&= zAVYgio~kNC%qHUuo7RlG1M36@Y-5@^M6O$E7V9dN1Nu|-9)a2Jf~ z6ijPO)l!0?t@MT6Uv8tf!3LU8K8UGX#gUguvZCDu;A|5BB)(OXSAa28wRD!>C$?X- z2~agDDg_bL_X_FaG$qwMO+wIdN-CG&26(JsP{Jsd2bV_!s-h<<1%~6NqF|Ln>s1a1mE(L!FIP?t zC@ShlQcG)@9G0EP=np6uX_`Xs@V0NGX(5?65ZHb|mCC8g`K~7PhO@|7n*2c7t?O)} zWlY4YM;%PWsYe}51RyO`p2j%^(^V2cnlmV9%%Cg(6oc*o{S5 z@X5@KU-0IlptvHqj5n9+P0KzYZH4)V5Yd|2%>i?Cg+mvx6PG9Sj}VftA}@16+;N7G zMl4IlxM;d)z?dB_>j|+RR3H`;yReU$t?ry5DDoUcZpU`fMzpGKXxDMu4uv)7u%tNo zNz~wQ3;d9{Gda%#fGq1YV)*Pv3_I7r_5%q2M?h9qKqsx4cZiVvP3i04CW zY9NzV){wG;p0cJ6{$2hIde(rLKZ(#Vax%RY_rBX=b=d3>$hae+qnYF(m38H4@w6<${(%Q?m!S zRv{X!q2@H;zdVM7FA__m$gogm{soG-A05RBsDETyW!)}u;0_*&ut4TG2CG04Q-Gl* zF+k8&vu@)Uh9r=P#8*4I1S9~>&OW*Xl0I{E3GQ>%Gl6+nyELe+2%s@8ju8epO!?W>O>) zT{P&NrP2lj>4qpVx2Q29YQM|22~PU(l0wt&lbVDVlzBubg?kSq6$S{ch?W5_x-^VD zlGZ@OI|oh5oMXXSXCiK>h%HMS1dkg>{n`oE>`)qSY}ei11Z~vc?M%?t&Q<_0q|KPX zp;!e$xneC?4q@IAdx8TRUiOm-tEsclH_zcj+U`FJb`iMMgkBQa1D5JZVv8LVAjECb z1n*4Co4C*(Xap4OOhV;{Bm_#2#g_`C3}*Tvf3QUlk44&SH*D&X9K$5LB7-{&gcZ7* zf#R(!c%?d}0lu6Rly2^2%b}H$z!ne_-Uy3iEq~*=aJI&pN$1;}p>ZTu`lwngRY9vZ zxci>L^1eax`&-&AQfJl%PyOttKmO-`{9j)_73|%m8N76l7HTTJ$V4RpD zDcNRJ)J27UHb{k?4bW9G+*AS_*96vQv?xko=z%V>ot^kBuJfOu4n{Xv;|6^9bKD;TjTD6I&f zVT2ckcwR6g7bC-lh=C3iD{lsd4lRQro=VJ~fG^lAE2vb5O5-iGKs~0m{-7(nkJ*9< zJl#h^$$aYHClnVf*L~EKer6s1K9)#XlerJG?hnY=;L4{$TCMv`3j2e4b{{pR?(UOV zu0QC>?)NMYu+Zv#)S>=B+nHE9>4v(^eQd9Uu4nffTK{18F*qr;vwxouA41Ob{uJGx zn%yVm;#7AZp!EmSvit1+Jk8xFX(NdTviq#=nC|YAhowK5k==)>&v5s#{PqXuWcM-c zoa64FqxYkLH31C`=Xq1crBW#Js(|Gjo zN0M+aAMqv$fJwh{7hu4kkW@yn}h=cDXJ){ z<7(Rk&K0)R$%@dM8mx{t zX%TRDfSBo^lh9^^m4Y5I;=vABML{PH4=xiDv=~CnK^ilLN&}|hz)qZ$0GN$Q`JPsn z-eCCwL&orWXz~!l?mhNZm%d^(6fXczkOHyClh6v_{jv(cT4)96Ipg3SwyV@b_c2wU z{-srH=+^P2VGiFI9;#((wJ?feIOolP25@>$?P-D)+xDJu^CLJVhR)mzgL;Y;A$Inj zPSduG)_t-xv3|`0Gy7WQgIS=$8B9LqS7!2IZDqbD#Y{e0Y}sO_%xI%5vjv55fwsw~ z;7vZ}D@w=6u)u_6OdP?|KC(qB}WDj34AJhgyB3oejEyu_Jw*nE{Ym63FMVI{%rDRWaB#9`3wm z)y$#JYeeLM&g%#A3m-8~IzaT3^mUr9L*7zz>?(^WcX8*=uF<(_Mw(C>D}|{REC3hj zwphf?Z8D3aLZYB{QeUAO`Q95VEpwkQuoEavxlCkCFDv70hN5YdI0SWyaY)7!rq!b@ zW)=L`EmIV46iAREMlBDN*58grt94b6m_CZXG5d~e9u2GCBw05>*!f;!iY<(ESP)e{rl zXcD7UsU+kMcddKEE4(j$e-s{>;dZ=*MD|-}Au$s%U8w97+oMV2%p+zGZCGTPvxmxS zE)B`o3bb36wG19hb9{;jGwatDhM{~m_E;ae>`iROg0f^cLqv!Qld|+|vFw#4F#~7p zJwd=vPW?t0#sQRqGU=rM#f-INqWRAU*B!4NeX)8^G0 zIkn#Wl$`c*wu++>JN@a320Te=K{i;$a3(MrFpE(TKXBvNUa29idxE{tyaPOGn+0jF zWp3aS)lL3y=j4 zw;<>?MM_hB=|e#*dAlee)1Q>6&Y`@G9v!oxJnloN@J0#c7sNLqtF)H`i*)o+`eqrD ztle2ncrH;Tto2M!)DmE;m2LRb7Sr82DJ#{aKnpHxt8}}V4Kim%Xo4tyQ;@=77>`$T ziu*_rRpy!{COe58A@F)W@K7;xS!|IU4YX<{4lN9U|59k2NyugQVN?V32sI$&v(PN{ zoVh%7%QhvT5%geb{peS^j2w&Tdg(GLVF}xvFe!D~m%PEK*&YH#0Y#g66DMa~1}a5e zVD|@i@>~n7Y}NwH7`)3fD-?C1&F+cN%9hq^iq=Axf`7E885oGUoBtn%c`xIu&Fz(SMo4+_f zT#FtIg85Qa3~kHHQXO396{!JS=dDodxXzoXrgNRQU;W}b@0`X!7%H9%jAccHx+=8dEBEV+f%oFz1{Y8&}$)H&5%$b3Pcl_iGy@GLqTMv=LsWqH5v zIXrw9rjKz@miJV>W=P;1FAD6Wkin-9rKn1rMf1VC9a zvbCW8Bep4tPxXq5paA6@v=>o?nN?A}%obKYb`>tEo$@K&vMADg%TlNT(X$dtQboQ= z8pqJc%SLjhI98R=}sJ1thEw)CYMp}VDOA1p$K|{6+XJ)Uqb0fOOS=(T0DpE5E z>0TwVcPhrJg|=*}p?kq+Qgj!sNWclMU>E_8fQS;GVDjkzkHj5a1T+p>YOTG~&Z=l| zFmQz4A*E+n>P+7th^8~E7O zqU>7nwmezJw@{%{)Hlqwy4a-|)-DuI&=%79Ud|TM`@i?NR4`uWfJs;K*JaHnEWk2B zb5EfSH&}`MgN~NP1w~R^5dd1mN}Huc@bE9ReTvYfL7^rugx<=DJod8~$HVA|`nuDuIGee1gzc3M>qQOh%e)Bb_63!ff3LJH z-TijG%l2O{SEk!{?NsmJ&u~s*x9mUW zIoG-WMh@ROxQ8i5W0&SNaHs~mgfgRJ%g@~!CGk3kGmf(7dcE85#u<%KyOd{7xB!J% zFoHR*m0H#C!(7;K7l$6T`@={oM|*C)I}K@lBo+6QClDP1HN?z6-BW^qZKz@IuqA2i zWG_uXHRh{nzp?{LbyaRC=CPu&h3+t)!^4#15=C*4LZu0ZVu@l{4?|BNQLQQJvH&2q z9SNQKpIZWG-&3%i(&YNklfkA0h3H8XB4<@n_Ej{Z0(>5;64~MTHs(TXTbcyBbA2r! zw<1$7nR=eo`Ru4epFpIUJ|Sz2K2}L2JCi2m-%bi9@*==V61DO$#V&6bQ%`vB6w+RlwlFT%OIPM3EYH=he^0e z|8RpTE3qgvqI;v6v)P{Z@et8Gpx!c&!Uet=wXH%a;=q_j0b?YBR2SFGNk2Mm%%hxBv8F+;969)IK2f;)5CoIq1!DsZ;*r~nukP` zNlBY(;W%}LN{ay5JPEik3@KqK(@($oe6KXOtwrO{oW=bN{{q_++C_1G5DiTDFJF7> zh+q<9lY;$yAmyWM4{NptlKWny}-WuyD695wn1W2BFVLr@^qH z{Mvk0R`~A`GhHiHB*;FDf{!9duVR~Wu*g%h8#eDHK!^vAv1ClgBW!pn|2Fyjnbue^ zW{(RIVjPnmnH$vD*$HzEd8`TbscC4L3npwrs}wW5D3_4by6xI-Fz*p3}0MH^VLTV=6kl12ntud9>&hL19*{{X~2JZfDm6KZgVV!krR8AeW8Zh zfMBgc(6ZMSlOIzC>EnX<(FhAH+<8p)G9Q1(Mm-1F)#z2vGJ~-P*5u-Vz!;&8U1jz% zCjXkn*}NW8^$TLJkdiYa*#H{3E8~reh*X-H0}-=(H@r=Y0WwTeq8UI6o{_;wE0mbL zh{z4W5G96SAes=&kgHp~U?Qwd{)kWF&Mdjit({Z(J1F%S<|(5V3j|Vr72=)Q|1cK8 zI8o=4&3F`ac00pBg#$VKW2Phg!(7hPhxBhkB4|MO#9a-ep&&3p+wc01s7F|h5syG8 zf;JSz=5o$JM9YH%A|i{a4nN~gwes{+?o^SN|EJXe28f9&n%0H=nE8=VDTV)JWr8`f z^W}+Wn3DMlB=v2L2)ArWCZ)QVW(~__8Xw_m6EwD%Y27vYqdXTq{3z-ua9AMnaJZ=S zGu14Dm`7p-`*g&WyjMq%drU{19U1}#R_l5H+C7B;KY z3!2Z`z_HHNowHDig_uGWl-wuvl;aaRy13yZI;AqV7)m4WR)ePOR-GRB&f|=ky5@4x zXyvY~r=9g!*&x1^&C$wsvBa;qY`1j#2*ObfJ(O?MM%xy|2UrL8C<=?^-^A{1s)(pu zdcGzd5^?mPgXCmknLu*1jn1%U$4OYufV)EwWu{>o=L~+ZV_DmhBAIE-LJeqbWst$? zDaJ_fI_i%{{P9tLeApik`Qrh9e83;~`=iEQWQqLw8;Ul1S3aIkWxq0`pu%!%f<<1~1iLxA@hCL70Ad_tkDFjTK0>0=~m3IHz_^(kiA1 zr+V4cRou+;JStLi$hP0n9H9IEZga>M-_aZf5fM>u?)1M_+hE{#s- ze*kDa_>jF`mY)v39DrhN(1i`_H~^Bpf|2?%B44rG|6OZx+{oc+Jc!$cO^XVyl;e~E z^Afea=u+R44m8weQg*|OCn3Ud8pM}dG1fsbC!jxu)IRjvH1J`h^cNY9kh6g`jeyG} zv?0?8%8-49y}z|JZ`Vjm3#AOX;3#H+TApDPH;8#Kdv%6ULO{cDYL*GFjZryB6wh-; zka!d?9u!=(^t0Uux7`36xJ}Xro;g#L_2DFa2s>IW0h)HU+7DzQYjrLZq~MIBgS^${ zmu)pBvx6cBtu$=6dav8yf=%8hLY`+*Lp&Le0Xx4pD8Y9zXKVLOns)n#$p+y(mwR4mip1w({S*+gl<<(%w zcX)|1Hp%E5tlP3Y6pJ+E@4*c3nLKj?wf)yUA)w*g8`+Ii5gMhm>zxD)Nlu2$vlKe~ z<=yW<{nJCuxu5@nioq;CI_iSPq({#XVL5g%-z6y)_ z_e%v}0_o-3?B+ZdJ#?eup(><|PRiQ05!&S&%^pz_Sbnt)?ob*XX^NxAuC%Q(?3h5| zzO6v0q*F(kMV|HO}PLT#e&+igsa?f}*H#%h2K8DkPm$OelzAZJAEzLTmI6jCtO-778 z{;QfS7&|GJ@m1PIvjov_Sg2xWTs`e^YkYBhA0Kh9L8>#j%R39)g^$W;6{r6zRN$C0 zcC(+9inIrU44)YIm1}%NUXipLGcma-6~9ji@i>sj0=d0O>fPEKSLv?kOeNjjj8O(8D@bjHnZn7Ccj#0d74C7*uF7 zAG1kd_7w!~zdGrGBHnJGw6$XRRHUR#rGpED*oaICj#Nl`r#6|RbPNM+vBp9s%EaO( z`Bj4l*Z_6mo3&054M$&0|7i8JpPFclGtsCr79ze%rre60mwVxfbc)Ms+88oKbI;O=zZRCsvHBk;wrnR=rX4$ z0R&T^TJanLhgPn3PCL%9`K2q^^=It);5(CfzH5v*9|bH^fJM5lh67p@vn0E1Si zfwW!e3TdpWI(sZr6iV>JfPh`wBA!|DR@j(|UyXSW@3N6g_Le+W3u3I0w(4ASEiJw4 ziyn!<;`)vjaY4AKDM%jnQ*y*R8o9dQ=Y-*5~P$WqzuKx5txMn8tIW%37Q#>Q^a9xwu93nG zkxwm|fyu^5~XzIm2(t*fXT=x3d{SI z%q|s~spdREdE)Fx%m!3QS6m5?6%Qz2j*HA*jNfB$cOwgWR30d${A9M^5{OYy0M5t8wLK4UGTn(T>j^JMA;B z?XyS4EWOEImf9*bL#a^FcP;qD`Xeu|wbZgR=bL-0G$g`1m^fPbiiO3R9fVd~?4& zil|OsmmZVEek+o}@9p|DqCqiffD=EH4E!>`o5fs9?PZFzGz@SWoLh8C?6g~$R+sw; z|BcaLTMq&u(e?0T;XXrVYuBF4dN1P~E%Je|&k_yiT(QOz;W;XrPItt^Qjrz{T1BFS z6R{7Y0p!0Kr_UbrVs5vtUd*nJ{yXdHv;RKp>USR3w%az0=?PtANv8nX_*uVAQ%q#~ zY06uPw*oebw$MLpYi&N+fG;EYV|7ou8Atkxea`Ql0jY4w5a2cc^C*k&AcDZ0+|+t1 zkSgGc)WK%M6e}lNUT6}d3~C(n;(?}A2hiSP4=9KY*0H}J`EV=7D7w=sp}6I?MG(auuxkB-?3rrUt&}D#( zdTD*@0Ae^95Ea6bxP3Rl2M0&>S+NERg6o};Gym!J-#S2F!nSkB+v)FSuy#>33?Er^($lP>+wlr(Ji zLgcoUu=H1{jx}~L^VpA_AGB_=g=*}` zyuur*&OyDiPvxXRZ>c{k4z|_YigYHp70rb{gNHF8<_kbNP>7eRw-7EW>JRQnqbtppK>HTbZ7F}8_Q8H@>0Za3(XN05 z{QU++(o0NG02BFE)!?LCm?sU-T|m0Mg>W;Jcd_So3%s^ba{B>tD-t7LM)Npa$`#xl zldZz6_%{#n&X$Mdah4`|Y+Ju(Q*+P)#FKT5UH!}^=R2nn22!@IAD@x;tKCVa+hSh? zv2_S+(Ia0l-(u1v69Qu_vbUv1J>W?x=}}M%QdLWhum_#Ed){j3y9CiUr|@x?z@?69 zReu+E6>BF((z(otIT$Yp!=|tTTrsqbPln<5nV1%NWXp^s-5n3wgJxhm@rr;Z z+2_w?Pp8{*rIUo;pW;a#>PjfeBG-0nx1J5vNyXo31-q&gSDp1OgKw z#eT9}xn(quoYWak2|dyv4-BUd@KlSt1()>xXSq0+3)y1!W*5>D?Lzr7m0U7^0%J@M zselRJ2ukMEWM~uyAe$11PU|M;`}#_ru+o2L7HlFLE5@A=2=jW`JP)|Y;K;>q9r$!~w>Xi$47Dw+8A z`_duk2MFjSDpl+S31ER}k;FxkSUh|L?MnEU-KK!pjnnDlgW;#cM@5s$Iqmr${L9iz zC$jHWrP?nDFmZOIneL3Ng5POPi!wD@T$Qob#hMLwERQl)*{2GW+mQB(|0XTw@ai`z zPtmPvB7E3CCoD>g-Y!ED=b^N=~$KQvw zRc(E?HJLv4ij~@Lo~G2AcGna=O{&`}b~@K>5x`DkM$1!E`43EWs;)_ELqL?E|37NC zodfm{VeN0HwYM0SV%t`C-IaDIeX3}RF6j4XQl~(DfQp3W0;i~P0v(Qc1AUa!gI^p{ z*2I?SAJ}c;3LCZ=v=+cm*7G}-q3$yRBZLY=Lsi?oiA`u*xg7APNs z0B!aP>8P=6{YG?^l3CS5XpPkynB9U&*_nT)JOys5wIFx~{T|RLDNd$N+q6Lz00lb& z8!B%$24w>9`$1JF1w5c<37<{aD6TIrP76?6Nj7ZAfsV~nfd)6wl(G&|8_Bl;eL*RZ z@Z~Hp4n^{)3cE>2c^&+)%nX(R{}7-~kC~Rr2;}MCFvwv>C9-0|00`6?fNEBs4S=|} z5Gq?47o~4vMx*s_hHEfCpX~=|6H{jc00^w1KeS_axizGhbc6XslrF{ZFjl;*^Ai{{&7)` zDapDOw-53_bh^rnB)*)-5gWs91C9Zg_A=l{iGj%m z93C8{qBuX`RF+4_lHri}0+cDf2vmXY@L@`3PY`C9yLm&JuV(nWx zXTblbj^!1h(83S-)8WR3#(d(4Yo2`oId|HSC&h_09OfzsVc^Wi84z?mSuseH@IA%= zp({xvWjlR79}}*>m0$P)VgO)n=@bnR8i5ALuCf90<$5bt7WQpJi5$yVONSXNkf~i* zn5|w1t)0F>qnfY_Fe*xaA01tsJA_@O4|CDUKn@jw4=S2t9GrOdB}Vr&&1FHL#RK4F z*fgV?U+hg`Ra&33@QiC8vOU8(xx`U%i6e$IM?IHdh%oPq~Lk$q-HDBLY~Dds1joS;^)$hCac+kSj*}BM7C0&b%2s z2d`Qh{13ZJs=(#JAL*2R9N>dY6k%}rlJWGf#4$78q-stSmIhx#Gs3hY=EB6!7>h^2 zBaq46G(r9&3<$D)rw!ASxYLq!;H?98&-u(V{>|W-@Zj+m?eWJt z_vcs;{e(>l*+Zg}V%VDJdFy6h@E>4Q>hfjqJV;z=H{3MDm6r9f>iTEG&GipZYBm61 z*yRUIUlDvoM}zUw;OlmdE2gj{_^!_DD}#U1v1dhi!oLR-{V}Jh{9kN%@I0>yqviNA zl=J`%(&y|Bo%16Zq<0&cx&_$w(LTFTZ@&Tgv4;**ZxzN~sE&Pe123TfNw9l%3-4C& z1GS0q#?^!3=)|=qHAi%fs&XLYsKu2Kmxi>xGrEZUMTk0`ihO9sZMLfiQ^&Dgt}?2I z>Z#`pk>xz;V7DRDUfOMXg5`wEXCDHo^k_cG(e-wl)&^=_F{=7kp|Na)AZv8j#l%!f$CVdS}|T9 z?*`bP*$OmoBJwn`qwTtBacY`LqQB2l(kz=xS?H9lTH+F|!}La=%`_Xcsczx+l?hGm z;bWn8!G=gV$}#*DN7=ZJa-10tMC~PB*jY51Nu!5(Um>i;19Dq3FrgOo<5+o^#85J* zLCP|imH0!Bu~pUY-$)v3uep~bXjH6gnt4XYA9gmP8Y>&$Pf0wsxTTbr6JT~o^0NI*3R z)!D-UYXGS$v1EUn%>*cuk4UkKn5B$3ZP_rTElOhD8#xl+u7600&2lvS$}YhqAvEZy z7#_6RpMk|_O+i}J?_plm2vex>6I5@hnb5h$nm-t4Uo=<#CHOQ+hg{N$Nw7pBZKjrd zN>M97mO<|J+DCFE*~T*b|HNbNnlDz09l7a`F)CRhxI;#D)ATjZZ?OQ^?#kMN{gubI z|6Cs@*ifN{2z?$U)s$A0rRKR{`G8t`Dd-sK>q}+1qu{=1ndCoKJsXX<6O%Dgg2{9g zUP8zCwO-T}xt4$uOp8wAy&FG21ype&pH zRrLcqZhPAnrgNq5WpuTkwvF8O;+YUTF9hUv1~TNjnTVyYFSZfI_ono z>aIfbL<|$Y!X9^eL9gtVK^ISt2aI~P&-d~^J6iJu3R_*Ieu8Yix15T`HGP7`wzAYD z2y0P3ci|i@pQD7ULtM#c1qW$OmoRrK3|Cd|!bw`TjajBs96b{5k=UmI(sSfUHRjxmb>!5enE}q>z82mDfg{i=>F>G^piN0`yHfF$; za94scQ*3*+T6%z4Ju}OP$n4P>KanX;-NE3u4n2O)<6nI76ZP6Nxos4~Lx+ME4Me7z znZtQF1*OD8mPD(7EKh zk>fnQuyIL~F;O;ZfCSuZNfuxX-cHymVl?D&Ak|QzS84=P9`h*8lB8j(JTon9)0w#N zlb<^RD>jAFwSae|^{U|Xel@Su(L5?Z9kXX2)wBBeXDAQ$Y)sE^u;kt1J5&Oz@N63x8PqRyaPE0(8_@Enr|^3ex9QHm;s_*(Pz7m>l7ugyqZ{H4TVBlk~XW zmhph|MU6G-*yp*{v%Pl{-*=nHz?vZ>{?m#BLk#*ru1v6`y zAGN%5e{woB6&c|I8|d8Jz;}j16Dn}tMO1c@9@UY8gV6 z)Y$Px*-au*2|Y@CuL2}AY^M-M3SC5FNP(2@K@$m?VTI{Q*Ly3GXDMF~H?E^jFyl;h zE`5!vG~d-2?ho43crVQr?Aljklaxa$32KW0jG)d?GIS)6PWS&a{x`dozW5`5+D59{ z9Nx93;F1+XWQZ%Oqm3A_1#Ikn-7B4vacun*0#z!0TXDI)%1?&#dMql^-2 zm8pg%Fh5hsGR0{|$F%Bdsx9dSCF^X`b&6s?jIQBy<~C+0N(O3Gq4g$RY^xzqv2n<# z&yl5XQ0&a4#5xQzjKe3aSR`;Wq>aV|OloLoPJ3Iujkm$znG^C%-B3;T&;6+c( zq6r{`6^t*-LZs@@v#{M`oS-(^Ae=8iJr>Aq;vi!8c+v5uVD9n4+6Ye7-tM^=Fk#ti zKn)@}&{N;FiZaUy=bLmx2P*)O`}qNjOPOH;=-L-TUr>TJs=_kdb&@0vJnqJt)C%JQ z>kcU6?7$)`mztU`2q09VVdH&8Qv?M3gI!v2z%C>eCd0Ipf&@f`d#b?>)rBlgVGkeB zT9BPL$5&(Msm|X~!he_lmw@puj_YnM+sIB!ceS+Y0_)$tR^={R=ojm}bQkMRF$uEL zL26up-daVU0kiJn^{(q&W&_^byK9$__Ad46u3c>MQmK78IxU%#AlB-jcS+w)m_c*~ zGtqmA4l)1o7~rS;eq1}~9SBwg!JOWSY>I_nTEXq76;nE1~z;#`b0Cy`q6)2#wwubXxVw(e~fKwGq7Nb20J)5$OB^D2;M4cpGsn9ZW8U z?3$nfS&uQD(Pa$>&l>Za;&i6Z1hwt1T#{-d&p|NrH^8eMp0P`U<*(~Zs<+T*1uLxgqAe*P7V%y}%u-8VL=w-0nuvw9@C*nV> zH&tzBL}Fl4MY0N1qK!A1D%RePy{1OI6fcNe6($GzRE=2J?xt>uX|RG=01s&_(Wr_3 za}#_*dG!SJWAU1VjZ!zaU_@5_jGgdYGQ`-LRbIeklpsv9XWdDc)_R<8_S3|ZPZ z^S+?=!Et3fMG8E6#Tb~OY`f5Li1_s|FTV&T7WI0!*bw6m3gHRM6>7~vc%V55sYNvh z;h;H)HV5G!1&-Ss1To1RBvYMDdMN@cSDwj~$pnO9fYmEk7Vk3!fwlQ52!g$ht+_H*Huqv}nWV(8z@&+XFh%2(G2v_m+czxm&Pybad$U zt)t24mSp{gog3Bg}m?Ik-1efxc)v!@Mf+O2EXZ5aJfvSsJc$Ss?;{0i?sMV+ea5sq^?c1J-_9;X#jWvHer~gEw|i0 zN;?L^Mlh4->!~Z5^u6t)g2-+Dy$2|8G4Ey9{|DE^GQA4$A01hA`EOZ?rz-!lGgqoY16>bVY^aE5@cUImrT=(dKFMl5|%<8y)j_c`M4-T!n z?UIXccZX{>+%dHNt!uXrt=_zI?WPUu*Q{N?9=z~ZZ@q2ph(Eo0X!EV3x8?U;v#!~& zeLHWzXK3r%k+q{+Mih$v^Za^GRj16DKKuM~(SLQ`shRazU6pfBO+9DcoN1@RAa2y= zcK4JDk$d4~o-cau_MwsOi*6nPmA8*>**vu9!gXs$Zr!qIWa!onVBUv}Y&L9OKXk{1 z>uB-N_6s*`>0ffoMN8LTeDTdt^JR+{Uv$f&n>Vc;9=iDE8@F$`eSQB$7cRc=V*76E zmXXo5H*XpOslFLEf(^j(zZ+Hw7NTx*Q7=huRhRjyyv zhHrm~hEv!6f@}M4ajv%S`k-t3r+nM9>p$^r&#wQ*x1CRd+duJ7KiL1>-}%Y~ANZ}0 ze(n34-t-&4blu3ur~g%P%Bf%ekIy`_s=V>ao*VD`wJ-j`i{G3zbL{Fl?_YoZr{Dej z@*h?Ix6glf*8e>GgZGOzy7?2I6J2y(iw1RXtk^KRL`zpbT{}14@ahcmy4yy!Y~Iqp z_`*vsyyPMqU&j^~fy>)9#*b0I==p2>VLT49>u>nBtlbW>8Na+`?FPtqd2;)P&B@9) zC%3P?V{55ld_-d}p8G0|?-|>S0N5;Em(_a#b)Lsxc8x*ApSU-lzpOF=NQ@`D{vqcAM0WkX z)2{#SwCj)xR9E)?bgtEzo335EVdv1>AlvIU+&+}NDS6utXc&QYQ!e&~)YjYU4P(sb zl!D;(T)$?(-*U?>+lNLM-69?e^iPx>!u$z!uI8`M)7uqw*L%vy zKi}+C?>L+P#>U=p^)LRPnsDu!v-xjqY|XoW@&8o+b=RNGe`90U-*7hnjg8&#zF+)5 zwf_SjJe&W<#y)t{FaDqEzj^)H{C65Z?w^z&zh=yLvxLE-^+(fI4dgrz**4X2hWQBZqD?d z<;%AZt=qDB{q|(bj?rYxEy>MWc5wBTMkAPqoO{hkaJ|>Rr29~pbgvKbr%Co}R;MHT zPLdDSPiX|2j9^ZLgWnW> zQ~6EfH=W-Me&_Hzm)}f&v;1#1zd8Ko@;i^;`TXYbOZYYUwfJ4YZ$7`*@q0bLH}LD@ zw}9Up`MrtXLVo@HF66g}-(r3j`QOF-F5!16zsvY7;kT6EGJe0rZ#lme{8sXNGrza+ zyPTi6Udm4l?5+IX#_tM#SMqy1zg7HJ``O1Be(&b@9)8#H zdoRE1`TZMyH}Lyqe(&S=etsX|_iy=qkl&5`ZsNC=-)oEv8#ZHR+q`zucJMIM@nl?) z@hiI?;(SiWwG1<}`FqWp<|mWh1{HKqZU_Z^y_`GPIeJZpofM!Si!?Zm%aW zh0(PeHgD&tS$D7}VW?RLxMak7fp?qvyBoGAo41To+?E};-loRTxDDHO3?&k=SOV84 zBD%(mMnDG1VDpw_>lTbZm_x?D!0dN)-EDb`-^TlWoppE)HL`Z|ts0Y2p34YflkO2` z0b7`C9~#0mdZPnH-vVpR`XRcqY5N+@&aaSB^2Qsl#lmyPZEG>rq6!SIU4Nz7HLu&U zf5u1zjMdvJ8yZn8giY7^4qqI=I^B=>$YEkjrK!t z+i>fK(JTHR;@$&3imF}wpR;{8g@jZfz|uoaZ!`%IAk@&MC}|rAge0a=6eSb|6$Atn z8v@cnij5))77#1gQBgp}ii-NGSowdS*_~yv+WF7GF6t8oXJW2?^!yL1783IOvwI+W+p1it7|$|lom5Nk<}?ppPRsUsovU{=rDca zP@f1P-wa(QOn5_18PxoDkGc5zJie%a(_cXWL& z@ABJTXLk)Vr)8dUH&xnQ$Q@C1uR|=F_7Z*}v~x&li`}~Ybt)%6zRXin<91h-PMlQJ zHs9sO!;+fL?)>}?F0WCJJG<-0hUxj(9;y3ieq)eaW!#}wx!;PdbbygVvYFft` zm(plVj>+Og56A|&t+=mqb#Qe+xUY9#AMc9qV5|!GS00#%PbjaQO4h%O|I+74JClju z#2Ip5NlZ>mNlZ;lOH5D9NX$&kN|IYma#BiCYEoKKdQwJGW>QviVset)cal?+(~{GZ zGmIU^+_H6txUZfThrS(%BMNtwx+ zDVeF6X_@Jna+Ax-B8piApM}?1T$Y9D@F2#Z?7}WJ#yIc2-g{0DsoGsY)s>phMY^IL z=Nd`lCrBBe$q?!P&p$2u$R6e*Q)HO-&wnz23;dJ8oJGr?zn4pQ&r_x4MHRAg#r>(a zrleygi)b8Hd$?J881`CZQau+(NReW$}N*? zAK@Cg!9R_ZPWuQ_3fu5QIyJdZ7wGqL??|7O$1ZMJ#*-U;wX7z(<0YEa_%AUs;&TDN zWJ0t_<1qcE&>G2evfuB&R{u+@Xd`edor#?uOD1Fg;+D}uQAM#Qp_1W?u}p%yI<9Ml zl#V|xK$m6Y97s`-GV;`T@EFq!#+0I>Z`sDV%y4W*+***6?|H`qD@V1z8M~@qS^@L*2;$>U7(y&OHa8JNEXeyL;TZDsf5E*WP&L;@}}; z$JrhA!rCN!{Y`mAMz@eHS@-P)_v6_QyDm2PE*xtxK z+HSJN1m{_LnLC=5CEQjoxU;2ks|oI@mdUN^j#}sLGB>o%{oOpq9_@^EMg~U)Pqw*i z4Q*rW?X7)W9W22XWljp}U};%z@K{Gect>-y zdd=$9EwaqLvq4bQ!WGs8YZtpIG}c-7Wb>Nfy0;nzTk9@b>rMrKcb_@KIcI!i-CjrC zi&j@`7qiQj>FDDKw$%hRF^{#3an{X?jdMji2U_ZGv29x)9AimZYnk(IYkRQOTDLK5 z&JT9wZfE1{VoTkV=0@gvA(~C`cA3RwwcAZRH=100V;IWs1&5pJTO-0FRg@{l)F33z z+St)lwN#TWQ%pO}yZJq!W2RS4Zv?;Te9QE<>0Nc&ddBpr!lreDmzs^B(V zx(^<*WcBL%ue){m9qS(2f75Q8-I?C4`>5}aziNq$P0tuLdiK^GJD*59U4OyNx2^V9 zMyZQ~hZK9pKene)oZaCHii}Cm>b!Z&iFceCOP6i7ySj8QDP3}Vc*TT+Uw<>U@L#`O z96sXidpaewi6432nzawC-?aI`{Rf`41qDYn?tDeg(2bj3dU=h#LBr;)x?l0>xo<8W zK4NjVXw^DCC9`wC{sZ%dj~pd+FTcoBGNpRP^|NnTzjf!XL&taQEU$QS`PI#@vzjd( z%_U})(5Y^2V{=lyI7>@s6Ki{GZ%b&qx~;aBmX?-yM{3aEUUM>>(Jn`9mz*qfk;9o7 z&BoDuo;<7vKJ=|?9!OnDZXKO>dCD@*qn~@TdV(;W|&1pSsKzm2K=!UK1 zB4eC`37~gK1G~%C&(X$N8`QIVJ6jj4%Qn=etYKzr-K~X9`Z-*68?SDj6XddmM0B>f z(mPmU>h^Ul9vFSfysGnnaNUq&g_hTPdV{_l!40EW%Wy`X==A<{UcQMC}QuUHU z=G{|L8&vn~Edz@}<|l?lFWEY`@7jHHv+V6G<87^7eO&R@h`GDQc?MXr?BPA6O5AzD zG5@W0&UK&8NvWqA+d?glIg4(#OtFTTo%XQX^ZPn$y4L;Zs&-UH^|@A>!f0oMx&?Fk zns4k`FKT{XQ=6^s&Gy#r%~WMab3==1PLHPHovmul@pf}Rsr#wzK#R*_nit-CV7I!b zyV{gxq_t71X-;SdOL6chSKW@x#vvUnPFjSm?(TUfEaB!5^K{Du8||xJuqBhk#XFi0 zo--o2F>y(EgyPg`uY0kTYrgFt`*2@hK7mKS0){;!s%G}#UOHUfY0)3OM%I*M>IeE$ z<%HVG2{je^%L@}2!Y~kP;eF`KqkEUZy`hiiSnh|r7|CB1vBp$C#@+mhruofhb#NzI z6Z2D})|^heus%N{c;lLkCcmD}(Ed2x>vvVqyUyOMpK_h)*D_>n?zYfo0}^5j26Smq zFeWkX%$V<+6kL^<+N|LG6=w?1?_67S-+{Ho+OY!9#8=OFw08?iw9{)R?)b1- z>6afjn{xcKwNrlm{>&7mm6x4Y2g}+hn+j4b7z_2~GgAd7lg9oi7UeW4^{f4uvZ|zx zoG|g9>Y##bt{{bbS5%ntIFw8AGYd*JX*4dVa}d|6*x2^w4vMbP(YAAj_~)RXv%7e0 zwVvv%EEb1~G&LlQEP54-Oi`&Oyv`pax>XTsq$!#{r?UixTe~U9)I@dTP1a=kA^bC2 z>5jzOSa(CUH$|G_Oj#zc&y7_-MGvmVDw93PS*T1-r&9J@QzK%cR7NO?FtRxMhcX43xu~Nxg}2U|t7&G7$*FAS_e>_j z=XXK*GdXN7lWLs-=bTg1X9fm6>NJo2Cja z;xf*XX9{gTSjCtpn$0GM^x3KbFE`$4YFIx+wRSWJ>SRtNdrhWr`s|)I8FB_I2jv)~ zQiy@cWF^BJsZd=OTb<^a#KS^(%fq!WvkDGtj=qHi8?_}tbu{%-QN-dywbIIcqMdST ze7R$_#G5T@gNebLDbL&|D9Li2O0Ux$duC?JYC9G6G2|{VqsZ?^7BW~pyw^*k|Q&)sj z3rGv@#BYn%$rPiRona<>6Uz#|8?+=lRH%x!s(SbyuKR42er!#srn*^p0)?*)u`xd)aog!g?brO6W*6PDnq_S)ZN5QW z@R>$yV$a0&C@*NNy|K3O#<$Ki)=tf7o_M;TmG*hF)`{O1w7%urhgxShv^7j1SILAJ z)l@x0Gdr$Y+D%QY`Kq1dISrenOEM$XaCNI=qiW5jLan8yn%a}9Q6`(_w6`~zdmQSa znQW%k;J~%~H8rJ4F40gK4Vi3~5L%$UMz5P5LZrgdDDhqXsMuJTZ?-7D6gTl&*jby! zYzo#x(mK^|GQWh%(M9T-vc_7KS!=ICZFw345REiwdHLKjYl5ikQbsH(GL&zyt;O{_HMHg+UYYR zcYIhd`rOCO#?TLsacf_XIj?4q<#jx}=I>+s1tm(~N*5}PrG>X|r5}r>OQsC1?af^s z?P8TXuy>W-km@Y8UAg{8{VVw^{j1qU|JsFXgSj&PZ~Is}F?w0)bETiX6pHk zp@!1m20IGrZ-f3HdfUJDwNAxYTGjrSJ#C`&v((6c>Sb32^sz^JXzCf>a9&LBso6@U zCG#?Ko{6U};{NwNWE<7X-#;b<^p5SQVqZ(P(JN}Kk}x0V?fd=*y`Eil`)_+Z3PDAy z(12c#T%}=2mMhbjl+>cI0oOqaiH7PUWISrHTVu*hwtGHke9RGgdgB7_z_OP1$YO3hIio?cn^Ms zlkgk755L0)@CV3jCif3+6m%;F01i=sjp->OPfDhxe`Vax~;#U+z zLkz@1184}1APyRXytUmF+|Ue~LknmLt)Mlufp}o6BCQ>?hYrvYIssz~EfF+2*koi1 zq(T~`Lk46)XJ7*ktt)hc?r;V4fNbaqy`VSbKp*G}{h&V#fPpXw2E!1@1L~SK42Hu9 z7zv|bG+YT|U@VM-@o*I|TGJ*#J`_M96hSd~padquBq)W+z`$24gQ-vs6;KJ+KowL& z4b;Lkm<}`GA~`b?c`aNAv*3ET0p`GPlcvo@&V%`IBisbCD7yf;5N?K1=(iw8BX316 zg4^JISOR0PFGY?+E<@f9%OM|q1+oZv2a>)_yAwGHDQ}VA1@}S)`hCbM> z-~c=U&%zNn?32$SpNFG9`2zAKc+n?cM!o{a;5fVrufgl^2D}Mx!P{^G-hp@FJva#; zz$rKlXW&El2tJ0h@CkehpTXzw1$+tT;4AnVzJYJyd-%>L|AqVkeuS0q6C8vK@H6}Z zzrt_uJNy9`K_gEXKWaQ#X^e_A3s}Jhc5r|bTo45Er9udVLOlqBaHtOv5D8Hb4KWZ4 z4WJ=3f;ealO`s{bVKwn+j%)!fp%t`-Ho%818lPur?Vvq$fR4}!5+D(hAQ@7iGk7}C z4(*F}&fS=(z_yvB2-{Cs=17^WRxE^>G)bfBQ4Q(ixU>KNTIIyOn zjQ}f*1RIP3JB$VgTnRjpu~P-@1ja%TjDuhp4NO4<0t^hamfM$>l&7mi>fL_oN zdP6J7f!5Fm+CX23hkno&a-kjchxRZ4I>12a2!o*A?40{Kt?g-`^=;DHjD2$P@` zCc_jcgQ-vs6;KJ+KowL&4b;Lkm<}^wCR_{G!7R8QZh+ZP2Xo*SxD^(`Vz>>Kz*1NS zx5IK+0W0ASxD!^vU2r$t1NXvxuo~`%HLw=e!2_@!Ho!*M1e;+CY=v#G9Ug=ouoHH{ zZg>bDhDYF0cntQyUf2iw;c++sPr#FK5DvjHcnY3|XW&^l3`gKOI110h3-BVm1TVuY za2#HR*Wh({1KxzU;B7bo@4&n89-M^t;R84Yr{N5I2p_@6a27s+PvJB89KL`r;T(Jg zU&A+W9=?U|;CuKN`~W|~PjCT#hF{=U_zixCKj0#0R@ye0zzi0!f(`8804KO02zc7p zLLe0CK^TNXeTaZah=OQ{fmmn&4WSXlL1SnFO~DP#pgFXFme2}XLmP;Pw$KjRLkH*x zoge{{pa7Dg5K^EBQlS{qzys+}0vRw7GGP*AK`C^G$$8}gtI zhQb^e26JIJ%!3gyA4bBBFbZx0na7g73f3YOavhTGV6+F2vKhvDqy@PFDH}v=M9N3= zn~-+oW~2kT1?fa?MY@pNkU_}p$YA7y$PnZXWGHecvL135lHcssb|b@)4Nj4{7MayD`ovJSZ$IS2U= zaxU^=VN>@)_iE^X$iv8$$Ro%*kk29SL>@)1LOzeY3;6=_Zsd!| zdyp?7??t|hybt*bay9Z8@_yuT?;*D#Pa?M?-$y=(`~bNFc?!7`c^bJ3c?P)~`62Ql}h3b_V(8o3sE2DuLTA@Tv_N67Wa zkC7XYXOSC`pCC6OKSgdveumtF{2aLz`2}(t@=N4)Pn$ghw)kY6KrBELcILY_zN zMt+NY2>BiIVdVG7N09$QK8pMS`55v?75~3g)Vjvb8KtpH*anKl=KvQr- zGiVMipe3||*3bsxp)It7_Rs-3LMKRoL`Z^UNP$#HgLKG%Ovr-H&;`0eH|P#mKo7`< zp3n<=Lk{$TzR(YHp+5|OfiMUL!w|@Wp)d@F!w47&qhK^#31eU^jDzuT6;uow2hes~-Xz!UH!9E3yg6g&;jz_V}|j=*zp z6rP6{;6-=|UWQlT7#xRJ;Wc<2-hemZEqEJFz&r3Ryay-YefR)Q!D%=HAHqlQF`R`@ z;8XYvK8G*hOE?E#!PoE&oQH4WJNO>{1wX)#@Dp5spWzqy6@G)?;Sab74*4ntoS?u3 zCI|vE1cL=azzU&YgL+_xSO|j#5DpEYJ~V;|h=WLI3{emdZfFb5pdB=a_Rs=4KuhQd zt)LUMh6HE>nUDxskOZ9}8M;6UbcIyt25Hb8(%}lofPT;eav>Y~Lr)k0y1WRB6tb(O*7c7Ij;dZzOmczZU0`7yAuo~`w`{7R53~OKutc9(x4z|Gq zupQRJgRlX1z(&{!n_w^Of_<dC6+92s@B-Api%<(M!833Ij>9|f zD!dER;60cQpTTMP9L~TOa4mcZ*TFeB3m0Gp{0uYU7nlXV!u9YQd=IUg5hvz_+99XV z6xt)tB9oDWAs;HC0Iq>TsDdJ>hGM7z57a^lOoNFq9VWpHD216Y8LovXa2=GvESL({ zLpj_46)+p-!F+hECHVq-;8oZQufaZe9rnW;@Ho5)2jDGu0^Wuv;RGCnci<4b3s1p& z@HCu+XW)H!7CwN(a0-sVX?PCKz)|=Ro`;X%1^5_VgtPDxd;%}Sr|=4V2FKuYkS|vf zK|vCjAQ{Y%0v1RGE2M!9(!mZH;DAhULKe87GXy~w2!^f@0^J}KxYpNghMve zhn^4ty&w{LLlopdH1vTO=nJvX4;nx&G=%=p2nIkL41~ro2%5lPXbMBX4SCQEhC*`~ z1}$JXw1g4R3PwU}7zJ%$G{nP|&=$r(I~WV?VH|XT@z4>jf=+NXB)|m7hXN>sDa>yd zA+P6U;$o5L6Oogk6ehzID1)gml=J1tYmgO4*08loS?U z2G+tlcmUSJ2G|IjU^8rit*{NY!-KE`cET>$4G+P?@CZB#kHH?;3;SR{JPrro33w6? z!XbDHo`z@OSvU+w;5j%7&%+DwBD@4I!z*wMj>D_)8oUl~z?<+EybUMd9e5YsgOl(+ zd;q85G@OAC;UoAG&cRpkHGBu(LywKL3+M^Gpf}_|ALtAHAQ$?>02l~^U@#1UJQxbY zU^t9`kuVBI!<8@w#=4(7mIm225iEw=UR`U7#y;gYIwz^nh&W3B8~<10^sKCP67ohAB`6 zQ=uFxU?yA(*TF2f9%j<-+<=@7bub6!!aSG{H^NP@0B(gvuoyh_Crgpb;C5IJcfg&n z3hsit;U2gb?t|5EKdgbZunr!8^{@dp!Y0@ZTVN}c5Y9H_c;t3u0rEj)8FB}5GIAGk zHF7s{0rDZ_eaMHA!_?kOQru543^45D)#J zE#yKw=nw5-0Ca$X&=CefCm0L~Fa#1I50YRgB*QRBf#Hw}BOnb%LOP6s3>XcWa3y5H z80ZXRL9-~v4+pJjHsP zVm(i>9>! z&>dpn3gAsT*5efGd5ZNo#d@A%Jx;Nnr&y0utmi4#;}q+8iuE|fdY)oEPO+Y+SdUYz z=PB0X6zh44^*F_Po?<;tv7V<`k5jDYDc0i@>v@XxIK_INVm(f=o~KxkQ>-^B)^rqW zPKtFM#k!MXZAY>8q*&iktUoE%cob_;igg~vI+S9qN3j;ASnpA+M=92P6l+q7bsxpL zlw$2iu{Nbx|52<@Db|1#YgCGLAjLYBVl7CqR;5@EQmj`g)`S%6Rf=^X#hR63ZAh_h zrC1+QtX(PAh!pErighBz8kSsyL-B*hw+Vl7FrMy6O#Qml6=)|3=$UW#=k#k!YbZAr2ArC485tcfV?PPhhE zK^5Eu)o?e|z&%h4_rf%|52nLvm;v|0OjrZg!djRG55V=X9&UgQFdH^P9c+R*uo>pU z7MKTHVLohw8(}-#1P{Uj*Z~V+C)^CX;1<{oi{N2c43EHV@F*;S$6zV!fn~54Zijua z0v?BzZ~*FpDLa@0Pr+Px8s@<>Fdv?U8{shA1V>;2JO>NmDBKLs!!7Uv+zKzkB6tZF z!^?0RyaG$$7%YY3unb;>+u=1>4zI%scmr0#n{Wrb1$V;RunJDVUGNUv4e!D|@E+U? zC*eMLA6CN$a6g=aHEc3>)DHY=Y-t zGaQ92@H}jV7hoH_2;1Q$co1HO9qB$v6&`}u;9+S6;D(;i40=H+Lj=3L_ru?=HKG06G_q;;#V+}6 z125X<2S0J|)!*2))dTA`uDsE%HU7=gFL|#?dtky2SG`V%tM1f8D26 z?eVT_ijHL2wTq8^_)5Cl>fbDYO)S_ZwxWDgL0K(3V@#A!vi#dv@Yz-|I|3DZie;OS z(sI5n9)XkcDdiQ@%jL^pUGw<%v@j$agVYw3acXew)IyK3@hkcueDYm9%!AKWL)c~5 z`?0zl$$rDbJvBqBI73Jr_O7kuTQT+x8B$v8yO8ZHPzD)WQ?PYMQ9*eH-wYR&4fJg- zTkI(zsx=;;UXknnyxWMOd|u{_r5yEP*G;zT=p&mQ_0hNBH1>uZUNMz3<(|A@xudd2 z_*#w^~PbD+iN?CT^sH&BXJ_h>Sjzk9Gw%3rsBXX`B z(Q$ZQPOscPxxL)un1A4$d>8&_|FZM)2Ilt49+5j_usGB1#a+nXxa&D6Z@9kO%cxR) zCl#;K*HiAPVlUonJ;k!m<;W4eMC4|t8E%U`6*Zn2UTuU&K7aQqdB&&L-g6Wq1ybZy zhUbhJo-=f0&fs1-BS-Yf^cr&pj~tXUj2!AcEGK*5ke>Z>dW~@B_RbkRf+Xe)8$5F0 zz#+Xx!cJtQ1g6vmNJ;|+q1Me>S^Tyow;+|zrEurY&*qC?{9v4>8hS?b=DY;XM zzBLTHfctj8JA~gO@Ov!CcbD?_2OB3#8JPN+wO{tkzw(D62hM#nWYJv}*2CM!>~0ym z>&KdXBU=t@_wWmMhkWyV*>A7LPkVaBua0XwwGRDb^N$xsPx$Kds_p&OkC>R1(dfNz z8f{oTv;TF?nw~fmdF+PQ{>ZU(-P7Txx2_Jo^Z4Abqc1IYGegoPRjDo$ZCiGxc)ob# z`p_i8w)k*r*$WRmr3HQIk{HRERNt8#WNcZOHUYT~WU}`1D|wpBN-s=FFG$NwOf4)a zDDspfrDr91k}^_?Qqt24QZq_Q(h3VQQwvg4OOn%zi;FWnnZ;Qp8Ck__*q&TmlK3Cm z>ZVZN9QrSNEb}cbU&{Vt7;GVwpl{~Qei79PQwypqJEnPxl0C`Ai3RDIMM)*8B?(0- zMd^vD8T#1?{ywSNyZ;ZH4cAPlnOP}26K9Juf@8LO@NRwOX441XZaT8rNR6D4&Wug3 zv1{6C_INHKPp^Ye_&BG~HoTTfYipm|5cd(+p=QMuJUSbQzSW|W=5%O4ZJ8nOe-VI&d?eReIUoOb(5hF;y5ut zAIx!}d&#r(fb$-XlLK@qU;iil2K2!2R-$JHxWD61 z`YLp}BO2kY=2+^Rp|9aMFnnq2f$kqbmv(Qs-^j6SYh~!0{-kgIll~NXV0xZGmz_t9 z>kt1)KZ4H0v;X{a90!)iYv_UTc^y44zduF~Oy4Je(&g)|!1MC;R$%%w=m^_mGvb$t z9(cYxx{dP^2l+$Q`hnp-4}-+lH^`C=->N1z6iw>o&6SEm*(gs>zV2FX^SPu=Zq?FZ^oOp+VcIa{U@~1Ql{AcCkn;mT z8nZOuz`~No_}4W2d%sGc|84_!{>)C^{p!8l8Uc8`KPVxe3rXdynK5DdB*xKY^!~=6 zi9v$9z?;jy0hDpZuojnA7s)u7Ok;N;YJ+V4;I1qvt*Rl9B~6k}K{}-&kOzO=Ic;ib zvFvPA?I|ni#2`JsZNP7xOs*i0xo1^SQOnBM1dcA!J-t-+ykZxm?(W)ZM!lUo56Zss z-~9oWOFxc6J*Ko%OTzR9vx~S!gQ4HWv6SIRb`bRL?AJRW{l2{EDSIgSj&sWymFM`z zhnKGEsTazqf~r!o?a$_ZRh7N<$1vG7FeTY{tY7iJPv4~+`bSV#{Z+R5@_sIGYPSd@bU?#lKLlY;;(h0w0MTQtGicrUUskC5o7)@;#1Be zsw6&aQkW)>TuWH4aQft35L>4Z9>* z?IpsDAS}bJysqE$Z^PCbg)gw~Zt3WaYYP7{zWmUSw;f4ri>qtAig8^WWlV5>f9|D$ zN%xQHLJzoPC%_ikQ4{MWtUyUst@)gQ7tbIW4z>nUcR(kf2@JDbX0q%!GYr2S{E zlf3wE!uz`@`@?o{_x?>a-zLjU`?==-J0?=TfjnIL5{K^n_-DVFdJT|OC=L8UQw zB|ok-l?qf^?(yD?Qj*<;Y^SH+jr47f-1=;qOf)bFP??;TJ|!t&+Mu(WWmFzsTjA$F zI`_{(FaRr(!O2X0(&E2&-`O2EHB5gXmZyK|&jTM~i>fJR6g39T@_8r`+7V*f^0#l;PZO0}k1Ax58b<#DwSHEt~3^V*PowRDN@*pt!KM zM873zj}SK*8@&i}k1+HipZ*^R&%4VfTc0v_DMMKa-?yiuXGW37!%moGGS9~px_4jA zmdISfPbSl(J{o#&^wz$3{ds&h6#JjYcg#V$8F%=?l3)v_=zBC033-h7u+uYBqvc*B zBOu+_Q8&7)YYQha>>Rn=mcU#L`eQ(jrHlx&vIVbm}e>ZRaTgvJi zj3!jGnX+D(b@(e|K>4&!`bI;SPxoR2boq==9`23v@=0II09`(5>k**KXJO*6;a)!5 z8ioGX{#zba=yAQWlC-|1iHQQPr6Kmh|O`$r71ia#~9a^ z`Zn+$<|*!9TR6G4FrfdD_Vhogr^Ox_;aAF_n?1GJEVS6eHsUf(U)+Vx&f{hXTv=8y zQ{qWc>FE9RKW%IMznv&ydU{HkXMzk~rD7c-?Gp(%jIl-}GB928kQ=CHqKhrF>hFIf zEPJXx3}KeJ*3+|~OdbS{ty=YscdIL<4#^{OMU`*P%BLAQ{g+4SQhwpfc+L)(;_>a< zerfFQp5}cTU!>O7bWG+K>@J%S2q&DY30HoEQ`&FQw4$lC`VEPD4;!wR z6Qg2mVu0@)RX8Ly_TInyBP?KX5LX7C~5&PpKe%Ob)E~#rG+WTLDXg@@ts+ z!#vfsWej|4%coZrRK~aEXWp2hDR<{HXv(LR(X=b4OenrAW&Ah*m&!bsk=BWSa$%H) z+*Q~bo>_Dmjma7&{b7y1$^SoQ#(%J(GGoOL5tPe>Swex{S?Se#_p~tmH&h;`twxg< z-k%nf--#_LWr#6-QUwn=jH*gJRa#ftaU)-z{k!-0fbp9A2&_zy<@#xN%abk*j6L$J4)mDD@}W@=yb<|xJ#gpC3a_so=)>qK?8-frX+$Rb25>l} zS{?=&9P7W#Ya~=33**w;n`qb1@Gi$?(^h1;Z{(#P^N)}~@ zFkM=u|8~S6rc$eBUg{EZDoS$YnY605l6z`))kGFx%WDQQ4d?q!3SZXB!ZmYh9;|~3 z%4ZHNEuT`|tBRk~@KpEqOk)ufQBW*1r#Uk`MYZ}$qFmFnwt6OwqN28{NIw>v_$&i< zIj)|`a?jMEwG}l5Ir@m#Q_R?OxW_X^o{fil=Ry0_mX(d>CuTik`MF_PSuQ3mzKF4Y zUf?^-mERi~UQ@slyQgM)Mb#7upda&sWdT7Atf;6QMpTECm(9Esf!?KzDhO;Qf$P7Z zkz1V|XuG70uBa_5)~ATG%VgXZWp!;4C01QhTSonnXR|N}hk8Eh`*azjNq;Qo zgFv3ur0+GPoR{mwz1-WR{fkZ9iobHL91AfZ=LB&t=j9sFC0rTz8*U{GIhOIO9KU>R znDzv0hx_0*m<3Z{0*rz_&;^p9B{YN(P;h=`nDzl2hyAbv9)Nq`c31$jzyo7o5OjeQ zXaxbq*j zX|LPpnfc3-+~%*+O~kg7y`~;GG#%g{b0&ASTZyr02^$Aq--(yliMr`U#hXznxU?*r z%sbU@V0spdY(i!2#{D`E-|Vv85HE}G#`jOe#f!G>6j@(R%bn8nlGFY$J{Y>SQ&6aV z6ZzpctY(wv&H*kg!<EGz6z$3&{nBvBTb>)UHkh7zD;ziO$1 zWm{R@AW>6Ol{^|dAHfR`jwJ6bEA6rNr!Y*#AeGE=hrO3`4&L|;7cBKPyPb1YI1ztv zlIN8DR18DJZ{`@yxd$h;I3We%L`sN1>us2%Wf<9$?&kPS^5d9mAK9~l(*&=XjypAL zy?j1?L$Q3NneM-@k1wZRcgfRO0q0~SZhAxNi^)V3DHdY1Ybe7se6pQpQ&#!BW}zrA zQG9!z;K5uJd70OAgv-pcr)t_9V%T62TZ2A5nePll`31+HF|4_h+-h*ajIX9E$sO}G zJOU3Uu6EuKHVvYEny1~XX@|tp)T}O3G;`~0-zi>qzD&_fyNQHJc9YU;jOi_cF}K>u z$6f^D*hk&cO!GNwo-vCT{x4vAga`#HlH%S{M<0?ze8ayP$0*6C?4)aeRNLmfeX&F17pMiMJ5Vk24)9UkE3~p%%968;kAVn-W6ktCPAsN!J_W$iW`wd_^zfO* z&zv34*&?nrTkhedf3XbL;~6cfSS+4aFDDx~(ezyjQJR8zPno8zIl)K}x8}F#1M6|V z)mf7`&|@Rh4yHL0uTA`8zJ@$+@d+(@8@t1Ly70Eq+bByzlzCYapEdk|a)Aw@M_c zU`Qh_nrWC6$i0&I12pjlKkw6>Ut(s@W-jJRnGG=& z;IB6)f{1MXX*#7DWZF`$vwX9ZKH(IW5t8&G$+I^(vRJnARRtl$_ZIt6>Ymq!UDxo{ z%vmy{oia@n*OEZXg~fc&@iEo}W!W)_!P$HIF=sy8%$F6SwbNr{Dv~T6DgWr@(Do=F ztWu&8Cn$#(M^mXNhlro3vj2F7{LG>3t8f@IhKAJXAm6FHg~H43rmOruhsAr%&3NzK z95&b+OfzY0g&n-XG+RzEmz`O}4K4i_itv*^p9#E1^0hVLm?uBRmrFz=vWrVyrf8;a zlI2ar(VG60J5Q<6E7^EkPxCXSK1((&`>W~Xjhw!bx_6mlb4pKUkICZb-EucDwQ%sy zFe%Ro{9|sO>CJ;ipXp_78Y(H5J)kU|D2%tiWmUPQvaGy|59M4OhbzmE3;8HVj)N|@ z2f7(RrL=s9n<2__ClA6ei2K;fkN+qB1o-J|P?B^@r~CPqiX=5|s{)VdB&E*1N$u3n z-a_rvTkeWAy!S{2YdlnPK=-02WmJVc+mS$8z=ValWgsMd{s? z&#Y-R?sD;J4vsC$SNTjb4?-OE4)hF-HfYa4?_?Xd2<`rE#59gbum`_04R^;l*%8U! z>{i}n#=T|23cgc(71IampN`GzW0~B39XGR;vc$FGjR3je8)bf&7SaASjw?BFK1Iq}Km_5hzW^PO~b3#c3F zD>@+QFif|h1t*N5x{)l>Y`*`-t#p$TT%5AErSkUTB)O%jW-87uyL^fvR1CK$xAW5g zgY9M2RI^u5%2(l}zBsu}Mc8GRSHoaGWpOxV`%Ax}rJ0IipM|M{i)am+TEayI0S5m? zh6}s*qK9x(Yh1KUHFV1NZid0}m||3#P6Fgltx)Zq?{NBY+|$U#5aH~}*%vX$fKRPt z7j^qXSk7V5Bi~Gg*!NJ+{)K@{HFaLKw{HnB1kF~Vp>c(mTm)UGLPDJZhTvjlt#`QK zl8caq(%5}}rnA%_(>eYzzYxU>a1@{A4f?%*RuZoF)(gx1bV4sNDqMeoYd)Z2$UaI^ zODwn23GST5i^byG?RI5W8~CD8&J5)y_`+0r2P&3@jmgau8!}NZ&iD|?oJ6nwgOpj6R%t7r~BcQ*qzU81@b8pnl?%qDr2V%0}lezm$MyM2@^*eHD+HR(Wsbtps z_}zxJ3=OApl|CA5*_!VqiHG$$i~Vbg=t)k}@-^#GOMU0b07G<76BXqZynYHN5^L-8 zI9W{1If_9lm!*ZhnKO}Vel!g6_7MAfT=F#r?3$&GJ6KjFYxb z+3n9T=%ZlG{wtuJAtx)QMs|2OQ=sA!*8Z4iJOHJn%W-9c+4*7|#J`Fz7j8JO}s< z{^x*CeE#^K0}R8Z=YY>~BFQtJ1I!bO*y@0WWF18H4=iP#18`>!rBP-RTWiN4W!~6= z@h!2krUX$-Q zU4IxoL-d(Mfx3LZ)O2nPnkk$z*6)B}R}G@4#bi9EG=GjZKqSoXIPAlN=n*-OW6k`o zBi!x?Fj$v5+uH|Hc?=hpY*$C86}NuF+dM+apWdg3O~$84G$SNiH*a#xryTY=JeKnj zoHw5)q&>%sNA!xZoBBuu}Y4RJqLD&0$T$zp|sStQWL-J+pA0xak zycm(mXzz>6Cl32^f|B!1aBcpS$V~Ma5SsOrqoKWdpg|&2LS*C;2}h4iV82P|S z-#vXF4(tC!uds`QpqUH+>ffBgm(HTqa0~s0TeIB!gT^bC?n4VrvG$#`idQjw zD$#mLwY1BIz=k1gWr)*Oiid_FJUQI9F2F(kSj>3F&)k+5nW$B2zvvk)r_#;fLJCIh?Pz;5Ko9!T>3TL$-Ngm7R>R=Gvl@oW?yOFVL#xGe$+Xa7e(LBP!7Rqm!fS9N^uf3J(6fB- zO?{26lAleHA}$~U%~^NxWJwnjyPVs`zJWZ*GeU~JmvPeuveMjd1D~9WWi_XNNn`9q z2O)}Pc0R)Eo}%Gc3-c5ZE}3G~|0!28)j$BTmCA7>+&i?2y+%biexOix5v$m0Q-tF) zl%pt8I*tt##%WO~nMf&_3n)gZ+>HN$rDEupN@Xc70!zivjZ*RJ{!-aL@$ynJbbqP% z4MwRL27jp-hQLy>%buwli470#TI`Lco_5*%)i5-p@j9OkFvKV;#o}m2!G)2+*rldM zA+o!BJW7M#RE%bqQ?KG*r1-~ar<@i8`Ra8Nl1j>Zc(0Dqy;|s1yk4zA(Y?}7?F~G2 z{IXM@1)h=`d+93KVBQ}$*$w{EDcQr_e=7g7Q!@fjEj3PQ&P7zw_Y1x6Q^(FwO&oSQ zmCfS#S``!dIjK2>(s)sW$iW<+kmC;ZBjiEpJjZdZLtSIBXBSc7$b-1#`Vm#=4$jAA zMMT`dv0IL_Te(U&?uR?+s^dDz5ZLiP`IgD)*f}cPQHck6qTH#vMqY(73B}#Lo;{D_ zlUx|jCGIp;&(Q%xot%tT!I82b{VHg3vT4W!Sbsb_9uj}I`k`o!e4$tJ;ui#Fr(oCN`TvsFwKioaiwk}&-J-0N>M z`!pYKiAlH3;Ad0jYrhC z^&z67OOF#bBGQ(M-UgkTqt#1Jvn?c^e!V!^DVq=*db2O}cKNS*LpS{J z>pnjW-RFnv4&pV?@FU-$`&nSbe_*riF;R)9LoNefnn(BTU zy5Xl^_xWk)hMzm|a{+#kOL2RFAH_OhQ1M8d zvoHqtS(<+pWq&GApQ0jNzflXG#Q|NW)*?|x8S>|tMM)kd*w|GnG4dXi(f zn?2qa<=wM@b2bYIZ!wmuhxvu9Kpcnr+{VN9{03wRT^v8rvTaG+d)v z+SXC}eiyIDIadbgMlGfu$1tu-!=rETi0DSy-y={ld@2U1$Ff(PVW1w{4+Q98Z@QfJ zAILXxu!$?eE;<_9q~kCQbRI70JVe(s-q(RlL?_Dr4n#&hMh9}G9Q!(ua^pJbKsZY-5>74 zz;K8EIb1PZ9xmN|SPxC#sy}v~imCTnclHj#mDHz}{Y{~v?fbET4+b6$wU&cSA)%QV z_G6$KXsteQwh29%8(>(e8bzxcR=yzdG@b^Iu)j&UDnu8UMIISB~v-L=|u~2uy&4B09LLHO4T8v{8vEVZGn3E@g zlQvAH&3UO(OxtWKrmT<+nM9djS25dXGcQIw)GCzmOIdcJN~?#MNqKD-7g9|8mY)jS z+>T8sxD6VE!I{M20Bu3tVAV&gk8@s%RNZKekZC0CJq$9gSGQOj*jILD&JzR4(bTQh zXj@`{p>dMa)-phkb31KIdR@98Zoxp?7d+=XVJP5|w=hBRwYJigdA z_zY5c8jgswWs?MPAbO*`XqUCiB|T2|7j<>wiuS%>8yXKixOP_X&nDlcYOA%OV-|*^ z#8&OGwzSvlt+8LC{46MjCbs(a-|)vU#6HIp!gAs-1_?TLs)ds+fXwbpltpBGTXzYy^+jNrsCdlAka;X1Q;USVqDn#%lv-*7Mv zGr5DK_i+>|o^P|doe6j$2C3mq7Mr7;T`_#-^QOsdOr6^2bIq4nq{~v&NuO6pvx%JHC!sGQlDB|+2;os-2KcccG>XIaN(9w;BVA5 z!(fq7;5zyy!_b^j;K~5q>Kg^JrxxY0n~-`CZ9-~c|A`7MhK~q3Y7tMqZOOi)7$oIU zS1NC{Fbw{b`we=vupgw7zQRRM$wgySm~E}kumJ;Ob6YqG5(m1@Emqo%=+ac8xy3f- z{2zwS7(ESr6;X(0jGoQ$E;;5F+m>T1y`S`z(fSzO-j%Fef=LR3u;ZKw0R}yPH9dZJ z;AWh-S%RDj?u(xuhoinYNZbx_PHHak)8qc4p(jhV)9hPu^BuM% z6m`xTVgDw;;NH(O>}m|3;bSv-Mv%QJB`i132m?CNAUL+@&j^EP749M4)VcL%1ZOVk zYl}&*;ODGydN){wq0oZ6pRFzQZqP8$4Mq+owTXBc*W787EiJFZaVR0ib#CJS`#McSH#$v2_jj6x!RRy%-RLy?5gq9?Bc;=vghGcI zDII2=91kBBBAw+bj(r`aLpsV`auJkj}9O$9l&YAsu5S z+NDlWPuqw*Hqz76mZqo8ulv$w=tkNM-JdqYV5H5^Nn40i>@|cbY189;NECmZ_1Jjh zEIDq(+0F5#IQKxi6z9lfD$$kVpnrXPJl*dB;!nlVnm=J}WDf~2*jKQY^ecI2xUgsG zYe}-Z=<{3@EEhd#ZDN-#N)3ahuPw%YfX>2i*ktSC+!x$G+;@kK@4Vn~(0V3QJLp>U)S| zqbi)|_)=AHb4$2X6})XTfdMG{yJ^ON#;^MlXXr-a4BekN!(b%N(2d095vV_Lbtp#S zR>`q1al1GUOq{%0;ZIy<@TJ6U=GaJFY)D|@7NP|tjzv$3>^wy`4XMnwczul0pE0ad zcmEj0F!*mqVvye77^AGEO;r1A;*CSbDCY@GS#44J_{2f)=O3TMqxi=s{X{XwC+qP< z>Xmk%s$VaUr3;onBtI;SvqhdU{)@Nd(d9RqZ|n~7M9YgrHu|1Qclg0n9g zuD?rn_6^W=*O9BaNw~%J?|nm-aV)>FrB_N@Ic{5%D(RoXe@}N71nBy;t{sHb5f@Uo z!her)?B)7A@yo9IhHOKbWO!y@$i+hq&+IDQd0l`(_s#h@zFmnEJ$LLX%JCXL)QNXy z)gcqpJ8 z^!4PJuO19AX(st?(b#TONxiuE^-a^~CQ9l|J&NOZ!sx|MqCd;q9KPd!(Ga`;;j z<=Y7Fv1Y;tE%x9Q+y+biIlkXk?SR*Bm`U&L51@tD)$6!AdoZ{DEBAMM}aSkBYR@ zGLFjeJ>d~I_0Y7_a-0|Ca^TBIuA&UI@S*XpZnzuk*W+CG^wzWqhOa}TT^qTJPBrx5 zk&!1ke?k26L^_TokU$Ed^?2osT#1s2VvBIv*Kxd-i=@l2{Yy2lrzHj$&Z-XfmZ<@T zc~qpB0E1qUxK#ckhY-Ol@i+X5sK^M8`AW{8sD*OuOVrE$M8vxaaQB*Dk8^D%(r+7y z@FnW3p_8c3Ilo5yB2n)V_d^`}6LlKJNK}KAKPAdP0rfr+Afq3_XRbEurY z9!N9Qry@2w*_Zt&Ii;^0PU~URCR0yjVL$2Kk3IEZw@54} z>D4#PwYj;_{ddk%)sf!^D9+Y;4PpKJ6R zDtQx54<`;Mc&@dbsG@X37Yus9rAm@3f2KM7)3`4UN}7U^L2=x%r2#8`-rAhIl9e8+ ztg`Ul?_f$ST+TGGDwAw%ZGNB~gGw5-yacI*y)%{}BCS;)S2e!D*Thd)oVjGpx16Ns z*3>(e29AUOAA9csURAL*{O_~p?0rfaq>vC=5=sba0#Zbch#h0^v0}iWC^b1jloG(+ zFoG2|h+^jg>QxXMHpGHrL-bleK@`0fEZ6S;w`T7-*@?u9dhhps&-Z`N^Q>gnnl)?I ztm$>{Mcz}ELhpq;Wj%=4)gWEV+FH@)@%c!6{)x}i_{d_I_Nk}4&I1X38_thh3Q~6h zyKo{g#mR~MYkQB-MhJyWH;*Z8B7|9*cGoZxk|4t2e%UehYykJmOj;vidrKUJPCH9S z@+u)HB^(=N6;~n=0$sGQc##-FG7>{Dkr+G}$&h3u@ zkEhT!5~ideGxBl(@3gxUi4`-PnAk-?}p^X*Uj=;}AkjARllF(i;5nu?OkO!%oFTf%r>t0Ja zB>NAf8bahXCoTSLfUsRY^BODmE-`#ZAZf`vO|mI6?vh@2{CxV zh;S^jXMoUaAG;(n5akRi+#3aLVV#?BqHiTrN4Z%w^tmN)-C}&?s=?{17=uPQLVK?Ue~37n^ z+Ji;F9-+3ehmf`sew`IU5a0J9==2X{-YLee6!!9vXYFwTYxk|Qnp@itJZo!dv*FRw z2I(#>ck!9NIk}@_sq02?S41H`i?f1hXE4MTm;~?dlCTOp_`m+DAB^ce3$HGa9{jmoMcdSCs3E-#XE2bxiBIJ9tn}gD3SALO@T; zDJthNc%$gA5Bhq&8W)`t_z81J?$6n>_L937MN`h%hK`S+`onm(>2ee!59+m1efiyf zZ*XHQHj#c{d+`@3jj>o%_E|0@KSMXgaQ>cY+a2LImX>hZZnZ*6AOoRKYfLg{1;v%(HyMZ0F z41*gPN!*uVa9@Vr-6nhQ9h1FwP9j}d9mEsp%9LPAT6!k7#bKk5yD6aGJvq> zhIWj7v@_>%{nXg!h6=2R)X0TUq@LL~v*hwMEQ^9HR%E}uSOkaE2AJr_jocfMcLwCeFXLkS@3{p5KmYspSQrw?pWXtByIC0c$)~AJu#cJr+aVClEhgqZ5TqCt zE-G@b>*wK;QH30}xio+$J2c%E>`o{>EW0DtHrgF?$e1>CXDmPJfaHN}KaIVtfgrsF zDGBZ3o{Ld-u4GIL_x8)%$1CtOxc!mDY3YJ}G(5WHzblp#J(EB~MD~Iht7RZ3gV=Jh zAbJ?uPZY03v3A)Pf;50&(8wg*_aJ01Bo@lyB(drp;wgudWJ_&1h_TOF+)ZX(M>u2F z$W;jX2!f=vP!1>c4HD#V(tZ6r4yi_BvD*T8(qi5e;cH(I>UtG8^;oY;gcir79XJ#b zZOTS5iza?k>E%1ldUo)L7iIwd9bpVT8jD4w0^AOvN(hg|GIa&G8$z=RundBf+++oK z2!ayAxgoy-d}mlp1^7P5V(AQC+TM#qXYgu}WCnjEh{@oOMmNu3g9kI%8RKX0Q?WFi zv?`XJb`3}#h__vSBL;bo zaF4-3)19aL6hlb1)k4UF!|PMD0{79!3n}zs3_WbrvDBCnKhERhsL^!cQFkn=ZUkUJU!%SFzUq!U3G=5s_25Y%!0PO4b|gsrWQQo*XH8M9a^O z$?#CrKO+p=rjQDHG>gVcUZu3TcbU9OX>*mbxsSP?DQw;cpMn3=b-q8B^t^sHukH??v>3`fQXjr< zk(Y`cz7gwXNV4;2XKcBrlY2d_>8lLD)c%|#*N#O)ym6E|7!&^b2uG1btwZqEj>3z5 z%3*Tu+zS6Pk+%OwI0l9656FgaH5S}M2nU418_yvj;yxx6ezO;=$Uk}X9J=r?eEMCt zvDfR@&;0*jw2;EBHxu2f;LpIcvpAJ;3k4P;L!Nf0lfE)G{>}MgU1dCcZ#PYU& zxcqHB)aW#cCP8wL;;@e4W{0m&<-3G|jJoM|Kcm*UOL}mj6>ptS>tpUJ(v^Rj_lUk^ z)A9hWv*|4eMKC0@X)dWx5+s}Y_irLdHuVhPCYz=rr3@*_Yn%t^n#H@Hob#_ z22Wo7EB(_Um#Pa-hcG#Ga>n+~E%N?c9Z6d#M3e}YOjEu%hlNWc=zCh5U-efq^c zsVB!z@YdP%Ys?LimO0?2HhoPx4X(2(9fq4>h!Ul)O-q6V$)-cd7Q-Rgv}XV}*>nkF z7a=8?P4dNKAtbXYzDE;5vgtjt$uKl!(_(_O%%+^vJ-L!i9Z;@PEJrpyiF+LGrrIoerq!oiCw*qm;K?(4hEU|6*)zDWMfL}Ku~;Wc9D=9K#SJy7&tEtaX++W~cvwaemgx&<1DPyVjEGWF;BYV|Q zzrF|INfY&tizq{DLFBQMudb#(3i1h$mz+6^%w7X>9pj>s@kAxaULg0+=Y0K7AbieK zy*}8o{wZN3o@U74VycQnDx#kQ>G|H)kTJAYeMB7xt-GAZ^5*9tl7y$Ip3YYw-MR98 z_DbrIACJxF$}IKCCG4kd!MhjUKR?V7E#VcUvXHxxUwa4xw3{%CN#dw#CO5z0C5dll zi2?m-vrW`5?8kgtB(#jWxwO;faAoBS*hJk$Gg;34=HG>}Y`F^IJz*gW+6k0#c}`*G zkz6Aozr_8{4KzWR#CDta#%zWtTFS0mvM(&2|lcpmdEEFy+r_|?lSYqW$vzUV|Yf|Bu{ zPsw^j!VlNs8wlf`!owLWP*6QYS#R?tl+P)H9jEc0jvya^^t_q5?03G;WxH^uSPYJj z|CY#6MA!9QNSUE@6A$X96sDowi6&-P&; zd6~XS;l&7{!-Eo&YnOx*9^!cNxf zp8H2ejo(sAguZYdLx)wK`?WHvAEgtzy83apQ)ybVw-*EH2bXpm!_5H^&9vdXyBTcb zF5>Z}ANS`$qhCDtx$tC*2zY8A@g8N$J^=~x#D`jYvZq9md}>wC4a1`^W3Pk&MeU&s z7p9wrlMiG~HmwLrdx7E+-n@kOP~+6L{-N+a{a9recTp%@HHh)4xU-wNJDkK>3pN## zMftH@B4!X(JS4jh!DU&c_=t9Ca%?vbw=>XbM{y=|rxOr)R`Jo9k^BIjJQZpfyi=ii zXXg^aU}>xPn9lA2NZ$xf2X7S*>ku0hz;$Qjp3{d!v2;z%v5Gl*dF9R?j<=da@N5@96G?9yWln5og-<@6 z77u$Cb8gD6j1aMup73=~@`A|^+8E=v>tyoGkq4C8 zS8@hb5P3jp;orGY^rr7~;Wb<{5JY@rrXX)S=`cS$;61wZf7EBe1>A3=251es9Z1!n zd(#>W+=X-C6kzSM_yi6)2_jmz?qi?G<~OaeC+!F}lwb10R3FS*$d|s)D@7au7eu7( zd_MI~Y*5OM_5Zv~p-gvmALl ztMzzV^`RM9aUrJ~$f2~j8|wH4T@xdKWPQF0ORoJ4&&{F0`(bxeE~Sj=_rp$Qp8UY~ zAo70L|0$mhM|+0T8tgibWTh^Y! zRVbtqjHn#!G>eKEd6g)Fh<6fEg;B0a@I;YPHzgp&QeU6J*$e7Yu z{V;bBv6L(rXs_8MJcwA{X;=g~gn{=?3EuJF0wVEb+{T6| zB|3IKY5R~cd4k+Wn8bJ%w<%2;X?w=!83uHJGz3U zVN4MFC=xk-PnIU}JQUB^#}UUpBtS?%-_b*`Yq3%K)fuqZQL3pragukwjl)M29`D#R z`dZ0%EaSm&mK{Seri`2-PftQEfYD9&tCM%!N*3c5*{er8W@l4|+jJ`>H zdxXM6?#G@IswNa3U(bs?aolIOO83WqG9?UGEoM0aF`arhdsOe=J+QYHYpU*7Gb5oG zbsB|<9=n1$pCAX2hx*KoI_PX}DGF&2O$k{|ok+e5*p7DrZ$6ia!I#XV0&fkr3%oVh zF7Vc%(9OGm;+}AzWj#*SYF8gpnPrwWlMz(AG`7bNT+Z2GLC$RE_J)tiNB;_V^iOr` z%A+VKRj{AJ+F9PyfCW4a_;_EYlJDTbqor!p-?<`5Dhh^?_ot2H6ghS)uz7+-9IBjx zDw$Fc)5Fgd*wPzvn|A~YgYO6y_LbrBbjo&^A*6n1u<&-~aI}Up>*Mp>$wvVTpJ?W` z^n4)f&N&1>dc|cV?;GjX7}Hmq?qb7b}MB?%hA?b-kEuUp&>f1ISy-cCUA& zp34y)wd7`>q$f6oU!6`v3#(E6y7nAGp{y1h(RFtYLmn+fP+IMqF#eq+vEUTJ(iALr?g#`-Sq-j@y|5Gzi&U1Vi?T41G=${M791pJ1GZ zyO8HeVGG0RL`Cal1B%@a6eYG2ig&ex?#Ed*y(AqST|5S_hHA^ z(&@ei|GV+m={}|p3xoJ)?-=TyT>vsoBmJ{G&}rTw?o)P5OUJz$x8uDXqK);P_!(mr zheC&n_n%oD}a>@qhkjZ_;hyewhSS77-tQR2 zJ(}FGzm;}msZ@qsdRFIR2DJ1)$#T7(R(>4gfz%(|r)c=an}48A>?CqTa7kBaaHuo? zEcA~rs`mQSBP$}Bf?2%LzyN|YOOu@ zwGm8EU&sIW_z-CQ@S_{B`kqMYkntK8vgKNg2<{%dR!E}^AcjD$FsZZ}0AGZhX0Di^zr4LTG zx*kj5D?COuH~u$@2YRI6sB?I=gyDIWuIKG-=?62Arsif5RB#Wjxb999 z^v0*$M!cJa&hxsly!r-+ym9^QJ{+t^&zxf@F@?v`bO>?@Icx=L|H+43+$zYtC42aVu{Hqo==$SoQ}* z7*>lJbyL3b9?|j3tIz7oNh~p~=6KnU45AM9&~r!gI3#yNc!Z3 zcl$eBs@?t+MiSnGV-MSicr&mL`CM8^6Z=X9|75y1BJMH6l{*Oi@so7*m=TZ26$HTt zfG4jY2mzA^t{|KPpsOHYC@XmdAxTJHLHGwO#NTuU z;Rgsh;-)JIj2f^!)t(J=kNU*2-^M5e6%j;&LmDb}G@@*HG1ajChp! z9ayL{#lw+N%2&f^3u7!jVjNqpSlaTOrJcc_@XY-z>n*+@sp7yCyn>yFg9Z@#sw{n-#J`P4;c#fG( z-(7)(wfM;k+Tn^=8Ud}Pm@a(1%1Gb6*s^}c zvoFN(4XRuE7dtf(+z)xyG|%V2PFCdDOmgW!TI6-a-rAK!GxloV?xlyTRd!$49odG<`6V4IoN(@7kCk-Q00|dnr&aqtr1iR@8XE%$M>jIQvL}O*# z-ZAzqDKL2Eul}p7R{TlGOJJkh!ZsL}be&Iq+6e7@5Xq9_UgW+M?a2BL>4*2H*(|ax z)VESO!QQUgCy+QR@_u&P6!JnCG|mZjoqydR6ljJa8s7(8>PqB&GS`Zfq3kF~bYE6@ zo@yT{3J_SJ3q{KzB;r|cN!t;=MfFVofO2OD?zCtuL&}{Hw4QRG$;x>ho+028Ag2>nIT<8EE0XgT zawdX^Nzy+9>Cx3U0hgFE#3o6Cv>;k|KUnavB3xq9gk32LIySEb(RJfQm~`EkuS#{@ zFoa~?5JIwU=*(LIWwNA;Fgo)ZL`^^Q{)OiOcqTJX2x2Rfc}d)4UZ`91%qvRbI`gW? znhHq&lo)ST-6M=9k_4B_WW~q9cLyqjXUBCC4uMeF@fVjx}Uh&Jt&>k)+@P0O@oU9H9Zx_}@!b}$5xQ=#1 z+O0)Br97;aR4mGs5PD!(vI2g*lf$WpX+`rl*ed-Yx^m;0ztL{rc42_fp*y8D<=P!W z`U{)1m!Y|b(F1gi5IEh==zbzc_3;F?^7o*9UxVj;5ZZf$&KZ5+SY7@`Br2P!t4jH*f+m2l;-vi5V~%NxvwM4Fm(N?O{|pQg42ju`NxJj zM}|N+3>T$r z9EQ;Sm9+R#ND>@_SVh^=bXoRna>4F~rHh`&)Z$|bQq>Ul5`Lu_ofY4V|7&oB!tFA>hBZhyaSJh8#ZxBF`G^}2Z7 z&nMrHj<$S1I(io!wjREIxtyhbl#Ff;h2zI_;ZxjSbCB@oaV*jhQFIKf>m|mI`4i5Y z$8hBpT2=0Td_0xGxwxl?!oOU}RO4mbpTn+~U=BZo21a~kZ`=xeO<-o37c8C61mK~+ z)>DGbJ%!F`l7{e*gn1ixqreFDbMI)ITKKKh+LJ%>7Kx;KZ4Gl#NT?tONmd>@0F znR3b0QVZKK#(T%>&D5RyFrs|jN7_^0AAj3NE;x)h@`!Gxj#AT82O6UGPCx$Me7lo#ejZSa#%@iQ((7xNk;5XLU)~ znu0@*>+&A-8pP}e+x|ky+=yQ`BC?m!^A~fv1RPc?`{534FD6s$USW$OYGpr`Q+(VG zteB%v_CqxUvF+X`vj7E7 zJ;T3U+d~d#DsAv1vWw5df1t>!&d!;50F4Xo_Ph^KJP2eWNM2Sf{Q%sv2*kkH%HytK z`ny2_cMa25QGSygoJ-4C9w20zSKJ6*Jc>kJD-k}DQ~WT>E*7_+sFmXO6ZL2^5k1?+ zdvUlZiFa&!7)q}+iSQG(#^5As7XIr*7Ku6+RV9m)i7EgwiHeh<%@UO)m_#KBCQ*hE zOcXJA@7T=Rkz-c0x$cg-wl-~Eji#8!DJh-UI)a&KEmeCpYm8S)yxU@v z=Rj&CDY?M!n(P9qiuWz2tHrj4Zdw2#hB*4f&l4ygV>1GeK+s9Pz^i{N@K92(4BZG6 zVl#mY0|IX+>*xCdZ~d*nv!$FEx)CTuU*PG|`C0D;1ePFhvoG-P2*gyDmt+us!}bzJ zDjOxEDkvJ8PN7mtc{)@|;qKzvi$CEB*!)?#i!e>Ky8IXhSlBqk^$J@Yj*)GY(!Ljm z+*65fZ4nAy5^5Vc4&)1v@aR}{Pu!2A18un#Zj%#}5+HaA|Lwy<{WReP2)cwH#xn)G zk+@Y&9K{x!mI(J*!kIZ*=9}O#k(ud*FgP-ctwp0>SpdOiRbn}pkXAq=iVK$o22kNOu4?5QN269?qm^JtO!Y6aWNR0sPmC^W5B z>LIszo3{2sZB^ah>Ib!C(2@4+t)8!XCLC&pShkD;(o%>*dq{VG9tQ5+yg?YY(KVl#oaU1jOP#XTWbGX7@bIr9MTKM zE;KcUd_GHJnHEx)EvI7=_q8Ea^f3P^J@&<+AdjUc?#=N#VOk%u<-e*JOG1V40Y_{{ z(fEt|(@>DkO8ljC&f<~I2JHe2Cl4B80_tL4EYIbb7V7dF^b2uCl(mpNb4x$7Iu0wR`6zLPd!}2*#_p=PH zLBK(iLd%*Pk=MBl?j69ZB+L{zP{Oi{+D8ru;97>|9*)-{SPm1Gnl!ftmTygv!@EbS z1GpdLBv{>2(k4O1rMZ{F(g|Ga(YtG;K7f0g-Ug)`99oaE?)tfMgZn~@7+v-^Lh7<2 zd`&iqdx?ocxfack#imeJ|4#F|famxvDypf54$^y6-VmZQw8O1Qr zeF5B4j5;wMhTF(jQ|>Gn&4e}`(J!+L81EIabw zem%{73YLpZkcRG&B>~(I@+j$y!h(vH{Zdxso&c`*T4NvKbD^a59mjna0nZtMsQq#P z_XW!E>~ajL`>Yl1BdY?qmrJqxka&~Wy?dJbCV8?Hc2n102;g2_n*jHtaG1LGH39`U zb!}4s_e!ulr}jP^rmhVS;9gNmNAHi}Fm){y!2L2lgJ?UDL#D3vgk_k?G3r`}0PY8w zNst;?OkLX(mW#kmUF#pfJxx!5(qM#A*W_V1gZo0~qx?Krr2n?5Yu5yDFEJNG3B!RM zbX}Vgz&#EIz}6VSu1Bn)8%YM_7tQOBP|>Od1U#=r9{{ zB2EDJ?6*CXqu|hLZpw;u3E-ZZ@l5>AfMq9<>LPVtc!Zz}=r=i$oddWh^<*el8mZrR zi5wBYeW`4pShvAKDYoo5Pmi7r&*LTxnv4$MUKkAa5)6z#k_P+x9!xyR1ZkYML*g3z zcgNoy5VKN#<%iXi%r7lshVL;u;x{PZcg94n*7}0v!nOGCEQ0ooS!xUI-J{=w3<>Fg z4+H|%pUkX=kqeKCy0TIx0VPY_3uEq;BtdZGTJAw5kqZL2PEu?O%@gXmU4W-$Zyzuq8KcG)V2L`Ra zK6}woirxMu`pk>PW+LZWkx6XRaW6MAV|Rdk(U%P6cgAAhB4??{B({%nuQM`Z@`6#c*o#f!?upx!!0T87cwg$$C@*KFD>FHJ!hBA< z`j))RkL_M5)%V6+dA3wy*LA7BN93UZ?l%u=!r@%jkzDd+v&5E^EnlJS=E;tJ?8}xK z!m|}20;Cq39FBURt@C+U;qRAB{-j#sM2zco2lwq-*A5gk_lbZfVQfEBd zMUVFbZjylVqOrd$si#RYtqPm-t%~!l;rqw2ic03HDks$3XB*jq0$4PZ2>w0|-Ra>X zbBW-a>%9mf2Q#PgmqL@D>kb#q_Kw?!`CNDS>1?`O z)`)bNKjZUfH25=N{)}#|hiRrk&%w-lX~M!4*t>O~X3}b&vaAJSHwn|aPwUc}`D|m( z{h2V$bZeH|e@5D$2t%PwURw(9O`Bq_AcY?+@ye^*YWaV`C;fD<9LbHFKb4-38ULR_ zt0=dD!gnxsIa3g^&SeZY%{`&8TS!+;8>oGbW!x}^c1?%bbRMHpLH?{oTi2jx4QF==Cu)0%+j;kA%IBQ&Wt&AI)1oOMIgZ}5OI%(|veS(xrs=W0p zp?RUgW9hy<7N2rHl)=p?E1(?KjFL;6-<%>x*qc)xgfhlc%a>eS+mdG@lp;D^{rl{Y zG(TZARbx)AuAM|Lcr74%N_PV0C*P7Sq>ma8TnWqvmICH?GH&s^9uPnCYmL6&Ni6sP zhXK`q`NUk^M68Iq)-JenI*V0s6)O zZy0?$0o{PzfW3hefm47fz`4MUzfT!nk8V2GFS$dVYZ34R;Ap2JF@feU|`z zZ`}I>hX5zGLO(7*KLz(lU@S1D75do$`nk9-0xkz`Y=u52K%bBMPGAx6NGtSZ0s0o) z4-RKEM_G`(H$S020DaLPpnp%8mi(iTF&an!Qvvh44)+{j9&mer{xl5?UlQh9z$TpeeE`1Mr~k-*^J|T+s#z}tM4z_;`urZf9sC`DV!-^OxHEiu z!8Xum!803}1I!QbKZbib@C>jjK$lM}_66kIiF*R(w?FQKfg^z91N7Or4FS8PGte6lfAfn&-Z1qup!^Y8w8l&{kWe4+Opz4Ojh0f zpL}X=3GgcL_BPV*+zS7g(vWo?a5ZoTa3AmpV1A)8_B??SU{_#Y;84K)rX9qY4`4QM zHDG?N(MyjCqVkBg-p!dXtB5HPYZs;EZd51EO0(J+E1k6w9U3~h!e}TS*IKBpc0)7Ep zWJQ5CfW)yAAaR?Y@PFm=|LpTe@E88Jfbb6l%~SqKoJ+x`_Uwi|8-91oami z9uL$2b-)F{#en&pfm{6Q0r9&MxE3(Kp#L=A|Em9>zsQt$MP@MGhryo!^Q1MUaJ-~9e4JwaHxwKEl%23!bS49o&V)>DAQZGOUE2MB*X zAp92r!ao}j{-*)+`)~7$E~3BaBKnIiqQB@O`im|>{pH(W2Ll%Z4ZsV)OF#_1OrQtQ z4=_KWr~33A{sO&g^M&O^o=fJ;! z?|~3HiH@Cp9gW_>_|AY&;;REBzIs67yA_c576B69gMh^MBp~r^03^Q80EzEAK;pCg z_`2}l{Qh5$Pi!dpCN`9O6B|mti47&+#D`H|3%2`fquIK2L|+9_B(X>HrJ=X z=YaWrk6T_hk?&)51X{}btN!DWSqDr82&IPUl%&#@NT*%*Jr;s%k zXavmfIm*I%U<(jtJtPBYA6OSTBQ0cI23!l=449vMn4|~L6Oa#&m|tu3U%N7Q=@zmI z0Qn|``7Ofz0PqAL--|H6*65=WtOWq_HHLfY3^#Qh{1)IrK)xbie(CV!0ENKx0Do)r z@}t-tKbk(Xl6f9renPjP%j5Zf)_-qgw3Pb*v?ai!fIO;iejni80DJ;`8KA#O{PMc{ z9CTY8&~IDl*9Z95A?F+52f#s|`E3IK%%}g#fAedN9wlripa>`p$jQLn&Zie|1ARI? z4*&(|cvL{o*60gb;g1ux15gK?6_C>!T^^#Br{8l~bKD`Y?)ZE3)$qR#tOv~RQQS}a z^tZNweh}A(4hAZLV*&GPjeZ$C4S+nZ%$GwAyVV+9o?Lw%sMsfD9RZl%FVJ$pj|3)e z16_DK1M)^{-vGZnQF;fk5%{bHU7jj!Nxv7`gTNEOGl0B8`Z6G4<*`xoGkmT2PviZI z>A)4hEZ}C~7Qp=eoxePn@c{4xpawUEYmL4$Jcj~D0VfCe_r$%wPd{!O=(X@v>=Ckt zV*hb~`N?w{@}R{mK%UO{qyDXh6Il-c&jW7)p8-DsZPB3%&=1%ZFuz6k3IAF^__?+4uoo z0>WcVDDEt@LO-Py`Xl?IKkz*80$_fv(dFx+@)gk%K)x1geuHrz56G81C;SDvd=m4; z{X^CUz^Q1G|2FiaefpRDH@{84LzfRGJ9PP$`H|65OEUI{D)?g7lNHTp()J_6+R`eOm} zYmMHWP3j&%Z=ergey!1~;W-VEXV>2h@V7>nhYEfGF68F-<-pP0j+PtNdvS$WZaJHu zT=ZWF$nE@H0Q37#(&b|Q*5v=U>F2Z(?;oMJ#dcYMJOwZuFh9AfKLL;%`WI{ieH}bU zVSl+Be{8_cacEnkr?x`>4Eh&<+-~19kdJcpd^T_;uv0+(2jCk4xl10T%LVZxfdp`l z+^OI+^ zt^wpls1pErR(1;@?<)NY$OBNDfgcEyGKzRgLe_2Ut=$2fg}?p=H2YifQu;-JJd6Gg zATOVv3&=C)<5;_r{i~nRUEYfN0FdXPc3{0mJ`lYxAm6n;9+0onUc+2pUOw*w$TR1k z0rHvm6IgeUXRPmHtw7cbmatAB50#daM)|tw8bH2a-wBY{J0Ak%>!#=L%pP1<){DA_ zEcuY&%w70~FJST2J^8|4CLo{lE991re0XmTAm3elrH*w$Y%TLHd3Ddl2J#7?eC9Xu zd5hf`qszA}QW+1((|a}Vw@q5dff!v47|lyK)ytN8GW#P^ji8m`H*oQAa4T| zAHX;PIPJiYC10MYq2G{Cn`{ZR@A4SUY1C)A5&bkEcb(@0Qh(0{-PCy zyEM1Uzq7}`^EYMiKgIX&1}d5r2l=-_H9E)<+5G0b4aCgyu8#JldjTtN!isY zNMT}`L{kKHN(r3l4$kv=Ai14FeX@jDnR0B(F%TjFf+K{e5RS^lU!$>3Rx*Gl!~+B^ zA*Dqu&hG6m2(@RIv?ZMb9TK)lb~;hD@s-iCGw^RlXwy>0?g(xh;L;joCX?;OEosk6 zMx|M@lc~~Zj>O!gNV|X{+CMiaQN-mX2`Rg`7ef0a!5QF_@{O{6vRGR+h_StB-m%nb zmLU#(bW4)(bv8ti)Wsl1l4TXNBuSvc-x75`6t&buQ+ZmknJT0~)4R2#OX{>w_hgDw zz{LhVnkbq_XvIm(YD4yvywZl&Xo->TmD!~1UWRT;vyO4Q-xU2!SAW~Fx#l8;!BGol zMuer7jwZ!qkoDWTBm$f2k|)>GO=SLG5WpD3D|M|^@FtaY1#c?Zx_UR+UsrBPrK$eg zP`#U1q|{)ZONOT<3AzdTPnc}04V-S#n$oW$YuUh2{kPi0wP?bklBDKVFm0YLrEzI2 ze1F|8rL?G8I-b;|VKfi750`~f{IlAlMvkdY$)G{DUQ?8!Q0T|>H2Ej%ML=lO$YC{A zHD{GojT$v}cv)@DaLZ7JkFBW|LfMGXi6k+}RyLt}E39McMkiTK_}>t`xRT`B#3?$O z+OulQ&aA2(T~=CJRyU@$Zrr%BHHqp|TJj}vBfqS6)X3r0Wg`>SHNW)}YFVPDYGk6e z70;?us>Z>z74LD0tq2@`TGdvlqpOD3jNOWe(bd1TM_KLGit8BZrSYg;TIK zRc8*xb#ZNq3ZcjWuJ3SJ&2x4;Tf-=V^*9 z#$+AyRwc&Ph+mxW!-v<6_T8hav?5gA(RGQT7+mP71Q{aY+ zkI{)GiKD9%r;R;D`!i$n4MI?oZj|thM0$N+hAE?~&Kg!d6swLIsdc4hOsE<)@|3E? z$gyLzur{8;A_J!%Qix+tsh%iQrv6@m@>zxxFsS<}W9x>Esve4_UdE8*+A~Iu8+ztx zBS%$B7*gwJ>rmpY5>jR|CZvxYC&~z!gp2XS37sLBAH2Gz#xo~+F}Sj2X+&MjJulmd z#npxN(L^}pIO==L-XUaXQQgA|E?GEHYx#@U0|n5V$;!G z)|S*dZRl=s^~u!ef^deDryiby9^Xx^R2PO8@ISf+$A)klC&yKHWkw6aZAsrbqwU>8 zn)_8Nnknqja@Y!RB=}!p*vozHgwNZ6=~LATJ6i6ldr^txtyT+MwV1S6IM+B?BS5(2 z=cE!{XoIWPkWW#kyBKf=qZ)Y5Q@lkTc_EMd-7?^;;(>8S~)gRAZd!ST}6 zL^v)s=>mc+$Us+prPhSg)tBfTw$=K{_D-R2Oughr6He|RS8YBsx*?qIxUTvz?AwWl zT5VZ-Q_qE@pzS7#skK&uqLi;b3RjZvY3d2P5_(L{J*ynwCH3V_S{V#6^-kI{SN$@U z0_>{yLIvS0CsR#>IUB#v+LVXG>bbKQU=mk7P_xV{g%Y7G@~IbfgoR;8ZOE?FJU5=v z*u;}@GCZ$np6G6Bygi7Q0#uu5!cLd$b&w)UgK9-A6mI84avg&(e2e! zwUUTb^>Av!N!!iYVVU`{E0P-Lf?pCL*RHNhV9GsQ^<1nH$r1uvGI& zogLLpSt8t#BBOfPT|-Xv3RfM=tH-YD6D`2-RSEi*vlC8K6*su*pmJ$3GHDO;RS)q# z4sW+WidhNX`y{IM@wCG{*-cD z3I11xJB!G*GzncM;*PlqXE;W_bXXRP}l>Wtv;WmH(nf6Y zXj{@WeT=aMnYJ}!=w5ePX0$;YWA6A0bvD5^O_nB{!ZF`&ts|-=p-bvX=-X2&3>(e# zZ#t|1pWj6`MyN~gPh6>R@IP7_rmnmlZ47rq0V$q$` zT^R+stKV@)nGWyHyrA{zaB^}PoctzD?4s;Q|uD>)yMX>HR?j|p$fsgq0vlM}sW zb1|7cnMqsIq_^2T7o=&n1>#F$#+#gC$-K61%y2U$V?OIMAzmBz41cCgaE%WgIcCII znW)KJcg$Jh!i#Ja7s+Wpx0OhnroPB*nmRMr2u_`u+qRghqFQn?jjz6zFOe`GZZYR4 zLH<;mIq-;)qx?xF$nddsWAscIVP>w0(@!JQW8c*@7sj_~{u>B|iK|Ja%=Ps&7vWVk zBNL~IMq;(!&b)omOl>J3!%x%mT*;`hVPGNz@H{ZCb1lsHAF_HT# z8HDnMQ9}Ii!VgwGv1)i?Xzj?ewNjXAm^bs6g^4jDscKYUaYQnam4`$XQ+N?T=^8$2 z>=AdE19&o@r7R7DDzM`gP?d8ow$hf z9NXqI*Xcw%V`_AS(9|y>x<{WP8VO~APiV9YB+j>f0=bLcQKy@B`w=BhdB`q9w}r}IJ@*-W)5gaY`gpV}0-jd;`N zHI81R{XS$I?tAHQb4NGQU7X!0A{RKFT=i|J)XAQwCr}?UnmG0hcSUjPq;o*@x2>11dq zG2h~cdPj%KQS)W~^7E7m>0+*6{v;FA7emY79Ymkm=%fvD;^*l81-r^w5cM-p(WS-* z)88c;#9r&tnZ2u9CzIeG6Kh1yxha*H>Z&k3+oo{~)O8sR($jt^BZhAirA&8pze>E{ zO`SmpyORHv;T-8g@6B4kRMJtKMlB2HGU)5*DRh@gNGNsZ6dg!9WTLqwg|7IUL_!AI z*QYREb{o{jM5&A_=B89c-(dXSDN3AACJMv?^HVlBT^YgWm#eo4gy#2UR4};x*Q5fc zk4h_bI;gj;1!%E=Dc3)1DK!iZQ`TrXxkk=*M3uV58R#a|#@Z1gq%oE5`2AXjMG>bT z1N`_5I{j4j)mc(L)6~6@O7v)~mrff+m?fg^6B*0!?54gU>b7OkLRUSLV7L{Gm4fb{B$!TRyXu-0Bz>HykkzJZQ%L?tFm12tPCQ!Z)kPOka-vA}kSgER zTeA?N94}J+{FdMo-4eJIjH{>kEx~4+mcXZYEx~4cflu&T0udvplhhMd3*mBBiHVFa z{ne!Og+}@-MmiKv`YXQlg;PA~$d;2%>K}a9HRfc-)lZB-sTk_~NCWe=nEGLYtet?z z%ALGDbj|$iOd)krvshotf#n-%P48g3;b;6ZUC3un7(pG7`3G~+|L**wn>9Pqv79VhUBWguar436>a|1QfV7Lq*W}$YGzXGdXwTxxb&nr zj^8X4FKn6=Q$1gbVB>oxN-nzUYg&n%krZg=^{)CCb(}PQmr}`mFRE^#+`6i|oI2H& zB^{Qnx*VX_#coQc@LqI^a8bsev`FbA_2oDkSmax?vIhBr4BTV5*f>~%l=*axd!zTo@V!E%}A;s&Fkl^W_nV}eR`u& zO}t-i)!S7Elw%z-=@}M0GS6gYSib<nPN=(gwJt;!pO%*@%`M}yhG;IUXcqJfv zrTDAk%Uk$LnHlFP(XHY4SeORaYd)EDQWA*~hb+9WG1+Ab(hQwV*Jb_1669qnZ9Dz2 ziK4M(&i}DO*$Q_crt<%8On$9k`HQ6yqSeOK=DI6WmZ^VtY$3~o6pRTYC>E7lEf#6& zRI+AHQ%3253-!)qO>*4QiY5LSxkVk9oZ4c>a}_#OuVys(sT$F|uIg-+Q8QifPbsBN z|7#fFw25X01~_^!6P@vgM>D6(V20K+IGE|F9-mC(T1I5GfuW2)7DAcXtQGnFoNPz2 zsTHEz5GnpZM~_2fpd)PRX^&J|%u7V2I;x9Oz2S-8(~|SY ziVu__gt^46HcP_kPA~QT6q!^&Q;jw|K$M5~>CuxrVRBlTIR$;i^r?3L5PL{$Dca>u z$~~;L$bwaW9d>5Oo0>4Om}>J4RgpJS&qRd6NcgiU>`}^U(xr^4#rMjP%~$EunHhXK zHM+%+woj4_m&|5_r_mnh4YcIkwE8kLZXY-W2dMM5xa~@oV!xZpHLwallNk}LuZeYE-kRn*N8~0{XuNT z70wP+gq%us!-Qz5!#MRrhMJ5H)SL+{i}}?{MyQT@ExLxeU%DRNtHv`aA)Z9t9j^2w zei@rVajZc2u}1Jr5lj<#eta3KRp7ir|IcwJI(^j{Bk0s}q>uQfPWED?mrznr+Nss< z7DiXEO>D%yAW}}@eq$n$UO0uh2Xlk?2&cUatiP^f$+Sg4S;lTH;H3oI;T_#&J$hCH zG3BbK-BMIyEt}p%wm2`IketJ4yU9>HpTZeyWc-<`-aAwFA!F+1Xt|SrjF$A8mQ<+z zh9vSy&6q$vv{@{SuWTCe%0YPb7DLtacu^Boj>W6@O)-8XIaGbh3{@XCLsckVA$r&! zsy^ipRrQouj@7HBW@T9`=~XQ zc%-;$XA-?1EA6h@Ws9r!-5_ZnG{Q;gpt`SQPo8lQ?ORv!QH-%rH4rLCUBQli$q6z< zQ6I4_T})+rW&)XUMx(B!jG9Ug*1nrs_#*sMd{a~n+z1!LyXp(Yw7G%o@id~&?3d6Q zQOowFT#A}f%=epN>;V7elbLQ6OPOVr+A}~;3?P>?)EW%X&Pj=@KI9pLOVR535Q{)g zs@*+{*~8A|PVQQ)z)1&^H`9)8At^W4W8T$pQ>|u&DxK^iv^6MgLUxhU_DoV=z`*)h z_E$Z=1wp=RSk%c;+slIcBgpQ}W^;(1=3$mC3UDsNwahsJ*9csdQk0g;X^NHb64|m4 z3ao(M(NzyhxM!r{en@tvo}N0wDG@4T)n`18hlJy4Cf8cs24B5ARlI=dyhpx<33#6B z0O3W#!1t-db|q^Ye)}WCwSH$+4Qc}DCP6!dKzyV z6)bY_rh3tny~>Y51xyGN>e{Irg!kjn2q%}#v}@6yGIC+46d7si$Oe%xzn;Z1Pl7kG zk5W41%8t4!&F_LQo?!P0F=TMmzheF*{vz5zL-E?KizY1a35|AFuLG0|KWe?6!mImI z3$V_r+USC&r98O^)cRsy3_awEklK_X{bW?mZN$`)TIyIj8%y6NhXWVWq26<+VMrAbNnyOQca;c|D=Vi(pogtx=S-?2~Ji-f;pQ)E|i zLWZeRqhgrViQ08fh^FM8Nr^Bewh$phmr403qAZ`ZhA9^@UYto;=*nRNT@IfapU^@t zmfhf6>t{%~?gdQakBe5Y)vba?NnV`?aNy@3NG>}#nFnwa#dOd)XwVbTInd%zoyd4bXFJ9muT-xQ#lx= z{$^7!A00;!){)Yv(x22s8^lFW((_$yl*0X$1jT5-Sk#0W&E@B)&6Ep7W~*mnG+E0y zpHd=Y7TE$6<1OD57+`8kdjXcL zV_<0h^xoySF(`0Mh5+A^Jo;7MFUm=IZeat zD;nh_!yi6P<4t8(vAW-mA>rGTsnq-U2WzB-*v5l3Op;}PgC;}f{I-n6uU7Vfv?H5o zRw?1Xvg+Sg&6T5&??cIBheB285z`f%9kFRxx5!-d`^l_bAw&)cAaHWy?{JO?Z!gvO zZ3f@Ou#$zzR9C&g7E*!g%{XhKw{$7pCP#VjRD04DtesjBrfHRfJT2EO;aGSUwQ?;Z zarr0fpMOlq2pvsQNOH(g$m;01Z2CwsS=79mG84GlVW_F*cTgw*DqRB%-zC~GJ z))w_TJS?i_h&^96_M{+*Ju%$dtYNz9b?K_05RB5m84Jgfoo#N^;*`2$e6I)^Ll+6}>$VO`-hit+O3@>XlBV0zVo-?kJ$p(955jh0; zM-SU5O28;;B*jFxe%XU*Vex<99Dwk2~s}_3vt)A0IY*VmRb~xht5ZxjC?@RIaHZ}&} zQ&HNCpHe-5%&%xJMX0pd+f3cMgsoH2riEPW(XYUt}dc zlWBh=e+jXpgeW9LlU?M3dyWhd<7yQ%&pzt!lUKU$g=yE{V;888xU4i1 zlL>NO>a#9SA}WoX-^8Sosid4*-zf9XKGG26c)Q#WD}pbYyuj0M2BW%8YC%dlJnhuX z@mnZ+$>m=xAX^@Nq_4`Cjjc}V%k&Dt)#c+W^^W(`G6MFK-cm7E zI16+qx=cR(sP~1ri!J0|0368^V2Mo$)!pxBWHj&6Zv|+e_zu$est2-?DDdiT^;K))zC)Xl4wGp{PG^iq3yo z2OQz^7x-rVpW+u|-DflWSVdMBNL$6CFDs8z^s-7TU3v0&>NfN|$!!esO|ge8d#|_4 z>C`JI>~WSPf@ha0$OnlUzI6tMYOxs@}`1lvb~(OPm`z?+OO zvSn>s+Vq#F*eq4c5MmM2WRF4)UtZ7WedSarB3m1-abbmdo9mr}eqmy%46*K~kCHjU zdWVYIeC$B2o|D#??EAGjr_sL`vHIRl_qnW4u-qp)FhcQ0-E(bwH&0O+v}ir&`g*RP zf}X!lwFh$)S2o#iW{Hg2 zvCi_AA`l>Ntf=R+;QIS0uV}M9vocktqpu`5D5pp(%I$I>vI1ZL!weS zUM~mHZ(xmrT>aE0kFT6ELoUW`wB<5lOs%0MPA=qSv%hCEaGk^Tln=d?1!_=gIULz6 zx{}yA%~v#X)GKw;v|ZOkCXsqktcNuFGZ=SE5k`A?_Q;eN>6E&wM;SSa(i>$vmoOWk z4rKK{tY?N?7Lb*cZAlzg16n3-#D6t$es+4bQ>JY4mJ`?d^px$B|DCe^C#G!Ym?@js zSKi1{KYR9F^}qK0PuO>!Z{JTj@g26F=0JGsw?_W#QwAp7$g#<*6-}fveH9_;k-UTw zyqS`Gmf<&Szaf+bubm{rZT&fit==fv>Sc~ruDE!2In0F^?|MYlFy3|8`b!g2JkXNcTjfZ2Z%0oPb_i=x>@83HDsk$}m+sv_E%`x{(k zX?cHx+bRC#3p7aHv*CV)uLGA*batBSF<6OmAR2Lv!Yc{pq71i8nr`7BZ5XEGipg+& z1A~hvr(p{Th!^ijj2@|9qTmL^@aj>ce0t59lDUHLlmgBw?ZKNxzBXvv#F8y{H~5mh zEl&ulmF&tp?0BjZ<%Gv^tkG}hs$y9i?4Z73B-#i&1mF1MX2tB5hRR3DbeR?l*qTk!kjbiWJ>*aIx^ z#uO15IkA=}TjmK)STGORte-^Slr=lr5Y!JLc_ScQoi~LAr7PInbU4bVdP=gps2}}g zZ}F3@$4y~wo?hZGZqnMnBS)p0#!XupS~P0yW!$6#$Ofn$H!%z{<0jPi#!ZpnxJgrG zCn&;VwHY_bIC~GS6w8P_raomr7tD`R^_=8KK{VSb?d-+(_C)5X0sHDJ7|reLY50fj zyTP+nFR_)w$vLlx{KDzzCY40VyEDjYVYy;F4xxOWUht5o-4A1$hvsCFk1Tm>l8nBORJVow=M#ig-q#GUHabW8Z+hse zB(H9&g{;gIWn6ujBF4#8a}x60LkyfzYnc6`If)J4Ks_6Y@}psIrmIdP%$+H#i(lCC%_wc165AtEwJn!qSnaKU|iO$2G9; zyGE^I*CF58jiX20s3vOl+~i6KY3c!4pniCg-tSl$mV5N9Y5IOkRXLxvCti^(54F+) z$@*1ZTz#5VPVn~XosqoK0Y%-?uAJ4|O!f2W6?k;y0dp>(#%PUE!KtV$CQb|B) zJ?^uIy0>%d-r}Bnt5QPXy*wuz5XA{mpV+oj)6U(f*lIVn!jo38ZFoMU?M7@{v{jsX zaO&@Wt+n@F`%L%T8d5>Y=aahU?6ddUYxuAK`1}9Yn1vF|7Z(4+bQhDEU2I-8sdF_#n7650_4Ch~`MgvDL*`3aE0L;lJ6-w;S%M!S78 z-pU7W<(n@_EB|yxE03q2*QjrqJ0e*)l!=R9ILXTHPfxdYyuisQMI$`Dr?nBgtUn?B zemzH~Uw!yf9((APcU}87qH*l%zb5?4r9*%4puTYE8@2COzDv073G$s_o!I!MBvUz;1KMmo8|Pt^6}p2*xzn?6D(GOJ=0 zT=aI3S-t2+#J=e-&Bp07o0^JQfr-Ruy*h36E;RL?V%%x&JUO+jvLU2+>!I(zS{9Rv zLGO4iEalrtb0ROn{z*cX;0{0k#k}M( zFF*YB>pt@}hd=WThl5uTTasEU&*C~!5$Vgf@G^PN?~#k}cqmg^|s!UVtlwY>Nv3jf>Rv#Dl9B*rWR z@Qg$Mjd&n>{-IyjY~uPuAC^plzXA4VmhHEQ`UyArUh2(|KFAWiinaR{_0yK1eh5I1 zC8(d~#v`PT8-jWpkX+XD2d{#l-VYQOf_f{!+J>S~4lyY-Wtdky{m`%SiqG6R{DOwe z@Ms=7i*a!1bAFmn?+~*3*lXYAKJ&CgKi=!sd(@wB=l4m~lE0yf+QU~}x1#HkIIroA zL~bKKaRkHXi^;kG&bff*h$?BC1HadjoYYvh+ZMy`-nXFw969uV11WMgE|qj1G#gjy zkjZAjb`tAM-ldZ)Wh4F1tKMaQlfCg-vR`GS6z(z4e5s$ieWXsb9ShL%Q|M89Uw`=VdsK-b!vk#X8?S!x z;m2S1%EM2&8RsuSVY~g>1ZB#>|E+#rYvFH#F1X{+A6;dttHI;i8?F}Y`W~C_!wF&Bk<>BI*?r0su^)R^l(>#$GQ7fu8%T$zdfmfxQVe6b;Fl3Z>iYN77ui4Q(%~n31D%xj{4}qr5Bt}?`xCuo2Cm@T zf<^fmHsX^H33T*3U-xo2$BX&R$tgwzL{keoJf?ORIm#FV`NHi;~e`X?2;4 z(CU*R1M|9F>UDj9=if*;bqohROVIGizM#)ve&~bhJNaAl*S7u_KxyTbU!(i+Fb_O= zEVsVjy+u;3m8?cO-FlQMj=Eihu;-Z-_I%nGc#H<~F%sw$|(52nf6J&L@>i5qUmQ8!~ z83dH5cu&BE{IG0!T84vP_l(2WUw!gPjevU?0rxk$V%tj&*!KG`aocEb%!BDSc>uFJ ze(zEp|D`uv^VpTL8tt7L5?y(Lo}PjUMxYCi?-C%^sFeTbVzqV`kRehPy2qu+jNKcrFfY}|f| z+D~EoDQG|W?Wgub95#)#AN(|EuEp)Ap#2p3?Z@_mpZd)s{-FJsw4Z|ZQ|PxJoliVW z1+eknPIW{3bLurs4%$yi`zdZeMeQfQ{Ujfn2Kl@(9-i5$c6ZOK+ld8kj5anm_BLp3wRYybl%}H; z+7{0a$5p;ltPe-$hYwWe)g2N&dkG&*@b(-rz)Jd9Z#<}8SZ!518^t|&$rnTZo7?BA zo#A@be5Y=|{jP(8su{&_o1$*?@|=B+ZL>#VwymnUu)BALvgz`@+LD_0rtjirEjP|e z@pH9B0eB{Z7OSCoxm++M?A0B*l0EmFBUnsDb{k{V(dMQkoQaAKb+mR^=Np^Don3oP zcXqhFL#4TiU&g!n&Mrm$DE~g(thOe8rOf+`-%eG#y0D(@g!wEr=!O!@RAx@a`D@&> z^2tPW)jc|}+l#iUJEe5qeY$47s&}?e56g{H8@p1-P73FZt6jzWdef?2Agt-6o?=yR z??63@FuEFVoZ1?m+1wj9Gcs1U?3|)#-xlX1*kclGlv}ms9Gj4}Dz}+h$^~kI8WXc6 zm`>=N`iH|WspR|vr#_*5!C1=b^ZE1{ir+)7)biV0M$XbQ*(8W}%S3wNh z5A2;0%FyK?`7;7wz)d+9_#bdg2OFR>%+m=fZL0Nrygu65KDVWI_O`f1mxmN;Z$Kjk zTfqJp7dspJYz1|f`O_5klqU_l=_pvE2&C$#z$e)EN=SToZlmO5S!o7knBs7Z*IGQ+ zx)!IBBTV!zrBT*xd;2t7`?L(nX<dy*=J#^XSh)&U(?es#7xB@m`H! z9d6h35E(0+VNV2wv}-;qAM5+?ZZa4P;!_Dy$|H$##R9~Ypf?jbECq3(KKQhl)luE%}{F} z`jS?UjJP@_S_@@f&2^47veclL+=$A1Mfq^fX)$^k`$7uAqQi`gF4o2mR8!o8KDY=v)zfMN?UKt%s14nVuL64Y`Nt zs2Xi*eaHqD>LCdo|8?_mH>Ji}Bv5>xF!=Q=xxwYgOw-X~~m<#HUW@2MuI{)6AreLY`h)eP&Ia+Q9E7=-M`c|fmq%l7c=V2Y zj~>0}_M>PTNB08n=*#rK`B?uEjKazan(4}l-m6Dnc<<4y)l+D-j$=QJ!%>mPgCeo5 zM)Ft)Lrjb|nI!rxBGa5!(HvJ+v_`$MazYeq>B$LsgT4tW(tl}2uEqLJ-WcRnI`9K; zQ08SCR%RULjr22XH5ET_g&Ge=RaPd0yeiVt_k-SpM2r1E6FNM&MirHjWnmcxz&6U# zFa6%_yeCA~`+;i-4Y(>olSi{M3rCe7=LMferPIa4s3CrAbPpNvy)wE1^bez~OFy*BigUJkq}PqTRB1$9*yO@qGfk}zG}iD@Fz z7KtJx0hQl~`1sd=Q3VnlctMj9FX*Wg*r^nIBJRSF}>*b{!Jw)=Z*eQ3FBSw= zR<((ALW|f#Y>IAE4h?vh_PjxzB~>vPrD;@E17~uDbp(c%TApFqfS-t7;yOK~XQSFH zqfr?#`C-4MgKn7+?oEp|`#!3|fnVi$oY}s`dQ*h#2|uxW;@+dT9lb>+eqi-2p3Ty% zuCjrbg?V0w?zrad{t!_u69QH&@xp2p#8p%#&V)47mKn!@VH>v{JuYvy564-CL`#A+ z9TgEEG|k>MTO3#cJu?|7#mAM2TF zeLA#KBz~FZbR;YDsA)y_BGy!FxcDIIvo2>_>@N={D87{o5GJctRsLuI_l=whhy)_; zhhj*XnKSF9c^!RD0w2ou!a-HB%|r9xqP0#?&qZR$#>(?in3O@D)%@$c(~*AgrCGyW zw5u(#YtGfkL1&WY^A4d0c0XMk!JX40_6o#^A5C`6&-Cm2PB5MdkgQo;c=aIl28Hho z$gE$G^@lRC7!m5c%*!B4{VMR&Fc7}(HgjMZ22F~$2IUm_D4O47f|-duDLrJlQy8M}CrMCyQv*$ef35HMk@?$b!91BIc{xam8bS-w(moj* z{;qAczPu6rNoHkb#h#b3VovK=Ik|UQZ(l6C00P+?jL>`d$GV023`>`Pc&87JGzhDJ z6;0!`d8cMahENk@ko%s#HWaQY6BK*39~Ehq)^Q&79viTq$FZ2?EH= zOJKWYFbXp-DiX;4K#TW+jk9`F6Re=6dW4;kdcg=t_y9C`_kK}-)5fLl`;MbNHg2i? zID(huX_Y2vRAhNo7IT~Sa5-6jZ6tQ`zN}_cdQfi|#;7hq(=b$|`M#kT!_Jz>z{-jo zDztKEenx2L1PamZG6mDL)8~TnO`O%(e)f|*6^X-wnak3oWXTeA`? zt}l&D_b!oOy&a#0WA2h9buVILu2REaS;{RU|AXEP`P;^ zG(rRIeNy9>6Qfpc+0uJ!uzQ|J*M`hW!ickal+aiiCbkvdGkj(C+8AHX%F1o}{35eL z7rr{;3Z)--zLT`@3+x$y2{fG zJ@Lc3&POq_4`$Nz^a4g4<(@x4$t>~|^}&qzLYy!YJQRf_j1#O*54JlPSo*@p*wLf! z8%C$Sp=sk(Wi|6GOz;flS(Xga*gmUSw)1c{Bb>vt0p<||FhELq7!d9X9@z2{M+*Wg z3yRPyio6OEf8e&blq?RsS>fz`xn5k4V5nI(@(^8#ed|0Kik{SEob=6h}5IFN+P!C z0MmHlA>Vg`5wRA=yeLimpw3}wVO9lM@0kl-OFU;WB6O6NNiquEUa-ICS@?lA?=b`= zc1yp!%qvq=*-=)+e!mq4+euMRvls2dH$s$W{m5rogCr>YxT*uwgV@+(*2P6nPy<$0 zPU^qYyjXKCt+$APn%H+Z6pJv33k-IV^6m(GeRCyzh6lr-J!EhxF2*z*Bym}jIc6Hb zcq}9FOD85>*^f;y%_wIgCM8*&r>lyrGL)3K@)B?4zFu2mR!*jktW6tHiN-f>$1h^9(QcT9xCD_>aFrsm?HZ+ZAG@R2+u0F!5$fIcjwoY(8u?{{OM-?5 zq1#2VCI(i~4JVvM;tOs3+1e8t8s>s37-Hzx;{QdbvE*RvD6KXm6P^i6BLqY-h){R8 z?~(|KCX&W3pD=;bCX6DT2Mkt}aC(1$>(zH(ml(234An&vo#cTu2(u9`HXKZYLG5+a zkeHxF3|1n?vLmvp(K#JKNcdG|9=}ymmT`m^vMg-_j{haZR7xi%%P0<##=mkmAeW7N z^Z2WzVLh;0`PaG@MN8=o`j+;$pn4TA$4j1jsULfZkI&3~c_&D$TNlPydyzk?6SN`r zcU^XDZ7JHbUXDC2$|_3|?2R%UO}ApaLxE`$2e-g?dhi~)jhTi=Gsu0k^f;exV;`S~ z?Fk0oXpoLP$@@$^x0M6CJbb;qahclC8+}9?(n~KKky?sqJm_p?0F=hS7Br0z{fH2E zSvW}Xz1p@KC)I2%(jFGNa=Sh>)M$~n9liU)_59dqA?y|HGTB!?*Zv?!<4%hK9rc^> z$6EbAddJ-au=syUjn=GtO6knfD67H|ewz1b1offvehl_JZ6aulVpNcpD4#6;E( zB4l1*Oi-Cs8y*=2Md}xW!2s&?LbtEJN8AFzHQp%3K0zT|WK;ezd@V^3miSGg!N7N) z*3Ekj=<9wzZ2$+5S}+<^?547;i-cgAGe2`Q7JvM}#u*DA+;<^89Fi#>ECN0_Tx{!s zRRx5wT8{f5byHSu-PTdT^8A)wT|=Fw&dSPhDYwF9TxL2-c{<;DK>Tbc5uCFwvj=7q zEKxH-nXIfdCg1Eu6SIl`35Q}CHu^%-iHVTSOvz_CWC~LkDQs6_2@LY5cnbs zQNT^>FwLTj*reCH{Q~tdELME7QIhyMzOmfx0O8DFBA{rTnT*)?n{c*~qEX7{5)aP9XLrK1&8)0HXs45d~0c&`Axaf0;x<>t}2JQeNQ5pvp zWEV{O1tcKJKvFa>4H{u~VIo0A%n;5}jH1k)i-XQ?7iRU@Z-_tiRh+}bnKf@2nBG{T z!spsXRNLs$^sH!)pauhp*$gH~`XB2Hbe`Bt73HaqQj53KzNhuFWzEJO`{^`lShjc` zjnIVH&Frz{Y_-qt76XO!~v@Mhh z4OOYL|%^N9gT`0Oh(vnh7}r(ZBCf1ks6|$_9KK0Up|xa$w;Ti-o^u=yrI|QAh^4GPKbiFI$wCI9 zhgFb)o2b*Gu#0i|LRM;Zo>Y~2l|Par3xiRN7dXt5$V0bsUVWivp%*e7B1I)kDfW9UB2@jrt)&=v zPI^zPEVr)NC<9mnVu9Tcv@kQBdqflyFcJtwT*4>_Xtd6>%&`UupiiVlu&!Ai1eK^Z zafTmw;ItyrLKsOrVSG4(=03?KdlB$ZSJrHR>w|)CFT`o*Dv;I`daY^$ZmAZGOh^%dZT#21`cgI zM$@}+y8}}_E(xPoaZU6(Hl6c*!<@}jPJs>^W4AT)W^7y$!7WC2Ux7SBX65+p3;aw` z9Mps@yLDMkW`%VSgRKgPq5E;T{PWhoEkUsqB_t$jT9=XYdfC&B6Fse~Dioed^aF^C zxodDKHI?a!!Ef(DtMKuJsj=@|f|?wdAzB_Dp~Oqep!C2hbaQ)PhSeFcxu0h_(aAUk zyaByCvdswSNRoFFM44cR2wxO=fHNHc4G^pYXV2SZpBTq(|hi{?sCvIzYNK&Lg(m@1zcuGc|dl%fZi@>>2uEgY3?W@dq zIKwg&@e!d9AMc>5O?iC#_qO+muHkigTtc{h4s?d#42aKB zU61r@2Sx}vvIohuvT}zcH#Y*Phs@TpL*L_O0vK0~War8>Uv9oeCC&ud6SrOKGU6qy7{Br8Km`7bA z5TlP!7zsnMww_1y+i6fDQ9zd;D@P|4m&?J=E7(w$@C*etO9hEK|rQkk_7}VS1hL-{L zgSf*EE2EOZ;)Zd?Xeh`AEUVZOY}(|ib=Mpi2lN9%#9a-JeUk$jYf4?WfdVElxk*JbAW}EkhQ7x zGP!vCf*SL)fRM<$=@^Is9N6p(-^nNi!)r6|cpj}FAR1MMQC1U2v%}IV?tE`cN(!$I z+Ry;5LLW~jnOmr=zS{xeL7kT>Yil_0P)d}y^g%yYb&9{+>GfQsx5>-l*&4dWCmaT4 z83G@6p4CcVFb|K6C~ak5nyyD1@Q3*H?`B)Vucn|Zh01ntW+_e8H}nT2iKAU zj|Vp8G_!;~G?R@tx&w1a`-Omg887~SVm-F~3xS#SKt*K+?k^euF#tk=>!YI`y>z!X zH3EEe38{(G76Floi$)+DMkCzxq_@~-7m_V1k5i@rigmsUdo;_}kxT zEkTV0e^!LL2zBHPagH7Do2aNR-65D3?Yi1mn`4RL*f1CZ;@QZ3O{@#aq{ggj{e_Ng zx_stOD47y~QC`~OPg?d2wUgsYRI7r+4D9=5(gwDmNvw+-Fd+r$3D9X2;{o@YJB{vc zH7P%El{zQA6NnyJDdf|UP%2RINpjJwzz=(IPaD$(&s?HP0O`Y}iZ0MSDzeR>BK)Xd zVuj9*$VIEj(DD+%guTZaLJE87*?N4Sra*QABpSf$oj3KAjTD@rcGdET=f#5>Dj_yq zmua2r&Ncd^A57#e%ub|i@+boVo9hw>7I{@-jAsc7P#NO_F>RaaX5cNLd%~S;4S)r1 z2p$%7yJtr^MCE2m-McLYX{(=U_mRn!B43R(r2wyA8sZIbI@g=4d12+iW7ke)uQQ=_ zMPf;f3Fo5)@DgXW59;f&P?NauNmc_fon`~#*KUiJ8CZxlx$ShB@kj@r}QlyIS;sEb%}s=K2MHjLC=cBhEwFfIy@*s`(^AEY5t9pBqZLs^Bq~S}HM1@n zVz99tYa=zCU|5bj&=})xkcbBfkZiB4=toypdR1*Csq&20PqaWm3r~srqyesSH2g9) zFQ2n~(XweKDWmdALL4ZLB<-a!IcA!cE~yJ`I)*{YykFv?8IABV*P%bD1YJpfpWHl^T?Ylr%ASLKm{APLP^PKRUEn z69^^NT2`LZ&b&=M%hr>*BnPt~8zDzN;COTzlKs21@4C}xfXz@YMslL@VUKKs^XPx+ zRA12`2R>e1z&tp30Ld?C8O)RvL`sKJaBp`bW46_u^bC%+H%Buk@&<(TanzfZ7X4Md+ zP%76iX?AkQb zc*Jl^XKBP~9^;A%f*f~iY#*O9Io9LfbC~(W1xX5kJ9br3nVLj!jc}NhbjO?|2;D2| z85CwXd+lI9?E+fuRbs%#cq8|W0#a!m_+?E9-87jPda!!C{bPz$Ouxhae9NYm(}4QL zJn%4>%?0s>QM8IRC5eH+6o;rev$-tnbyVWAr$!FIZ|oGe1#!K!9*CL3e-SeA03L{G z$OQv!eODP^1wspwx|tTB9y)>vQFe*^Hw9}jsZrHN0Hu-oSlZo*m14k9Ny!?8M4j|Mj3(Q{6-n)4DI`P$BA4Bogcul0 z4ks~=Oxg0J2eK{^aChoaG=rH@>yX{Cl%FD?q#oKaSTvBV#PPcMbvVE!tyRdDBPA@v zD-Om8#ISzj945YeX%kWSDG!L!O<8-qLq0m%!j>J`xu_=Fkn9Z-Zpl=~*#soI0^TMvv|#Nsq>KL`*We+nsiIJip&2BiCv3`(@az@yR=n>?W!%b<=- z!%5Gz1RK}kKH<^#Q7?aCV%L-^L(v5(D246`LjWYBdwv1Y-Zsy?S7Z~A5%e!DQ@^ii zeTGjbYb(1m<`g(G+PHjKx*RiF=LE^NJp!>Eg09 zkO0gYk=;ruDlAFcK)()SkC1!On*GSwO^iY@nl7#`8VnI^QZ*z9C*#3qL6Jh7MqW-j z=fYXgIE#H8H$;%gfWtK=syz5cJ*$bqnmG=Oro@oIu1i}7XnJs3Kc7u{VMN9;HDgmR zNBwHn!753U`r>SVXIon{>S}9>@x`pL$1w=KqDEp<)YXi_5H^-qhf+hCC%XsNou6>K zbK6pq*2Lp*cHydk8Y&73P}b2lGoc&IEQc1$V$h+XWKmJ}ke;JQqEUe<^?WJLHPxkA zF+eJ?P=^?NwBTN7Bfy8{B{^CpodrEv0{@^xDUqvNZJ9;qjkQ5~ zUH)1Q+~83PR#tuz7CXEESU0<@QD*sMP;RrfJD_ z6f`nNTJAnwMAP`t_P@&yVqQFkB40eIdoNhPM-p^x6-Hic~Icn&rA>|t5<)E}O$>j4$9 zh!bZ(BRL`6=?2g4lN&PAjoi--@yO`Jia@jUygZGZ_UF)0JQ2$8YUIXLcu0L*93aZ5 zsOU5$Xx0SapMiH#%?CU+Ub3!qSRgRL+T-g6&`Z8g{4HPHGM&FfEumyNUCC!PpH>yj$LD%Qw|7GOH+N+PY2%u z{VgS2P%%jPud4FxftJ`aQ&oFq5kMN^14$^HLqWqjar!rpge~x>1}-YxmBLI^Gmh;n z8H0Zkgs{+eB^sD;CJRZR)`}Da0HsJuQ5e~Fjq-Ql$HL%m!n7I0Qfw~_E@uyIxQ@_aK zKMZ%5vO+5>$Mum;#*&?iknJlo95H}+3sa8CiHSCY;xR956`f8|cA!%Ia*-538$`l1 z0*^Y|w!>}#;C_eeoFMnKdU=p8gQkX6PAlKCGG8X26;@Mr8hqxxW)F9kR3WgoK zjj~N`l;@JV;uDXo1@fcAj07;=>@Wtl?#!9DDU1+gP6~fG5(_i~Dd#&6FJ&qEzLU`< z^i4!yfPaQuN(zm;&GnT5N|TG9sGwSghmUC#ATNk->#onyw#7MW)YB4mP7WZVZY&^I z&*`(gMB7ypCZ3^q&B!`ZCxEQ!r=MU#!ZD}y05B=MG$rG{o6Y5PK0g-(>SzG`GRXl6 z>*o#6pK$L^p$)~nrP2#Z9?o5grgpFHPoALr#n?0suHKP;vILaW#Ro_*AW7Jerj@=7 zG@GPzh*YRZq7UK^Io^CU{t!0!#qBZTj+BFrOF6oL7_R9}<{FGFYg2c~p2uiU@{=B1 zL?OmTKP6v}1tB4c>_Ut>cUIZuOGAAuVg>sW@D}ILqy&a>Lw0Ml9C@%dB$ZPRX+&v4 ziW}(XEjwtmZo%`dtW2DvHN6ZDId?)YC&rt_K*dskoS;Ix{)tj88P!q{aNLC!pcszy z;dmHoH-H@kdG9{c56_PnG&L9o2|4jp+H!BCag*agYCVDl#Y4cI?#YDvLSpe=vqphO zXml{>wQjeMISDy2Cn1}x8+wn8tg|UA`UKG0bjmyt&NOm4y^OjW_`7X+Yn?0#RyrnQ z4FIK&D(;TVvXr?wGpMG%AA+VM4%mW!u3k{^ULZWfr!5vZ8NYIp%@eueGV0NJc`Q;?ppgI`VupC0O);kw!YVQJ;n*%Xm`q z)edHZWl4;&DSkBZ&*&HQeG^mk(jMh23P6|CcCkd$TIC+$YCo)i-;kX`!gxpiZkTTA zlq{w(kSx`pitxZtO3JpFm=Kf>?60h_k=>l@sln)3m|GPU*mp{8Z2U~QBuO}3zR)0y z)U^?T2?NYH%gJeR2|Z?CQBH|WDRxSXhr^G6Cg>@S-D;C?C#n)RdDU~2n>KJiaW(-? zH;`#D$GLG<86_sW6%-|>?+S@ER-r2^$#Ri49u@-_8P!n&4iIx5@3HC`MPGQDG!3%t zQLTVMb0U=U^isa9nT7sQTcryD=!!i#lq8og8 zMM4Y(1M#BNZp-Ti_@!uCsTo*^BkhdwxPGFVh6$(4G)_%cYqIXND1!S z^k3mJ=DtP>Y53O({?!afXKo13l>b?OU=bYuSm5=A2*PT&Gh~s;>0zXZ14f{hw6n>X z8S&BOsY>A7Kh0CLd$l`OUKWC)shC9|5lDISdXM*b zisSxTwvH49QHZ$`AEj>tY9pDR*(z?6KqWbpQOgp0=3f8UwhZtHVRzJbG?6-Y2D#M2 z+88O7`%!Zx>>ww!xvk8T9ka4<77LNZzcS%92`FIlNhnHj*0`PM!9(!jP@x34bx3)5 zY&>UKdz*{R;|B1}QFTL8=}X>5`V!}G$_Z4Isma`CI`hoPw}}c6u6Z_97Ol}kgH27c zsO4cq>0SOM`^0?-BzQSohZ#II3xV2+5l!B3H#{S#<+%;!tCJwvZ_ujk$u}9ia(gHZLF00?DQN$alc<1z*sk<- zb@R&3arkIJQi1D3`s2V(IVhbn>60_K&mlLnX_)Eo@xyX@J4JnnB9*aecMg>^k}}d#|B(iB_ikuPf^B%i}mUn*&;FLWLRI+P3O@QCN)f!|{kSwIO;>=O37+GAGy7zDV!Cc|m%~`{ev3QIR=# z5sR5hLa0IF?&#*ytdQ}r1OshH&8UEAl59B}jWY!^s#;U$fK+td$`W2zQR{?*jL_u3 zrsL+r5muPTdjS-vye(RA@O&3=;+)muq09$3ut!UGk5Y(}C;WjpqZ}aUWWaoqJJZdV zj#E;^NV6aO5S&fP`J`mX<<3owGM$})Q|;!XP2z*=mCm`S;VkHE2}$e#q}_)m5-Rb- zkc>`#7Tpu*i0%S>z&$;cQtqd~cbYR)O#IX#$ zmGkE#R)Jcssq_A&cu#7a6}#JX50vttLK~q*AxTAUQ~FeixiwXzalX5U(uPpt-~i55 zT!&}~xXCG)9G;U5oK79w>1T9RYK|u(qLwx4Dw>{Yah^)EY-*k4?2X8jrrZipc6bg$cH@(!;ql%4$C-j{yv>gz66{+N9RI?L%!=8a3IV6KZnf)tMDr;9>3A zaf49D?8a75R&tmH=_twvFOM(|wm`s@)Ulm_7Ixh+I%mnyG;b4o&Y8qb1ISS-ph3 zV^^Uv6Qh^x1rty!m>bFyrwdclJEc@bxBfHpwoPq1Qj1Bm&gmy*-zd4zJ+#!Oo9Rg9 z#2?&)kfLNT1J5#T9z6ME1Re$uIRzJsh_p@9&K1nDqeKZQFjNmNsVdhDOBO`n`8GN{ zalortF4z6Q;ioR4+bw2JIL$f2UCa?sH8Ir~oyC~Nwq^y;41SiQmO1BSlm#3qJ_7&g z?145*oipCf)loK4pt*uW97@?{GDhx+j znpUT){2R~7Z9;`^|IH>fKQ6%LGmZw%gu|a|df>z&pQ3d3eG4^sKQd5pjt2(vq7PKV zfRQsTa4Hv6+e}TT2PQwWnbLg%mKI6W9W5%ZfkvK6e9CnYUfsaRmYU^*Cpu#h%Td=-?%%89f$75%h6@QS zw%Uu0h>XYv!ikKh7x#JM%*cTPNOs?*vTX;HqN6SlFcLd6Zf0wv0AcK#vqwdhS(p&I z93^rl84zPRZ@KVsIhZ#;;#iP2tWQczM8;7Z0(&OE&@PV={hE6^Y{Ma$Gv?5)#iw_n z*`yrez}cNhH^kH;jwgAGrZ5dzUwAYq)I@a}43&jXb$)J28%buputv7ez{ev^Nvce8 zJ2Loge9fYo0lK&cnS&h0 z1RQ{Ry9vcByLnoQ&19S$S?Gcv%sD7C50GF1T#kxNzG=&B^f`#af$^3Ob-@O`-)II{ zb6OGqStF?S##i`PScaKlL(PW#WqTA*$x<+K|oOxX<{4Hc!h5}kSJ5hfbw9t z<9t$JIkmZ{gKlOUmr>S1RgHgG#BQ;P(?}?|%s9hPPOCyKFNxmk=VSZwF_?P3X2Va6 zX*5+qWq>|RsvAdMXB8))m|o6NmzIGol4KX+`QbD{^yH>Jy{vOajq7L38EvPwws*vf zE~sce5qy#*ZMk<&<{*VBN&n%XW3rI-QqF}5 z+-DZ`;xsANgB(+Qkbp`!?9z_@%2f_vui(=|lw%PCN2X6#U_q?D#SvXpE};t<3DE%f z^>Sox5thkNAgA?8QP{JhT%3@ly}-q3s+|GGs8hV`I#QII4lJQ;HOI)2Rl3ay!sBY^ zY^95v7{?}a%pq_T9)Rej4io2U7=CmCX<-wfG-~&bILbiq_x=bFNXLvWM6Kg&TI?%8 z$&_^hLgTb%@(FB4GAe42at4>QXA+ zOn)Ki$w5iJS{P6yk>j7b6>gN9Io0wDP7cKVlnZi?BZHg=4U!KVn=Faujk=;Ust4<(@diFNX{C&n?3 z5k*>SqFxn8sud`gx<{A6WnvGV0oy08W{d(3LMHjyzR)Pb@V*XpFaTsw8dLyi5{?m* z(xMcshCI5t?mVuEnUINigLdEDO&N=gd=rnW1x?)Aio_JAabr;1MH zyB%Wb#5b+^(}XQELmwA{ZKl4Lw@!USP-L9(AdqrQHQl>`o1usrL9VfAH}cnbPi{MU zyj{^=ZnjpKcX!SYwG%~WPRNbPh^tMOXob$1IEvM30g!0_fwDna*8>^*J-h_)jM>D9{{y!u^vL&Aq7d3 z0^Wx)NZOqqV86C2kO;|SxOMx%hAo-;JX7L%rjAciw77>jk5I}axxF(QRa1g3KnE$- zcZOTJfolN&Ce=e=A5@kgW7e7Zo;<}4=@Qi$kRL^u_W1Bi3ZN2a)X!bg;M;U&V1fgW zGIQ)AN;MM_*6%cObt$#6iyu*9olu?}v{_ON&$iTR5eF_i&{vYtZ7Z}}^85&(N4;*g zanqRZwqv&0U<9?aWQm=6T{0oK_9ofU=7~^6Z3N zfPLI6$w!r6FU7*35NCl222T}$cPC&9g2R>gYx+#A5RcB(pYk|7+W8%q=%?^r8Qs~XI6EZtX zc4lBB;9e)rF?Hx^Nc~EJ&;w3B0kdLX@Q4H`Mi4$oya7flvZeMWiRFUOF#g+~!L-wxmbwotm$}ln?&BbjLXU=GVpUCRecK8`$?=z)k{l_pOS&{B z-EeqxZy@K*@(;n9@!n3w=Ta_|lIMv2QxKu1lq_g+;QHObdTcW>Y;)G3I>I`o8jaLl zCcB)9R8AveLG)p3@Ptkk*2OGofZTLSJrIavy@0Wr2IgqLGg*yj*e}q^#&O~t3CAnPy-If|f4>2P%StOiv%pqt5ZAs%Z6LDEtVzxtB z&JBCGoE&GAha_`z5I~7B(ow@au5gfg#Uv9_44L!#C>M?b?zG;y2+Swz_@MNEolxee zB;-$V0q21bQEUof27`O@ty9(Pasqw=KOqZ5!c{|4Hxp~Cw|&!|sgp9| zo_or;rX(0wy=qUaS~*V(1CfKb0aH*o&z%SBbeUTXhw3Lm!XdsyLC9-MCLZlvZM{<< zP$3}eA}24XqAH2|5Il5gH&$o_7)9^W3j85e;(fFv)0&YEn(0VkHz|TN)Fr@8*<1!q zc!4zPkXpT*=j_K$Cl(#Rh%Q^{O}+vzN+vTw zF5m6;JlB2J;@}uH&fpw}Q&blPV_xR*uHtIKktl?XVd_+h#Zb~45&yETIWLBIyAZB* zUY_65OJSUo`YaLDY&lYd%gA8RyeliG^PL9-r*-oDRx#@`uCLjCM@!Tl9?$lHImUNP zgZi(2mi1p*kr+X9wdq6)tDMnA9ujkYT!oAA!QV0#KETv&hgO^okHZaYA*dJ1a!)!S}4$bhmYKc#S zFps@b?6c235`N+Hj>v4Uu3Df<`(Y@EmDN>8xn5mm*XrsItE;cJD%%bIL){m0uFi5{ zO_4)zOl$bhJ7WJ_qSy7_AR~C*kxSI_^guiY(yAcG_R?meplF6HOz#Gd!Oy4%JuxHF z&}d^T$2B{y@||KG*XpqwP45B0Kc{K{nNtQ)-$H%WBDe zGkPc|uQss8Ll?V8Qr|NmdymuZds}q zxdgQXQJwWySDO)|5GLa3(`(Tbtl3_EiL=(2(6%bquE~U}%YND6{HN#G+f7=w)veF0uc; zW`l6+nw7A}Vg}j&enpT zC(aXFaHo#)H#3HJZ^5m44)LTcz6<4uXyf90C?LJyJrb5&@Ut$@x8QdRH(YSXOibc} z`=?;#3+{CaZ7jG;f<(joG!9i<`{jZg&1^2XK{mpT*fdsswOc{+k0Z~u7mYQux_Xk` zIDHWl2UTbGKG8c4Z}94>=G20;TU~t-n21`gaM-M_ zk_lKzN73r_DNGmgVE&+aYE5Y>(L7VX)J&NDXoa5rCQ{FSY$|Is-|0y6(Qr_EpDh*9 z{KnVu_UWqmsy*RN#kKCDKKr@*N>#SjTi#r4-tWj-`^K&mQ+w~;sQK7nF&b*AMDX6m zzxIab9hoFB$59T0x1RDTea{+IYpPKU+zU=09m}Yh$i36HGe#$BYRjyyVzI5R-ig^} z^(OJs^;BuCm-K|ZF@Z$!ug1F@r{RzK2uGb+JJn}&jEnDsqs~wEzuwr?YQO1JK&PyF z4j<7kp6by>>+{`^G^t`AHbkLINA7NG=4G$xTf(5dhwsixVFk|l#ZhY-MfZMx&b>X8 z(s{4WmR?i4=g%O)8Z}pwn&ocDtQu{ivuT@HmjHrbNV?LK=!|#g=Qp?W67A+yT64tu z7X4A_4gE^}%FjI#alvlomI5`cIsC= ztgezL(lEN|?$4itIlOcKCTGg!otzW*s)F9+=SUWxuj;I+na}HI_jyp%5vwa`sMhK# zP_4_SHmxZgf3ovLBy4q6$F^jQgu?EHB?`I&8WDbI2x!q=3tPJ#4|3a{dnafy5oqT} z6&wej6gt?Q@{O!5zA(?NQ)uelGurIDJ{Ng+8wi})>8Zvi?pn}|_RdBQ&6(KtK9|Qn zTLilmDD5AI%N$6`H~N9ubS8~^b(MUwJyyiFl)`l2uk-lHGTrYOy# z$ccZPB(fPB^bwWiv-F7BwD)m2l+J>0=KoS?W0R}AuWHiM84vpOoF5Y+r#qOhPBGxACG9ApcOW9k|$$fDJ zgmYhHJu+J4qi?l#KK_OJBrWYPjXrLL{)G&6q0G_*xo~IhC$Yq0{KO8Qg$0>htSi9d z;K!409Sdrh?3_6*h`@n9EvV7*r~LEK3oTB;jI?LDrx$fz-pQMX4^-!;H6P=f76-Ly zW)4cpfm=_v5r)q@LQ>14xrg!dj*yP}DDGjv3V;ii9v;lu)q3O2`t9!>n{?JM|D5>p zm%m#>BD#~&7Lv9OF%XU@tKpe#!V}ci0Yg1*fJZa+P!n7<{n^%_QKFgpulNA_D8-AQ zZ~viVEq(VONSAcyOl@u1t;a6yE08FS8_eMw2j1{9A^pxY$x7{2G;CzDeq*L35f< zq80Dsp)&oLl%tP+FHs|5btr(y>+a6><`8)CIT8N)+wFYOI53$rJF`0%G_`K|Ss*Ig zdtf1tlRH$NXy;rTAb%I8dXf^BFhdT$6g@bZ$QPxLuGOE6yq0TQ)j54d?<2WDikqaC z7UY#tvPb_WF+2Uda{{HlV8T0ye+zAH0EedG2X}SdR()p&K9t{d0wT~)sdl~zUJo#j zzDr*x{yXC*3#*Y1asW~Ztc@o1L)@4V;F-+ab6f~l% z_jr5Jor3ma14Bz9I|cG#-zl&+EuI2<(>_z6U)5&~z*eO#B=jZBkX&m-NPy z-}dday=if$?M?f1TJD;+hn$_Z=r(7o$!*SF)0@`ODVnL6t#Jsrz`+{Lxv8^Nrdn@L zoVO;4zTv);Cth)Uc;_wmyyW;jQx-1rxJ6L{a<-km={J>&;nK$8KyKJ4DpX zWerrExGcirM#D(BNl!=jbwY4-KUga1F6cyKEi<}nGWhOW^N2u8;b5dBP12PEEi;>< zccDEV38=p@y5#S0sB5YrIELeOsh}Wb#Zm2wiWG=Guy)1?P&20Z8K{y{@<|Z5Lpftc zNfM#?RBdV7P+uJmH#g6o9_~Z^!;~I2H!j)%y&D(q0`9!I$ahX{-@L<-R^Tem-#p$a zZr0*|r9hRf#w#1!Hzg!2QZ$89JEZw?lGx31xG1Lp?~uI(jFH@jo4gf&CG+*un;WB* zo$<}jpT27)r~EPnje?tsQ+q3Zd?nw`Y2H_G?16Vh-yS7ba_mv;5b`2TYZy5EsLX{NuIM0#uTXGoFlDmALOFr(CNI7z^q=z_rGEPzS6#ni>foK(8*SwjjJV}9mAYEVe?RavrQXa(ZL6w# z4=YkNXJtTq`Blm!dQxDTthf%m(#v4^oNE=S-yQB!{qFTNZOhQ8(~nb1r!c9H9pbWp z0@e3&t!Fu_yXn=-B&AgS@inw&62{cO<8x~jU;r3RrRxUTf=a_arbbmfxTAgAs zwJSl&=FFp-yXY`UMyfWwU7Y^+V=W<~T+eIq`&pl!4WORFJ5auLRj2NBL-hmDCyJ@g z9aH~|MRsfsLjhfe%zj>@M-dq6*BPxLu^@R!c`QOBN5<*|l*nSRZ`JKglX11G&wgU_ zM8Xr#VkX3C`w7<~hK7(g)=I;DY`F!yJq}7FMDKdOoTa?Hzi^$fWu1wwp2wO&$ijU% zk9KdXM6{`Y&U)XGpWoiwRlmwd?nMPte{MImdLbK&JOT9p&l<9b9#U_E4YgZS{i5vS z>9R!XF|^TITkqw0o3x--%!bk@efB%^VRoo_i|;?AeuMjZdE=KL5BP9_9#-;tDiTM$#XlQ+Mn{=|Y5!_c4d_38`{aC%k;1g(T>16t^We4t`oqa2J;k;IF zM=F`)Vkk^X4L>WNz77I4r#h&!%yolRRsUX82&?_5|4JJ@g=%p=`pxWbCpSj@9PjAj z7Vm*aAZFE%F;?55h7aA!C9!hY&iZ&866v*<+p1*tPg8Z^S^ofmVrvHpKUF`(do}N< zH+BesjLxI)`OOAyRFA1Rq4DHRuGZfHVtTy{y*jJ@^~^nVdDPs=Y22Ua)t|xIwUQ~p zo3d0N;ya-f_2-jBr{gZ4j=0cDrTG2SyCIl^mLFIZUFD1fpB~&b@uybFnjoC#d`2tL=TW!kLoe(t=`bJ1u@mCda*WDNavM3tNs=qLrp%iwqX#Q z3Zwo54Yt)VnsbrWuduTFRHe8cmSNZrn&S@ZV8WF6k#(w{`b#cd(R|ur+ov`4-3{Ln z9g3K`;XEH?+c}#5%EVfm(iNrw$h z4jxh~XxvV1M)j$TLT@OEtRLo!xzkS}-)BP1`&F{JmA7ATnUZg{Yaw_c^?fj~gH*Bk zOWv%JP3jHZrVAcYA6@9^xKL|Qy~YgGX6CBjf;xnZ)F(kMkb(MI1VO`<7wA`i2}^J& zPQA44dze0?9zUs7%-{ESUd9R_U`ZOGW#?-u=h;aRe;S>3K}zaPT=@H$5^=iSK;A>@ zCm`s>RiwT~wDu-2+Qq@uyJ_a*e*!0kv)E!UX?YRU{$uO~Lf&||#v8?0DRz)4u%Moe zBTP&P^;50yL-a&-OFO@Q%2toLO+r8D2jgAkv6UPhRQ(!TR9h}MupJfR6;Nm6D^wqX zS%`VIcjipBqurz9jZ>#{^=+)(^7YbwpC(0ByPfW}dbAcWUqv7Em4o7;?_x5v=Dcq) zvp4W^%d{U-zb2}q%+zc4+rSPUX-&P~5}Z0hV>2Lz>h_ z#Q$Hd+OB?qk9sZDtq|;V2`2STtXcyZLJU6xQG*R`T;SkH4P~l`MViF;y_cqq>8^I6 zm}N@={tQih0_&?!vgmt>XYj1XGqya|_suXZu?8fRqW*%ZHrq3T<^3*pcw@4O_fl;0 z?_{~HW!DhOP@F{Sso0D2LpF1KFKgfv;bsXUN&xZ0$CY~D=dyb@x3{UQ z+jC7HOBw@3uCJ-Ty3MjvqaGR)Q6dzU*`Q4R2Dg~g+trhKx&bG(K&WSbj&LI?6sUj7 zov!J7(G_|>^gQX^=Eg4A)J7-!l&tAojQBnG0`b(CnsH`CPh#|TVlaUJXVCRnesADZ z|M&*kqo{JaCXMiI_5DYA*#jGAY*FYAgZb)bG%flp?(v)Oa8Fi`cG5E}hcsP~g4B~Y zKO$*e-^uz<#UtC<+85dSs;}iOXx`c} zFs8qzdYszfX%Xonq0~3iSJ!L$w`_$5zdj)0+BJ2u8fBZ~l}nE_c7LLvUP{ zDYBc8Z&c)%hAMxZ?@Yv(R6EW%AeOotjDC!?!qFQ2rG_ICahL7Gwy9UwGFU9=h zO;^Q${rN}n4s24D*3>Hvg{o&UrDyV8tjI0(*I~~J!bdMQ`%4Qp1R<;Pfk0JYoVufFhDv%8G{ zf05Pr$B2>%WUQ>sz;Zy)B_V{}zZIMdlv z6~5DaQwNFFe{*IOA$cDQ0ZSb3)oiWdcCDV=t%g(Zu%b81=)F3-V0HxL3gjP0qaIIr)}2vnZC~Fl z$#ct|H1QcX7{;PJ*2(ZHxbp9C6>pFFGCE*(x%wA;tk4Q1H4-I~yNv)7m7;{@XnoLx zjJ2#(Z)@dXjDq@UJZ_p7%V8BHpi8;1T=m0GZSH`H{W7;}Ro%?qv^j3BYto%jkn~5} zox!Y7M%HYNpxGMVj=tRkYIT)hTTk?NqR0SZ(_R5}7M3km%vgP6%WT0_KBR8Dq1CU= zcyRivdl}u!G?wu9+?55q(JB_7@ho}(>SMh+O=g)6Gpa9zU}WLO>a%IJf$)6{&TnRd zpVrjRcC#wVB~GKp)|T9r9W+G2$9*mG*332v$tzoBLsXCBPt#i!&1-k}y!sS2F#}T} z`LYMHcD~yM=lG1MN2TNjSj`Se_LuN4 zxPirDwi2xo33JlM4a!I#*4ahSI(XIWV|adt6|qRQ_p+u&XJryyMS1qA4_Bl1y-tS;)srnBSm$uwP!o-fk?;HFX>kGW%**{U=zKy${lDnIuP~VO1NYS{>u^Wy^8< z;ZDodJbO+30W?^>O8qu8w@{SmN#`H+S8R7ZI`ueboDE}{(*LIm>;51jtl}YY%sJ)!CMlr|kB}M#2!ABXr)#vt~9Co&VBED;qIUseoGzy?W=6vSzYJaA)_CD zoea1OrriT|HPeA|_(_N>gPq@2kRZo>4RaQ=8fvGM$eDMVNtUHsQ$N7Wwt-3YKDM1> z?5MX-t%rVSV?TYTM`c%|&QYyE$^@~+PwlR&v+Z)iHcwr&C^zTa%|0vbiH;Ho7mGzd z>P{;89y(%~^jQ5{ws|Xm-Aw&Yh7zm>KgF!n5g4f~Zs$MyHI~)n?tDF;dbv06;PuwL zoPEaDH_@RU(W$Rs08Pft{bETjc?#F|Q?v!zT37;Y>6cpicQagQm#%28a zKA3(}ziN>oxu#x7*CfbqxPYjD6UPjF5i2$GJe%*Mt9FMb5d3wmdmBaf5jgmB)XEwi z`_{7NdmNvH6`McHB{3cJR~;g0gvcDL!HJV}wt^0_IW->E)RQ1HG4&mc+M2qJg|I0R z&oTU1TURPzs%of3s8#*h3<5O*$E<-he9hE8vQd^+WUZ+`pI{r!PwKrePs28J9hnI& zM_tuO1yQcx@0j@-NRUI$<;JOvU3H7y!jicEPa2({j9Ni*)%R~|=v8`rCp?|_nyx-E>=2#mM=vRBYYN@0 zsaMe3i;K@JI_C93p&M~(s^>oqtWGuBJ9Y2g88q7OLJ~>Q4OtiU)#!Vo{;KPkSK%Su zWEPNSpJ3-q;%kr)I<9-+Cl#M)9qO`So%b{*-|A|oTd%2)irF-yoX-22JkWz5?x{jagIWKwWF;rB@8)Lo|EIj*WipW#BsKzD$5gNb=7`O2hE;J91 zSekGBy=&_2pKe*kN$ulT55OI)4`M6FhpGM}d_$OnlE?)70f1{-V(&)r(kMOFC)4@Mg^s>4_EEIE@py+XYmAq~-uhmsO$N$>5rZ5-vMt0i|2 zM5QCQ>N;etR3!K_82%>pFeK6{E{(BzV&`8lkQu`Jr`4U@@0v4)-?cN-Fq%0^qHzk$ zjfhw)XHDIW#-#nHR+~$q@osRUlZ+9J*7yUe@H8_|R#!E%^Qy}sPg-jKsYasC+-z&= zpTh@a1nQ#*p1(WWqU9p5fL?I-%}9#>iKjN?j~=Vuc!M_YP35v*gzruqr@7Ab?3 zO%z*v98*V^yV+Cpud`Ri>gmvpKAo`*qp06ttt2V!)y6}pZlL?d+iy1Z{t<#^>Zk0) zp11Z}-SI);-`q}xvG&yO(h;)(0^H3zGSv-3&N|SlK^@Kr#xB+JvD_(x7-J)S=^-W6o@m*H2^3%m)87rchY>aGl+u zZn91cp|g+3E@{(PZfU5ppSJRKY@xo{*4sap)!c{h*53Uy1f|tS8NKACJb+WFe=zB# zIKje0M)ZJyFK9@>2j=lju%3fa;+p!JPtjx4jjHFvQfFjAT=#fm=BDbaKe>5dgbaOa zM}Wizzr2&Lgj(LM)snrORhSwPf+SNA7gm<zR06d~};CFwf@2yX!Ip z_17%3sXDDnR)amPKE|(x#emS+qzR~_D{%b<=0a0a`lCGGFLwGAnyD+W%R1Wd0dBXA zK`uYZ-Hoj=RyXk5x>Q1V6CccstH<(DD)T&-{*KR&)&JQ(&*M4ECGuX+`v&K8>rflD z;3@yMeR@s(8+zW9ihYvtlE|Tb=vO~iC^O_re@C1vd{)SW_HK_ zf7pK4p5O1_K9S(+wLGp%;i-Q_pG-qB(S?ujmZfW;9Hup?ZgK~W>5)hxi`aVC^TPp3 zmaK9+Yh;$BP0hEI+!P%70bYV~w1WY7hB?Q#_Z(*EHfT=0(@d*c;ewXQMl*gti)Fll z$1EFT?pIga^;=UdPP^*-Kib&SX8 z2b($#=NZVEooy70u{uhRW!tEa(39?AZQKmk*^TNnKR615#+pJt>R-`3E-)rsJ(o2T z`)E`B4>-l;sL^|Z0a(wjnc5#>4EL}Y>ebAU^x-{p$ErC0n%~T%5pBPk0h;Y8yQxuh zKTDp{zR4eC(kvOXKYoW+wA6bAL%Qc2jOGFL&)PMJ;Ly*cCo>ry#Iw4}#?zeJl1`t3 z*3`NtA(+O0X}gF(_%dG5a0vB6e!(bwC)c#5s}@O?}k2`U8Fu zHAH<6Etm-Uv3Y&9TYQ;g!V#TjpNe8=p!NTPms&=kuHh9TxPQcq!EV1F=OL}?e*qt$ zj0eW*Jv`L#4w{-jOX^0NvMU1M-R`Ijv+Q@%%J}?Np-#0o!8864s)N-LA3A-SxZ4(e z5+-M7|B*SQ9L0s{*Y7{y3{`zDy>Ac_R`2;TezbXz_wjM(oJ^03`6y|QmsglEFJ@v> zsP0AdjcQBHj`$0DAauQ8Vg4V6xUsEk|DDUnr`5OHd1leq)E$NZ*aeGM_p5l1UKo=( zNR#Ru|6i@H*;eXdKFLIOI_AB6Bf~8(Hg?Ysn|wrd7gukmm5tE`IUnaI$`Oj>WBof+ z{EN8&$t%=n#HFnU%x9%OpZ~8`U(Tza&!^AgeKAAEW?|4$Xj)nh_2kjlJTE{l>N|6!K#>{HK}XXSNb0$?T0Zbk;ye{|hGI{SXx zAgkPa>or%&TYrj2x2~rD{BNiNW##4V7ntF#yiTKTFgU@sNpxcNdp@Imip?lh{4^hu z-iiV73h8xwNX6aeo4Sv`chX|W?IP*^7oA$)P}j9hNdnOm_|`^8VBdP7O=qPFrHrrZ zp!#!~u#5wk(Pz8QO5a87wUwm#Y-j%;j+aT!=p1+hnTGf z(=OeZO;0~=MzOGvHGFDKJ(G!NqNMZlkAjgIeJ*{GZ7D!9GtA$6+!Bwx)8YX!KlgfMqxj!%I<>hy$~VW)y-s|BHxbYI-?MN1e*gdg diff --git a/wasm_for_tests/tx_read_storage_key.wasm b/wasm_for_tests/tx_read_storage_key.wasm index 8c7108bb5e2402bb220d20de3bcee597670c55a6..da087ff9b6f6ffe33ef38336337e473061ef5100 100755 GIT binary patch literal 435118 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�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 aL+0su{jRx%)>M6=bLAO&rm+jR!~X{bJrKVD literal 447332 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

    g`r7*3S7dz5ww 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 Lx1Z}~W9LKv)5AF{1D!G;Nt2D)tz$&jvc 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ȉ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

    &u_@CU+wvIkL1_c^Xs$g8qcdPZVt2; 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 z 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 literal 450471 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�>XL{=x;sl<|NF=aZAeBUcg%bz0>}I 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