From 89fe2606d3de8f279b926643940a3e90732de8bc Mon Sep 17 00:00:00 2001 From: "Raymond E. Pasco" Date: Tue, 27 Jun 2023 14:19:55 -0400 Subject: [PATCH 001/120] apps: add namadac epoch-sleep Add the epoch-sleep helper, which queries the current epoch and then sleeps until the epoch is greater (polling once a second). This can replace the multiple namadac invocations used in e2e tests. --- apps/src/bin/namada-client/cli.rs | 7 ++++++ apps/src/lib/cli.rs | 28 +++++++++++++++++++++ apps/src/lib/client/rpc.rs | 15 +++++++++++ tests/src/e2e/ledger_tests.rs | 42 +++++++++++++++++++++++++++++++ 4 files changed, 92 insertions(+) diff --git a/apps/src/bin/namada-client/cli.rs b/apps/src/bin/namada-client/cli.rs index bf9921a527..1b1514ad52 100644 --- a/apps/src/bin/namada-client/cli.rs +++ b/apps/src/bin/namada-client/cli.rs @@ -307,6 +307,13 @@ pub async fn main() -> Result<()> { let args = args.to_sdk(&mut ctx); rpc::query_protocol_parameters(&client, args).await; } + Sub::EpochSleep(EpochSleep(args)) => { + wait_until_node_is_synched(&args.ledger_address).await; + let client = + HttpClient::new(args.ledger_address.clone()).unwrap(); + let args = args.to_sdk(&mut ctx); + rpc::epoch_sleep(&client, args).await; + } } } cli::NamadaClient::WithoutContext(cmd, global_args) => match cmd { diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index b3078037ff..a060688b28 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -181,6 +181,9 @@ pub mod cmds { .subcommand(QueryProposal::def().display_order(3)) .subcommand(QueryProposalResult::def().display_order(3)) .subcommand(QueryProtocolParameters::def().display_order(3)) + // wait until next epoch (can't be in utils, needs the ledger + // address) + .subcommand(EpochSleep::def().display_order(4)) // Utils .subcommand(Utils::def().display_order(5)) } @@ -223,6 +226,7 @@ pub mod cmds { Self::parse_with_ctx(matches, QueryProposalResult); let query_protocol_parameters = Self::parse_with_ctx(matches, QueryProtocolParameters); + let epoch_sleep = Self::parse_with_ctx(matches, EpochSleep); let utils = SubCmd::parse(matches).map(Self::WithoutContext); tx_custom .or(tx_transfer) @@ -251,6 +255,7 @@ pub mod cmds { .or(query_proposal) .or(query_proposal_result) .or(query_protocol_parameters) + .or(epoch_sleep) .or(utils) } } @@ -315,6 +320,7 @@ pub mod cmds { QueryProposal(QueryProposal), QueryProposalResult(QueryProposalResult), QueryProtocolParameters(QueryProtocolParameters), + EpochSleep(EpochSleep), } #[allow(clippy::large_enum_variant)] @@ -1581,6 +1587,28 @@ pub mod cmds { } } + #[derive(Clone, Debug)] + pub struct EpochSleep(pub args::Query); + + impl SubCmd for EpochSleep { + const CMD: &'static str = "epoch-sleep"; + + fn parse(matches: &ArgMatches) -> Option { + matches + .subcommand_matches(Self::CMD) + .map(|matches| Self(args::Query::parse(matches))) + } + + fn def() -> App { + App::new(Self::CMD) + .about( + "Query for the current epoch, then sleep until the next \ + epoch.", + ) + .add_args::>() + } + } + #[derive(Clone, Debug)] pub enum Utils { JoinNetwork(JoinNetwork), diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 8aa466420e..7d9643d2a6 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -1828,6 +1828,21 @@ pub async fn query_result( } } +pub async fn epoch_sleep( + client: &C, + _args: args::Query, +) { + let start_epoch = query_and_print_epoch(client).await; + loop { + tokio::time::sleep(Duration::from_secs(1)).await; + let current_epoch = query_epoch(client).await; + if current_epoch > start_epoch { + println!("Reached epoch {}", current_epoch); + break; + } + } +} + pub async fn get_proposal_votes( client: &C, epoch: Epoch, diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 7c82dc84d3..a42742312f 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -4399,6 +4399,48 @@ fn implicit_account_reveal_pk() -> Result<()> { Ok(()) } +#[test] +fn test_epoch_sleep() -> Result<()> { + // Use slightly longer epochs to give us time to sleep + let test = setup::network( + |genesis| { + let parameters = ParametersConfig { + epochs_per_year: epochs_per_year_from_min_duration(30), + min_num_of_blocks: 1, + ..genesis.parameters + }; + GenesisConfig { + parameters, + ..genesis + } + }, + None, + )?; + + // 1. Run the ledger node + let mut ledger = + run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; + wait_for_wasm_pre_compile(&mut ledger)?; + + let _bg_ledger = ledger.background(); + + let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); + + // 2. Query the current epoch + let start_epoch = get_epoch(&test, &validator_one_rpc).unwrap(); + + // 3. Use epoch-sleep to sleep for an epoch + let args = ["epoch-sleep", "--node", &validator_one_rpc]; + let mut client = run!(test, Bin::Client, &args, None)?; + client.assert_success(); + + // 4. Confirm the current epoch is larger + let current_epoch = get_epoch(&test, &validator_one_rpc).unwrap(); + assert!(current_epoch > start_epoch); + + Ok(()) +} + /// Prepare proposal data in the test's temp dir from the given source address. /// This can be submitted with "init-proposal" command. fn prepare_proposal_data( From e7cc5e3a59ef47139ee9ffda749c90219496e7fd Mon Sep 17 00:00:00 2001 From: "Raymond E. Pasco" Date: Tue, 27 Jun 2023 16:20:08 -0400 Subject: [PATCH 002/120] tests/e2e: change epoch_sleep to use namadac epoch-sleep Instead of manually querying, we now epoch-sleep until the next epoch, limiting the number of clients invoked to 1. Also update the epoch sleep e2e test to check the return value (parsed from the output of epoch-sleep). --- tests/src/e2e/helpers.rs | 37 ++++++++++++++++++++++------------- tests/src/e2e/ledger_tests.rs | 5 +++++ 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/tests/src/e2e/helpers.rs b/tests/src/e2e/helpers.rs index 60dc119051..19a3f9a373 100644 --- a/tests/src/e2e/helpers.rs +++ b/tests/src/e2e/helpers.rs @@ -358,20 +358,29 @@ pub fn epoch_sleep( ledger_address: &str, timeout_secs: u64, ) -> Result { - let old_epoch = get_epoch(test, ledger_address)?; - let start = Instant::now(); - let loop_timeout = Duration::new(timeout_secs, 0); - loop { - if Instant::now().duration_since(start) > loop_timeout { - panic!("Timed out waiting for the next epoch"); - } - let epoch = get_epoch(test, ledger_address)?; - if epoch > old_epoch { - break Ok(epoch); - } else { - sleep(10); - } - } + let mut find = run!( + test, + Bin::Client, + &["epoch-sleep", "--node", ledger_address], + Some(timeout_secs) + )?; + parse_reached_epoch(&mut find) +} + +pub fn parse_reached_epoch(find: &mut NamadaCmd) -> Result { + let (unread, matched) = find.exp_regex("Reached epoch .*")?; + let epoch_str = strip_trailing_newline(&matched) + .trim() + .rsplit_once(' ') + .unwrap() + .1; + let epoch = u64::from_str(epoch_str).map_err(|e| { + eyre!(format!( + "Epoch: {} parsed from {}, Error: {}\n\nOutput: {}", + epoch_str, matched, e, unread + )) + })?; + Ok(Epoch(epoch)) } /// Wait for txs and VPs WASM compilations to finish. This is useful to avoid a diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index a42742312f..f08b08377f 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -39,6 +39,7 @@ use super::helpers::{ use super::setup::get_all_wasms_hashes; use crate::e2e::helpers::{ epoch_sleep, find_address, find_bonded_stake, get_actor_rpc, get_epoch, + parse_reached_epoch, }; use crate::e2e::setup::{self, default_port_offset, sleep, Bin, Who}; use crate::{run, run_as}; @@ -4432,11 +4433,15 @@ fn test_epoch_sleep() -> Result<()> { // 3. Use epoch-sleep to sleep for an epoch let args = ["epoch-sleep", "--node", &validator_one_rpc]; let mut client = run!(test, Bin::Client, &args, None)?; + let reached_epoch = parse_reached_epoch(&mut client)?; client.assert_success(); // 4. Confirm the current epoch is larger + // possibly badly, we assume we get here within 30 seconds of the last step + // should be fine haha (future debuggers: sorry) let current_epoch = get_epoch(&test, &validator_one_rpc).unwrap(); assert!(current_epoch > start_epoch); + assert_eq!(current_epoch, reached_epoch); Ok(()) } From 5c34343875f286acec040646a2187c80fa6606ac Mon Sep 17 00:00:00 2001 From: "Raymond E. Pasco" Date: Tue, 27 Jun 2023 16:21:51 -0400 Subject: [PATCH 003/120] tests/e2e: replace PoS test epoch waits with epoch-sleeps PoS hammered the node as fast as possible with epoch queries in its tests to wait for a specified epoch. Instead, epoch-sleep repeatedly until the target epoch has been reached. This could be improved further by adding an --until parameter to epoch-sleep. --- tests/src/e2e/ledger_tests.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index f08b08377f..30c71afa70 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -1993,7 +1993,7 @@ fn pos_bonds() -> Result<()> { delegation_withdrawable_epoch ); } - let epoch = get_epoch(&test, &validator_one_rpc)?; + let epoch = epoch_sleep(&test, &validator_one_rpc, 40)?; if epoch >= delegation_withdrawable_epoch { break; } @@ -2186,7 +2186,7 @@ fn pos_rewards() -> Result<()> { if Instant::now().duration_since(start) > loop_timeout { panic!("Timed out waiting for epoch: {}", wait_epoch); } - let epoch = get_epoch(&test, &validator_zero_rpc)?; + let epoch = epoch_sleep(&test, &validator_zero_rpc, 40)?; if dbg!(epoch) >= wait_epoch { break; } @@ -2268,7 +2268,7 @@ fn test_bond_queries() -> Result<()> { if Instant::now().duration_since(start) > loop_timeout { panic!("Timed out waiting for epoch: {}", 1); } - let epoch = get_epoch(&test, &validator_one_rpc)?; + let epoch = epoch_sleep(&test, &validator_one_rpc, 40)?; if epoch >= Epoch(4) { break; } @@ -2325,7 +2325,7 @@ fn test_bond_queries() -> Result<()> { if Instant::now().duration_since(start) > loop_timeout { panic!("Timed out waiting for epoch: {}", 7); } - let epoch = get_epoch(&test, &validator_one_rpc)?; + let epoch = epoch_sleep(&test, &validator_one_rpc, 40)?; if epoch >= Epoch(7) { break; } @@ -2565,7 +2565,7 @@ fn pos_init_validator() -> Result<()> { if Instant::now().duration_since(start) > loop_timeout { panic!("Timed out waiting for epoch: {}", earliest_update_epoch); } - let epoch = get_epoch(&test, &non_validator_rpc)?; + let epoch = epoch_sleep(&test, &non_validator_rpc, 40)?; if epoch >= earliest_update_epoch { break; } From 9a6724fa751af019b218ebe8eec074ac404a91e5 Mon Sep 17 00:00:00 2001 From: satan Date: Wed, 5 Jul 2023 17:58:05 +0200 Subject: [PATCH 004/120] [feat]: Removed unused associated type from ShieldedUtils --- apps/src/lib/client/rpc.rs | 108 ++++++++++++++++++------------------- apps/src/lib/client/tx.rs | 43 ++++++++------- shared/src/ledger/masp.rs | 63 +++++++++++----------- shared/src/ledger/tx.rs | 12 ++--- 4 files changed, 111 insertions(+), 115 deletions(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 5c73b3f4ec..2118804907 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -60,7 +60,7 @@ use crate::wallet::CliWalletUtils; /// /// If a response is not delivered until `deadline`, we exit the cli with an /// error. -pub async fn query_tx_status( +pub async fn query_tx_status( client: &C, status: namada::ledger::rpc::TxEventQuery<'_>, deadline: Duration, @@ -70,7 +70,7 @@ pub async fn query_tx_status( /// Query and print the epoch of the last committed block pub async fn query_and_print_epoch< - C: namada::ledger::queries::Client + Sync, + C: namada::ledger::queries::Client + Send + Sync, >( client: &C, ) -> Epoch { @@ -80,7 +80,7 @@ pub async fn query_and_print_epoch< } /// Query the last committed block -pub async fn query_block( +pub async fn query_block( client: &C, ) { let block = namada::ledger::rpc::query_block(client).await; @@ -98,7 +98,7 @@ pub async fn query_block( } /// Query the results of the last committed block -pub async fn query_results( +pub async fn query_results( client: &C, _args: args::Query, ) -> Vec { @@ -109,8 +109,8 @@ pub async fn query_results( /// Query the specified accepted transfers from the ledger pub async fn query_transfers< - C: namada::ledger::queries::Client + Sync, - U: ShieldedUtils, + C: namada::ledger::queries::Client + Send + Sync, + U: ShieldedUtils, >( client: &C, wallet: &mut Wallet, @@ -243,7 +243,7 @@ pub async fn query_transfers< } /// Query the raw bytes of given storage key -pub async fn query_raw_bytes( +pub async fn query_raw_bytes( client: &C, args: args::QueryRawBytes, ) { @@ -261,8 +261,8 @@ pub async fn query_raw_bytes( /// Query token balance(s) pub async fn query_balance< - C: namada::ledger::queries::Client + Sync, - U: ShieldedUtils, + C: namada::ledger::queries::Client + Send + Sync, + U: ShieldedUtils, >( client: &C, wallet: &mut Wallet, @@ -295,7 +295,7 @@ pub async fn query_balance< /// Query token balance(s) pub async fn query_transparent_balance< - C: namada::ledger::queries::Client + Sync, + C: namada::ledger::queries::Client + Send + Sync, >( client: &C, wallet: &mut Wallet, @@ -395,8 +395,8 @@ pub async fn query_transparent_balance< /// Query the token pinned balance(s) pub async fn query_pinned_balance< - C: namada::ledger::queries::Client + Sync, - U: ShieldedUtils, + C: namada::ledger::queries::Client + Send + Sync, + U: ShieldedUtils, >( client: &C, wallet: &mut Wallet, @@ -626,11 +626,11 @@ async fn print_balances( } /// Query Proposals -pub async fn query_proposal( +pub async fn query_proposal( client: &C, args: args::QueryProposal, ) { - async fn print_proposal( + async fn print_proposal( client: &C, id: u64, current_epoch: Epoch, @@ -759,8 +759,8 @@ pub async fn query_proposal( /// Query token shielded balance(s) pub async fn query_shielded_balance< - C: namada::ledger::queries::Client + Sync, - U: ShieldedUtils, + C: namada::ledger::queries::Client + Send + Sync, + U: ShieldedUtils, >( client: &C, wallet: &mut Wallet, @@ -1093,7 +1093,7 @@ pub async fn print_decoded_balance_with_epoch< } /// Query token amount of owner. -pub async fn get_token_balance( +pub async fn get_token_balance( client: &C, token: &Address, owner: &Address, @@ -1102,7 +1102,7 @@ pub async fn get_token_balance( } pub async fn query_proposal_result< - C: namada::ledger::queries::Client + Sync, + C: namada::ledger::queries::Client + Send + Sync, >( client: &C, args: args::QueryProposalResult, @@ -1274,7 +1274,7 @@ pub async fn query_proposal_result< } pub async fn query_protocol_parameters< - C: namada::ledger::queries::Client + Sync, + C: namada::ledger::queries::Client + Send + Sync, >( client: &C, _args: args::QueryProtocolParameters, @@ -1344,7 +1344,7 @@ pub async fn query_protocol_parameters< println!("{:4}Votes per token: {}", "", pos_params.tm_votes_per_token); } -pub async fn query_bond( +pub async fn query_bond( client: &C, source: &Address, validator: &Address, @@ -1356,7 +1356,7 @@ pub async fn query_bond( } pub async fn query_unbond_with_slashing< - C: namada::ledger::queries::Client + Sync, + C: namada::ledger::queries::Client + Send + Sync, >( client: &C, source: &Address, @@ -1371,7 +1371,7 @@ pub async fn query_unbond_with_slashing< } pub async fn query_and_print_unbonds< - C: namada::ledger::queries::Client + Sync, + C: namada::ledger::queries::Client + Send + Sync, >( client: &C, source: &Address, @@ -1409,7 +1409,7 @@ pub async fn query_and_print_unbonds< } pub async fn query_withdrawable_tokens< - C: namada::ledger::queries::Client + Sync, + C: namada::ledger::queries::Client + Send + Sync, >( client: &C, bond_source: &Address, @@ -1425,7 +1425,7 @@ pub async fn query_withdrawable_tokens< } /// Query PoS bond(s) and unbond(s) -pub async fn query_bonds( +pub async fn query_bonds( client: &C, _wallet: &mut Wallet, args: args::QueryBonds, @@ -1534,7 +1534,7 @@ pub async fn query_bonds( } /// Query PoS bonded stake -pub async fn query_bonded_stake( +pub async fn query_bonded_stake( client: &C, args: args::QueryBondedStake, ) { @@ -1617,7 +1617,7 @@ pub async fn query_bonded_stake( /// Query and return validator's commission rate and max commission rate change /// per epoch pub async fn query_commission_rate< - C: namada::ledger::queries::Client + Sync, + C: namada::ledger::queries::Client + Send + Sync, >( client: &C, validator: &Address, @@ -1633,7 +1633,7 @@ pub async fn query_commission_rate< /// Query PoS validator's commission rate information pub async fn query_and_print_commission_rate< - C: namada::ledger::queries::Client + Sync, + C: namada::ledger::queries::Client + Send + Sync, >( client: &C, _wallet: &mut Wallet, @@ -1666,7 +1666,7 @@ pub async fn query_and_print_commission_rate< } /// Query PoS slashes -pub async fn query_slashes( +pub async fn query_slashes( client: &C, _wallet: &mut Wallet, args: args::QuerySlashes, @@ -1729,7 +1729,7 @@ pub async fn query_slashes( } } -pub async fn query_delegations( +pub async fn query_delegations( client: &C, _wallet: &mut Wallet, args: args::QueryDelegations, @@ -1748,7 +1748,7 @@ pub async fn query_delegations( } } -pub async fn query_find_validator( +pub async fn query_find_validator( client: &C, args: args::QueryFindValidator, ) { @@ -1773,7 +1773,7 @@ pub async fn query_find_validator( } /// Dry run a transaction -pub async fn dry_run_tx( +pub async fn dry_run_tx( client: &C, tx_bytes: Vec, ) { @@ -1784,7 +1784,7 @@ pub async fn dry_run_tx( } /// Get account's public key stored in its storage sub-space -pub async fn get_public_key( +pub async fn get_public_key( client: &C, address: &Address, ) -> Option { @@ -1792,7 +1792,7 @@ pub async fn get_public_key( } /// Check if the given address is a known validator. -pub async fn is_validator( +pub async fn is_validator( client: &C, address: &Address, ) -> bool { @@ -1800,14 +1800,14 @@ pub async fn is_validator( } /// Check if a given address is a known delegator -pub async fn is_delegator( +pub async fn is_delegator( client: &C, address: &Address, ) -> bool { namada::ledger::rpc::is_delegator(client, address).await } -pub async fn is_delegator_at( +pub async fn is_delegator_at( client: &C, address: &Address, epoch: Epoch, @@ -1818,7 +1818,7 @@ pub async fn is_delegator_at( /// Check if the address exists on chain. Established address exists if it has a /// stored validity predicate. Implicit and internal addresses always return /// true. -pub async fn known_address( +pub async fn known_address( client: &C, address: &Address, ) -> bool { @@ -1826,7 +1826,7 @@ pub async fn known_address( } /// Query for all conversions. -pub async fn query_conversions( +pub async fn query_conversions( client: &C, wallet: &mut Wallet, args: args::QueryConversions, @@ -1892,7 +1892,7 @@ pub async fn query_conversions( } /// Query a conversion. -pub async fn query_conversion( +pub async fn query_conversion( client: &C, asset_type: AssetType, ) -> Option<( @@ -1907,7 +1907,7 @@ pub async fn query_conversion( } /// Query a wasm code hash -pub async fn query_wasm_code_hash( +pub async fn query_wasm_code_hash( client: &C, code_path: impl AsRef, ) -> Option { @@ -1915,7 +1915,7 @@ pub async fn query_wasm_code_hash( } /// Query a storage value and decode it with [`BorshDeserialize`]. -pub async fn query_storage_value( +pub async fn query_storage_value( client: &C, key: &storage::Key, ) -> Option @@ -1927,7 +1927,7 @@ where /// Query a storage value and the proof without decoding. pub async fn query_storage_value_bytes< - C: namada::ledger::queries::Client + Sync, + C: namada::ledger::queries::Client + Send + Sync, >( client: &C, key: &storage::Key, @@ -1942,7 +1942,7 @@ pub async fn query_storage_value_bytes< /// [`BorshDeserialize`]. Returns an iterator of the storage keys paired with /// their associated values. pub async fn query_storage_prefix< - C: namada::ledger::queries::Client + Sync, + C: namada::ledger::queries::Client + Send + Sync, T, >( client: &C, @@ -1956,7 +1956,7 @@ where /// Query to check if the given storage key exists. pub async fn query_has_storage_key< - C: namada::ledger::queries::Client + Sync, + C: namada::ledger::queries::Client + Send + Sync, >( client: &C, key: &storage::Key, @@ -1966,7 +1966,7 @@ pub async fn query_has_storage_key< /// Call the corresponding `tx_event_query` RPC method, to fetch /// the current status of a transation. -pub async fn query_tx_events( +pub async fn query_tx_events( client: &C, tx_event_query: namada::ledger::rpc::TxEventQuery<'_>, ) -> std::result::Result< @@ -1978,7 +1978,7 @@ pub async fn query_tx_events( /// Lookup the full response accompanying the specified transaction event // TODO: maybe remove this in favor of `query_tx_status` -pub async fn query_tx_response( +pub async fn query_tx_response( client: &C, tx_query: namada::ledger::rpc::TxEventQuery<'_>, ) -> Result { @@ -1987,7 +1987,7 @@ pub async fn query_tx_response( /// Lookup the results of applying the specified transaction to the /// blockchain. -pub async fn query_result( +pub async fn query_result( client: &C, args: args::QueryResult, ) { @@ -2026,7 +2026,7 @@ pub async fn query_result( } } -pub async fn get_proposal_votes( +pub async fn get_proposal_votes( client: &C, epoch: Epoch, proposal_id: u64, @@ -2035,7 +2035,7 @@ pub async fn get_proposal_votes( } pub async fn get_proposal_offline_votes< - C: namada::ledger::queries::Client + Sync, + C: namada::ledger::queries::Client + Send + Sync, >( client: &C, proposal: OfflineProposal, @@ -2219,7 +2219,7 @@ pub async fn get_proposal_offline_votes< } } -pub async fn get_bond_amount_at( +pub async fn get_bond_amount_at( client: &C, delegator: &Address, validator: &Address, @@ -2235,7 +2235,7 @@ pub async fn get_bond_amount_at( Some(total_active) } -pub async fn get_all_validators( +pub async fn get_all_validators( client: &C, epoch: Epoch, ) -> HashSet
{ @@ -2243,7 +2243,7 @@ pub async fn get_all_validators( } pub async fn get_total_staked_tokens< - C: namada::ledger::queries::Client + Sync, + C: namada::ledger::queries::Client + Send + Sync, >( client: &C, epoch: Epoch, @@ -2255,7 +2255,7 @@ pub async fn get_total_staked_tokens< /// sum of validator's self-bonds and delegations to their address. /// Returns `None` when the given address is not a validator address. For a /// validator with `0` stake, this returns `Ok(token::Amount::zero())`. -async fn get_validator_stake( +async fn get_validator_stake( client: &C, epoch: Epoch, validator: &Address, @@ -2269,7 +2269,7 @@ async fn get_validator_stake( } pub async fn get_delegators_delegation< - C: namada::ledger::queries::Client + Sync, + C: namada::ledger::queries::Client + Send + Sync, >( client: &C, address: &Address, @@ -2278,7 +2278,7 @@ pub async fn get_delegators_delegation< } pub async fn get_governance_parameters< - C: namada::ledger::queries::Client + Sync, + C: namada::ledger::queries::Client + Send + Sync, >( client: &C, ) -> GovParams { diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index be8936f6ee..2cf159909c 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -47,7 +47,7 @@ use crate::wallet::{ gen_validator_keys, read_and_confirm_encryption_password, CliWalletUtils, }; -pub async fn submit_custom( +pub async fn submit_custom( client: &C, ctx: &mut Context, mut args: args::TxCustom, @@ -59,7 +59,7 @@ pub async fn submit_custom( tx::submit_custom::(client, &mut ctx.wallet, args).await } -pub async fn submit_update_vp( +pub async fn submit_update_vp( client: &C, ctx: &mut Context, mut args: args::TxUpdateVp, @@ -71,7 +71,7 @@ pub async fn submit_update_vp( tx::submit_update_vp::(client, &mut ctx.wallet, args).await } -pub async fn submit_init_account( +pub async fn submit_init_account( client: &C, ctx: &mut Context, mut args: args::TxInitAccount, @@ -84,7 +84,7 @@ pub async fn submit_init_account( } pub async fn submit_init_validator< - C: namada::ledger::queries::Client + Sync, + C: namada::ledger::queries::Client + Send + Sync, >( client: &C, mut ctx: Context, @@ -381,7 +381,6 @@ impl Default for CLIShieldedUtils { #[async_trait(?Send)] impl masp::ShieldedUtils for CLIShieldedUtils { - type C = crate::facade::tendermint_rpc::HttpClient; fn local_tx_prover(&self) -> LocalTxProver { if let Ok(params_dir) = env::var(masp::ENV_VAR_MASP_PARAMS_DIR) { @@ -455,7 +454,7 @@ pub async fn submit_transfer( tx::submit_transfer(client, &mut ctx.wallet, &mut ctx.shielded, args).await } -pub async fn submit_ibc_transfer( +pub async fn submit_ibc_transfer( client: &C, mut ctx: Context, mut args: args::TxIbcTransfer, @@ -467,7 +466,7 @@ pub async fn submit_ibc_transfer( tx::submit_ibc_transfer::(client, &mut ctx.wallet, args).await } -pub async fn submit_init_proposal( +pub async fn submit_init_proposal( client: &C, mut ctx: Context, mut args: args::InitProposal, @@ -625,7 +624,7 @@ pub async fn submit_init_proposal( } } -pub async fn submit_vote_proposal( +pub async fn submit_vote_proposal( client: &C, mut ctx: Context, mut args: args::VoteProposal, @@ -892,7 +891,7 @@ pub async fn submit_vote_proposal( } } -pub async fn submit_reveal_pk( +pub async fn submit_reveal_pk( client: &C, ctx: &mut Context, mut args: args::RevealPk, @@ -904,7 +903,7 @@ pub async fn submit_reveal_pk( tx::submit_reveal_pk::(client, &mut ctx.wallet, args).await } -pub async fn reveal_pk_if_needed( +pub async fn reveal_pk_if_needed( client: &C, ctx: &mut Context, public_key: &common::PublicKey, @@ -921,14 +920,14 @@ pub async fn reveal_pk_if_needed( .await } -pub async fn has_revealed_pk( +pub async fn has_revealed_pk( client: &C, addr: &Address, ) -> bool { tx::has_revealed_pk(client, addr).await } -pub async fn submit_reveal_pk_aux( +pub async fn submit_reveal_pk_aux( client: &C, ctx: &mut Context, public_key: &common::PublicKey, @@ -948,7 +947,7 @@ pub async fn submit_reveal_pk_aux( /// Check if current epoch is in the last third of the voting period of the /// proposal. This ensures that it is safe to optimize the vote writing to /// storage. -async fn is_safe_voting_window( +async fn is_safe_voting_window( client: &C, proposal_id: u64, proposal_start_epoch: Epoch, @@ -958,7 +957,7 @@ async fn is_safe_voting_window( /// Removes validators whose vote corresponds to that of the delegator (needless /// vote) -async fn filter_delegations( +async fn filter_delegations( client: &C, delegations: HashSet
, proposal_id: u64, @@ -995,7 +994,7 @@ async fn filter_delegations( delegations.into_iter().flatten().collect() } -pub async fn submit_bond( +pub async fn submit_bond( client: &C, ctx: &mut Context, mut args: args::Bond, @@ -1007,7 +1006,7 @@ pub async fn submit_bond( tx::submit_bond::(client, &mut ctx.wallet, args).await } -pub async fn submit_unbond( +pub async fn submit_unbond( client: &C, ctx: &mut Context, mut args: args::Unbond, @@ -1019,7 +1018,7 @@ pub async fn submit_unbond( tx::submit_unbond::(client, &mut ctx.wallet, args).await } -pub async fn submit_withdraw( +pub async fn submit_withdraw( client: &C, mut ctx: Context, mut args: args::Withdraw, @@ -1032,7 +1031,7 @@ pub async fn submit_withdraw( } pub async fn submit_validator_commission_change< - C: namada::ledger::queries::Client + Sync, + C: namada::ledger::queries::Client + Send + Sync, >( client: &C, mut ctx: Context, @@ -1051,7 +1050,7 @@ pub async fn submit_validator_commission_change< } pub async fn submit_unjail_validator< - C: namada::ledger::queries::Client + Sync, + C: namada::ledger::queries::Client + Send + Sync, >( client: &C, mut ctx: Context, @@ -1066,7 +1065,7 @@ pub async fn submit_unjail_validator< /// Submit transaction and wait for result. Returns a list of addresses /// initialized in the transaction if any. In dry run, this is always empty. -async fn process_tx( +async fn process_tx( client: &C, mut ctx: Context, args: &args::Tx, @@ -1108,7 +1107,7 @@ pub async fn save_initialized_accounts( /// the tx has been successfully included into the mempool of a validator /// /// In the case of errors in any of those stages, an error message is returned -pub async fn broadcast_tx( +pub async fn broadcast_tx( rpc_cli: &C, to_broadcast: &TxBroadcastData, ) -> Result { @@ -1123,7 +1122,7 @@ pub async fn broadcast_tx( /// 3. The decrypted payload of the tx has been included on the blockchain. /// /// In the case of errors in any of those stages, an error message is returned -pub async fn submit_tx( +pub async fn submit_tx( client: &C, to_broadcast: TxBroadcastData, ) -> Result { diff --git a/shared/src/ledger/masp.rs b/shared/src/ledger/masp.rs index 7ae4f142bb..a49e647661 100644 --- a/shared/src/ledger/masp.rs +++ b/shared/src/ledger/masp.rs @@ -345,9 +345,6 @@ impl pub trait ShieldedUtils: Sized + BorshDeserialize + BorshSerialize + Default + Clone { - /// The type of the Tendermint client to make queries with - type C: crate::ledger::queries::Client + std::marker::Sync; - /// Get a MASP transaction prover fn local_tx_prover(&self) -> LocalTxProver; @@ -631,9 +628,9 @@ impl ShieldedContext { /// Fetch the current state of the multi-asset shielded pool into a /// ShieldedContext - pub async fn fetch( + pub async fn fetch( &mut self, - client: &U::C, + client: &C, sks: &[ExtendedSpendingKey], fvks: &[ViewingKey], ) { @@ -699,8 +696,8 @@ impl ShieldedContext { /// transactions as a vector. More concretely, the HEAD_TX_KEY location /// stores the index of the last accepted transaction and each transaction /// is stored at a key derived from its index. - pub async fn fetch_shielded_transfers( - client: &U::C, + pub async fn fetch_shielded_transfers( + client: &C, last_txidx: u64, ) -> BTreeMap<(BlockHeight, TxIndex), (Epoch, Transfer, Transaction)> { // The address of the MASP account @@ -710,7 +707,7 @@ impl ShieldedContext { .push(&HEAD_TX_KEY.to_owned()) .expect("Cannot obtain a storage key"); // Query for the index of the last accepted transaction - let head_txidx = query_storage_value::(client, &head_tx_key) + let head_txidx = query_storage_value::(client, &head_tx_key) .await .unwrap_or(0); let mut shielded_txs = BTreeMap::new(); @@ -723,7 +720,7 @@ impl ShieldedContext { // Obtain the current transaction let (tx_epoch, tx_height, tx_index, current_tx, current_stx) = query_storage_value::< - U::C, + C, (Epoch, BlockHeight, TxIndex, Transfer, Transaction), >(client, ¤t_tx_key) .await @@ -913,9 +910,9 @@ impl ShieldedContext { /// Query the ledger for the decoding of the given asset type and cache it /// if it is found. - pub async fn decode_asset_type( + pub async fn decode_asset_type( &mut self, - client: &U::C, + client: &C, asset_type: AssetType, ) -> Option<(Address, Option, MaspDenom, Epoch)> { // Try to find the decoding in the cache @@ -938,9 +935,9 @@ impl ShieldedContext { /// Query the ledger for the conversion that is allowed for the given asset /// type and cache it. - async fn query_allowed_conversion<'a>( + async fn query_allowed_conversion<'a, C: Client + Send + Sync>( &'a mut self, - client: &U::C, + client: &C, asset_type: AssetType, conversions: &'a mut Conversions, ) { @@ -963,9 +960,9 @@ impl ShieldedContext { /// context and express that value in terms of the currently timestamped /// asset types. If the key is not in the context, then we do not know the /// balance and hence we return None. - pub async fn compute_exchanged_balance( + pub async fn compute_exchanged_balance( &mut self, - client: &U::C, + client: &C, vk: &ViewingKey, target_epoch: Epoch, ) -> Option { @@ -1047,9 +1044,9 @@ impl ShieldedContext { /// note of the conversions that were used. Note that this function does /// not assume that allowed conversions from the ledger are expressed in /// terms of the latest asset types. - pub async fn compute_exchanged_amount( + pub async fn compute_exchanged_amount( &mut self, - client: &U::C, + client: &C, mut input: MaspAmount, target_epoch: Epoch, mut conversions: Conversions, @@ -1156,9 +1153,9 @@ impl ShieldedContext { /// of the specified asset type. Return the total value accumulated plus /// notes and the corresponding diversifiers/merkle paths that were used to /// achieve the total value. - pub async fn collect_unspent_notes( + pub async fn collect_unspent_notes( &mut self, - client: &U::C, + client: &C, vk: &ViewingKey, target: Amount, target_epoch: Epoch, @@ -1226,8 +1223,8 @@ impl ShieldedContext { /// keys to try to decrypt the output notes. If no transaction is pinned at /// the given payment address fails with /// `PinnedBalanceError::NoTransactionPinned`. - pub async fn compute_pinned_balance( - client: &U::C, + pub async fn compute_pinned_balance( + client: &C, owner: PaymentAddress, viewing_key: &ViewingKey, ) -> Result<(Amount, Epoch), PinnedBalanceError> { @@ -1249,7 +1246,7 @@ impl ShieldedContext { .push(&(PIN_KEY_PREFIX.to_owned() + &owner.hash())) .expect("Cannot obtain a storage key"); // Obtain the transaction pointer at the key - let txidx = rpc::query_storage_value::(client, &pin_key) + let txidx = rpc::query_storage_value::(client, &pin_key) .await .ok_or(PinnedBalanceError::NoTransactionPinned)?; // Construct the key for where the pinned transaction is stored @@ -1259,7 +1256,7 @@ impl ShieldedContext { // Obtain the pointed to transaction let (tx_epoch, _tx_height, _tx_index, _tx, shielded) = rpc::query_storage_value::< - U::C, + C, (Epoch, BlockHeight, TxIndex, Transfer, Transaction), >(client, &tx_key) .await @@ -1297,9 +1294,9 @@ impl ShieldedContext { /// the epoch of the transaction or even before, so exchange all these /// amounts to the epoch of the transaction in order to get the value that /// would have been displayed in the epoch of the transaction. - pub async fn compute_exchanged_pinned_balance( + pub async fn compute_exchanged_pinned_balance( &mut self, - client: &U::C, + client: &C, owner: PaymentAddress, viewing_key: &ViewingKey, ) -> Result<(MaspAmount, Epoch), PinnedBalanceError> { @@ -1322,9 +1319,9 @@ impl ShieldedContext { /// Convert an amount whose units are AssetTypes to one whose units are /// Addresses that they decode to. All asset types not corresponding to /// the given epoch are ignored. - pub async fn decode_amount( + pub async fn decode_amount( &mut self, - client: &U::C, + client: &C, amt: Amount, target_epoch: Epoch, ) -> HashMap { @@ -1355,9 +1352,9 @@ impl ShieldedContext { /// Convert an amount whose units are AssetTypes to one whose units are /// Addresses that they decode to. - pub async fn decode_all_amounts( + pub async fn decode_all_amounts( &mut self, - client: &U::C, + client: &C, amt: Amount, ) -> MaspAmount { let mut res: HashMap<(Epoch, TokenAddress), Change> = @@ -1394,9 +1391,9 @@ impl ShieldedContext { /// understood that transparent account changes are effected only by the /// amounts and signatures specified by the containing Transfer object. #[cfg(feature = "masp-tx-gen")] - pub async fn gen_shielded_transfer( + pub async fn gen_shielded_transfer( &mut self, - client: &U::C, + client: &C, args: &args::TxTransfer, shielded_gas: bool, ) -> Result< @@ -1611,9 +1608,9 @@ impl ShieldedContext { /// transactions. If an owner is specified, then restrict the set to only /// transactions crediting/debiting the given owner. If token is specified, /// then restrict set to only transactions involving the given token. - pub async fn query_tx_deltas( + pub async fn query_tx_deltas( &mut self, - client: &U::C, + client: &C, query_owner: &Either>, query_token: &Option
, viewing_keys: &HashMap, diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index 27245ecaed..d2a9f6b042 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -1156,8 +1156,8 @@ pub async fn submit_ibc_transfer< /// Try to decode the given asset type and add its decoding to the supplied set. /// Returns true only if a new decoding has been added to the given set. async fn add_asset_type< - C: crate::ledger::queries::Client + Sync, - U: ShieldedUtils, + C: crate::ledger::queries::Client + Send + Sync, + U: ShieldedUtils, >( asset_types: &mut HashSet<(Address, Option, MaspDenom, Epoch)>, shielded: &mut ShieldedContext, @@ -1177,8 +1177,8 @@ async fn add_asset_type< /// function provides the data necessary for offline wallets to present asset /// type information. async fn used_asset_types< - C: crate::ledger::queries::Client + Sync, - U: ShieldedUtils, + C: crate::ledger::queries::Client + Send + Sync, + U: ShieldedUtils, P, R, K, @@ -1228,9 +1228,9 @@ async fn used_asset_types< /// Submit an ordinary transfer pub async fn submit_transfer< - C: crate::ledger::queries::Client + Sync, + C: crate::ledger::queries::Client + Send + Sync, V: WalletUtils, - U: ShieldedUtils, + U: ShieldedUtils, >( client: &C, wallet: &mut Wallet, From f6a2ce1f09c4e7b4195f634f2283fe0dd8d2dfbf Mon Sep 17 00:00:00 2001 From: satan Date: Thu, 6 Jul 2023 10:24:28 +0200 Subject: [PATCH 005/120] [chore]: Removed unnecessary trait bounds --- apps/src/lib/client/rpc.rs | 100 ++++++++++++++++++------------------- apps/src/lib/client/tx.rs | 43 ++++++++-------- shared/src/ledger/masp.rs | 38 +++++++------- shared/src/ledger/tx.rs | 6 +-- 4 files changed, 93 insertions(+), 94 deletions(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 2118804907..9ae4d9fa70 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -60,7 +60,7 @@ use crate::wallet::CliWalletUtils; /// /// If a response is not delivered until `deadline`, we exit the cli with an /// error. -pub async fn query_tx_status( +pub async fn query_tx_status( client: &C, status: namada::ledger::rpc::TxEventQuery<'_>, deadline: Duration, @@ -70,7 +70,7 @@ pub async fn query_tx_status( /// Query and print the epoch of the last committed block pub async fn query_and_print_epoch< - C: namada::ledger::queries::Client + Send + Sync, + C: namada::ledger::queries::Client + Sync, >( client: &C, ) -> Epoch { @@ -80,7 +80,7 @@ pub async fn query_and_print_epoch< } /// Query the last committed block -pub async fn query_block( +pub async fn query_block( client: &C, ) { let block = namada::ledger::rpc::query_block(client).await; @@ -98,7 +98,7 @@ pub async fn query_block( } /// Query the results of the last committed block -pub async fn query_results( +pub async fn query_results( client: &C, _args: args::Query, ) -> Vec { @@ -109,7 +109,7 @@ pub async fn query_results( /// Query the specified accepted transfers from the ledger pub async fn query_transfers< - C: namada::ledger::queries::Client + Send + Sync, + C: namada::ledger::queries::Client + Sync, U: ShieldedUtils, >( client: &C, @@ -243,7 +243,7 @@ pub async fn query_transfers< } /// Query the raw bytes of given storage key -pub async fn query_raw_bytes( +pub async fn query_raw_bytes( client: &C, args: args::QueryRawBytes, ) { @@ -261,7 +261,7 @@ pub async fn query_raw_bytes( /// Query token balance(s) pub async fn query_balance< - C: namada::ledger::queries::Client + Send + Sync, + C: namada::ledger::queries::Client + Sync, U: ShieldedUtils, >( client: &C, @@ -295,7 +295,7 @@ pub async fn query_balance< /// Query token balance(s) pub async fn query_transparent_balance< - C: namada::ledger::queries::Client + Send + Sync, + C: namada::ledger::queries::Client + Sync, >( client: &C, wallet: &mut Wallet, @@ -395,7 +395,7 @@ pub async fn query_transparent_balance< /// Query the token pinned balance(s) pub async fn query_pinned_balance< - C: namada::ledger::queries::Client + Send + Sync, + C: namada::ledger::queries::Client + Sync, U: ShieldedUtils, >( client: &C, @@ -626,11 +626,11 @@ async fn print_balances( } /// Query Proposals -pub async fn query_proposal( +pub async fn query_proposal( client: &C, args: args::QueryProposal, ) { - async fn print_proposal( + async fn print_proposal( client: &C, id: u64, current_epoch: Epoch, @@ -759,7 +759,7 @@ pub async fn query_proposal( /// Query token shielded balance(s) pub async fn query_shielded_balance< - C: namada::ledger::queries::Client + Send + Sync, + C: namada::ledger::queries::Client + Sync, U: ShieldedUtils, >( client: &C, @@ -1093,7 +1093,7 @@ pub async fn print_decoded_balance_with_epoch< } /// Query token amount of owner. -pub async fn get_token_balance( +pub async fn get_token_balance( client: &C, token: &Address, owner: &Address, @@ -1102,7 +1102,7 @@ pub async fn get_token_balance } pub async fn query_proposal_result< - C: namada::ledger::queries::Client + Send + Sync, + C: namada::ledger::queries::Client + Sync, >( client: &C, args: args::QueryProposalResult, @@ -1274,7 +1274,7 @@ pub async fn query_proposal_result< } pub async fn query_protocol_parameters< - C: namada::ledger::queries::Client + Send + Sync, + C: namada::ledger::queries::Client + Sync, >( client: &C, _args: args::QueryProtocolParameters, @@ -1344,7 +1344,7 @@ pub async fn query_protocol_parameters< println!("{:4}Votes per token: {}", "", pos_params.tm_votes_per_token); } -pub async fn query_bond( +pub async fn query_bond( client: &C, source: &Address, validator: &Address, @@ -1356,7 +1356,7 @@ pub async fn query_bond( } pub async fn query_unbond_with_slashing< - C: namada::ledger::queries::Client + Send + Sync, + C: namada::ledger::queries::Client + Sync, >( client: &C, source: &Address, @@ -1371,7 +1371,7 @@ pub async fn query_unbond_with_slashing< } pub async fn query_and_print_unbonds< - C: namada::ledger::queries::Client + Send + Sync, + C: namada::ledger::queries::Client + Sync, >( client: &C, source: &Address, @@ -1409,7 +1409,7 @@ pub async fn query_and_print_unbonds< } pub async fn query_withdrawable_tokens< - C: namada::ledger::queries::Client + Send + Sync, + C: namada::ledger::queries::Client + Sync, >( client: &C, bond_source: &Address, @@ -1425,7 +1425,7 @@ pub async fn query_withdrawable_tokens< } /// Query PoS bond(s) and unbond(s) -pub async fn query_bonds( +pub async fn query_bonds( client: &C, _wallet: &mut Wallet, args: args::QueryBonds, @@ -1534,7 +1534,7 @@ pub async fn query_bonds( } /// Query PoS bonded stake -pub async fn query_bonded_stake( +pub async fn query_bonded_stake( client: &C, args: args::QueryBondedStake, ) { @@ -1617,7 +1617,7 @@ pub async fn query_bonded_stake( client: &C, validator: &Address, @@ -1633,7 +1633,7 @@ pub async fn query_commission_rate< /// Query PoS validator's commission rate information pub async fn query_and_print_commission_rate< - C: namada::ledger::queries::Client + Send + Sync, + C: namada::ledger::queries::Client + Sync, >( client: &C, _wallet: &mut Wallet, @@ -1666,7 +1666,7 @@ pub async fn query_and_print_commission_rate< } /// Query PoS slashes -pub async fn query_slashes( +pub async fn query_slashes( client: &C, _wallet: &mut Wallet, args: args::QuerySlashes, @@ -1729,7 +1729,7 @@ pub async fn query_slashes( } } -pub async fn query_delegations( +pub async fn query_delegations( client: &C, _wallet: &mut Wallet, args: args::QueryDelegations, @@ -1748,7 +1748,7 @@ pub async fn query_delegations } } -pub async fn query_find_validator( +pub async fn query_find_validator( client: &C, args: args::QueryFindValidator, ) { @@ -1773,7 +1773,7 @@ pub async fn query_find_validator( +pub async fn dry_run_tx( client: &C, tx_bytes: Vec, ) { @@ -1784,7 +1784,7 @@ pub async fn dry_run_tx( } /// Get account's public key stored in its storage sub-space -pub async fn get_public_key( +pub async fn get_public_key( client: &C, address: &Address, ) -> Option { @@ -1792,7 +1792,7 @@ pub async fn get_public_key( } /// Check if the given address is a known validator. -pub async fn is_validator( +pub async fn is_validator( client: &C, address: &Address, ) -> bool { @@ -1800,14 +1800,14 @@ pub async fn is_validator( } /// Check if a given address is a known delegator -pub async fn is_delegator( +pub async fn is_delegator( client: &C, address: &Address, ) -> bool { namada::ledger::rpc::is_delegator(client, address).await } -pub async fn is_delegator_at( +pub async fn is_delegator_at( client: &C, address: &Address, epoch: Epoch, @@ -1818,7 +1818,7 @@ pub async fn is_delegator_at( /// Check if the address exists on chain. Established address exists if it has a /// stored validity predicate. Implicit and internal addresses always return /// true. -pub async fn known_address( +pub async fn known_address( client: &C, address: &Address, ) -> bool { @@ -1826,7 +1826,7 @@ pub async fn known_address( } /// Query for all conversions. -pub async fn query_conversions( +pub async fn query_conversions( client: &C, wallet: &mut Wallet, args: args::QueryConversions, @@ -1892,7 +1892,7 @@ pub async fn query_conversions } /// Query a conversion. -pub async fn query_conversion( +pub async fn query_conversion( client: &C, asset_type: AssetType, ) -> Option<( @@ -1907,7 +1907,7 @@ pub async fn query_conversion( } /// Query a wasm code hash -pub async fn query_wasm_code_hash( +pub async fn query_wasm_code_hash( client: &C, code_path: impl AsRef, ) -> Option { @@ -1915,7 +1915,7 @@ pub async fn query_wasm_code_hash( +pub async fn query_storage_value( client: &C, key: &storage::Key, ) -> Option @@ -1927,7 +1927,7 @@ where /// Query a storage value and the proof without decoding. pub async fn query_storage_value_bytes< - C: namada::ledger::queries::Client + Send + Sync, + C: namada::ledger::queries::Client + Sync, >( client: &C, key: &storage::Key, @@ -1942,7 +1942,7 @@ pub async fn query_storage_value_bytes< /// [`BorshDeserialize`]. Returns an iterator of the storage keys paired with /// their associated values. pub async fn query_storage_prefix< - C: namada::ledger::queries::Client + Send + Sync, + C: namada::ledger::queries::Client + Sync, T, >( client: &C, @@ -1956,7 +1956,7 @@ where /// Query to check if the given storage key exists. pub async fn query_has_storage_key< - C: namada::ledger::queries::Client + Send + Sync, + C: namada::ledger::queries::Client + Sync, >( client: &C, key: &storage::Key, @@ -1966,7 +1966,7 @@ pub async fn query_has_storage_key< /// Call the corresponding `tx_event_query` RPC method, to fetch /// the current status of a transation. -pub async fn query_tx_events( +pub async fn query_tx_events( client: &C, tx_event_query: namada::ledger::rpc::TxEventQuery<'_>, ) -> std::result::Result< @@ -1978,7 +1978,7 @@ pub async fn query_tx_events( /// Lookup the full response accompanying the specified transaction event // TODO: maybe remove this in favor of `query_tx_status` -pub async fn query_tx_response( +pub async fn query_tx_response( client: &C, tx_query: namada::ledger::rpc::TxEventQuery<'_>, ) -> Result { @@ -1987,7 +1987,7 @@ pub async fn query_tx_response /// Lookup the results of applying the specified transaction to the /// blockchain. -pub async fn query_result( +pub async fn query_result( client: &C, args: args::QueryResult, ) { @@ -2026,7 +2026,7 @@ pub async fn query_result( } } -pub async fn get_proposal_votes( +pub async fn get_proposal_votes( client: &C, epoch: Epoch, proposal_id: u64, @@ -2035,7 +2035,7 @@ pub async fn get_proposal_votes( client: &C, proposal: OfflineProposal, @@ -2219,7 +2219,7 @@ pub async fn get_proposal_offline_votes< } } -pub async fn get_bond_amount_at( +pub async fn get_bond_amount_at( client: &C, delegator: &Address, validator: &Address, @@ -2235,7 +2235,7 @@ pub async fn get_bond_amount_at( +pub async fn get_all_validators( client: &C, epoch: Epoch, ) -> HashSet
{ @@ -2243,7 +2243,7 @@ pub async fn get_all_validators( client: &C, epoch: Epoch, @@ -2255,7 +2255,7 @@ pub async fn get_total_staked_tokens< /// sum of validator's self-bonds and delegations to their address. /// Returns `None` when the given address is not a validator address. For a /// validator with `0` stake, this returns `Ok(token::Amount::zero())`. -async fn get_validator_stake( +async fn get_validator_stake( client: &C, epoch: Epoch, validator: &Address, @@ -2269,7 +2269,7 @@ async fn get_validator_stake( } pub async fn get_delegators_delegation< - C: namada::ledger::queries::Client + Send + Sync, + C: namada::ledger::queries::Client + Sync, >( client: &C, address: &Address, @@ -2278,7 +2278,7 @@ pub async fn get_delegators_delegation< } pub async fn get_governance_parameters< - C: namada::ledger::queries::Client + Send + Sync, + C: namada::ledger::queries::Client + Sync, >( client: &C, ) -> GovParams { diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 2cf159909c..81a13516ed 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -47,7 +47,7 @@ use crate::wallet::{ gen_validator_keys, read_and_confirm_encryption_password, CliWalletUtils, }; -pub async fn submit_custom( +pub async fn submit_custom( client: &C, ctx: &mut Context, mut args: args::TxCustom, @@ -59,7 +59,7 @@ pub async fn submit_custom( tx::submit_custom::(client, &mut ctx.wallet, args).await } -pub async fn submit_update_vp( +pub async fn submit_update_vp( client: &C, ctx: &mut Context, mut args: args::TxUpdateVp, @@ -71,7 +71,7 @@ pub async fn submit_update_vp( tx::submit_update_vp::(client, &mut ctx.wallet, args).await } -pub async fn submit_init_account( +pub async fn submit_init_account( client: &C, ctx: &mut Context, mut args: args::TxInitAccount, @@ -84,7 +84,7 @@ pub async fn submit_init_account( client: &C, mut ctx: Context, @@ -381,7 +381,6 @@ impl Default for CLIShieldedUtils { #[async_trait(?Send)] impl masp::ShieldedUtils for CLIShieldedUtils { - fn local_tx_prover(&self) -> LocalTxProver { if let Ok(params_dir) = env::var(masp::ENV_VAR_MASP_PARAMS_DIR) { let params_dir = PathBuf::from(params_dir); @@ -454,7 +453,7 @@ pub async fn submit_transfer( tx::submit_transfer(client, &mut ctx.wallet, &mut ctx.shielded, args).await } -pub async fn submit_ibc_transfer( +pub async fn submit_ibc_transfer( client: &C, mut ctx: Context, mut args: args::TxIbcTransfer, @@ -466,7 +465,7 @@ pub async fn submit_ibc_transfer(client, &mut ctx.wallet, args).await } -pub async fn submit_init_proposal( +pub async fn submit_init_proposal( client: &C, mut ctx: Context, mut args: args::InitProposal, @@ -624,7 +623,7 @@ pub async fn submit_init_proposal( +pub async fn submit_vote_proposal( client: &C, mut ctx: Context, mut args: args::VoteProposal, @@ -891,7 +890,7 @@ pub async fn submit_vote_proposal( +pub async fn submit_reveal_pk( client: &C, ctx: &mut Context, mut args: args::RevealPk, @@ -903,7 +902,7 @@ pub async fn submit_reveal_pk( tx::submit_reveal_pk::(client, &mut ctx.wallet, args).await } -pub async fn reveal_pk_if_needed( +pub async fn reveal_pk_if_needed( client: &C, ctx: &mut Context, public_key: &common::PublicKey, @@ -920,14 +919,14 @@ pub async fn reveal_pk_if_needed( +pub async fn has_revealed_pk( client: &C, addr: &Address, ) -> bool { tx::has_revealed_pk(client, addr).await } -pub async fn submit_reveal_pk_aux( +pub async fn submit_reveal_pk_aux( client: &C, ctx: &mut Context, public_key: &common::PublicKey, @@ -947,7 +946,7 @@ pub async fn submit_reveal_pk_aux( +async fn is_safe_voting_window( client: &C, proposal_id: u64, proposal_start_epoch: Epoch, @@ -957,7 +956,7 @@ async fn is_safe_voting_window /// Removes validators whose vote corresponds to that of the delegator (needless /// vote) -async fn filter_delegations( +async fn filter_delegations( client: &C, delegations: HashSet
, proposal_id: u64, @@ -994,7 +993,7 @@ async fn filter_delegations( delegations.into_iter().flatten().collect() } -pub async fn submit_bond( +pub async fn submit_bond( client: &C, ctx: &mut Context, mut args: args::Bond, @@ -1006,7 +1005,7 @@ pub async fn submit_bond( tx::submit_bond::(client, &mut ctx.wallet, args).await } -pub async fn submit_unbond( +pub async fn submit_unbond( client: &C, ctx: &mut Context, mut args: args::Unbond, @@ -1018,7 +1017,7 @@ pub async fn submit_unbond( tx::submit_unbond::(client, &mut ctx.wallet, args).await } -pub async fn submit_withdraw( +pub async fn submit_withdraw( client: &C, mut ctx: Context, mut args: args::Withdraw, @@ -1031,7 +1030,7 @@ pub async fn submit_withdraw( } pub async fn submit_validator_commission_change< - C: namada::ledger::queries::Client + Send + Sync, + C: namada::ledger::queries::Client + Sync, >( client: &C, mut ctx: Context, @@ -1050,7 +1049,7 @@ pub async fn submit_validator_commission_change< } pub async fn submit_unjail_validator< - C: namada::ledger::queries::Client + Send + Sync, + C: namada::ledger::queries::Client + Sync, >( client: &C, mut ctx: Context, @@ -1065,7 +1064,7 @@ pub async fn submit_unjail_validator< /// Submit transaction and wait for result. Returns a list of addresses /// initialized in the transaction if any. In dry run, this is always empty. -async fn process_tx( +async fn process_tx( client: &C, mut ctx: Context, args: &args::Tx, @@ -1107,7 +1106,7 @@ pub async fn save_initialized_accounts( /// the tx has been successfully included into the mempool of a validator /// /// In the case of errors in any of those stages, an error message is returned -pub async fn broadcast_tx( +pub async fn broadcast_tx( rpc_cli: &C, to_broadcast: &TxBroadcastData, ) -> Result { @@ -1122,7 +1121,7 @@ pub async fn broadcast_tx( /// 3. The decrypted payload of the tx has been included on the blockchain. /// /// In the case of errors in any of those stages, an error message is returned -pub async fn submit_tx( +pub async fn submit_tx( client: &C, to_broadcast: TxBroadcastData, ) -> Result { diff --git a/shared/src/ledger/masp.rs b/shared/src/ledger/masp.rs index a49e647661..44c459270d 100644 --- a/shared/src/ledger/masp.rs +++ b/shared/src/ledger/masp.rs @@ -628,7 +628,7 @@ impl ShieldedContext { /// Fetch the current state of the multi-asset shielded pool into a /// ShieldedContext - pub async fn fetch( + pub async fn fetch( &mut self, client: &C, sks: &[ExtendedSpendingKey], @@ -696,7 +696,7 @@ impl ShieldedContext { /// transactions as a vector. More concretely, the HEAD_TX_KEY location /// stores the index of the last accepted transaction and each transaction /// is stored at a key derived from its index. - pub async fn fetch_shielded_transfers( + pub async fn fetch_shielded_transfers( client: &C, last_txidx: u64, ) -> BTreeMap<(BlockHeight, TxIndex), (Epoch, Transfer, Transaction)> { @@ -742,9 +742,9 @@ impl ShieldedContext { /// we have spent are updated. The witness map is maintained to make it /// easier to construct note merkle paths in other code. See /// https://zips.z.cash/protocol/protocol.pdf#scan - pub async fn scan_tx( + pub async fn scan_tx( &mut self, - client: &U::C, + client: &C, height: BlockHeight, index: TxIndex, epoch: Epoch, @@ -880,9 +880,9 @@ impl ShieldedContext { /// Compute the total unspent notes associated with the viewing key in the /// context. If the key is not in the context, then we do not know the /// balance and hence we return None. - pub async fn compute_shielded_balance( + pub async fn compute_shielded_balance( &mut self, - client: &U::C, + client: &C, vk: &ViewingKey, ) -> Option { // Cannot query the balance of a key that's not in the map @@ -910,7 +910,7 @@ impl ShieldedContext { /// Query the ledger for the decoding of the given asset type and cache it /// if it is found. - pub async fn decode_asset_type( + pub async fn decode_asset_type( &mut self, client: &C, asset_type: AssetType, @@ -935,7 +935,7 @@ impl ShieldedContext { /// Query the ledger for the conversion that is allowed for the given asset /// type and cache it. - async fn query_allowed_conversion<'a, C: Client + Send + Sync>( + async fn query_allowed_conversion<'a, C: Client + Sync>( &'a mut self, client: &C, asset_type: AssetType, @@ -960,7 +960,7 @@ impl ShieldedContext { /// context and express that value in terms of the currently timestamped /// asset types. If the key is not in the context, then we do not know the /// balance and hence we return None. - pub async fn compute_exchanged_balance( + pub async fn compute_exchanged_balance( &mut self, client: &C, vk: &ViewingKey, @@ -990,9 +990,9 @@ impl ShieldedContext { /// the trace amount that could not be converted is moved from input to /// output. #[allow(clippy::too_many_arguments)] - async fn apply_conversion( + async fn apply_conversion( &mut self, - client: &U::C, + client: &C, conv: AllowedConversion, asset_type: (Epoch, TokenAddress, MaspDenom), value: i128, @@ -1044,7 +1044,7 @@ impl ShieldedContext { /// note of the conversions that were used. Note that this function does /// not assume that allowed conversions from the ledger are expressed in /// terms of the latest asset types. - pub async fn compute_exchanged_amount( + pub async fn compute_exchanged_amount( &mut self, client: &C, mut input: MaspAmount, @@ -1153,7 +1153,7 @@ impl ShieldedContext { /// of the specified asset type. Return the total value accumulated plus /// notes and the corresponding diversifiers/merkle paths that were used to /// achieve the total value. - pub async fn collect_unspent_notes( + pub async fn collect_unspent_notes( &mut self, client: &C, vk: &ViewingKey, @@ -1223,7 +1223,7 @@ impl ShieldedContext { /// keys to try to decrypt the output notes. If no transaction is pinned at /// the given payment address fails with /// `PinnedBalanceError::NoTransactionPinned`. - pub async fn compute_pinned_balance( + pub async fn compute_pinned_balance( client: &C, owner: PaymentAddress, viewing_key: &ViewingKey, @@ -1294,7 +1294,7 @@ impl ShieldedContext { /// the epoch of the transaction or even before, so exchange all these /// amounts to the epoch of the transaction in order to get the value that /// would have been displayed in the epoch of the transaction. - pub async fn compute_exchanged_pinned_balance( + pub async fn compute_exchanged_pinned_balance( &mut self, client: &C, owner: PaymentAddress, @@ -1319,7 +1319,7 @@ impl ShieldedContext { /// Convert an amount whose units are AssetTypes to one whose units are /// Addresses that they decode to. All asset types not corresponding to /// the given epoch are ignored. - pub async fn decode_amount( + pub async fn decode_amount( &mut self, client: &C, amt: Amount, @@ -1352,7 +1352,7 @@ impl ShieldedContext { /// Convert an amount whose units are AssetTypes to one whose units are /// Addresses that they decode to. - pub async fn decode_all_amounts( + pub async fn decode_all_amounts( &mut self, client: &C, amt: Amount, @@ -1391,7 +1391,7 @@ impl ShieldedContext { /// understood that transparent account changes are effected only by the /// amounts and signatures specified by the containing Transfer object. #[cfg(feature = "masp-tx-gen")] - pub async fn gen_shielded_transfer( + pub async fn gen_shielded_transfer( &mut self, client: &C, args: &args::TxTransfer, @@ -1608,7 +1608,7 @@ impl ShieldedContext { /// transactions. If an owner is specified, then restrict the set to only /// transactions crediting/debiting the given owner. If token is specified, /// then restrict set to only transactions involving the given token. - pub async fn query_tx_deltas( + pub async fn query_tx_deltas( &mut self, client: &C, query_owner: &Either>, diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index d2a9f6b042..8f8c154bf2 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -1156,7 +1156,7 @@ pub async fn submit_ibc_transfer< /// Try to decode the given asset type and add its decoding to the supplied set. /// Returns true only if a new decoding has been added to the given set. async fn add_asset_type< - C: crate::ledger::queries::Client + Send + Sync, + C: crate::ledger::queries::Client + Sync, U: ShieldedUtils, >( asset_types: &mut HashSet<(Address, Option, MaspDenom, Epoch)>, @@ -1177,7 +1177,7 @@ async fn add_asset_type< /// function provides the data necessary for offline wallets to present asset /// type information. async fn used_asset_types< - C: crate::ledger::queries::Client + Send + Sync, + C: crate::ledger::queries::Client + Sync, U: ShieldedUtils, P, R, @@ -1228,7 +1228,7 @@ async fn used_asset_types< /// Submit an ordinary transfer pub async fn submit_transfer< - C: crate::ledger::queries::Client + Send + Sync, + C: crate::ledger::queries::Client + Sync, V: WalletUtils, U: ShieldedUtils, >( From 67c9b44460a74bcea2c7be87eebd87602d40a12a Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Sun, 9 Jul 2023 22:09:19 +0100 Subject: [PATCH 006/120] Add MAX value for EthBridgeVotingPower --- core/src/types/voting_power.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/src/types/voting_power.rs b/core/src/types/voting_power.rs index 96a9579279..9fdc5f47c0 100644 --- a/core/src/types/voting_power.rs +++ b/core/src/types/voting_power.rs @@ -31,6 +31,12 @@ use crate::types::uint::Uint; )] pub struct EthBridgeVotingPower(u64); +impl EthBridgeVotingPower { + /// Maximum value that can be represented for the voting power + /// stored in an Ethereum bridge smart contract. + pub const MAX: Self = Self(1 << 32); +} + impl From for EthBridgeVotingPower { fn from(val: u64) -> Self { Self(val) From 2425cce7097075f46aa37652ac35d73031b3a86a Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Sun, 9 Jul 2023 22:10:25 +0100 Subject: [PATCH 007/120] Use new const in From impl --- core/src/types/voting_power.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/types/voting_power.rs b/core/src/types/voting_power.rs index 9fdc5f47c0..8fa594d693 100644 --- a/core/src/types/voting_power.rs +++ b/core/src/types/voting_power.rs @@ -47,7 +47,8 @@ impl From<&FractionalVotingPower> for EthBridgeVotingPower { fn from(ratio: &FractionalVotingPower) -> Self { // normalize the voting power // https://github.com/anoma/ethereum-bridge/blob/fe93d2e95ddb193a759811a79c8464ad4d709c12/test/utils/utilities.js#L29 - const NORMALIZED_VOTING_POWER: Uint = Uint::from_u64(1 << 32); + const NORMALIZED_VOTING_POWER: Uint = + Uint::from_u64(EthBridgeVotingPower::MAX.0); let voting_power = ratio.0 * NORMALIZED_VOTING_POWER; let voting_power = voting_power.round().to_integer().low_u64(); From 8df0a04663bfa6faf1f7ec7bf5325e7f52aeeb19 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Sun, 9 Jul 2023 22:12:35 +0100 Subject: [PATCH 008/120] Replace From with TryFrom impl on EthBridgeVotingPower --- core/src/types/voting_power.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/core/src/types/voting_power.rs b/core/src/types/voting_power.rs index 8fa594d693..946e08b834 100644 --- a/core/src/types/voting_power.rs +++ b/core/src/types/voting_power.rs @@ -37,9 +37,16 @@ impl EthBridgeVotingPower { pub const MAX: Self = Self(1 << 32); } -impl From for EthBridgeVotingPower { - fn from(val: u64) -> Self { - Self(val) +impl TryFrom for EthBridgeVotingPower { + type Error = (); + + #[inline] + fn try_from(val: u64) -> Result { + if val <= Self::MAX.0 { + Ok(Self(val)) + } else { + Err(()) + } } } From 9187d2e847440c5c099f44f92d4676c22d60c92d Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Sun, 9 Jul 2023 22:16:03 +0100 Subject: [PATCH 009/120] Call try_into() instead of into() on EthBridgeVotingPower --- core/src/types/eth_abi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/types/eth_abi.rs b/core/src/types/eth_abi.rs index 5beaa6fe8f..adda87e6c9 100644 --- a/core/src/types/eth_abi.rs +++ b/core/src/types/eth_abi.rs @@ -198,7 +198,7 @@ mod tests { ) .expect("Test failed"), ], - voting_powers: vec![8828299.into()], + voting_powers: vec![8828299.try_into().unwrap()], epoch: 0.into(), }; let encoded = valset_update.encode().into_inner(); From cfdf73a43d31ac5d7ee1f5216a6fe106d6c548b1 Mon Sep 17 00:00:00 2001 From: yito88 Date: Wed, 31 May 2023 19:07:08 +0900 Subject: [PATCH 010/120] clean denom validation --- core/src/ledger/ibc/context/common.rs | 10 +++- core/src/ledger/ibc/mod.rs | 3 +- core/src/ledger/ibc/storage.rs | 22 +++++-- shared/src/ledger/ibc/vp/denom.rs | 85 --------------------------- shared/src/ledger/ibc/vp/mod.rs | 42 ++++++++++--- shared/src/ledger/ibc/vp/token.rs | 2 +- tests/src/vm_host_env/mod.rs | 2 +- 7 files changed, 64 insertions(+), 102 deletions(-) delete mode 100644 shared/src/ledger/ibc/vp/denom.rs diff --git a/core/src/ledger/ibc/context/common.rs b/core/src/ledger/ibc/context/common.rs index 1d03ffbeec..4c96c034b5 100644 --- a/core/src/ledger/ibc/context/common.rs +++ b/core/src/ledger/ibc/context/common.rs @@ -1,5 +1,6 @@ //! IbcCommonContext implementation for IBC +use borsh::BorshSerialize; use prost::Message; use sha2::Digest; @@ -363,7 +364,14 @@ pub trait IbcCommonContext: IbcStorageContext { denom: PrefixedDenom, ) -> Result<(), ContextError> { let key = storage::ibc_denom_key(trace_hash); - let bytes = denom.to_string().as_bytes().to_vec(); + let bytes = denom.to_string().try_to_vec().map_err(|e| { + ContextError::ChannelError(ChannelError::Other { + description: format!( + "Encoding the denom failed: Denom {}, error {}", + denom, e + ), + }) + })?; self.write(&key, bytes).map_err(|_| { ContextError::ChannelError(ChannelError::Other { description: format!("Writing the denom failed: Key {}", key), diff --git a/core/src/ledger/ibc/mod.rs b/core/src/ledger/ibc/mod.rs index 9a184ee51e..ef9ddc7735 100644 --- a/core/src/ledger/ibc/mod.rs +++ b/core/src/ledger/ibc/mod.rs @@ -9,6 +9,7 @@ use std::fmt::Debug; use std::rc::Rc; use std::time::Duration; +use borsh::BorshDeserialize; pub use context::common::IbcCommonContext; pub use context::storage::{IbcStorageContext, ProofSpec}; pub use context::transfer_mod::{ModuleWrapper, TransferModule}; @@ -151,7 +152,7 @@ where { let denom_key = storage::ibc_denom_key(token_hash); let denom = match self.ctx.borrow().read(&denom_key) { - Ok(Some(v)) => String::from_utf8(v).map_err(|e| { + Ok(Some(v)) => String::try_from_slice(&v[..]).map_err(|e| { Error::Denom(format!( "Decoding the denom string failed: {}", e diff --git a/core/src/ledger/ibc/storage.rs b/core/src/ledger/ibc/storage.rs index 3f0eb94d2a..1e84cb24ef 100644 --- a/core/src/ledger/ibc/storage.rs +++ b/core/src/ledger/ibc/storage.rs @@ -528,12 +528,24 @@ pub fn is_ibc_sub_prefix(sub_prefix: &Key) -> bool { DbKeySeg::StringSeg(s) if s == MULTITOKEN_STORAGE_KEY) } -/// Returns true if the given key is the denom key -pub fn is_ibc_denom_key(key: &Key) -> bool { +/// Returns the token hash if the given key is the denom key +pub fn is_ibc_denom_key(key: &Key) -> Option { match &key.segments[..] { - [DbKeySeg::AddressSeg(addr), DbKeySeg::StringSeg(prefix), ..] => { - addr == &Address::Internal(InternalAddress::Ibc) && prefix == DENOM + [ + DbKeySeg::AddressSeg(addr), + DbKeySeg::StringSeg(prefix), + DbKeySeg::AddressSeg(Address::Internal(InternalAddress::IbcToken( + hash, + ))), + ] => { + if addr == &Address::Internal(InternalAddress::Ibc) + && prefix == DENOM + { + Some(hash.clone()) + } else { + None + } } - _ => false, + _ => None, } } diff --git a/shared/src/ledger/ibc/vp/denom.rs b/shared/src/ledger/ibc/vp/denom.rs deleted file mode 100644 index d58edfdc33..0000000000 --- a/shared/src/ledger/ibc/vp/denom.rs +++ /dev/null @@ -1,85 +0,0 @@ -//! IBC validity predicate for denom - -use prost::Message; -use thiserror::Error; - -use super::Ibc; -use crate::ibc::applications::transfer::packet::PacketData; -use crate::ibc::core::ics04_channel::msgs::PacketMsg; -use crate::ibc::core::ics26_routing::msgs::MsgEnvelope; -use crate::ibc_proto::google::protobuf::Any; -use crate::ledger::ibc::storage; -use crate::ledger::native_vp::VpEnv; -use crate::ledger::storage::{self as ledger_storage, StorageHasher}; -use crate::types::storage::KeySeg; -use crate::vm::WasmCacheAccess; - -#[allow(missing_docs)] -#[derive(Error, Debug)] -pub enum Error { - #[error("Decoding IBC data error: {0}")] - DecodingData(prost::DecodeError), - #[error("Invalid message: {0}")] - IbcMessage(String), - #[error("Decoding PacketData error: {0}")] - DecodingPacketData(serde_json::Error), - #[error("Denom error: {0}")] - Denom(String), -} - -/// IBC channel functions result -pub type Result = std::result::Result; - -impl<'a, DB, H, CA> Ibc<'a, DB, H, CA> -where - DB: 'static + ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, - H: 'static + StorageHasher, - CA: 'static + WasmCacheAccess, -{ - pub(super) fn validate_denom(&self, tx_data: &[u8]) -> Result<()> { - let ibc_msg = Any::decode(tx_data).map_err(Error::DecodingData)?; - let envelope: MsgEnvelope = ibc_msg.try_into().map_err(|e| { - Error::IbcMessage(format!( - "Decoding a MsgRecvPacket failed: Error {}", - e - )) - })?; - // A transaction only with MsgRecvPacket can update the denom store - let msg = match envelope { - MsgEnvelope::Packet(PacketMsg::Recv(msg)) => msg, - _ => { - return Err(Error::IbcMessage( - "Non-MsgRecvPacket message updated the denom store" - .to_string(), - )); - } - }; - let data = serde_json::from_slice::(&msg.packet.data) - .map_err(Error::DecodingPacketData)?; - let denom = format!( - "{}/{}/{}", - &msg.packet.port_id_on_b, - &msg.packet.chan_id_on_b, - &data.token.denom, - ); - let token_hash = storage::calc_hash(&denom); - let denom_key = storage::ibc_denom_key(token_hash.raw()); - match self.ctx.read_bytes_post(&denom_key) { - Ok(Some(v)) => match std::str::from_utf8(&v) { - Ok(d) if d == denom => Ok(()), - Ok(d) => Err(Error::Denom(format!( - "Mismatch the denom: original {}, denom {}", - denom, d - ))), - Err(e) => Err(Error::Denom(format!( - "Decoding the denom failed: key {}, error {}", - denom_key, e - ))), - }, - _ => Err(Error::Denom(format!( - "Looking up the denom failed: Key {}", - denom_key - ))), - } - } -} diff --git a/shared/src/ledger/ibc/vp/mod.rs b/shared/src/ledger/ibc/vp/mod.rs index 5986a01771..f5625112ff 100644 --- a/shared/src/ledger/ibc/vp/mod.rs +++ b/shared/src/ledger/ibc/vp/mod.rs @@ -1,7 +1,6 @@ //! IBC integration as a native validity predicate mod context; -mod denom; mod token; use std::cell::RefCell; @@ -10,7 +9,6 @@ use std::rc::Rc; use std::time::Duration; use context::{PseudoExecutionContext, VpValidationContext}; -use namada_core::ledger::ibc::storage::{is_ibc_denom_key, is_ibc_key}; use namada_core::ledger::ibc::{ Error as ActionError, IbcActions, TransferModule, ValidationParams, }; @@ -23,6 +21,7 @@ use namada_proof_of_stake::read_pos_params; use thiserror::Error; pub use token::{Error as IbcTokenError, IbcToken}; +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::parameters::read_epoch_duration_parameter; use crate::vm::WasmCacheAccess; @@ -40,8 +39,8 @@ pub enum Error { IbcAction(ActionError), #[error("State change error: {0}")] StateChange(String), - #[error("Denom store error: {0}")] - Denom(denom::Error), + #[error("Denom error: {0}")] + Denom(String), #[error("IBC event error: {0}")] IbcEvent(String), } @@ -86,9 +85,7 @@ where self.validate_with_msg(&tx_data)?; // Validate the denom store if a denom key has been changed - if keys_changed.iter().any(is_ibc_denom_key) { - self.validate_denom(&tx_data).map_err(Error::Denom)?; - } + self.validate_denom(keys_changed)?; Ok(true) } @@ -173,6 +170,35 @@ where upgrade_path: Vec::new(), }) } + + fn validate_denom(&self, keys_changed: &BTreeSet) -> VpResult<()> { + for key in keys_changed { + if let Some(hash) = is_ibc_denom_key(key) { + match self.ctx.read_post::(key).map_err(|e| { + Error::Denom(format!( + "Getting the denom failed: Key {}, Error {}", + key, e + )) + })? { + Some(denom) => { + if calc_hash(&denom) != hash { + return Err(Error::Denom(format!( + "The denom is invalid: Key {}, Denom {}", + key, denom + ))); + } + } + None => { + return Err(Error::Denom(format!( + "The corresponding denom wasn't stored: Key {}", + key + ))); + } + } + } + } + Ok(()) + } } fn match_value( @@ -2187,7 +2213,7 @@ mod tests { )); let trace_hash = calc_hash(coin.denom.to_string()); let denom_key = ibc_denom_key(&trace_hash); - let bytes = coin.denom.to_string().as_bytes().to_vec(); + let bytes = coin.denom.to_string().try_to_vec().unwrap(); wl_storage .write_log .write(&denom_key, bytes) diff --git a/shared/src/ledger/ibc/vp/token.rs b/shared/src/ledger/ibc/vp/token.rs index 18234abd35..204fa21ea0 100644 --- a/shared/src/ledger/ibc/vp/token.rs +++ b/shared/src/ledger/ibc/vp/token.rs @@ -192,7 +192,7 @@ where { let denom_key = ibc_storage::ibc_denom_key(token_hash); coin.denom = match self.ctx.read_bytes_pre(&denom_key) { - Ok(Some(v)) => String::from_utf8(v).map_err(|e| { + Ok(Some(v)) => String::try_from_slice(&v[..]).map_err(|e| { Error::Denom(format!( "Decoding the denom string failed: {}", e diff --git a/tests/src/vm_host_env/mod.rs b/tests/src/vm_host_env/mod.rs index 1766d7bad3..81192796a2 100644 --- a/tests/src/vm_host_env/mod.rs +++ b/tests/src/vm_host_env/mod.rs @@ -1195,7 +1195,7 @@ mod tests { // original denom let hash = ibc_storage::calc_hash(&denom); let denom_key = ibc_storage::ibc_denom_key(&hash); - writes.insert(denom_key, denom.as_bytes().to_vec()); + writes.insert(denom_key, denom.try_to_vec().unwrap()); writes.into_iter().for_each(|(key, val)| { tx_host_env::with(|env| { env.wl_storage From 0e5d22b9216ca6981bf19e7a9d65b1bc5084c259 Mon Sep 17 00:00:00 2001 From: yito88 Date: Fri, 2 Jun 2023 23:41:41 +0900 Subject: [PATCH 011/120] change multitoken --- apps/src/lib/client/rpc.rs | 79 ++++------ core/src/ledger/ibc/context/storage.rs | 23 ++- core/src/ledger/ibc/context/transfer_mod.rs | 84 +++-------- core/src/ledger/ibc/mod.rs | 2 +- core/src/ledger/ibc/storage.rs | 116 +++------------ core/src/types/address.rs | 87 ++++------- core/src/types/token.rs | 94 +++++------- shared/src/ledger/ibc/vp/context.rs | 152 ++++++++++++++----- shared/src/ledger/ibc/vp/mod.rs | 6 +- shared/src/ledger/native_vp/mod.rs | 1 + shared/src/ledger/protocol/mod.rs | 21 ++- shared/src/ledger/tx.rs | 36 +++-- tests/src/e2e/ibc_tests.rs | 49 +++--- tests/src/vm_host_env/ibc.rs | 40 ++--- tests/src/vm_host_env/mod.rs | 92 +++++++----- tests/src/vm_host_env/tx.rs | 8 +- tx_prelude/src/ibc.rs | 35 ++++- tx_prelude/src/token.rs | 156 +++++++------------- wasm/wasm_source/src/vp_implicit.rs | 7 +- wasm/wasm_source/src/vp_token.rs | 38 +---- wasm/wasm_source/src/vp_user.rs | 7 +- wasm/wasm_source/src/vp_validator.rs | 7 +- 22 files changed, 497 insertions(+), 643 deletions(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 44955967d0..da2078139d 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -305,17 +305,13 @@ pub async fn query_transparent_balance< let tokens = wallet.get_addresses_with_vp_type(AddressVpType::Token); match (args.token, args.owner) { (Some(token), Some(owner)) => { - let (balance_key, sub_prefix) = match &args.sub_prefix { - Some(sub_prefix) => { - let sub_prefix = Key::parse(sub_prefix).unwrap(); - let prefix = - token::multitoken_balance_prefix(&token, &sub_prefix); - ( - token::multitoken_balance_key( - &prefix, - &owner.address().unwrap(), - ), - Some(sub_prefix), + let key = match &args.sub_prefix { + Some(sp) => { + let sub_prefix = + Address::decode(sp).expect("Invalid sub_prefix"); + token::multitoken_balance_key( + &sub_prefix, + &owner.address().unwrap(), ) } None => ( @@ -559,30 +555,20 @@ async fn print_balances( let token_alias = lookup_alias(wallet, token); writeln!(w, "Token {}", token_alias).unwrap(); - let mut print_num = 0; - for (key, balance) in balances { - let (o, s) = match token::is_any_multitoken_balance_key(&key) { - Some((sub_prefix, [tok, owner])) => ( - owner.clone(), - format!( - "with {}: {}, owned by {}", - sub_prefix.clone(), - format_denominated_amount( - client, - &TokenAddress { - address: tok.clone(), - sub_prefix: Some(sub_prefix) - }, - balance - ) - .await, - lookup_alias(wallet, owner) - ), - ), - None => { - if let Some([tok, owner]) = - token::is_any_token_balance_key(&key) - { + + let print_num = balances + .filter_map(|(key, balance)| { + match token::is_multitoken_balance_key(&key) { + Some((sub_prefix, owner)) => Some(( + owner.clone(), + format!( + "with {}: {}, owned by {}", + sub_prefix, + balance, + lookup_alias(wallet, owner) + ), + )), + None => token::is_any_token_balance_key(&key).map(|owner| { ( owner.clone(), format!( @@ -599,19 +585,18 @@ async fn print_balances( lookup_alias(wallet, owner) ), ) - } else { - continue; - } + }), } - }; - let s = match target { - Some(t) if o == *t => s, - Some(_) => continue, - None => s, - }; - writeln!(w, "{}", s).unwrap(); - print_num += 1; - } + }) + .filter_map(|(o, s)| match target { + Some(t) if o == *t => Some(s), + Some(_) => None, + None => Some(s), + }) + .map(|s| { + writeln!(w, "{}", s).unwrap(); + }) + .count(); if print_num == 0 { match target { diff --git a/core/src/ledger/ibc/context/storage.rs b/core/src/ledger/ibc/context/storage.rs index a99a659187..e047395a68 100644 --- a/core/src/ledger/ibc/context/storage.rs +++ b/core/src/ledger/ibc/context/storage.rs @@ -6,6 +6,7 @@ pub use ics23::ProofSpec; use super::super::Error; use crate::ledger::storage_api; +use crate::types::address::Address; use crate::types::ibc::IbcEvent; use crate::types::storage::{BlockHeight, Header, Key}; use crate::types::token::Amount; @@ -54,8 +55,26 @@ pub trait IbcStorageContext { /// Transfer token fn transfer_token( &mut self, - src: &Key, - dest: &Key, + src: &Address, + dest: &Address, + token: &Address, + sub_prefix: Option
, + amount: Amount, + ) -> Result<(), Self::Error>; + + /// Mint token + fn mint_token( + &mut self, + target: &Address, + sub_prefix: &Address, + amount: Amount, + ) -> Result<(), Self::Error>; + + /// Burn token + fn burn_token( + &mut self, + target: &Address, + sub_prefix: &Address, amount: Amount, ) -> Result<(), Self::Error>; diff --git a/core/src/ledger/ibc/context/transfer_mod.rs b/core/src/ledger/ibc/context/transfer_mod.rs index ad0aa75800..9dfce35e6c 100644 --- a/core/src/ledger/ibc/context/transfer_mod.rs +++ b/core/src/ledger/ibc/context/transfer_mod.rs @@ -388,7 +388,7 @@ where _port_id: &PortId, _channel_id: &ChannelId, ) -> Result { - Ok(Address::Internal(InternalAddress::IbcEscrow)) + Ok(Address::Internal(InternalAddress::Ibc)) } fn can_send_coins(&self) -> Result<(), TokenTransferError> { @@ -446,42 +446,22 @@ where // has no prefix let (token, amount) = get_token_amount(coin)?; - let src = if coin.denom.trace_path.is_empty() - || *from == Address::Internal(InternalAddress::IbcEscrow) - || *from == Address::Internal(InternalAddress::IbcMint) - { - token::balance_key(&token, from) + let ibc_token = if coin.denom.trace_path.is_empty() { + None } else { - let prefix = storage::ibc_token_prefix(coin.denom.to_string()) - .map_err(|_| TokenTransferError::InvalidCoin { - coin: coin.to_string(), - })?; - token::multitoken_balance_key(&prefix, from) - }; - - let dest = if coin.denom.trace_path.is_empty() - || *to == Address::Internal(InternalAddress::IbcEscrow) - || *to == Address::Internal(InternalAddress::IbcBurn) - { - token::balance_key(&token, to) - } else { - let prefix = storage::ibc_token_prefix(coin.denom.to_string()) - .map_err(|_| TokenTransferError::InvalidCoin { - coin: coin.to_string(), - })?; - token::multitoken_balance_key(&prefix, to) + Some(storage::ibc_token(coin.denom.to_string())) }; self.ctx .borrow_mut() - .transfer_token(&src, &dest, amount) + .transfer_token(from, to, &token, ibc_token, amount) .map_err(|_| { TokenTransferError::ContextError(ContextError::ChannelError( ChannelError::Other { description: format!( "Sending a coin failed: from {}, to {}, amount {}", - src, - dest, + from, + to, amount.to_string_native() ), }, @@ -494,33 +474,20 @@ where account: &Self::AccountId, coin: &PrefixedCoin, ) -> Result<(), TokenTransferError> { - let (token, amount) = get_token_amount(coin)?; + let (_, amount) = get_token_amount(coin)?; - let src = token::balance_key( - &token, - &Address::Internal(InternalAddress::IbcMint), - ); - - let dest = if coin.denom.trace_path.is_empty() { - token::balance_key(&token, account) - } else { - let prefix = storage::ibc_token_prefix(coin.denom.to_string()) - .map_err(|_| TokenTransferError::InvalidCoin { - coin: coin.to_string(), - })?; - token::multitoken_balance_key(&prefix, account) - }; + // The trace path of the denom is already updated if receiving the token + let ibc_token = storage::ibc_token(coin.denom.to_string()); self.ctx .borrow_mut() - .transfer_token(&src, &dest, amount) + .mint_token(account, &ibc_token, amount) .map_err(|_| { TokenTransferError::ContextError(ContextError::ChannelError( ChannelError::Other { description: format!( - "Sending a coin failed: from {}, to {}, amount {}", - src, - dest, + "Minting a coin failed: account {}, amount {}", + account, amount.to_string_native() ), }, @@ -533,33 +500,20 @@ where account: &Self::AccountId, coin: &PrefixedCoin, ) -> Result<(), TokenTransferError> { - let (token, amount) = get_token_amount(coin)?; + let (_, amount) = get_token_amount(coin)?; - let src = if coin.denom.trace_path.is_empty() { - token::balance_key(&token, account) - } else { - let prefix = storage::ibc_token_prefix(coin.denom.to_string()) - .map_err(|_| TokenTransferError::InvalidCoin { - coin: coin.to_string(), - })?; - token::multitoken_balance_key(&prefix, account) - }; - - let dest = token::balance_key( - &token, - &Address::Internal(InternalAddress::IbcBurn), - ); + // The burn is unminting of the minted + let ibc_token = storage::ibc_token(coin.denom.to_string()); self.ctx .borrow_mut() - .transfer_token(&src, &dest, amount) + .burn_token(account, &ibc_token, amount) .map_err(|_| { TokenTransferError::ContextError(ContextError::ChannelError( ChannelError::Other { description: format!( - "Sending a coin failed: from {}, to {}, amount {}", - src, - dest, + "Burning a coin failed: account {}, amount {}", + account, amount.to_string_native() ), }, diff --git a/core/src/ledger/ibc/mod.rs b/core/src/ledger/ibc/mod.rs index ef9ddc7735..b56e8ce54a 100644 --- a/core/src/ledger/ibc/mod.rs +++ b/core/src/ledger/ibc/mod.rs @@ -141,7 +141,7 @@ where } } - /// Restore the denom when it is hashed, i.e. the denom is `ibc/{hash}`. + /// Restore the denom when it is hashed fn restore_denom(&self, msg: MsgTransfer) -> Result { let mut msg = msg; // lookup the original denom with the IBC token hash diff --git a/core/src/ledger/ibc/storage.rs b/core/src/ledger/ibc/storage.rs index 1e84cb24ef..1a47680f00 100644 --- a/core/src/ledger/ibc/storage.rs +++ b/core/src/ledger/ibc/storage.rs @@ -23,8 +23,6 @@ const CLIENTS_COUNTER: &str = "clients/counter"; const CONNECTIONS_COUNTER: &str = "connections/counter"; const CHANNELS_COUNTER: &str = "channelEnds/counter"; const DENOM: &str = "ibc_denom"; -/// Key segment for a multitoken related to IBC -pub const MULTITOKEN_STORAGE_KEY: &str = "ibc"; #[allow(missing_docs)] #[derive(Error, Debug)] @@ -44,67 +42,6 @@ pub enum Error { /// IBC storage functions result pub type Result = std::result::Result; -/// IBC prefix -#[allow(missing_docs)] -pub enum IbcPrefix { - Client, - Connection, - Channel, - Port, - Capability, - SeqSend, - SeqRecv, - SeqAck, - Commitment, - Receipt, - Ack, - Event, - Denom, - Unknown, -} - -/// Returns the prefix from the given key -pub fn ibc_prefix(key: &Key) -> Option { - match &key.segments[..] { - [DbKeySeg::AddressSeg(addr), DbKeySeg::StringSeg(prefix), ..] - if addr == &Address::Internal(InternalAddress::Ibc) => - { - Some(match &*prefix.raw() { - "clients" => IbcPrefix::Client, - "connections" => IbcPrefix::Connection, - "channelEnds" => IbcPrefix::Channel, - "ports" => IbcPrefix::Port, - "capabilities" => IbcPrefix::Capability, - "nextSequenceSend" => IbcPrefix::SeqSend, - "nextSequenceRecv" => IbcPrefix::SeqRecv, - "nextSequenceAck" => IbcPrefix::SeqAck, - "commitments" => IbcPrefix::Commitment, - "receipts" => IbcPrefix::Receipt, - "acks" => IbcPrefix::Ack, - "event" => IbcPrefix::Event, - "ibc_denom" => IbcPrefix::Denom, - _ => IbcPrefix::Unknown, - }) - } - _ => None, - } -} - -/// Check if the given key is a key of the client counter -pub fn is_client_counter_key(key: &Key) -> bool { - *key == client_counter_key() -} - -/// Check if the given key is a key of the connection counter -pub fn is_connection_counter_key(key: &Key) -> bool { - *key == connection_counter_key() -} - -/// Check if the given key is a key of the channel counter -pub fn is_channel_counter_key(key: &Key) -> bool { - *key == channel_counter_key() -} - /// Returns a key of the IBC-related data pub fn ibc_key(path: impl AsRef) -> Result { let path = Key::parse(path).map_err(Error::StorageKey)?; @@ -473,26 +410,20 @@ pub fn token(denom: impl AsRef) -> Result
{ /// Get the hash of IBC token address from the denom string pub fn token_hash_from_denom(denom: impl AsRef) -> Result> { - match denom - .as_ref() - .strip_prefix(&format!("{}/", MULTITOKEN_STORAGE_KEY)) - { - Some(addr_str) => { - let addr = Address::decode(addr_str).map_err(|e| { - Error::Denom(format!( - "Decoding the denom failed: ibc_token {}, error {}", - addr_str, e - )) - })?; - match addr { - Address::Internal(InternalAddress::IbcToken(h)) => Ok(Some(h)), - _ => Err(Error::Denom(format!( - "Unexpected address was given: {}", - addr - ))), - } - } - None => Ok(None), + let addr = Address::decode(denom.as_ref()).map_err(|e| { + Error::Denom(format!( + "Decoding the denom failed: denom {}, error {}", + denom.as_ref(), + e + )) + })?; + match addr { + Address::Established(_) => Ok(None), + Address::Internal(InternalAddress::IbcToken(h)) => Ok(Some(h)), + _ => Err(Error::Denom(format!( + "Unexpected address was given: {}", + addr + ))), } } @@ -503,17 +434,10 @@ pub fn calc_hash(denom: impl AsRef) -> String { format!("{:.width$x}", hasher.finalize(), width = HASH_HEX_LEN) } -/// Key's prefix of the received token over IBC -pub fn ibc_token_prefix(denom: impl AsRef) -> Result { - let token = token(&denom)?; +/// Obtain the IbcToken with the hash from the given denom +pub fn ibc_token(denom: impl AsRef) -> Address { let hash = calc_hash(&denom); - let ibc_token = Address::Internal(InternalAddress::IbcToken(hash)); - let prefix = Key::from(token.to_db_key()) - .push(&MULTITOKEN_STORAGE_KEY.to_owned()) - .expect("Cannot obtain a storage key") - .push(&ibc_token.to_db_key()) - .expect("Cannot obtain a storage key"); - Ok(prefix) + Address::Internal(InternalAddress::IbcToken(hash)) } /// Returns true if the given key is for IBC @@ -522,12 +446,6 @@ pub fn is_ibc_key(key: &Key) -> bool { DbKeySeg::AddressSeg(addr) if *addr == Address::Internal(InternalAddress::Ibc)) } -/// Returns true if the sub prefix is for IBC -pub fn is_ibc_sub_prefix(sub_prefix: &Key) -> bool { - matches!(&sub_prefix.segments[0], - DbKeySeg::StringSeg(s) if s == MULTITOKEN_STORAGE_KEY) -} - /// Returns the token hash if the given key is the denom key pub fn is_ibc_denom_key(key: &Key) -> Option { match &key.segments[..] { diff --git a/core/src/types/address.rs b/core/src/types/address.rs index eca6c87251..c647b85238 100644 --- a/core/src/types/address.rs +++ b/core/src/types/address.rs @@ -54,10 +54,8 @@ pub const FIXED_LEN_STRING_BYTES: usize = 45; /// Internal IBC address pub const IBC: Address = Address::Internal(InternalAddress::Ibc); -/// Internal IBC token burn address -pub const IBC_BURN: Address = Address::Internal(InternalAddress::IbcBurn); -/// Internal IBC token mint address -pub const IBC_MINT: Address = Address::Internal(InternalAddress::IbcMint); +/// Internal multitoken mint address +pub const MINT: Address = Address::Internal(InternalAddress::Mint); /// Internal ledger parameters address pub const PARAMETERS: Address = Address::Internal(InternalAddress::Parameters); /// Internal PoS address @@ -84,18 +82,16 @@ mod internal { "ano::Slash Fund "; pub const IBC: &str = "ibc::Inter-Blockchain Communication "; - pub const IBC_ESCROW: &str = - "ibc::IBC Escrow Address "; - pub const IBC_BURN: &str = - "ibc::IBC Burn Address "; - pub const IBC_MINT: &str = - "ibc::IBC Mint Address "; pub const ETH_BRIDGE: &str = "ano::ETH Bridge Address "; pub const ETH_BRIDGE_POOL: &str = "ano::ETH Bridge Pool Address "; pub const REPLAY_PROTECTION: &str = "ano::Replay Protection "; + pub const MULTITOKEN: &str = + "ano::Multitoken "; + pub const MINT: &str = + "ano::Multitoken Mint Address "; } /// Fixed-length address strings prefix for established addresses. @@ -233,11 +229,6 @@ impl Address { InternalAddress::IbcToken(hash) => { format!("{}::{}", PREFIX_IBC, hash) } - InternalAddress::IbcEscrow => { - internal::IBC_ESCROW.to_string() - } - InternalAddress::IbcBurn => internal::IBC_BURN.to_string(), - InternalAddress::IbcMint => internal::IBC_MINT.to_string(), InternalAddress::EthBridge => { internal::ETH_BRIDGE.to_string() } @@ -247,6 +238,10 @@ impl Address { InternalAddress::ReplayProtection => { internal::REPLAY_PROTECTION.to_string() } + InternalAddress::Multitoken => { + internal::MULTITOKEN.to_string() + } + InternalAddress::Mint => internal::MINT.to_string(), }; debug_assert_eq!(string.len(), FIXED_LEN_STRING_BYTES); string @@ -320,6 +315,10 @@ impl Address { internal::REPLAY_PROTECTION => { Ok(Address::Internal(InternalAddress::ReplayProtection)) } + internal::MULTITOKEN => { + Ok(Address::Internal(InternalAddress::Multitoken)) + } + internal::MINT => Ok(Address::Internal(InternalAddress::Mint)), _ => Err(Error::new( ErrorKind::InvalidData, "Invalid internal address", @@ -327,15 +326,6 @@ impl Address { }, Some((PREFIX_IBC, raw)) => match string { internal::IBC => Ok(Address::Internal(InternalAddress::Ibc)), - internal::IBC_ESCROW => { - Ok(Address::Internal(InternalAddress::IbcEscrow)) - } - internal::IBC_BURN => { - Ok(Address::Internal(InternalAddress::IbcBurn)) - } - internal::IBC_MINT => { - Ok(Address::Internal(InternalAddress::IbcMint)) - } _ if raw.len() == HASH_HEX_LEN => Ok(Address::Internal( InternalAddress::IbcToken(raw.to_string()), )), @@ -532,12 +522,6 @@ pub enum InternalAddress { Ibc, /// IBC-related token IbcToken(String), - /// Escrow for IBC token transfer - IbcEscrow, - /// Burn tokens with IBC token transfer - IbcBurn, - /// Mint tokens from this address with IBC token transfer - IbcMint, /// Governance address Governance, /// SlashFund address for governance @@ -548,22 +532,10 @@ pub enum InternalAddress { EthBridgePool, /// Replay protection contains transactions' hash ReplayProtection, -} - -impl InternalAddress { - /// Get an IBC token address from the port ID and channel ID - pub fn ibc_token_address( - port_id: String, - channel_id: String, - token: &Address, - ) -> Self { - let mut hasher = Sha256::new(); - let s = format!("{}/{}/{}", port_id, channel_id, token); - hasher.update(&s); - let hash = - format!("{:.width$x}", hasher.finalize(), width = HASH_HEX_LEN); - InternalAddress::IbcToken(hash) - } + /// Multitoken + Multitoken, + /// Minted multitoken address + Mint, } impl Display for InternalAddress { @@ -579,12 +551,11 @@ impl Display for InternalAddress { Self::SlashFund => "SlashFund".to_string(), Self::Ibc => "IBC".to_string(), Self::IbcToken(hash) => format!("IbcToken: {}", hash), - Self::IbcEscrow => "IbcEscrow".to_string(), - Self::IbcBurn => "IbcBurn".to_string(), - Self::IbcMint => "IbcMint".to_string(), Self::EthBridge => "EthBridge".to_string(), Self::EthBridgePool => "EthBridgePool".to_string(), Self::ReplayProtection => "ReplayProtection".to_string(), + Self::Multitoken => "Multitoken".to_string(), + Self::Mint => "Mint".to_string(), } ) } @@ -875,30 +846,26 @@ pub mod testing { InternalAddress::Parameters => {} InternalAddress::Ibc => {} InternalAddress::IbcToken(_) => {} - InternalAddress::IbcEscrow => {} - InternalAddress::IbcBurn => {} - InternalAddress::IbcMint => {} InternalAddress::EthBridge => {} InternalAddress::EthBridgePool => {} - InternalAddress::ReplayProtection => {} /* Add new addresses in - * the - * `prop_oneof` below. */ + InternalAddress::ReplayProtection => {} + InternalAddress::Multitoken => {} + InternalAddress::Mint => {} /* Add new addresses in the + * `prop_oneof` below. */ }; prop_oneof![ Just(InternalAddress::PoS), Just(InternalAddress::PosSlashPool), Just(InternalAddress::Ibc), Just(InternalAddress::Parameters), - Just(InternalAddress::Ibc), arb_ibc_token(), - Just(InternalAddress::IbcEscrow), - Just(InternalAddress::IbcBurn), - Just(InternalAddress::IbcMint), Just(InternalAddress::Governance), Just(InternalAddress::SlashFund), Just(InternalAddress::EthBridge), Just(InternalAddress::EthBridgePool), - Just(InternalAddress::ReplayProtection) + Just(InternalAddress::ReplayProtection), + Just(InternalAddress::Multitoken), + Just(InternalAddress::Mint) ] } diff --git a/core/src/types/token.rs b/core/src/types/token.rs index b7c731e0e1..2e12e06220 100644 --- a/core/src/types/token.rs +++ b/core/src/types/token.rs @@ -15,7 +15,9 @@ use super::dec::POS_DECIMAL_PRECISION; use crate::ibc::applications::transfer::Amount as IbcAmount; use crate::ledger::storage_api::token::read_denom; use crate::ledger::storage_api::{self, StorageRead}; -use crate::types::address::{masp, Address, DecodeError as AddressError}; +use crate::types::address::{ + masp, Address, DecodeError as AddressError, InternalAddress, +}; use crate::types::dec::Dec; use crate::types::hash::Hash; use crate::types::storage; @@ -767,6 +769,8 @@ impl TryFrom for Amount { 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 head shielded transaction pointer keys pub const HEAD_TX_KEY: &str = "head-tx"; /// Key segment prefix for shielded transaction key @@ -841,23 +845,37 @@ pub fn balance_prefix(token_addr: &Address) -> Key { .expect("Cannot obtain a storage key") } -/// Obtain a storage key prefix for multitoken balances. -pub fn multitoken_balance_prefix( - token_addr: &Address, - sub_prefix: &Key, -) -> Key { - Key::from(token_addr.to_db_key()).join(sub_prefix) -} - /// Obtain a storage key for user's multitoken balance. -pub fn multitoken_balance_key(prefix: &Key, owner: &Address) -> Key { - prefix +pub fn multitoken_balance_key(prefix: &Address, owner: &Address) -> Key { + Key::from(Address::Internal(InternalAddress::Multitoken).to_db_key()) + .push(prefix) + .expect("Cannot obtain a storage key") .push(&BALANCE_STORAGE_KEY.to_owned()) .expect("Cannot obtain a storage key") .push(&owner.to_db_key()) .expect("Cannot obtain a storage key") } +/// Obtain a storage key for the multitoken minter. +pub fn multitoken_minter_key(prefix: &Address) -> Key { + Key::from(Address::Internal(InternalAddress::Multitoken).to_db_key()) + .push(&MINTER_STORAGE_KEY.to_owned()) + .expect("Cannot obtain a storage key") + .push(prefix) + .expect("Cannot obtain a storage key") +} + +/// Obtain a storage key for the minted multitoken balance. +pub fn multitoken_minted_key(prefix: &Address) -> Key { + Key::from(Address::Internal(InternalAddress::Multitoken).to_db_key()) + .push(prefix) + .expect("Cannot obtain a storage key") + .push(&BALANCE_STORAGE_KEY.to_owned()) + .expect("Cannot obtain a storage key") + .push(&Address::Internal(InternalAddress::Mint).to_db_key()) + .expect("Cannot obtain a storage key") +} + /// Check if the given storage key is balance key for the given token. If it is, /// returns the owner. pub fn is_balance_key<'a>( @@ -934,56 +952,16 @@ pub fn is_total_supply_key(key: &Key, token_address: &Address) -> bool { /// Check if the given storage key is multitoken balance key for the given /// token. If it is, returns the sub prefix and the owner. -pub fn is_multitoken_balance_key<'a>( - token_addr: &Address, - key: &'a Key, -) -> Option<(Key, &'a Address)> { - match key.segments.first() { - Some(DbKeySeg::AddressSeg(addr)) if addr == token_addr => { - multitoken_balance_owner(key) - } - _ => None, - } -} - -/// Check if the given storage key is multitoken balance key for unspecified -/// token. If it is, returns the sub prefix and the token and owner addresses. -pub fn is_any_multitoken_balance_key( - key: &Key, -) -> Option<(Key, [&Address; 2])> { - match key.segments.first() { - Some(DbKeySeg::AddressSeg(token)) => multitoken_balance_owner(key) - .map(|(sub, owner)| (sub, [token, owner])), - _ => None, - } -} - -/// Check if the given storage key is token or multitoken balance key for -/// unspecified token. If it is, returns the token and owner addresses. -pub fn is_any_token_or_multitoken_balance_key( - key: &Key, -) -> Option<[&Address; 2]> { - is_any_multitoken_balance_key(key) - .map(|a| a.1) - .or_else(|| is_any_token_balance_key(key)) -} - -fn multitoken_balance_owner(key: &Key) -> Option<(Key, &Address)> { - let len = key.segments.len(); - if len < 4 { - // the key of a multitoken should have 1 or more segments other than - // token, balance, owner - return None; - } +pub fn is_multitoken_balance_key(key: &Key) -> Option<(&Address, &Address)> { match &key.segments[..] { [ - .., + DbKeySeg::AddressSeg(addr), + DbKeySeg::AddressSeg(sub_prefix), DbKeySeg::StringSeg(balance), DbKeySeg::AddressSeg(owner), - ] if balance == BALANCE_STORAGE_KEY => { - let sub_prefix = Key { - segments: key.segments[1..(len - 2)].to_vec(), - }; + ] if *addr == Address::Internal(InternalAddress::Multitoken) + && balance == BALANCE_STORAGE_KEY => + { Some((sub_prefix, owner)) } _ => None, @@ -1012,7 +990,7 @@ pub struct Transfer { /// Token's address pub token: Address, /// Source token's sub prefix - pub sub_prefix: Option, + pub sub_prefix: Option
, /// The amount of tokens pub amount: DenominatedAmount, /// The unused storage location at which to place TxId diff --git a/shared/src/ledger/ibc/vp/context.rs b/shared/src/ledger/ibc/vp/context.rs index 0da78bba53..cee112854e 100644 --- a/shared/src/ledger/ibc/vp/context.rs +++ b/shared/src/ledger/ibc/vp/context.rs @@ -2,7 +2,7 @@ use std::collections::{BTreeSet, HashMap, HashSet}; -use borsh::{BorshDeserialize, BorshSerialize}; +use borsh::BorshSerialize; use namada_core::ledger::ibc::storage::is_ibc_key; use namada_core::ledger::ibc::{IbcCommonContext, IbcStorageContext}; use namada_core::ledger::storage::write_log::StorageModification; @@ -12,7 +12,7 @@ use namada_core::types::address::{Address, InternalAddress}; use namada_core::types::ibc::IbcEvent; use namada_core::types::storage::{BlockHeight, Header, Key}; use namada_core::types::token::{ - is_any_token_balance_key, is_any_token_or_multitoken_balance_key, Amount, + self, is_any_token_balance_key, is_any_token_or_multitoken_balance_key, Amount, }; use super::Error; @@ -117,50 +117,111 @@ where fn transfer_token( &mut self, - src: &Key, - dest: &Key, + src: &Address, + dest: &Address, + token: &Address, + sub_prefix: Option
, amount: Amount, ) -> Result<(), Self::Error> { - let src_owner = is_any_token_or_multitoken_balance_key(src); - let mut src_bal = match src_owner { - Some([_, Address::Internal(InternalAddress::IbcMint)]) => { - Amount::max() - } - Some([_, Address::Internal(InternalAddress::IbcBurn)]) => { - unreachable!("Invalid transfer from IBC burn address") - } - _ => match self.read(src)? { - Some(v) => { - Amount::try_from_slice(&v[..]).map_err(Error::Decoding)? - } - None => unreachable!("The source has no balance"), - }, + let src_key = match &sub_prefix { + Some(sub_prefix) => token::multitoken_balance_key(sub_prefix, src), + None => token::balance_key(token, src), }; - src_bal.spend(&amount); - let dest_owner = is_any_token_balance_key(dest); - let mut dest_bal = match dest_owner { - Some([_, Address::Internal(InternalAddress::IbcMint)]) => { - unreachable!("Invalid transfer to IBC mint address") - } - _ => match self.read(dest)? { - Some(v) => { - Amount::try_from_slice(&v[..]).map_err(Error::Decoding)? - } - None => Amount::default(), - }, + let dest_key = match &sub_prefix { + Some(sub_prefix) => token::multitoken_balance_key(sub_prefix, dest), + None => token::balance_key(token, dest), }; + let src_bal: Option = + self.ctx.read(&src_key).map_err(Error::NativeVpError)?; + let mut src_bal = src_bal.expect("The source has no balance"); + src_bal.spend(&amount); + let mut dest_bal: Amount = self + .ctx + .read(&dest_key) + .map_err(Error::NativeVpError)? + .unwrap_or_default(); dest_bal.receive(&amount); self.write( - src, + &src_key, src_bal.try_to_vec().expect("encoding shouldn't failed"), )?; self.write( - dest, + &dest_key, dest_bal.try_to_vec().expect("encoding shouldn't failed"), + ) + } + + fn mint_token( + &mut self, + target: &Address, + sub_prefix: &Address, + amount: Amount, + ) -> Result<(), Self::Error> { + let target_key = token::multitoken_balance_key(sub_prefix, target); + let mut target_bal: Amount = self + .ctx + .read(&target_key) + .map_err(Error::NativeVpError)? + .unwrap_or_default(); + target_bal.receive(&amount); + + let minted_key = token::multitoken_minted_key(sub_prefix); + let mut minted_bal: Amount = self + .ctx + .read(&minted_key) + .map_err(Error::NativeVpError)? + .unwrap_or_default(); + minted_bal.receive(&amount); + + self.write( + &target_key, + target_bal.try_to_vec().expect("encoding shouldn't failed"), + )?; + self.write( + &minted_key, + minted_bal.try_to_vec().expect("encoding shouldn't failed"), )?; - Ok(()) + let minter_key = token::multitoken_minter_key(sub_prefix); + self.write( + &minter_key, + Address::Internal(InternalAddress::Ibc) + .try_to_vec() + .expect("encoding shouldn't failed"), + ) + } + + fn burn_token( + &mut self, + target: &Address, + sub_prefix: &Address, + amount: Amount, + ) -> Result<(), Self::Error> { + let target_key = token::multitoken_balance_key(sub_prefix, target); + let mut target_bal: Amount = self + .ctx + .read(&target_key) + .map_err(Error::NativeVpError)? + .unwrap_or_default(); + target_bal.spend(&amount); + + let minted_key = token::multitoken_minted_key(sub_prefix); + let mut minted_bal: Amount = self + .ctx + .read(&minted_key) + .map_err(Error::NativeVpError)? + .unwrap_or_default(); + minted_bal.spend(&amount); + + self.write( + &target_key, + target_bal.try_to_vec().expect("encoding shouldn't failed"), + )?; + self.write( + &minted_key, + minted_bal.try_to_vec().expect("encoding shouldn't failed"), + ) } /// Get the current height of this chain @@ -254,16 +315,35 @@ where unimplemented!("Validation doesn't emit an event") } - /// Transfer token fn transfer_token( &mut self, - _src: &Key, - _dest: &Key, + _src: &Address, + _dest: &Address, + _token: &Address, + _sub_prefix: Option
, _amount: Amount, ) -> Result<(), Self::Error> { unimplemented!("Validation doesn't transfer") } + fn mint_token( + &mut self, + _target: &Address, + _sub_prefix: &Address, + _amount: Amount, + ) -> Result<(), Self::Error> { + unimplemented!("Validation doesn't mint") + } + + fn burn_token( + &mut self, + _target: &Address, + _sub_prefix: &Address, + _amount: Amount, + ) -> Result<(), Self::Error> { + unimplemented!("Validation doesn't burn") + } + fn get_height(&self) -> Result { self.ctx.get_block_height().map_err(Error::NativeVpError) } diff --git a/shared/src/ledger/ibc/vp/mod.rs b/shared/src/ledger/ibc/vp/mod.rs index f5625112ff..619e484ba2 100644 --- a/shared/src/ledger/ibc/vp/mod.rs +++ b/shared/src/ledger/ibc/vp/mod.rs @@ -1,7 +1,6 @@ //! IBC integration as a native validity predicate mod context; -mod token; use std::cell::RefCell; use std::collections::{BTreeSet, HashSet}; @@ -19,7 +18,6 @@ use namada_core::types::address::{Address, InternalAddress}; use namada_core::types::storage::Key; use namada_proof_of_stake::read_pos_params; use thiserror::Error; -pub use token::{Error as IbcTokenError, IbcToken}; use crate::ledger::ibc::storage::{calc_hash, is_ibc_denom_key, is_ibc_key}; use crate::ledger::native_vp::{self, Ctx, NativeVp, VpEnv}; @@ -2458,7 +2456,7 @@ mod tests { .expect("write failed"); // init the escrow balance let balance_key = - balance_key(&nam(), &Address::Internal(InternalAddress::IbcEscrow)); + balance_key(&nam(), &Address::Internal(InternalAddress::Ibc)); let amount = Amount::native_whole(100); wl_storage .write_log @@ -2607,7 +2605,7 @@ mod tests { .expect("write failed"); // init the escrow balance let balance_key = - balance_key(&nam(), &Address::Internal(InternalAddress::IbcEscrow)); + balance_key(&nam(), &Address::Internal(InternalAddress::Ibc)); let amount = Amount::native_whole(100); wl_storage .write_log diff --git a/shared/src/ledger/native_vp/mod.rs b/shared/src/ledger/native_vp/mod.rs index dcc0432ea7..c7d58f0563 100644 --- a/shared/src/ledger/native_vp/mod.rs +++ b/shared/src/ledger/native_vp/mod.rs @@ -3,6 +3,7 @@ pub mod ethereum_bridge; pub mod governance; +pub mod multitoken; pub mod parameters; pub mod replay_protection; pub mod slash_fund; diff --git a/shared/src/ledger/protocol/mod.rs b/shared/src/ledger/protocol/mod.rs index 8d360a3bd5..a71c2d0cae 100644 --- a/shared/src/ledger/protocol/mod.rs +++ b/shared/src/ledger/protocol/mod.rs @@ -8,10 +8,11 @@ use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use thiserror::Error; use crate::ledger::gas::{self, BlockGasMeter, VpGasMeter}; -use crate::ledger::ibc::vp::{Ibc, IbcToken}; +use crate::ledger::ibc::vp::Ibc; use crate::ledger::native_vp::ethereum_bridge::bridge_pool_vp::BridgePoolVp; use crate::ledger::native_vp::ethereum_bridge::vp::EthBridge; use crate::ledger::native_vp::governance::GovernanceVp; +use crate::ledger::native_vp::multitoken::MultitokenVp; use crate::ledger::native_vp::parameters::{self, ParametersVp}; use crate::ledger::native_vp::replay_protection::ReplayProtectionVp; use crate::ledger::native_vp::slash_fund::SlashFundVp; @@ -58,7 +59,7 @@ pub enum Error { #[error("Parameters native VP: {0}")] ParametersNativeVpError(parameters::Error), #[error("IBC Token native VP: {0}")] - IbcTokenNativeVpError(crate::ledger::ibc::vp::IbcTokenError), + MultitokenNativeVpError(crate::ledger::native_vp::multitoken::Error), #[error("Governance native VP error: {0}")] GovernanceNativeVpError(crate::ledger::native_vp::governance::Error), #[error("SlashFund native VP error: {0}")] @@ -550,16 +551,14 @@ where gas_meter = slash_fund.ctx.gas_meter.into_inner(); result } - InternalAddress::IbcToken(_) - | InternalAddress::IbcEscrow - | InternalAddress::IbcBurn - | InternalAddress::IbcMint => { - // validate the transfer - let ibc_token = IbcToken { ctx }; - let result = ibc_token + InternalAddress::Multitoken + | InternalAddress::IbcToken(_) + | InternalAddress::Mint => { + let multitoken = MultitokenVp { ctx }; + let result = multitoken .validate_tx(tx, &keys_changed, &verifiers) - .map_err(Error::IbcTokenNativeVpError); - gas_meter = ibc_token.ctx.gas_meter.into_inner(); + .map_err(Error::MultitokenNativeVpError); + gas_meter = multitoken.ctx.gas_meter.into_inner(); result } InternalAddress::EthBridge => { diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index 36644a3e14..6b11cac281 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -1145,13 +1145,17 @@ pub async fn build_ibc_transfer< // Check source balance let (sub_prefix, balance_key) = match args.sub_prefix { - Some(sub_prefix) => { - let sub_prefix = storage::Key::parse(sub_prefix).unwrap(); - let prefix = token::multitoken_balance_prefix(&token, &sub_prefix); - ( - Some(sub_prefix), - token::multitoken_balance_key(&prefix, &source), - ) + Some(sp) => { + let sub_prefix = Address::decode(&sp).map_err(|e| { + Error::Other(format!( + "The sub_prefix was not an Address: sub_prefix {}, error \ + {}", + sp, e + )) + })?; + let balance_key = + token::multitoken_balance_key(&sub_prefix, &source); + (Some(sub_prefix), balance_key) } None => (None, token::balance_key(&token, &source)), }; @@ -1334,13 +1338,17 @@ pub async fn build_transfer< token_exists_or_err(token.clone(), args.tx.force, client).await?; // Check source balance let (sub_prefix, balance_key) = match &args.sub_prefix { - Some(ref sub_prefix) => { - let sub_prefix = storage::Key::parse(sub_prefix).unwrap(); - let prefix = token::multitoken_balance_prefix(&token, &sub_prefix); - ( - Some(sub_prefix), - token::multitoken_balance_key(&prefix, &source), - ) + Some(sp) => { + let sub_prefix = Address::decode(sp).map_err(|e| { + Error::Other(format!( + "The sub_prefix was not an Address: sub_prefix {}, error \ + {}", + sp, e + )) + })?; + let balance_key = + token::multitoken_balance_key(&sub_prefix, &source); + (Some(sub_prefix), balance_key) } None => (None, token::balance_key(&token, &source)), }; diff --git a/tests/src/e2e/ibc_tests.rs b/tests/src/e2e/ibc_tests.rs index e071c2bd13..15356d3f71 100644 --- a/tests/src/e2e/ibc_tests.rs +++ b/tests/src/e2e/ibc_tests.rs @@ -72,7 +72,7 @@ use namada::ledger::storage::ics23_specs::ibc_proof_specs; use namada::ledger::storage::traits::Sha256Hasher; use namada::types::address::{Address, InternalAddress}; use namada::types::key::PublicKey; -use namada::types::storage::{BlockHeight, Key, RESERVED_ADDRESS_PREFIX}; +use namada::types::storage::{BlockHeight, Key}; use namada::types::token::Amount; use namada_apps::client::rpc::{ query_storage_value, query_storage_value_bytes, @@ -776,11 +776,7 @@ fn transfer_received_token( "{}/{}/{}", port_channel_id.port_id, port_channel_id.channel_id, xan ); - let sub_prefix = ibc_token_prefix(denom) - .unwrap() - .sub_key() - .unwrap() - .to_string(); + let ibc_token = ibc_token(denom).to_string(); let rpc = get_actor_rpc(test, &Who::Validator(0)); let amount = Amount::native_whole(50000).to_string_native(); @@ -793,7 +789,7 @@ fn transfer_received_token( "--token", NAM, "--sub-prefix", - &sub_prefix, + &ibc_token, "--amount", &amount, "--gas-amount", @@ -828,13 +824,7 @@ fn transfer_back( "{}/{}/{}", port_channel_id_b.port_id, port_channel_id_b.channel_id, xan ); - let hash = calc_hash(denom_raw); - let ibc_token = Address::Internal(InternalAddress::IbcToken(hash)); - // Need the address prefix for ibc-transfer command - let sub_prefix = format!( - "{}/{}{}", - MULTITOKEN_STORAGE_KEY, RESERVED_ADDRESS_PREFIX, ibc_token - ); + let ibc_token = ibc_token(denom_raw).to_string(); // Send a token from Chain B let height = transfer( test_b, @@ -843,7 +833,7 @@ fn transfer_back( NAM, &Amount::native_whole(50000), port_channel_id_b, - Some(sub_prefix), + Some(ibc_token), None, )?; let packet = match get_event(test_b, height)? { @@ -1272,7 +1262,7 @@ fn check_balances( // Check the escrowed balance let expected = format!( ": 100000, owned by {}", - Address::Internal(InternalAddress::IbcEscrow) + Address::Internal(InternalAddress::Ibc) ); client.exp_string(&expected)?; // Check the source balance @@ -1285,8 +1275,7 @@ fn check_balances( "{}/{}/{}", &dest_port_channel_id.port_id, &dest_port_channel_id.channel_id, &token, ); - let key_prefix = ibc_token_prefix(denom)?; - let sub_prefix = key_prefix.sub_key().unwrap().to_string(); + let ibc_token = ibc_token(denom).to_string(); let rpc_b = get_actor_rpc(test_b, &Who::Validator(0)); let query_args = vec![ "balance", @@ -1295,11 +1284,11 @@ fn check_balances( "--token", NAM, "--sub-prefix", - &sub_prefix, + &ibc_token, "--node", &rpc_b, ]; - let expected = format!("nam with {}: 100000", sub_prefix); + let expected = format!("nam with {}: 100000", ibc_token); let mut client = run!(test_b, Bin::Client, query_args, Some(40))?; client.exp_string(&expected)?; client.assert_success(); @@ -1317,8 +1306,7 @@ fn check_balances_after_non_ibc( "{}/{}/{}", port_channel_id.port_id, port_channel_id.channel_id, token ); - let key_prefix = ibc_token_prefix(denom)?; - let sub_prefix = key_prefix.sub_key().unwrap().to_string(); + let ibc_token = ibc_token(denom).to_string(); // Check the source let rpc = get_actor_rpc(test, &Who::Validator(0)); @@ -1329,11 +1317,11 @@ fn check_balances_after_non_ibc( "--token", NAM, "--sub-prefix", - &sub_prefix, + &ibc_token, "--node", &rpc, ]; - let expected = format!("nam with {}: 50000", sub_prefix); + let expected = format!("nam with {}: 50000", ibc_token); let mut client = run!(test, Bin::Client, query_args, Some(40))?; client.exp_string(&expected)?; client.assert_success(); @@ -1346,11 +1334,11 @@ fn check_balances_after_non_ibc( "--token", NAM, "--sub-prefix", - &sub_prefix, + &ibc_token, "--node", &rpc, ]; - let expected = format!("nam with {}: 50000", sub_prefix); + let expected = format!("nam with {}: 50000", ibc_token); let mut client = run!(test, Bin::Client, query_args, Some(40))?; client.exp_string(&expected)?; client.assert_success(); @@ -1373,7 +1361,7 @@ fn check_balances_after_back( // Check the escrowed balance let expected = format!( ": 50000, owned by {}", - Address::Internal(InternalAddress::IbcEscrow) + Address::Internal(InternalAddress::Ibc) ); client.exp_string(&expected)?; // Check the source balance @@ -1386,8 +1374,7 @@ fn check_balances_after_back( "{}/{}/{}", &dest_port_channel_id.port_id, &dest_port_channel_id.channel_id, &token, ); - let key_prefix = ibc_token_prefix(denom)?; - let sub_prefix = key_prefix.sub_key().unwrap().to_string(); + let ibc_token = ibc_token(denom).to_string(); let rpc_b = get_actor_rpc(test_b, &Who::Validator(0)); let query_args = vec![ "balance", @@ -1396,11 +1383,11 @@ fn check_balances_after_back( "--token", NAM, "--sub-prefix", - &sub_prefix, + &ibc_token, "--node", &rpc_b, ]; - let expected = format!("nam with {}: 0", sub_prefix); + let expected = format!("nam with {}: 0", ibc_token); let mut client = run!(test_b, Bin::Client, query_args, Some(40))?; client.exp_string(&expected)?; client.assert_success(); diff --git a/tests/src/vm_host_env/ibc.rs b/tests/src/vm_host_env/ibc.rs index b73fef0a3d..e3cbce8f30 100644 --- a/tests/src/vm_host_env/ibc.rs +++ b/tests/src/vm_host_env/ibc.rs @@ -60,13 +60,14 @@ pub use namada::ledger::ibc::storage::{ ack_key, channel_counter_key, channel_key, client_counter_key, client_state_key, client_type_key, client_update_height_key, client_update_timestamp_key, commitment_key, connection_counter_key, - connection_key, consensus_state_key, ibc_token_prefix, - next_sequence_ack_key, next_sequence_recv_key, next_sequence_send_key, - port_key, receipt_key, + connection_key, consensus_state_key, ibc_token, next_sequence_ack_key, + next_sequence_recv_key, next_sequence_send_key, port_key, receipt_key, }; use namada::ledger::ibc::vp::{ get_dummy_genesis_validator, get_dummy_header as tm_dummy_header, Ibc, - IbcToken, +}; +use namada::ledger::native_vp::multitoken::{ + Error as MultitokenVpError, MultitokenVp, }; use namada::ledger::native_vp::{Ctx, NativeVp}; use namada::ledger::parameters::storage::{ @@ -115,19 +116,20 @@ impl<'a> TestIbcVp<'a> { } } -pub struct TestIbcTokenVp<'a> { - pub token: IbcToken<'a, MockDB, Sha256Hasher, WasmCacheRwAccess>, +pub struct TestMultitokenVp<'a> { + pub multitoken_vp: + MultitokenVp<'a, MockDB, Sha256Hasher, WasmCacheRwAccess>, } -impl<'a> TestIbcTokenVp<'a> { +impl<'a> TestMultitokenVp<'a> { pub fn validate( &self, - tx_data: &Tx, - ) -> std::result::Result { - self.token.validate_tx( - tx_data, - self.token.ctx.keys_changed, - self.token.ctx.verifiers, + tx: &Tx, + ) -> std::result::Result { + self.multitoken_vp.validate_tx( + tx, + self.multitoken_vp.ctx.keys_changed, + self.multitoken_vp.ctx.verifiers, ) } } @@ -168,11 +170,11 @@ pub fn validate_ibc_vp_from_tx<'a>( } /// Validate the native token VP for the given address -pub fn validate_token_vp_from_tx<'a>( +pub fn validate_multitoken_vp_from_tx<'a>( tx_env: &'a TestTxEnv, tx: &'a Tx, target: &Key, -) -> std::result::Result { +) -> std::result::Result { let (verifiers, keys_changed) = tx_env .wl_storage .write_log @@ -198,9 +200,9 @@ pub fn validate_token_vp_from_tx<'a>( &verifiers, vp_wasm_cache, ); - let token = IbcToken { ctx }; + let multitoken_vp = MultitokenVp { ctx }; - TestIbcTokenVp { token }.validate(tx) + TestMultitokenVp { multitoken_vp }.validate(tx) } /// Initialize the test storage. Requires initialized [`tx_host_env::ENV`]. @@ -762,6 +764,6 @@ pub fn packet_from_message( } pub fn balance_key_with_ibc_prefix(denom: String, owner: &Address) -> Key { - let prefix = ibc_token_prefix(denom).expect("invalid denom"); - token::multitoken_balance_key(&prefix, owner) + let ibc_token = ibc_token(denom); + token::multitoken_balance_key(&ibc_token, owner) } diff --git a/tests/src/vm_host_env/mod.rs b/tests/src/vm_host_env/mod.rs index 81192796a2..13d371ce0c 100644 --- a/tests/src/vm_host_env/mod.rs +++ b/tests/src/vm_host_env/mod.rs @@ -28,6 +28,8 @@ mod tests { }; use namada::ledger::tx_env::TxEnv; use namada::proto::{Code, Data, Section, Signature, Tx}; + use namada::types::address::{Address, InternalAddress}; + use namada::types::chain::ChainId; use namada::types::hash::Hash; use namada::types::key::*; use namada::types::storage::{self, BlockHash, BlockHeight, Key, KeySeg}; @@ -1110,10 +1112,10 @@ mod tests { // Check if the token was escrowed let escrow = token::balance_key( &token, - &address::Address::Internal(address::InternalAddress::IbcEscrow), + &address::Address::Internal(address::InternalAddress::Ibc), ); let token_vp_result = - ibc::validate_token_vp_from_tx(&env, &tx, &escrow); + ibc::validate_multitoken_vp_from_tx(&env, &tx, &escrow); assert!(token_vp_result.expect("token validation failed unexpectedly")); // Commit @@ -1165,7 +1167,7 @@ mod tests { assert_eq!(balance, Some(Amount::native_whole(0))); let escrow_key = token::balance_key( &token, - &address::Address::Internal(address::InternalAddress::IbcEscrow), + &address::Address::Internal(address::InternalAddress::Ibc), ); let escrow: Option = tx_host_env::with(|env| { env.wl_storage.read(&escrow_key).expect("read error") @@ -1188,13 +1190,25 @@ mod tests { writes.extend(channel_writes); // the origin-specific token let denom = format!("{}/{}/{}", port_id, channel_id, token); - let key_prefix = ibc_storage::ibc_token_prefix(&denom).unwrap(); - let balance_key = token::multitoken_balance_key(&key_prefix, &sender); + let ibc_token = ibc_storage::ibc_token(&denom); + let balance_key = token::multitoken_balance_key(&ibc_token, &sender); let init_bal = Amount::native_whole(100); writes.insert(balance_key.clone(), init_bal.try_to_vec().unwrap()); + let minted_key = token::multitoken_balance_key( + &ibc_token, + &Address::Internal(InternalAddress::Mint), + ); + writes.insert(minted_key.clone(), init_bal.try_to_vec().unwrap()); + let minter_key = token::multitoken_minter_key(&ibc_token); + writes.insert( + minter_key, + Address::Internal(InternalAddress::Ibc) + .try_to_vec() + .unwrap(), + ); // original denom let hash = ibc_storage::calc_hash(&denom); - let denom_key = ibc_storage::ibc_denom_key(&hash); + let denom_key = ibc_storage::ibc_denom_key(hash); writes.insert(denom_key, denom.try_to_vec().unwrap()); writes.into_iter().for_each(|(key, val)| { tx_host_env::with(|env| { @@ -1207,11 +1221,7 @@ mod tests { // Start a transaction to send a packet // Set this chain is the sink zone - let ibc_token = address::Address::Internal( - address::InternalAddress::IbcToken(hash), - ); - let hashed_denom = - format!("{}/{}", ibc_storage::MULTITOKEN_STORAGE_KEY, ibc_token); + let hashed_denom = ibc_token.to_string(); let msg = ibc::msg_transfer(port_id, channel_id, hashed_denom, &sender); let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); @@ -1233,11 +1243,8 @@ mod tests { let result = ibc::validate_ibc_vp_from_tx(&env, &tx); assert!(result.expect("validation failed unexpectedly")); // Check if the token was burned - let burn = token::balance_key( - &token, - &address::Address::Internal(address::InternalAddress::IbcBurn), - ); - let result = ibc::validate_token_vp_from_tx(&env, &tx, &burn); + let result = + ibc::validate_multitoken_vp_from_tx(&env, &tx, &minted_key); assert!(result.expect("token validation failed unexpectedly")); // Check the balance tx_host_env::set(env); @@ -1245,14 +1252,10 @@ mod tests { env.wl_storage.read(&balance_key).expect("read error") }); assert_eq!(balance, Some(Amount::native_whole(0))); - let burn_key = token::balance_key( - &token, - &address::Address::Internal(address::InternalAddress::IbcBurn), - ); - let burn: Option = tx_host_env::with(|env| { - env.wl_storage.read(&burn_key).expect("read error") + let minted: Option = tx_host_env::with(|env| { + env.wl_storage.read(&minted_key).expect("read error") }); - assert_eq!(burn, Some(Amount::native_whole(100))); + assert_eq!(minted, Some(Amount::native_whole(0))); } #[test] @@ -1309,20 +1312,23 @@ mod tests { let result = ibc::validate_ibc_vp_from_tx(&env, &tx); assert!(result.expect("validation failed unexpectedly")); // Check if the token was minted - let mint = token::balance_key( - &token, - &address::Address::Internal(address::InternalAddress::IbcMint), - ); - let result = ibc::validate_token_vp_from_tx(&env, &tx, &mint); + let denom = format!("{}/{}/{}", port_id, channel_id, token); + let ibc_token = ibc::ibc_token(&denom); + let minted_key = token::multitoken_minted_key(&ibc_token); + let result = + ibc::validate_multitoken_vp_from_tx(&env, &tx, &minted_key); assert!(result.expect("token validation failed unexpectedly")); // Check the balance tx_host_env::set(env); - let denom = format!("{}/{}/{}", port_id, channel_id, token); let key = ibc::balance_key_with_ibc_prefix(denom, &receiver); let balance: Option = tx_host_env::with(|env| { env.wl_storage.read(&key).expect("read error") }); assert_eq!(balance, Some(Amount::native_whole(100))); + let minted: Option = tx_host_env::with(|env| { + env.wl_storage.read(&minted_key).expect("read error") + }); + assert_eq!(minted, Some(Amount::native_whole(100))); } #[test] @@ -1349,7 +1355,7 @@ mod tests { // escrow in advance let escrow_key = token::balance_key( &token, - &address::Address::Internal(address::InternalAddress::IbcEscrow), + &address::Address::Internal(address::InternalAddress::Ibc), ); let val = Amount::native_whole(100).try_to_vec().unwrap(); tx_host_env::with(|env| { @@ -1397,7 +1403,8 @@ mod tests { let result = ibc::validate_ibc_vp_from_tx(&env, &tx); assert!(result.expect("validation failed unexpectedly")); // Check if the token was unescrowed - let result = ibc::validate_token_vp_from_tx(&env, &tx, &escrow_key); + let result = + ibc::validate_multitoken_vp_from_tx(&env, &tx, &escrow_key); assert!(result.expect("token validation failed unexpectedly")); // Check the balance tx_host_env::set(env); @@ -1434,9 +1441,13 @@ mod tests { }); }); // escrow in advance - let escrow_key = token::balance_key( - &token, - &address::Address::Internal(address::InternalAddress::IbcEscrow), + let dummy_src_port = "dummy_transfer"; + let dummy_src_channel = "channel_42"; + let denom = + format!("{}/{}/{}", dummy_src_port, dummy_src_channel, token); + let escrow_key = ibc::balance_key_with_ibc_prefix( + denom, + &address::Address::Internal(address::InternalAddress::Ibc), ); let val = Amount::native_whole(100).try_to_vec().unwrap(); tx_host_env::with(|env| { @@ -1448,8 +1459,6 @@ mod tests { // Set this chain as the source zone let counterparty = ibc::dummy_channel_counterparty(); - let dummy_src_port = "dummy_transfer"; - let dummy_src_channel = "channel_42"; let denom = format!( "{}/{}/{}/{}/{}", counterparty.port_id().clone(), @@ -1489,7 +1498,8 @@ mod tests { let result = ibc::validate_ibc_vp_from_tx(&env, &tx); assert!(result.expect("validation failed unexpectedly")); // Check if the token was unescrowed - let result = ibc::validate_token_vp_from_tx(&env, &tx, &escrow_key); + let result = + ibc::validate_multitoken_vp_from_tx(&env, &tx, &escrow_key); assert!(result.expect("token validation failed unexpectedly")); // Check the balance tx_host_env::set(env); @@ -1587,9 +1597,9 @@ mod tests { // Check if the token was refunded let escrow = token::balance_key( &token, - &address::Address::Internal(address::InternalAddress::IbcEscrow), + &address::Address::Internal(address::InternalAddress::Ibc), ); - let result = ibc::validate_token_vp_from_tx(&env, &tx, &escrow); + let result = ibc::validate_multitoken_vp_from_tx(&env, &tx, &escrow); assert!(result.expect("token validation failed unexpectedly")); } @@ -1672,9 +1682,9 @@ mod tests { // Check if the token was refunded let escrow = token::balance_key( &token, - &address::Address::Internal(address::InternalAddress::IbcEscrow), + &address::Address::Internal(address::InternalAddress::Ibc), ); - let result = ibc::validate_token_vp_from_tx(&env, &tx, &escrow); + let result = ibc::validate_multitoken_vp_from_tx(&env, &tx, &escrow); assert!(result.expect("token validation failed unexpectedly")); } } diff --git a/tests/src/vm_host_env/tx.rs b/tests/src/vm_host_env/tx.rs index c9ab18a08a..bb66ee71b7 100644 --- a/tests/src/vm_host_env/tx.rs +++ b/tests/src/vm_host_env/tx.rs @@ -177,15 +177,11 @@ impl TestTxEnv { &mut self, target: &Address, token: &Address, - sub_prefix: Option, + sub_prefix: Option
, amount: token::Amount, ) { let storage_key = match &sub_prefix { - Some(sub_prefix) => { - let prefix = - token::multitoken_balance_prefix(token, sub_prefix); - token::multitoken_balance_key(&prefix, target) - } + Some(sp) => token::multitoken_balance_key(sp, target), None => token::balance_key(token, target), }; self.wl_storage diff --git a/tx_prelude/src/ibc.rs b/tx_prelude/src/ibc.rs index 12654df964..12747e6a15 100644 --- a/tx_prelude/src/ibc.rs +++ b/tx_prelude/src/ibc.rs @@ -9,11 +9,12 @@ pub use namada_core::ledger::ibc::{ }; use namada_core::ledger::storage_api::{StorageRead, StorageWrite}; use namada_core::ledger::tx_env::TxEnv; +use namada_core::types::address::{Address, InternalAddress}; pub use namada_core::types::ibc::IbcEvent; use namada_core::types::storage::{BlockHeight, Header, Key}; use namada_core::types::token::Amount; -use crate::token::transfer_with_keys; +use crate::token::{burn, mint, transfer}; use crate::{Ctx, KeyValIterator}; /// IBC actions to handle an IBC message @@ -72,11 +73,37 @@ impl IbcStorageContext for Ctx { fn transfer_token( &mut self, - src: &Key, - dest: &Key, + src: &Address, + dest: &Address, + token: &Address, + sub_prefix: Option
, amount: Amount, ) -> std::result::Result<(), Self::Error> { - transfer_with_keys(self, src, dest, amount) + transfer(self, src, dest, token, sub_prefix, amount, &None, &None) + } + + fn mint_token( + &mut self, + target: &Address, + sub_prefix: &Address, + amount: Amount, + ) -> Result<(), Self::Error> { + mint( + self, + &Address::Internal(InternalAddress::Ibc), + target, + sub_prefix, + amount, + ) + } + + fn burn_token( + &mut self, + target: &Address, + sub_prefix: &Address, + amount: Amount, + ) -> Result<(), Self::Error> { + burn(self, target, sub_prefix, amount) } fn get_height(&self) -> std::result::Result { diff --git a/tx_prelude/src/token.rs b/tx_prelude/src/token.rs index 685a2e51a6..e85edf6b7a 100644 --- a/tx_prelude/src/token.rs +++ b/tx_prelude/src/token.rs @@ -1,5 +1,5 @@ use masp_primitives::transaction::Transaction; -use namada_core::types::address::{Address, InternalAddress}; +use namada_core::types::address::Address; use namada_core::types::hash::Hash; use namada_core::types::storage::KeySeg; use namada_core::types::token; @@ -14,7 +14,7 @@ pub fn transfer( src: &Address, dest: &Address, token: &Address, - sub_prefix: Option, + sub_prefix: Option
, amount: DenominatedAmount, key: &Option, shielded_hash: &Option, @@ -22,70 +22,24 @@ pub fn transfer( ) -> TxResult { if amount.amount != Amount::default() { let src_key = match &sub_prefix { - Some(sub_prefix) => { - let prefix = - token::multitoken_balance_prefix(token, sub_prefix); - token::multitoken_balance_key(&prefix, src) - } + Some(sub_prefix) => token::multitoken_balance_key(sub_prefix, src), None => token::balance_key(token, src), }; let dest_key = match &sub_prefix { - Some(sub_prefix) => { - let prefix = - token::multitoken_balance_prefix(token, sub_prefix); - token::multitoken_balance_key(&prefix, dest) - } + Some(sub_prefix) => token::multitoken_balance_key(sub_prefix, dest), None => token::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)); + unreachable!() + }); + src_bal.spend(&amount.amount); + let mut dest_bal: Amount = ctx.read(&dest_key)?.unwrap_or_default(); + dest_bal.receive(&amount.amount); if src != dest { - let src_bal: Option = match src { - Address::Internal(InternalAddress::IbcMint) => { - Some(Amount::max_signed()) - } - Address::Internal(InternalAddress::IbcBurn) => { - log_string("invalid transfer from the burn address"); - unreachable!() - } - _ => ctx.read(&src_key)?, - }; - let mut src_bal = src_bal.unwrap_or_else(|| { - log_string(format!("src {} has no balance", src_key)); - unreachable!() - }); - src_bal.spend(&amount.amount); - let mut dest_bal: Amount = match dest { - Address::Internal(InternalAddress::IbcMint) => { - log_string("invalid transfer to the mint address"); - unreachable!() - } - _ => ctx.read(&dest_key)?.unwrap_or_default(), - }; - dest_bal.receive(&amount.amount); - - match src { - Address::Internal(InternalAddress::IbcMint) => { - ctx.write_temp(&src_key, src_bal)?; - } - Address::Internal(InternalAddress::IbcBurn) => { - log_string("invalid transfer from the burn address"); - unreachable!() - } - _ => { - ctx.write(&src_key, src_bal)?; - } - } - match dest { - Address::Internal(InternalAddress::IbcMint) => { - log_string("invalid transfer to the mint address"); - unreachable!() - } - Address::Internal(InternalAddress::IbcBurn) => { - ctx.write_temp(&dest_key, dest_bal)?; - } - _ => { - ctx.write(&dest_key, dest_bal)?; - } - } + ctx.write(&src_key, src_bal)?; + ctx.write(&dest_key, dest_bal)?; } } @@ -135,49 +89,49 @@ pub fn transfer( Ok(()) } -/// A token transfer with storage keys that can be used in a transaction. -pub fn transfer_with_keys( +/// Mint that can be used in a transaction. +pub fn mint( ctx: &mut Ctx, - src_key: &storage::Key, - dest_key: &storage::Key, + minter: &Address, + target: &Address, + sub_prefix: &Address, amount: Amount, ) -> TxResult { - let src_owner = is_any_token_or_multitoken_balance_key(src_key); - let src_bal: Option = match src_owner { - Some([_, Address::Internal(InternalAddress::IbcMint)]) => { - Some(Amount::max_signed()) - } - Some([_, Address::Internal(InternalAddress::IbcBurn)]) => { - log_string("invalid transfer from the burn address"); - unreachable!() - } - _ => ctx.read(src_key)?, - }; - let mut src_bal = src_bal.unwrap_or_else(|| { - log_string(format!("src {} has no balance", src_key)); - unreachable!() - }); - src_bal.spend(&amount); - let dest_owner = is_any_token_balance_key(dest_key); - let mut dest_bal: Amount = match dest_owner { - Some([_, Address::Internal(InternalAddress::IbcMint)]) => { - log_string("invalid transfer to the mint address"); - unreachable!() - } - _ => ctx.read(dest_key)?.unwrap_or_default(), - }; - dest_bal.receive(&amount); - match src_owner { - Some([_, Address::Internal(InternalAddress::IbcMint)]) => { - ctx.write_temp(src_key, src_bal)?; - } - _ => ctx.write(src_key, src_bal)?, - } - match dest_owner { - Some([_, Address::Internal(InternalAddress::IbcBurn)]) => { - ctx.write_temp(dest_key, dest_bal)?; - } - _ => ctx.write(dest_key, dest_bal)?, - } + let target_key = token::multitoken_balance_key(sub_prefix, target); + let mut target_bal: Amount = ctx.read(&target_key)?.unwrap_or_default(); + target_bal.receive(&amount); + + let minted_key = token::multitoken_minted_key(sub_prefix); + 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::multitoken_minter_key(sub_prefix); + ctx.write(&minter_key, minter)?; + + Ok(()) +} + +/// Burn that can be used in a transaction. +pub fn burn( + ctx: &mut Ctx, + target: &Address, + sub_prefix: &Address, + amount: Amount, +) -> TxResult { + let target_key = token::multitoken_balance_key(sub_prefix, 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::multitoken_minted_key(sub_prefix); + let mut minted_bal: Amount = ctx.read(&minted_key)?.unwrap_or_default(); + minted_bal.spend(&amount); + + ctx.write(&target_key, target_bal)?; + ctx.write(&minted_key, minted_bal)?; + Ok(()) } diff --git a/wasm/wasm_source/src/vp_implicit.rs b/wasm/wasm_source/src/vp_implicit.rs index 729ddb6fbe..70195a9c69 100644 --- a/wasm/wasm_source/src/vp_implicit.rs +++ b/wasm/wasm_source/src/vp_implicit.rs @@ -30,10 +30,9 @@ impl<'a> From<&'a storage::Key> for KeyType<'a> { fn from(key: &'a storage::Key) -> KeyType<'a> { if let Some(address) = key::is_pk_key(key) { Self::Pk(address) - } else if let Some([_, owner]) = token::is_any_token_balance_key(key) { - Self::Token { owner } - } else if let Some((_, [_, owner])) = - token::is_any_multitoken_balance_key(key) + } else if let Some(address) = token::is_any_token_balance_key(key) { + Self::Token(address) + } else if let Some((_, address)) = token::is_multitoken_balance_key(key) { Self::Token { owner } } else if proof_of_stake::is_pos_key(key) { diff --git a/wasm/wasm_source/src/vp_token.rs b/wasm/wasm_source/src/vp_token.rs index 29b639bd56..e59b0864b7 100644 --- a/wasm/wasm_source/src/vp_token.rs +++ b/wasm/wasm_source/src/vp_token.rs @@ -3,7 +3,7 @@ use std::collections::BTreeSet; -use namada_vp_prelude::address::{self, Address, InternalAddress}; +use namada_vp_prelude::address::{self, Address}; use namada_vp_prelude::storage::KeySeg; use namada_vp_prelude::{storage, token, *}; @@ -53,10 +53,7 @@ fn token_checks( ) -> VpResult { let mut change = token::Change::default(); for key in keys_touched.iter() { - let owner: Option<&Address> = token::is_balance_key(token, key) - .or_else(|| { - token::is_multitoken_balance_key(token, key).map(|a| a.1) - }); + let owner: Option<&Address> = token::is_balance_key(token, key); match owner { None => { @@ -77,33 +74,10 @@ fn token_checks( } Some(owner) => { // accumulate the change - let pre: token::Change = match owner { - Address::Internal(InternalAddress::IbcMint) => { - token::Change::maximum() - } - Address::Internal(InternalAddress::IbcBurn) => { - token::Change::default() - } - _ => ctx - .read_pre::(key)? - .unwrap_or_default() - .change(), - }; - let post: token::Change = match owner { - Address::Internal(InternalAddress::IbcMint) => ctx - .read_temp::(key)? - .map(|x| x.change()) - .unwrap_or_else(token::Change::maximum), - Address::Internal(InternalAddress::IbcBurn) => ctx - .read_temp::(key)? - .unwrap_or_default() - .change(), - _ => ctx - .read_post::(key)? - .unwrap_or_default() - .change(), - }; - let this_change = post - pre; + let pre: token::Amount = ctx.read_pre(key)?.unwrap_or_default(); + let post: token::Amount = + ctx.read_post(key)?.unwrap_or_default(); + let this_change = post.change() - pre.change(); change += this_change; // make sure that the spender approved the transaction if !(this_change.non_negative() diff --git a/wasm/wasm_source/src/vp_user.rs b/wasm/wasm_source/src/vp_user.rs index b32d801ef2..dd86f09cbd 100644 --- a/wasm/wasm_source/src/vp_user.rs +++ b/wasm/wasm_source/src/vp_user.rs @@ -24,10 +24,9 @@ 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) { - Self::Token { owner } - } else if let Some((_, [_, owner])) = - token::is_any_multitoken_balance_key(key) + if let Some(address) = token::is_any_token_balance_key(key) { + Self::Token(address) + } else if let Some((_, address)) = token::is_multitoken_balance_key(key) { Self::Token { owner } } else if proof_of_stake::is_pos_key(key) { diff --git a/wasm/wasm_source/src/vp_validator.rs b/wasm/wasm_source/src/vp_validator.rs index 9b10999d89..e79f51b629 100644 --- a/wasm/wasm_source/src/vp_validator.rs +++ b/wasm/wasm_source/src/vp_validator.rs @@ -26,10 +26,9 @@ 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) { - Self::Token { owner } - } else if let Some((_, [_, owner])) = - token::is_any_multitoken_balance_key(key) + if let Some(address) = token::is_any_token_balance_key(key) { + Self::Token(address) + } else if let Some((_, address)) = token::is_multitoken_balance_key(key) { Self::Token { owner } } else if proof_of_stake::is_pos_key(key) { From 8f62edd92f2a8a40d040d9ce0d98e054351bb4ca Mon Sep 17 00:00:00 2001 From: yito88 Date: Thu, 8 Jun 2023 22:53:47 +0900 Subject: [PATCH 012/120] change to multitokens --- apps/src/lib/cli.rs | 25 -- apps/src/lib/client/rpc.rs | 69 +--- .../lib/node/ledger/shell/finalize_block.rs | 4 +- core/src/ledger/ibc/context/storage.rs | 5 +- core/src/ledger/ibc/context/transfer_mod.rs | 6 +- core/src/ledger/storage_api/token.rs | 19 +- core/src/types/token.rs | 96 ++--- shared/src/ledger/args.rs | 6 - shared/src/ledger/ibc/vp/context.rs | 30 +- shared/src/ledger/ibc/vp/token.rs | 377 ------------------ shared/src/ledger/protocol/mod.rs | 11 +- shared/src/ledger/queries/vp/token.rs | 22 +- shared/src/ledger/rpc.rs | 4 +- shared/src/ledger/tx.rs | 45 +-- test_utils/src/tx_data.rs | 26 ++ tests/src/e2e.rs | 1 - tests/src/e2e/ibc_tests.rs | 62 +-- tests/src/e2e/ledger_tests.rs | 1 - tests/src/e2e/multitoken_tests.rs | 372 ----------------- tests/src/e2e/multitoken_tests/helpers.rs | 189 --------- tests/src/vm_host_env/ibc.rs | 2 +- tests/src/vm_host_env/mod.rs | 12 +- tests/src/vm_host_env/tx.rs | 6 +- tx_prelude/src/ibc.rs | 11 +- tx_prelude/src/token.rs | 27 +- wasm/wasm_source/src/tx_bond.rs | 2 +- wasm/wasm_source/src/tx_transfer.rs | 2 - wasm/wasm_source/src/tx_unbond.rs | 7 +- wasm/wasm_source/src/tx_withdraw.rs | 7 +- wasm/wasm_source/src/vp_implicit.rs | 26 +- wasm/wasm_source/src/vp_testnet_faucet.rs | 13 +- wasm/wasm_source/src/vp_token.rs | 47 +-- wasm/wasm_source/src/vp_user.rs | 23 +- wasm/wasm_source/src/vp_validator.rs | 26 +- wasm_for_tests/wasm_source/src/lib.rs | 47 ++- 35 files changed, 222 insertions(+), 1406 deletions(-) delete mode 100644 shared/src/ledger/ibc/vp/token.rs delete mode 100644 tests/src/e2e/multitoken_tests.rs delete mode 100644 tests/src/e2e/multitoken_tests/helpers.rs diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 06d6f3f406..241524d881 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -2382,7 +2382,6 @@ pub mod args { pub const SOURCE: Arg = arg("source"); pub const SOURCE_OPT: ArgOpt = SOURCE.opt(); pub const STORAGE_KEY: Arg = arg("storage-key"); - pub const SUB_PREFIX: ArgOpt = arg_opt("sub-prefix"); pub const SUSPEND_ACTION: ArgFlag = flag("suspend"); pub const TIMEOUT_HEIGHT: ArgOpt = arg_opt("timeout-height"); pub const TIMEOUT_SEC_OFFSET: ArgOpt = arg_opt("timeout-sec-offset"); @@ -3070,7 +3069,6 @@ pub mod args { source: ctx.get_cached(&self.source), target: ctx.get(&self.target), token: ctx.get(&self.token), - sub_prefix: self.sub_prefix, amount: self.amount, native_token: ctx.native_token.clone(), tx_code_path: self.tx_code_path.to_path_buf(), @@ -3084,7 +3082,6 @@ pub mod args { let source = TRANSFER_SOURCE.parse(matches); let target = TRANSFER_TARGET.parse(matches); let token = TOKEN.parse(matches); - let sub_prefix = SUB_PREFIX.parse(matches); let amount = InputAmount::Unvalidated(AMOUNT.parse(matches)); let tx_code_path = PathBuf::from(TX_TRANSFER_WASM); Self { @@ -3092,7 +3089,6 @@ pub mod args { source, target, token, - sub_prefix, amount, native_token: (), tx_code_path, @@ -3110,7 +3106,6 @@ pub mod args { to produce the signature.", )) .arg(TOKEN.def().help("The transfer token.")) - .arg(SUB_PREFIX.def().help("The token's sub prefix.")) .arg(AMOUNT.def().help("The amount to transfer in decimal.")) } } @@ -3122,7 +3117,6 @@ pub mod args { source: ctx.get(&self.source), receiver: self.receiver, token: ctx.get(&self.token), - sub_prefix: self.sub_prefix, amount: self.amount, port_id: self.port_id, channel_id: self.channel_id, @@ -3139,7 +3133,6 @@ pub mod args { let source = SOURCE.parse(matches); let receiver = RECEIVER.parse(matches); let token = TOKEN.parse(matches); - let sub_prefix = SUB_PREFIX.parse(matches); let amount = AMOUNT.parse(matches); let port_id = PORT_ID.parse(matches); let channel_id = CHANNEL_ID.parse(matches); @@ -3151,7 +3144,6 @@ pub mod args { source, receiver, token, - sub_prefix, amount: amount.amount, port_id, channel_id, @@ -3171,7 +3163,6 @@ pub mod args { "The receiver address on the destination chain as string.", )) .arg(TOKEN.def().help("The transfer token.")) - .arg(SUB_PREFIX.def().help("The token's sub prefix.")) .arg(AMOUNT.def().help("The amount to transfer in decimal.")) .arg(PORT_ID.def().help("The port ID.")) .arg(CHANNEL_ID.def().help("The channel ID.")) @@ -3864,7 +3855,6 @@ pub mod args { owner: self.owner.map(|x| ctx.get_cached(&x)), token: self.token.map(|x| ctx.get(&x)), no_conversions: self.no_conversions, - sub_prefix: self.sub_prefix, } } } @@ -3875,13 +3865,11 @@ pub mod args { let owner = BALANCE_OWNER.parse(matches); let token = TOKEN_OPT.parse(matches); let no_conversions = NO_CONVERSIONS.parse(matches); - let sub_prefix = SUB_PREFIX.parse(matches); Self { query, owner, token, no_conversions, - sub_prefix, } } @@ -3902,11 +3890,6 @@ pub mod args { "Whether not to automatically perform conversions.", ), ) - .arg( - SUB_PREFIX - .def() - .help("The token's sub prefix whose balance to query."), - ) } } @@ -3916,7 +3899,6 @@ pub mod args { query: self.query.to_sdk(ctx), owner: self.owner.map(|x| ctx.get_cached(&x)), token: self.token.map(|x| ctx.get(&x)), - sub_prefix: self.sub_prefix, } } } @@ -3926,12 +3908,10 @@ pub mod args { let query = Query::parse(matches); let owner = BALANCE_OWNER.parse(matches); let token = TOKEN_OPT.parse(matches); - let sub_prefix = SUB_PREFIX.parse(matches); Self { query, owner, token, - sub_prefix, } } @@ -3943,11 +3923,6 @@ pub mod args { .arg(TOKEN_OPT.def().help( "The token address that queried transfers must involve.", )) - .arg( - SUB_PREFIX - .def() - .help("The token's sub prefix whose balance to query."), - ) } } diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index da2078139d..b136ce17e0 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -305,43 +305,17 @@ pub async fn query_transparent_balance< let tokens = wallet.get_addresses_with_vp_type(AddressVpType::Token); match (args.token, args.owner) { (Some(token), Some(owner)) => { - let key = match &args.sub_prefix { - Some(sp) => { - let sub_prefix = - Address::decode(sp).expect("Invalid sub_prefix"); - token::multitoken_balance_key( - &sub_prefix, - &owner.address().unwrap(), - ) - } - None => ( - token::balance_key(&token, &owner.address().unwrap()), - None, - ), - }; + let balance_key = + token::balance_key(&token, &owner.address().unwrap()); let token_alias = lookup_alias(wallet, &token); match query_storage_value::(client, &balance_key) .await { Some(balance) => { - let balance = format_denominated_amount( - client, - &TokenAddress { - address: token, - sub_prefix, - }, - balance, - ) - .await; - match &args.sub_prefix { - Some(sub_prefix) => { - println!( - "{} with {}: {}", - token_alias, sub_prefix, balance - ); - } - None => println!("{}: {}", token_alias, balance), - } + let balance = + format_denominated_amount(client, &token, balance) + .await; + println!("{}: {}", token_alias, balance); } None => { println!("No {} balance found for {}", token_alias, owner) @@ -558,35 +532,16 @@ async fn print_balances( let print_num = balances .filter_map(|(key, balance)| { - match token::is_multitoken_balance_key(&key) { - Some((sub_prefix, owner)) => Some(( + token::is_balance_key(token, &key).map(|owner| { + ( owner.clone(), format!( - "with {}: {}, owned by {}", - sub_prefix, - balance, + ": {}, owned by {}", + format_denominated_amount(client, token, balance).await, lookup_alias(wallet, owner) ), - )), - None => token::is_any_token_balance_key(&key).map(|owner| { - ( - owner.clone(), - format!( - ": {}, owned by {}", - format_denominated_amount( - client, - &TokenAddress { - address: tok.clone(), - sub_prefix: None - }, - balance - ) - .await, - lookup_alias(wallet, owner) - ), - ) - }), - } + ) + }) }) .filter_map(|(o, s)| match target { Some(t) if o == *t => Some(s), diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 3af8ab9e3f..50e470c731 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -21,7 +21,7 @@ use namada::types::address::Address; use namada::types::dec::Dec; use namada::types::key::tm_raw_hash_to_string; use namada::types::storage::{BlockHash, BlockResults, Epoch, Header}; -use namada::types::token::{total_supply_key, Amount}; +use namada::types::token::{minted_balance_key, Amount}; use namada::types::transaction::protocol::{ ethereum_tx_data_variants, ProtocolTxType, }; @@ -707,7 +707,7 @@ where .expect("PoS inflation amount should exist in storage"); // Read from PoS storage let total_tokens = self - .read_storage_key(&total_supply_key(&staking_token_address( + .read_storage_key(&minted_balance_key(&staking_token_address( &self.wl_storage, ))) .expect("Total NAM balance should exist in storage"); diff --git a/core/src/ledger/ibc/context/storage.rs b/core/src/ledger/ibc/context/storage.rs index e047395a68..87555990a4 100644 --- a/core/src/ledger/ibc/context/storage.rs +++ b/core/src/ledger/ibc/context/storage.rs @@ -58,7 +58,6 @@ pub trait IbcStorageContext { src: &Address, dest: &Address, token: &Address, - sub_prefix: Option
, amount: Amount, ) -> Result<(), Self::Error>; @@ -66,7 +65,7 @@ pub trait IbcStorageContext { fn mint_token( &mut self, target: &Address, - sub_prefix: &Address, + token: &Address, amount: Amount, ) -> Result<(), Self::Error>; @@ -74,7 +73,7 @@ pub trait IbcStorageContext { fn burn_token( &mut self, target: &Address, - sub_prefix: &Address, + token: &Address, amount: Amount, ) -> Result<(), Self::Error>; diff --git a/core/src/ledger/ibc/context/transfer_mod.rs b/core/src/ledger/ibc/context/transfer_mod.rs index 9dfce35e6c..51ac801aec 100644 --- a/core/src/ledger/ibc/context/transfer_mod.rs +++ b/core/src/ledger/ibc/context/transfer_mod.rs @@ -447,14 +447,14 @@ where let (token, amount) = get_token_amount(coin)?; let ibc_token = if coin.denom.trace_path.is_empty() { - None + token } else { - Some(storage::ibc_token(coin.denom.to_string())) + storage::ibc_token(coin.denom.to_string()) }; self.ctx .borrow_mut() - .transfer_token(from, to, &token, ibc_token, amount) + .transfer_token(from, to, &ibc_token, amount) .map_err(|_| { TokenTransferError::ContextError(ContextError::ChannelError( ChannelError::Other { diff --git a/core/src/ledger/storage_api/token.rs b/core/src/ledger/storage_api/token.rs index 880d748274..383911d09a 100644 --- a/core/src/ledger/storage_api/token.rs +++ b/core/src/ledger/storage_api/token.rs @@ -7,8 +7,8 @@ use crate::types::storage::DbKeySeg::StringSeg; use crate::types::storage::Key; use crate::types::token; pub use crate::types::token::{ - balance_key, is_balance_key, is_total_supply_key, total_supply_key, Amount, - Change, + balance_key, is_balance_key, is_minted_balance_key, minted_balance_key, + minter_key, Amount, Change, }; /// Read the balance of a given token and owner. @@ -33,7 +33,7 @@ pub fn read_total_supply( where S: StorageRead, { - let key = token::total_supply_key(token); + let key = token::minted_balance_key(token); let balance = storage.read::(&key)?.unwrap_or_default(); Ok(balance) } @@ -44,17 +44,11 @@ where pub fn read_denom( storage: &S, token: &Address, - sub_prefix: Option<&Key>, ) -> storage_api::Result> where S: StorageRead, { - if let Some(sub_prefix) = sub_prefix { - if sub_prefix.segments.contains(&StringSeg("ibc".to_string())) { - return Ok(Some(token::NATIVE_MAX_DECIMAL_PLACES.into())); - } - } - let key = token::denom_key(token, sub_prefix); + let key = token::denom_key(token); storage.read(&key).map(|opt_denom| { Some( opt_denom @@ -67,13 +61,12 @@ where pub fn write_denom( storage: &mut S, token: &Address, - sub_prefix: Option<&Key>, denom: token::Denomination, ) -> storage_api::Result<()> where S: StorageRead + StorageWrite, { - let key = token::denom_key(token, sub_prefix); + let key = token::denom_key(token); storage.write(&key, denom) } @@ -132,7 +125,7 @@ where storage_api::Error::new_const("Token balance overflow") })?; - let total_supply_key = token::total_supply_key(token); + let total_supply_key = token::minted_balance_key(token); let cur_supply = storage .read::(&total_supply_key)? .unwrap_or_default(); diff --git a/core/src/types/token.rs b/core/src/types/token.rs index 2e12e06220..df50a5b1ba 100644 --- a/core/src/types/token.rs +++ b/core/src/types/token.rs @@ -779,7 +779,6 @@ pub const TX_KEY_PREFIX: &str = "tx-"; pub const CONVERSION_KEY_PREFIX: &str = "conv"; /// Key segment prefix for pinned shielded transactions pub const PIN_KEY_PREFIX: &str = "pin-"; -const TOTAL_SUPPLY_STORAGE_KEY: &str = "total_supply"; /// A fully qualified (multi-) token address. #[derive( @@ -831,47 +830,32 @@ impl Display for TokenAddress { /// Obtain a storage key for user's balance. pub fn balance_key(token_addr: &Address, owner: &Address) -> Key { - Key::from(token_addr.to_db_key()) - .push(&BALANCE_STORAGE_KEY.to_owned()) - .expect("Cannot obtain a 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(token_addr.to_db_key()) - .push(&BALANCE_STORAGE_KEY.to_owned()) - .expect("Cannot obtain a storage key") -} - -/// Obtain a storage key for user's multitoken balance. -pub fn multitoken_balance_key(prefix: &Address, owner: &Address) -> Key { Key::from(Address::Internal(InternalAddress::Multitoken).to_db_key()) - .push(prefix) + .push(&token_addr.to_db_key()) .expect("Cannot obtain a storage key") .push(&BALANCE_STORAGE_KEY.to_owned()) .expect("Cannot obtain a storage key") - .push(&owner.to_db_key()) - .expect("Cannot obtain a storage key") } /// Obtain a storage key for the multitoken minter. -pub fn multitoken_minter_key(prefix: &Address) -> Key { +pub fn minter_key(token_addr: &Address) -> Key { Key::from(Address::Internal(InternalAddress::Multitoken).to_db_key()) - .push(&MINTER_STORAGE_KEY.to_owned()) + .push(&token_addr.to_db_key()) .expect("Cannot obtain a storage key") - .push(prefix) + .push(&MINTER_STORAGE_KEY.to_owned()) .expect("Cannot obtain a storage key") } /// Obtain a storage key for the minted multitoken balance. -pub fn multitoken_minted_key(prefix: &Address) -> Key { - Key::from(Address::Internal(InternalAddress::Multitoken).to_db_key()) - .push(prefix) - .expect("Cannot obtain a storage key") - .push(&BALANCE_STORAGE_KEY.to_owned()) - .expect("Cannot obtain a storage key") +pub fn minted_balance_key(token_addr: &Address) -> Key { + balance_prefix(token_addr) .push(&Address::Internal(InternalAddress::Mint).to_db_key()) .expect("Cannot obtain a storage key") } @@ -885,9 +869,15 @@ pub fn is_balance_key<'a>( match &key.segments[..] { [ DbKeySeg::AddressSeg(addr), - DbKeySeg::StringSeg(key), + DbKeySeg::AddressSeg(token), + DbKeySeg::StringSeg(balance), DbKeySeg::AddressSeg(owner), - ] if key == BALANCE_STORAGE_KEY && addr == token_addr => Some(owner), + ] if *addr == Address::Internal(InternalAddress::Multitoken) + && token == token_addr + && balance == BALANCE_STORAGE_KEY => + { + Some(owner) + } _ => None, } } @@ -897,25 +887,24 @@ pub fn is_balance_key<'a>( pub fn is_any_token_balance_key(key: &Key) -> Option<[&Address; 2]> { match &key.segments[..] { [ + DbKeySeg::AddressSeg(addr), DbKeySeg::AddressSeg(token), - DbKeySeg::StringSeg(key), + DbKeySeg::StringSeg(balance), DbKeySeg::AddressSeg(owner), - ] if key == BALANCE_STORAGE_KEY => Some([token, 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, sub_prefix: Option<&Key>) -> Key { - match sub_prefix { - Some(sub) => Key::from(token_addr.to_db_key()) - .join(sub) - .push(&DENOM_STORAGE_KEY.to_owned()) - .expect("Cannot obtain a storage key"), - None => Key::from(token_addr.to_db_key()) - .push(&DENOM_STORAGE_KEY.to_owned()) - .expect("Cannot obtain a storage key"), - } +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. @@ -938,33 +927,12 @@ pub fn is_masp_key(key: &Key) -> bool { || key.starts_with(PIN_KEY_PREFIX))) } -/// Storage key for total supply of a token -pub fn total_supply_key(token_address: &Address) -> Key { - Key::from(token_address.to_db_key()) - .push(&TOTAL_SUPPLY_STORAGE_KEY.to_owned()) - .expect("Cannot obtain a storage key") -} - /// Is storage key for total supply of a specific token? -pub fn is_total_supply_key(key: &Key, token_address: &Address) -> bool { - matches!(&key.segments[..], [DbKeySeg::AddressSeg(addr), DbKeySeg::StringSeg(key)] if addr == token_address && key == TOTAL_SUPPLY_STORAGE_KEY) -} - -/// Check if the given storage key is multitoken balance key for the given -/// token. If it is, returns the sub prefix and the owner. -pub fn is_multitoken_balance_key(key: &Key) -> Option<(&Address, &Address)> { - match &key.segments[..] { - [ - DbKeySeg::AddressSeg(addr), - DbKeySeg::AddressSeg(sub_prefix), - DbKeySeg::StringSeg(balance), - DbKeySeg::AddressSeg(owner), - ] if *addr == Address::Internal(InternalAddress::Multitoken) - && balance == BALANCE_STORAGE_KEY => - { - Some((sub_prefix, owner)) - } - _ => None, +pub fn is_minted_balance_key(token_addr: &Address, key: &Key) -> bool { + if let Some(owner) = is_balance_key(token_addr, key) { + *owner == Address::Internal(InternalAddress::Mint) + } else { + false } } @@ -989,8 +957,6 @@ pub struct Transfer { pub target: Address, /// Token's address pub token: Address, - /// Source token's sub prefix - pub sub_prefix: Option
, /// The amount of tokens pub amount: DenominatedAmount, /// The unused storage location at which to place TxId diff --git a/shared/src/ledger/args.rs b/shared/src/ledger/args.rs index 35081d3283..ba1f071fb8 100644 --- a/shared/src/ledger/args.rs +++ b/shared/src/ledger/args.rs @@ -115,8 +115,6 @@ pub struct TxTransfer { pub target: C::TransferTarget, /// Transferred token address pub token: C::Address, - /// Transferred token address - pub sub_prefix: Option, /// Transferred token amount pub amount: InputAmount, /// Native token address @@ -147,8 +145,6 @@ pub struct TxIbcTransfer { pub receiver: String, /// Transferred token addres s pub token: C::Address, - /// Transferred token address - pub sub_prefix: Option, /// Transferred token amount pub amount: token::Amount, /// Port ID @@ -317,8 +313,6 @@ pub struct QueryBalance { pub token: Option, /// Whether not to convert balances pub no_conversions: bool, - /// Sub prefix of an account - pub sub_prefix: Option, } /// Query historical transfer(s) diff --git a/shared/src/ledger/ibc/vp/context.rs b/shared/src/ledger/ibc/vp/context.rs index cee112854e..17697f181c 100644 --- a/shared/src/ledger/ibc/vp/context.rs +++ b/shared/src/ledger/ibc/vp/context.rs @@ -120,17 +120,10 @@ where src: &Address, dest: &Address, token: &Address, - sub_prefix: Option
, amount: Amount, ) -> Result<(), Self::Error> { - let src_key = match &sub_prefix { - Some(sub_prefix) => token::multitoken_balance_key(sub_prefix, src), - None => token::balance_key(token, src), - }; - let dest_key = match &sub_prefix { - Some(sub_prefix) => token::multitoken_balance_key(sub_prefix, dest), - None => token::balance_key(token, dest), - }; + let src_key = token::balance_key(token, src); + let dest_key = token::balance_key(token, dest); let src_bal: Option = self.ctx.read(&src_key).map_err(Error::NativeVpError)?; let mut src_bal = src_bal.expect("The source has no balance"); @@ -155,10 +148,10 @@ where fn mint_token( &mut self, target: &Address, - sub_prefix: &Address, + token: &Address, amount: Amount, ) -> Result<(), Self::Error> { - let target_key = token::multitoken_balance_key(sub_prefix, target); + let target_key = token::balance_key(token, target); let mut target_bal: Amount = self .ctx .read(&target_key) @@ -166,7 +159,7 @@ where .unwrap_or_default(); target_bal.receive(&amount); - let minted_key = token::multitoken_minted_key(sub_prefix); + let minted_key = token::minted_balance_key(token); let mut minted_bal: Amount = self .ctx .read(&minted_key) @@ -183,7 +176,7 @@ where minted_bal.try_to_vec().expect("encoding shouldn't failed"), )?; - let minter_key = token::multitoken_minter_key(sub_prefix); + let minter_key = token::minter_key(token); self.write( &minter_key, Address::Internal(InternalAddress::Ibc) @@ -195,10 +188,10 @@ where fn burn_token( &mut self, target: &Address, - sub_prefix: &Address, + token: &Address, amount: Amount, ) -> Result<(), Self::Error> { - let target_key = token::multitoken_balance_key(sub_prefix, target); + let target_key = token::balance_key(token, target); let mut target_bal: Amount = self .ctx .read(&target_key) @@ -206,7 +199,7 @@ where .unwrap_or_default(); target_bal.spend(&amount); - let minted_key = token::multitoken_minted_key(sub_prefix); + let minted_key = token::minted_balance_key(token); let mut minted_bal: Amount = self .ctx .read(&minted_key) @@ -320,7 +313,6 @@ where _src: &Address, _dest: &Address, _token: &Address, - _sub_prefix: Option
, _amount: Amount, ) -> Result<(), Self::Error> { unimplemented!("Validation doesn't transfer") @@ -329,7 +321,7 @@ where fn mint_token( &mut self, _target: &Address, - _sub_prefix: &Address, + _token: &Address, _amount: Amount, ) -> Result<(), Self::Error> { unimplemented!("Validation doesn't mint") @@ -338,7 +330,7 @@ where fn burn_token( &mut self, _target: &Address, - _sub_prefix: &Address, + _token: &Address, _amount: Amount, ) -> Result<(), Self::Error> { unimplemented!("Validation doesn't burn") diff --git a/shared/src/ledger/ibc/vp/token.rs b/shared/src/ledger/ibc/vp/token.rs deleted file mode 100644 index 204fa21ea0..0000000000 --- a/shared/src/ledger/ibc/vp/token.rs +++ /dev/null @@ -1,377 +0,0 @@ -//! IBC token transfer validation as a native validity predicate - -use std::collections::{BTreeSet, HashMap, HashSet}; - -use borsh::BorshDeserialize; -use prost::Message; -use thiserror::Error; - -use crate::ibc::applications::transfer::coin::PrefixedCoin; -use crate::ibc::applications::transfer::error::TokenTransferError; -use crate::ibc::applications::transfer::msgs::transfer::{ - MsgTransfer, TYPE_URL as MSG_TRANSFER_TYPE_URL, -}; -use crate::ibc::applications::transfer::packet::PacketData; -use crate::ibc::applications::transfer::{ - is_receiver_chain_source, is_sender_chain_source, -}; -use crate::ibc::core::ics04_channel::msgs::PacketMsg; -use crate::ibc::core::ics04_channel::packet::Packet; -use crate::ibc::core::ics26_routing::error::RouterError; -use crate::ibc::core::ics26_routing::msgs::MsgEnvelope; -use crate::ibc_proto::google::protobuf::Any; -use crate::ledger::ibc::storage as ibc_storage; -use crate::ledger::native_vp::{self, Ctx, NativeVp, VpEnv}; -use crate::ledger::storage::{self as ledger_storage, StorageHasher}; -use crate::proto::Tx; -use crate::types::address::{Address, InternalAddress}; -use crate::types::storage::Key; -use crate::types::token::{self, Amount, AmountParseError}; -use crate::vm::WasmCacheAccess; - -#[allow(missing_docs)] -#[derive(Error, Debug)] -pub enum Error { - #[error("Native VP error: {0}")] - NativeVpError(native_vp::Error), - #[error("IBC message error: {0}")] - IbcMessage(RouterError), - #[error("Invalid message")] - InvalidMessage, - #[error("Parsing amount error: {0}")] - Amount(AmountParseError), - #[error("Decoding error: {0}")] - Decoding(std::io::Error), - #[error("Decoding IBC data error: {0}")] - DecodingIbcData(prost::DecodeError), - #[error("Decoding PacketData error: {0}")] - DecodingPacketData(serde_json::Error), - #[error("IBC message is required as transaction data")] - NoTxData, - #[error("Invalid denom: {0}")] - Denom(String), - #[error("Invalid MsgTransfer: {0}")] - MsgTransfer(TokenTransferError), - #[error("Invalid token transfer: {0}")] - TokenTransfer(String), -} - -/// Result for IBC token VP -pub type Result = std::result::Result; - -/// IBC token VP to validate the transfer for an IBC-specific account. The -/// account is a sub-prefixed account with an IBC token hash, or a normal -/// account for `IbcEscrow`, `IbcBurn`, or `IbcMint`. -pub struct IbcToken<'a, DB, H, CA> -where - DB: ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, - H: StorageHasher, - CA: 'static + WasmCacheAccess, -{ - /// Context to interact with the host structures. - pub ctx: Ctx<'a, DB, H, CA>, -} - -impl<'a, DB, H, CA> NativeVp for IbcToken<'a, DB, H, CA> -where - DB: 'static + ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, - H: 'static + StorageHasher, - CA: 'static + WasmCacheAccess, -{ - type Error = Error; - - const ADDR: InternalAddress = InternalAddress::IbcBurn; - - fn validate_tx( - &self, - tx_data: &Tx, - keys_changed: &BTreeSet, - _verifiers: &BTreeSet
, - ) -> Result { - let signed = tx_data; - let tx_data = signed.data().ok_or(Error::NoTxData)?; - - // Check the non-onwer balance updates - let ibc_keys_changed: HashSet = keys_changed - .iter() - .filter(|k| { - matches!( - token::is_any_token_balance_key(k), - Some([ - _, - Address::Internal( - InternalAddress::IbcEscrow - | InternalAddress::IbcBurn - | InternalAddress::IbcMint - ) - ]) - ) - }) - .cloned() - .collect(); - if ibc_keys_changed.is_empty() { - // some multitoken balances are changed - let mut changes = HashMap::new(); - for key in keys_changed { - if let Some((sub_prefix, _)) = - token::is_any_multitoken_balance_key(key) - { - if !ibc_storage::is_ibc_sub_prefix(&sub_prefix) { - continue; - } - let pre: token::Amount = - self.ctx.read_pre(key)?.unwrap_or_default(); - let post: token::Amount = - self.ctx.read_post(key)?.unwrap_or_default(); - let this_change = post.change() - pre.change(); - let change: token::Change = - changes.get(&sub_prefix).cloned().unwrap_or_default(); - changes.insert(sub_prefix, change + this_change); - } - } - if changes.iter().all(|(_, c)| c.is_zero()) { - return Ok(true); - } else { - return Err(Error::TokenTransfer( - "Invalid transfer between different origin accounts" - .to_owned(), - )); - } - } else if ibc_keys_changed.len() > 1 { - // a transaction can update at most 1 special IBC account for now - return Err(Error::TokenTransfer( - "Invalid transfer for multiple non-owner balances".to_owned(), - )); - } - - // Check the message - let ibc_msg = - Any::decode(&tx_data[..]).map_err(Error::DecodingIbcData)?; - match ibc_msg.type_url.as_str() { - MSG_TRANSFER_TYPE_URL => { - let msg = MsgTransfer::try_from(ibc_msg) - .map_err(Error::MsgTransfer)?; - self.validate_sending_token(&msg) - } - _ => { - let envelope: MsgEnvelope = - ibc_msg.try_into().map_err(Error::IbcMessage)?; - match envelope { - MsgEnvelope::Packet(PacketMsg::Recv(msg)) => { - self.validate_receiving_token(&msg.packet) - } - MsgEnvelope::Packet(PacketMsg::Ack(msg)) => { - self.validate_refunding_token(&msg.packet) - } - MsgEnvelope::Packet(PacketMsg::Timeout(msg)) => { - self.validate_refunding_token(&msg.packet) - } - MsgEnvelope::Packet(PacketMsg::TimeoutOnClose(msg)) => { - self.validate_refunding_token(&msg.packet) - } - _ => Err(Error::InvalidMessage), - } - } - } - } -} - -impl<'a, DB, H, CA> IbcToken<'a, DB, H, CA> -where - DB: 'static + ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, - H: 'static + StorageHasher, - CA: 'static + WasmCacheAccess, -{ - fn validate_sending_token(&self, msg: &MsgTransfer) -> Result { - let mut coin = msg.token.clone(); - // lookup the original denom with the IBC token hash - if let Some(token_hash) = - ibc_storage::token_hash_from_denom(&coin.denom).map_err(|e| { - Error::Denom(format!("Invalid denom: error {}", e)) - })? - { - let denom_key = ibc_storage::ibc_denom_key(token_hash); - coin.denom = match self.ctx.read_bytes_pre(&denom_key) { - Ok(Some(v)) => String::try_from_slice(&v[..]).map_err(|e| { - Error::Denom(format!( - "Decoding the denom string failed: {}", - e - )) - })?, - _ => { - return Err(Error::Denom(format!( - "No original denom: denom_key {}", - denom_key - ))); - } - }; - } - let coin = PrefixedCoin::try_from(coin).map_err(Error::MsgTransfer)?; - let token = ibc_storage::token(coin.denom.to_string()) - .map_err(|e| Error::Denom(e.to_string()))?; - let amount = Amount::try_from(coin.amount).map_err(Error::Amount)?; - - // check the denomination field - let change = if is_sender_chain_source( - msg.port_id_on_a.clone(), - msg.chan_id_on_a.clone(), - &coin.denom, - ) { - // source zone - // check the amount of the token has been escrowed - let target_key = token::balance_key( - &token, - &Address::Internal(InternalAddress::IbcEscrow), - ); - let pre = - try_decode_token_amount(self.ctx.read_bytes_pre(&target_key)?)? - .unwrap_or_default(); - let post = try_decode_token_amount( - self.ctx.read_bytes_post(&target_key)?, - )? - .unwrap_or_default(); - post.change() - pre.change() - } else { - // sink zone - // check the amount of the token has been burned - let target_key = token::balance_key( - &token, - &Address::Internal(InternalAddress::IbcBurn), - ); - let post = try_decode_token_amount( - self.ctx.read_bytes_temp(&target_key)?, - )? - .unwrap_or_default(); - // the previous balance of the burn address should be zero - post.change() - }; - - if change == amount.change() { - Ok(true) - } else { - Err(Error::TokenTransfer(format!( - "Sending the token is invalid: coin {}", - coin, - ))) - } - } - - fn validate_receiving_token(&self, packet: &Packet) -> Result { - let data = serde_json::from_slice::(&packet.data) - .map_err(Error::DecodingPacketData)?; - let token = ibc_storage::token(data.token.denom.to_string()) - .map_err(|e| Error::Denom(e.to_string()))?; - let amount = - Amount::try_from(data.token.amount).map_err(Error::Amount)?; - - let change = if is_receiver_chain_source( - packet.port_id_on_a.clone(), - packet.chan_id_on_a.clone(), - &data.token.denom, - ) { - // this chain is the source - // check the amount of the token has been unescrowed - let source_key = token::balance_key( - &token, - &Address::Internal(InternalAddress::IbcEscrow), - ); - let pre = - try_decode_token_amount(self.ctx.read_bytes_pre(&source_key)?)? - .unwrap_or_default(); - let post = try_decode_token_amount( - self.ctx.read_bytes_post(&source_key)?, - )? - .unwrap_or_default(); - pre.change() - post.change() - } else { - // the sender is the source - // check the amount of the token has been minted - let source_key = token::balance_key( - &token, - &Address::Internal(InternalAddress::IbcMint), - ); - let post = try_decode_token_amount( - self.ctx.read_bytes_temp(&source_key)?, - )? - .unwrap_or_default(); - // the previous balance of the mint address should be the maximum - Amount::max_signed().change() - post.change() - }; - - if change == amount.change() { - Ok(true) - } else { - Err(Error::TokenTransfer(format!( - "Receivinging the token is invalid: coin {}", - data.token - ))) - } - } - - fn validate_refunding_token(&self, packet: &Packet) -> Result { - let data = serde_json::from_slice::(&packet.data) - .map_err(Error::DecodingPacketData)?; - let token = ibc_storage::token(data.token.denom.to_string()) - .map_err(|e| Error::Denom(e.to_string()))?; - let amount = - Amount::try_from(data.token.amount).map_err(Error::Amount)?; - - // check the denom field - let change = if is_sender_chain_source( - packet.port_id_on_a.clone(), - packet.chan_id_on_a.clone(), - &data.token.denom, - ) { - // source zone: unescrow the token for the refund - let source_key = token::balance_key( - &token, - &Address::Internal(InternalAddress::IbcEscrow), - ); - let pre = - try_decode_token_amount(self.ctx.read_bytes_pre(&source_key)?)? - .unwrap_or_default(); - let post = try_decode_token_amount( - self.ctx.read_bytes_post(&source_key)?, - )? - .unwrap_or_default(); - pre.change() - post.change() - } else { - // sink zone: mint the token for the refund - let source_key = token::balance_key( - &token, - &Address::Internal(InternalAddress::IbcMint), - ); - let post = try_decode_token_amount( - self.ctx.read_bytes_temp(&source_key)?, - )? - .unwrap_or_default(); - // the previous balance of the mint address should be the maximum - Amount::max_signed().change() - post.change() - }; - - if change == amount.change() { - Ok(true) - } else { - Err(Error::TokenTransfer(format!( - "Refunding the token is invalid: coin {}", - data.token, - ))) - } - } -} - -impl From for Error { - fn from(err: native_vp::Error) -> Self { - Self::NativeVpError(err) - } -} - -fn try_decode_token_amount( - bytes: Option>, -) -> Result> { - if let Some(bytes) = bytes { - let tokens = Amount::try_from_slice(&bytes).map_err(Error::Decoding)?; - return Ok(Some(tokens)); - } - Ok(None) -} diff --git a/shared/src/ledger/protocol/mod.rs b/shared/src/ledger/protocol/mod.rs index a71c2d0cae..c1a8484e45 100644 --- a/shared/src/ledger/protocol/mod.rs +++ b/shared/src/ledger/protocol/mod.rs @@ -551,9 +551,7 @@ where gas_meter = slash_fund.ctx.gas_meter.into_inner(); result } - InternalAddress::Multitoken - | InternalAddress::IbcToken(_) - | InternalAddress::Mint => { + InternalAddress::Multitoken => { let multitoken = MultitokenVp { ctx }; let result = multitoken .validate_tx(tx, &keys_changed, &verifiers) @@ -587,6 +585,13 @@ where replay_protection_vp.ctx.gas_meter.into_inner(); result } + InternalAddress::IbcToken(_) + | InternalAddress::Mint => { + // These addresses should be a part of a multitoken + // key + gas_meter = ctx.gas_meter.into_inner(); + Ok(true) + } }; accepted diff --git a/shared/src/ledger/queries/vp/token.rs b/shared/src/ledger/queries/vp/token.rs index cbad27005f..030bdddd53 100644 --- a/shared/src/ledger/queries/vp/token.rs +++ b/shared/src/ledger/queries/vp/token.rs @@ -8,8 +8,7 @@ use namada_core::types::token; use crate::ledger::queries::RequestCtx; router! {TOKEN, - ( "denomination" / [addr: Address] / [sub_prefix: opt Key] ) -> Option = denomination, - ( "denomination" / [addr: Address] / "ibc" / [_ibc_junk: String] ) -> Option = denomination_ibc, + ( "denomination" / [addr: Address] ) -> Option = denomination, } /// Get the number of decimal places (in base 10) for a @@ -17,27 +16,10 @@ router! {TOKEN, fn denomination( ctx: RequestCtx<'_, D, H>, addr: Address, - sub_prefix: Option, ) -> storage_api::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - read_denom(ctx.wl_storage, &addr, sub_prefix.as_ref()) -} - -// TODO Please fix this - -/// Get the number of decimal places (in base 10) for a -/// token specified by `addr`. -fn denomination_ibc( - ctx: RequestCtx<'_, D, H>, - addr: Address, - _ibc_junk: String, -) -> storage_api::Result> -where - D: 'static + DB + for<'iter> DBIter<'iter> + Sync, - H: 'static + StorageHasher + Sync, -{ - read_denom(ctx.wl_storage, &addr, None) + read_denom(ctx.wl_storage, &addr) } diff --git a/shared/src/ledger/rpc.rs b/shared/src/ledger/rpc.rs index 90d05ebd13..28431a7ffb 100644 --- a/shared/src/ledger/rpc.rs +++ b/shared/src/ledger/rpc.rs @@ -1061,13 +1061,13 @@ pub async fn format_denominated_amount< C: crate::ledger::queries::Client + Sync, >( client: &C, - token: &TokenAddress, + token: &Address, amount: token::Amount, ) -> String { let denom = unwrap_client_response::>( RPC.vp() .token() - .denomination(client, &token.address, &token.sub_prefix) + .denomination(client, &token.address) .await, ) .unwrap_or_else(|| { diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index 6b11cac281..6b676eb020 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -44,7 +44,7 @@ use crate::tendermint_rpc::error::Error as RpcError; use crate::types::control_flow::{time, ProceedOrElse}; use crate::types::key::*; use crate::types::masp::TransferTarget; -use crate::types::storage::{Epoch, RESERVED_ADDRESS_PREFIX}; +use crate::types::storage::Epoch; use crate::types::time::DateTimeUtc; use crate::types::transaction::{pos, InitAccount, TxType, UpdateVp}; use crate::types::{storage, token}; @@ -1144,21 +1144,7 @@ pub async fn build_ibc_transfer< let token = token_exists_or_err(args.token, args.tx.force, client).await?; // Check source balance - let (sub_prefix, balance_key) = match args.sub_prefix { - Some(sp) => { - let sub_prefix = Address::decode(&sp).map_err(|e| { - Error::Other(format!( - "The sub_prefix was not an Address: sub_prefix {}, error \ - {}", - sp, e - )) - })?; - let balance_key = - token::multitoken_balance_key(&sub_prefix, &source); - (Some(sub_prefix), balance_key) - } - None => (None, token::balance_key(&token, &source)), - }; + let balance_key = token::balance_key(&token, &source); check_balance_too_low_err( &token, @@ -1175,11 +1161,6 @@ pub async fn build_ibc_transfer< .await .unwrap(); - let denom = match sub_prefix { - // To parse IbcToken address, remove the address prefix - Some(sp) => sp.to_string().replace(RESERVED_ADDRESS_PREFIX, ""), - None => token.to_string(), - }; let amount = args .amount .to_string_native() @@ -1187,7 +1168,10 @@ pub async fn build_ibc_transfer< .next() .expect("invalid amount") .to_string(); - let token = Coin { denom, amount }; + let token = Coin { + denom: token.to_string(), + amount, + }; // this height should be that of the destination chain, not this chain let timeout_height = match args.timeout_height { @@ -1337,21 +1321,7 @@ pub async fn build_transfer< // Check that the token address exists on chain token_exists_or_err(token.clone(), args.tx.force, client).await?; // Check source balance - let (sub_prefix, balance_key) = match &args.sub_prefix { - Some(sp) => { - let sub_prefix = Address::decode(sp).map_err(|e| { - Error::Other(format!( - "The sub_prefix was not an Address: sub_prefix {}, error \ - {}", - sp, e - )) - })?; - let balance_key = - token::multitoken_balance_key(&sub_prefix, &source); - (Some(sub_prefix), balance_key) - } - None => (None, token::balance_key(&token, &source)), - }; + let balance_key = token::balance_key(&token, &source); // validate the amount given let validated_amount = validate_amount( @@ -1476,7 +1446,6 @@ pub async fn build_transfer< source: source.clone(), target: target.clone(), token: token.clone(), - sub_prefix: sub_prefix.clone(), amount: validated_amount, key: key.clone(), // Link the Transfer to the MASP Transaction by hash code diff --git a/test_utils/src/tx_data.rs b/test_utils/src/tx_data.rs index 878217bc2c..a985479237 100644 --- a/test_utils/src/tx_data.rs +++ b/test_utils/src/tx_data.rs @@ -2,7 +2,9 @@ //! Namada transaction. use borsh::{BorshDeserialize, BorshSerialize}; +use namada_core::types::address::Address; use namada_core::types::storage; +use namada_core::types::token::Amount; /// Represents an arbitrary write to storage at the specified key. This should /// be used alongside the test `tx_write.wasm`. @@ -23,3 +25,27 @@ pub struct TxWriteData { /// The bytes to be written. pub value: Vec, } + +/// Represents minting of the specified token. This should +/// be used alongside the test `tx_mint_tokens.wasm`. +#[derive( + Clone, + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + BorshSerialize, + BorshDeserialize, +)] +pub struct TxMintData { + /// The minter to mint the token + pub minter: Address, + /// The minted target + pub target: Address, + /// The minted token + pub token: Address, + /// The minted amount + pub amount: Amount, +} diff --git a/tests/src/e2e.rs b/tests/src/e2e.rs index ffe2ec0d86..10a6f69d6e 100644 --- a/tests/src/e2e.rs +++ b/tests/src/e2e.rs @@ -16,6 +16,5 @@ pub mod eth_bridge_tests; pub mod helpers; pub mod ibc_tests; pub mod ledger_tests; -pub mod multitoken_tests; pub mod setup; pub mod wallet_tests; diff --git a/tests/src/e2e/ibc_tests.rs b/tests/src/e2e/ibc_tests.rs index 15356d3f71..a7f89d9a9f 100644 --- a/tests/src/e2e/ibc_tests.rs +++ b/tests/src/e2e/ibc_tests.rs @@ -718,7 +718,6 @@ fn transfer_token( &Amount::native_whole(100000), port_channel_id_a, None, - None, )?; let packet = match get_event(test_a, height)? { Some(IbcEvent::SendPacket(event)) => event.packet, @@ -787,8 +786,6 @@ fn transfer_received_token( "--target", ALBERT, "--token", - NAM, - "--sub-prefix", &ibc_token, "--amount", &amount, @@ -830,10 +827,9 @@ fn transfer_back( test_b, BERTHA, &receiver, - NAM, + ibc_token, &Amount::native_whole(50000), port_channel_id_b, - Some(ibc_token), None, )?; let packet = match get_event(test_b, height)? { @@ -892,7 +888,6 @@ fn transfer_timeout( NAM, &Amount::native_whole(100000), port_channel_id_a, - None, Some(Duration::new(5, 0)), )?; let packet = match get_event(test_a, height)? { @@ -1026,7 +1021,6 @@ fn transfer( token: impl AsRef, amount: &Amount, port_channel_id: &PortChannelId, - sub_prefix: Option, timeout_sec: Option, ) -> Result { let rpc = get_actor_rpc(test, &Who::Validator(0)); @@ -1054,11 +1048,7 @@ fn transfer( "--node", &rpc, ]; - let sp = sub_prefix.clone().unwrap_or_default(); - if sub_prefix.is_some() { - tx_args.push("--sub-prefix"); - tx_args.push(&sp); - } + let timeout = timeout_sec.unwrap_or_default().as_secs().to_string(); if timeout_sec.is_some() { tx_args.push("--timeout-sec-offset"); @@ -1278,17 +1268,9 @@ fn check_balances( let ibc_token = ibc_token(denom).to_string(); let rpc_b = get_actor_rpc(test_b, &Who::Validator(0)); let query_args = vec![ - "balance", - "--owner", - BERTHA, - "--token", - NAM, - "--sub-prefix", - &ibc_token, - "--node", - &rpc_b, + "balance", "--owner", BERTHA, "--token", &ibc_token, "--node", &rpc_b, ]; - let expected = format!("nam with {}: 100000", ibc_token); + let expected = format!("{}: 100000", ibc_token); let mut client = run!(test_b, Bin::Client, query_args, Some(40))?; client.exp_string(&expected)?; client.assert_success(); @@ -1311,34 +1293,18 @@ fn check_balances_after_non_ibc( // Check the source let rpc = get_actor_rpc(test, &Who::Validator(0)); let query_args = vec![ - "balance", - "--owner", - BERTHA, - "--token", - NAM, - "--sub-prefix", - &ibc_token, - "--node", - &rpc, + "balance", "--owner", BERTHA, "--token", &ibc_token, "--node", &rpc, ]; - let expected = format!("nam with {}: 50000", ibc_token); + let expected = format!("{}: 50000", ibc_token); let mut client = run!(test, Bin::Client, query_args, Some(40))?; client.exp_string(&expected)?; client.assert_success(); // Check the traget let query_args = vec![ - "balance", - "--owner", - ALBERT, - "--token", - NAM, - "--sub-prefix", - &ibc_token, - "--node", - &rpc, + "balance", "--owner", ALBERT, "--token", &ibc_token, "--node", &rpc, ]; - let expected = format!("nam with {}: 50000", ibc_token); + let expected = format!("{}: 50000", ibc_token); let mut client = run!(test, Bin::Client, query_args, Some(40))?; client.exp_string(&expected)?; client.assert_success(); @@ -1377,17 +1343,9 @@ fn check_balances_after_back( let ibc_token = ibc_token(denom).to_string(); let rpc_b = get_actor_rpc(test_b, &Who::Validator(0)); let query_args = vec![ - "balance", - "--owner", - BERTHA, - "--token", - NAM, - "--sub-prefix", - &ibc_token, - "--node", - &rpc_b, + "balance", "--owner", BERTHA, "--token", &ibc_token, "--node", &rpc_b, ]; - let expected = format!("nam with {}: 0", ibc_token); + let expected = format!("{}: 0", ibc_token); let mut client = run!(test_b, Bin::Client, query_args, Some(40))?; client.exp_string(&expected)?; client.assert_success(); diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index ca7bd974c7..9797dadf58 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -422,7 +422,6 @@ fn ledger_txs_and_queries() -> Result<()> { source: find_address(&test, BERTHA).unwrap(), target: find_address(&test, ALBERT).unwrap(), token: find_address(&test, NAM).unwrap(), - sub_prefix: None, amount: token::DenominatedAmount { amount: token::Amount::native_whole(10), denom: token::NATIVE_MAX_DECIMAL_PLACES.into(), diff --git a/tests/src/e2e/multitoken_tests.rs b/tests/src/e2e/multitoken_tests.rs deleted file mode 100644 index 0f2b15d877..0000000000 --- a/tests/src/e2e/multitoken_tests.rs +++ /dev/null @@ -1,372 +0,0 @@ -//! Tests for multitoken functionality -use color_eyre::eyre::Result; -use namada_core::types::token; - -use super::helpers::get_actor_rpc; -use super::setup::constants::{ALBERT, BERTHA, CHRISTEL}; -use super::setup::{self, Who}; -use crate::e2e; -use crate::e2e::setup::constants::{ALBERT_KEY, BERTHA_KEY}; - -mod helpers; - -#[test] -fn test_multitoken_transfer_implicit_to_implicit() -> Result<()> { - let (test, _ledger) = e2e::helpers::setup_single_node_test()?; - - let rpc_addr = get_actor_rpc(&test, &Who::Validator(0)); - let multitoken_alias = helpers::init_multitoken_vp(&test, &rpc_addr)?; - - // establish a multitoken VP with the following balances - // - #atest5blah/tokens/red/balance/$albert_established = 100 - // - #atest5blah/tokens/red/balance/$bertha = 0 - - let multitoken_vp_addr = - e2e::helpers::find_address(&test, &multitoken_alias)?; - println!("Fake multitoken VP established at {}", multitoken_vp_addr); - - let albert_addr = e2e::helpers::find_address(&test, ALBERT)?; - let albert_starting_red_balance = token::Amount::native_whole(100_000_000); - helpers::mint_red_tokens( - &test, - &rpc_addr, - &multitoken_vp_addr, - &albert_addr, - &albert_starting_red_balance, - )?; - - let transfer_amount = token::Amount::native_whole(10_000_000); - - // make a transfer from Albert to Bertha, signed by Christel - this should - // be rejected - let mut unauthorized_transfer = helpers::attempt_red_tokens_transfer( - &test, - &rpc_addr, - &multitoken_alias, - ALBERT, - BERTHA, - CHRISTEL, - &transfer_amount, - )?; - unauthorized_transfer.exp_string("Transaction applied with result")?; - unauthorized_transfer.exp_string("Transaction is invalid")?; - unauthorized_transfer.exp_string(&format!("Rejected: {albert_addr}"))?; - unauthorized_transfer.assert_success(); - - let albert_balance = helpers::fetch_red_token_balance( - &test, - &rpc_addr, - &multitoken_alias, - ALBERT, - )?; - assert_eq!(albert_balance, albert_starting_red_balance); - - // make a transfer from Albert to Bertha, signed by Albert - this should - // be accepted - let mut authorized_transfer = helpers::attempt_red_tokens_transfer( - &test, - &rpc_addr, - &multitoken_alias, - ALBERT, - BERTHA, - ALBERT, - &token::Amount::native_whole(10_000_000), - )?; - authorized_transfer.exp_string("Transaction applied with result")?; - authorized_transfer.exp_string("Transaction is valid")?; - authorized_transfer.assert_success(); - - let albert_balance = helpers::fetch_red_token_balance( - &test, - &rpc_addr, - &multitoken_alias, - ALBERT, - )?; - assert_eq!( - albert_balance, - albert_starting_red_balance - transfer_amount - ); - Ok(()) -} - -#[test] -fn test_multitoken_transfer_established_to_implicit() -> Result<()> { - let (test, _ledger) = e2e::helpers::setup_single_node_test()?; - - let rpc_addr = get_actor_rpc(&test, &Who::Validator(0)); - let multitoken_alias = helpers::init_multitoken_vp(&test, &rpc_addr)?; - - let multitoken_vp_addr = - e2e::helpers::find_address(&test, &multitoken_alias)?; - println!("Fake multitoken VP established at {}", multitoken_vp_addr); - - // create an established account that Albert controls - let established_alias = "established"; - e2e::helpers::init_established_account( - &test, - &rpc_addr, - ALBERT, - ALBERT_KEY, - established_alias, - )?; - - let established_starting_red_balance = - token::Amount::native_whole(100_000_000); - // mint some red tokens for the established account - let established_addr = - e2e::helpers::find_address(&test, established_alias)?; - helpers::mint_red_tokens( - &test, - &rpc_addr, - &multitoken_vp_addr, - &established_addr, - &established_starting_red_balance, - )?; - - let transfer_amount = token::Amount::native_whole(10_000_000); - // attempt an unauthorized transfer to Albert from the established account - let mut unauthorized_transfer = helpers::attempt_red_tokens_transfer( - &test, - &rpc_addr, - &multitoken_alias, - established_alias, - BERTHA, - CHRISTEL, - &transfer_amount, - )?; - unauthorized_transfer.exp_string("Transaction applied with result")?; - unauthorized_transfer.exp_string("Transaction is invalid")?; - unauthorized_transfer - .exp_string(&format!("Rejected: {established_addr}"))?; - unauthorized_transfer.assert_success(); - - let established_balance = helpers::fetch_red_token_balance( - &test, - &rpc_addr, - &multitoken_alias, - established_alias, - )?; - assert_eq!(established_balance, established_starting_red_balance); - - // attempt an authorized transfer to Albert from the established account - let mut authorized_transfer = helpers::attempt_red_tokens_transfer( - &test, - &rpc_addr, - &multitoken_alias, - established_alias, - BERTHA, - ALBERT, - &transfer_amount, - )?; - authorized_transfer.exp_string("Transaction applied with result")?; - authorized_transfer.exp_string("Transaction is valid")?; - authorized_transfer.assert_success(); - - let established_balance = helpers::fetch_red_token_balance( - &test, - &rpc_addr, - &multitoken_alias, - established_alias, - )?; - assert_eq!( - established_balance, - established_starting_red_balance - transfer_amount - ); - - Ok(()) -} - -#[test] -fn test_multitoken_transfer_implicit_to_established() -> Result<()> { - let (test, _ledger) = e2e::helpers::setup_single_node_test()?; - - let rpc_addr = get_actor_rpc(&test, &Who::Validator(0)); - let multitoken_alias = helpers::init_multitoken_vp(&test, &rpc_addr)?; - - let multitoken_vp_addr = - e2e::helpers::find_address(&test, &multitoken_alias)?; - println!("Fake multitoken VP established at {}", multitoken_vp_addr); - - // create an established account controlled by Bertha - let established_alias = "established"; - e2e::helpers::init_established_account( - &test, - &rpc_addr, - BERTHA, - BERTHA_KEY, - established_alias, - )?; - - let albert_addr = e2e::helpers::find_address(&test, ALBERT)?; - let albert_starting_red_balance = token::Amount::native_whole(100_000_000); - helpers::mint_red_tokens( - &test, - &rpc_addr, - &multitoken_vp_addr, - &albert_addr, - &albert_starting_red_balance, - )?; - - let transfer_amount = token::Amount::native_whole(10_000_000); - - // attempt an unauthorized transfer from Albert to the established account - let mut unauthorized_transfer = helpers::attempt_red_tokens_transfer( - &test, - &rpc_addr, - &multitoken_alias, - ALBERT, - established_alias, - CHRISTEL, - &transfer_amount, - )?; - unauthorized_transfer.exp_string("Transaction applied with result")?; - unauthorized_transfer.exp_string("Transaction is invalid")?; - unauthorized_transfer.exp_string(&format!("Rejected: {albert_addr}"))?; - unauthorized_transfer.assert_success(); - - let albert_balance = helpers::fetch_red_token_balance( - &test, - &rpc_addr, - &multitoken_alias, - ALBERT, - )?; - assert_eq!(albert_balance, albert_starting_red_balance); - - // attempt an authorized transfer to Albert from the established account - let mut authorized_transfer = helpers::attempt_red_tokens_transfer( - &test, - &rpc_addr, - &multitoken_alias, - ALBERT, - established_alias, - ALBERT, - &transfer_amount, - )?; - authorized_transfer.exp_string("Transaction applied with result")?; - authorized_transfer.exp_string("Transaction is valid")?; - authorized_transfer.assert_success(); - - let albert_balance = helpers::fetch_red_token_balance( - &test, - &rpc_addr, - &multitoken_alias, - ALBERT, - )?; - assert_eq!( - albert_balance, - albert_starting_red_balance - transfer_amount - ); - - Ok(()) -} - -#[test] -fn test_multitoken_transfer_established_to_established() -> Result<()> { - let (test, _ledger) = e2e::helpers::setup_single_node_test()?; - - let rpc_addr = get_actor_rpc(&test, &Who::Validator(0)); - let multitoken_alias = helpers::init_multitoken_vp(&test, &rpc_addr)?; - - let multitoken_vp_addr = - e2e::helpers::find_address(&test, &multitoken_alias)?; - println!("Fake multitoken VP established at {}", multitoken_vp_addr); - - // create an established account that Albert controls - let established_alias = "established"; - e2e::helpers::init_established_account( - &test, - &rpc_addr, - ALBERT, - ALBERT_KEY, - established_alias, - )?; - - let established_starting_red_balance = - token::Amount::native_whole(100_000_000); - // mint some red tokens for the established account - let established_addr = - e2e::helpers::find_address(&test, established_alias)?; - helpers::mint_red_tokens( - &test, - &rpc_addr, - &multitoken_vp_addr, - &established_addr, - &established_starting_red_balance, - )?; - - // create another established account to receive transfers - let receiver_alias = "receiver"; - e2e::helpers::init_established_account( - &test, - &rpc_addr, - BERTHA, - BERTHA_KEY, - receiver_alias, - )?; - - let established_starting_red_balance = - token::Amount::native_whole(100_000_000); - // mint some red tokens for the established account - let established_addr = - e2e::helpers::find_address(&test, established_alias)?; - helpers::mint_red_tokens( - &test, - &rpc_addr, - &multitoken_vp_addr, - &established_addr, - &established_starting_red_balance, - )?; - - let transfer_amount = token::Amount::native_whole(10_000_000); - - // attempt an unauthorized transfer - let mut unauthorized_transfer = helpers::attempt_red_tokens_transfer( - &test, - &rpc_addr, - &multitoken_alias, - established_alias, - receiver_alias, - CHRISTEL, - &transfer_amount, - )?; - unauthorized_transfer.exp_string("Transaction applied with result")?; - unauthorized_transfer.exp_string("Transaction is invalid")?; - unauthorized_transfer - .exp_string(&format!("Rejected: {established_addr}"))?; - unauthorized_transfer.assert_success(); - - let established_balance = helpers::fetch_red_token_balance( - &test, - &rpc_addr, - &multitoken_alias, - established_alias, - )?; - assert_eq!(established_balance, established_starting_red_balance); - - // attempt an authorized transfer which should succeed - let mut authorized_transfer = helpers::attempt_red_tokens_transfer( - &test, - &rpc_addr, - &multitoken_alias, - established_alias, - receiver_alias, - ALBERT, - &transfer_amount, - )?; - authorized_transfer.exp_string("Transaction applied with result")?; - authorized_transfer.exp_string("Transaction is valid")?; - authorized_transfer.assert_success(); - - let established_balance = helpers::fetch_red_token_balance( - &test, - &rpc_addr, - &multitoken_alias, - established_alias, - )?; - assert_eq!( - established_balance, - established_starting_red_balance - transfer_amount - ); - - Ok(()) -} diff --git a/tests/src/e2e/multitoken_tests/helpers.rs b/tests/src/e2e/multitoken_tests/helpers.rs deleted file mode 100644 index 27228cf266..0000000000 --- a/tests/src/e2e/multitoken_tests/helpers.rs +++ /dev/null @@ -1,189 +0,0 @@ -//! Helpers for use in multitoken tests. -use std::path::PathBuf; - -use borsh::BorshSerialize; -use color_eyre::eyre::Result; -use eyre::Context; -use namada_core::types::address::Address; -use namada_core::types::token::NATIVE_MAX_DECIMAL_PLACES; -use namada_core::types::{storage, token}; -use namada_test_utils::tx_data::TxWriteData; -use namada_test_utils::TestWasms; -use namada_tx_prelude::storage::KeySeg; -use rand::Rng; -use regex::Regex; - -use super::setup::constants::NAM; -use super::setup::{Bin, NamadaCmd, Test}; -use crate::e2e::setup::constants::ALBERT; -use crate::run; - -const MULTITOKEN_KEY_SEGMENT: &str = "tokens"; -const BALANCE_KEY_SEGMENT: &str = "balance"; -const RED_TOKEN_KEY_SEGMENT: &str = "red"; -const MULTITOKEN_RED_TOKEN_SUB_PREFIX: &str = "tokens/red"; - -const ARBITRARY_SIGNER: &str = ALBERT; - -/// Initializes a VP to represent a multitoken account. -pub fn init_multitoken_vp(test: &Test, rpc_addr: &str) -> Result { - // we use a VP that always returns true for the multitoken VP here, as we - // are testing out the VPs of the sender and receiver of multitoken - // transactions here - not any multitoken VP itself - let multitoken_vp_wasm_path = - TestWasms::VpAlwaysTrue.path().to_string_lossy().to_string(); - let multitoken_alias = "multitoken"; - - let init_account_args = vec![ - "init-account", - "--source", - ARBITRARY_SIGNER, - "--public-key", - // Value obtained from - // `namada::types::key::ed25519::tests::gen_keypair` - "001be519a321e29020fa3cbfbfd01bd5e92db134305609270b71dace25b5a21168", - "--code-path", - &multitoken_vp_wasm_path, - "--alias", - multitoken_alias, - "--gas-amount", - "0", - "--gas-limit", - "0", - "--gas-token", - NAM, - "--ledger-address", - rpc_addr, - ]; - let mut client_init_account = - run!(test, Bin::Client, init_account_args, Some(40))?; - client_init_account.exp_string("Transaction is valid.")?; - client_init_account.exp_string("Transaction applied")?; - client_init_account.assert_success(); - Ok(multitoken_alias.to_string()) -} - -/// Generates a random path within the `test` directory. -fn generate_random_test_dir_path(test: &Test) -> PathBuf { - let rng = rand::thread_rng(); - let random_string: String = rng - .sample_iter(&rand::distributions::Alphanumeric) - .take(24) - .map(char::from) - .collect(); - test.test_dir.path().join(random_string) -} - -/// Writes `contents` to a random path within the `test` directory, and return -/// the path. -pub fn write_test_file( - test: &Test, - contents: impl AsRef<[u8]>, -) -> Result { - let path = generate_random_test_dir_path(test); - std::fs::write(&path, contents)?; - Ok(path) -} - -/// Mint red tokens to the given address. -pub fn mint_red_tokens( - test: &Test, - rpc_addr: &str, - multitoken: &Address, - owner: &Address, - amount: &token::Amount, -) -> Result<()> { - let red_balance_key = storage::Key::from(multitoken.to_db_key()) - .push(&MULTITOKEN_KEY_SEGMENT.to_owned())? - .push(&RED_TOKEN_KEY_SEGMENT.to_owned())? - .push(&BALANCE_KEY_SEGMENT.to_owned())? - .push(owner)?; - - let tx_code_path = TestWasms::TxWriteStorageKey.path(); - let tx_data_path = write_test_file( - test, - TxWriteData { - key: red_balance_key, - value: amount.try_to_vec()?, - } - .try_to_vec()?, - )?; - - let tx_data_path = tx_data_path.to_string_lossy().to_string(); - let tx_code_path = tx_code_path.to_string_lossy().to_string(); - let tx_args = vec![ - "tx", - "--signer", - ARBITRARY_SIGNER, - "--code-path", - &tx_code_path, - "--data-path", - &tx_data_path, - "--ledger-address", - rpc_addr, - ]; - let mut client_tx = run!(test, Bin::Client, tx_args, Some(40))?; - client_tx.exp_string("Transaction is valid.")?; - client_tx.exp_string("Transaction applied")?; - client_tx.assert_success(); - Ok(()) -} - -pub fn attempt_red_tokens_transfer( - test: &Test, - rpc_addr: &str, - multitoken: &str, - from: &str, - to: &str, - signer: &str, - amount: &token::Amount, -) -> Result { - let amount = amount.to_string_native(); - let transfer_args = vec![ - "transfer", - "--token", - multitoken, - "--sub-prefix", - MULTITOKEN_RED_TOKEN_SUB_PREFIX, - "--source", - from, - "--target", - to, - "--signer", - signer, - "--amount", - &amount, - "--ledger-address", - rpc_addr, - ]; - run!(test, Bin::Client, transfer_args, Some(40)) -} - -pub fn fetch_red_token_balance( - test: &Test, - rpc_addr: &str, - multitoken_alias: &str, - owner_alias: &str, -) -> Result { - let balance_args = vec![ - "balance", - "--owner", - owner_alias, - "--token", - multitoken_alias, - "--sub-prefix", - MULTITOKEN_RED_TOKEN_SUB_PREFIX, - "--ledger-address", - rpc_addr, - ]; - let mut client_balance = run!(test, Bin::Client, balance_args, Some(40))?; - let (_, matched) = client_balance.exp_regex(&format!( - r"{MULTITOKEN_RED_TOKEN_SUB_PREFIX}: (\d*\.?\d+)" - ))?; - let decimal_regex = Regex::new(r"(\d*\.?\d+)").unwrap(); - println!("Got balance for {}: {}", owner_alias, matched); - let decimal = decimal_regex.find(&matched).unwrap().as_str(); - client_balance.assert_success(); - token::Amount::from_str(decimal, NATIVE_MAX_DECIMAL_PLACES) - .wrap_err(format!("Failed to parse {}", matched)) -} diff --git a/tests/src/vm_host_env/ibc.rs b/tests/src/vm_host_env/ibc.rs index e3cbce8f30..9baafd6fc2 100644 --- a/tests/src/vm_host_env/ibc.rs +++ b/tests/src/vm_host_env/ibc.rs @@ -765,5 +765,5 @@ pub fn packet_from_message( pub fn balance_key_with_ibc_prefix(denom: String, owner: &Address) -> Key { let ibc_token = ibc_token(denom); - token::multitoken_balance_key(&ibc_token, owner) + token::balance_key(&ibc_token, owner) } diff --git a/tests/src/vm_host_env/mod.rs b/tests/src/vm_host_env/mod.rs index 13d371ce0c..a8c72cf9b7 100644 --- a/tests/src/vm_host_env/mod.rs +++ b/tests/src/vm_host_env/mod.rs @@ -29,7 +29,6 @@ mod tests { use namada::ledger::tx_env::TxEnv; use namada::proto::{Code, Data, Section, Signature, Tx}; use namada::types::address::{Address, InternalAddress}; - use namada::types::chain::ChainId; use namada::types::hash::Hash; use namada::types::key::*; use namada::types::storage::{self, BlockHash, BlockHeight, Key, KeySeg}; @@ -1191,15 +1190,12 @@ mod tests { // the origin-specific token let denom = format!("{}/{}/{}", port_id, channel_id, token); let ibc_token = ibc_storage::ibc_token(&denom); - let balance_key = token::multitoken_balance_key(&ibc_token, &sender); + let balance_key = token::balance_key(&ibc_token, &sender); let init_bal = Amount::native_whole(100); writes.insert(balance_key.clone(), init_bal.try_to_vec().unwrap()); - let minted_key = token::multitoken_balance_key( - &ibc_token, - &Address::Internal(InternalAddress::Mint), - ); + let minted_key = token::minted_balance_key(&ibc_token); writes.insert(minted_key.clone(), init_bal.try_to_vec().unwrap()); - let minter_key = token::multitoken_minter_key(&ibc_token); + let minter_key = token::minter_key(&ibc_token); writes.insert( minter_key, Address::Internal(InternalAddress::Ibc) @@ -1314,7 +1310,7 @@ mod tests { // Check if the token was minted let denom = format!("{}/{}/{}", port_id, channel_id, token); let ibc_token = ibc::ibc_token(&denom); - let minted_key = token::multitoken_minted_key(&ibc_token); + let minted_key = token::minted_balance_key(&ibc_token); let result = ibc::validate_multitoken_vp_from_tx(&env, &tx, &minted_key); assert!(result.expect("token validation failed unexpectedly")); diff --git a/tests/src/vm_host_env/tx.rs b/tests/src/vm_host_env/tx.rs index bb66ee71b7..d3da806ca5 100644 --- a/tests/src/vm_host_env/tx.rs +++ b/tests/src/vm_host_env/tx.rs @@ -177,13 +177,9 @@ impl TestTxEnv { &mut self, target: &Address, token: &Address, - sub_prefix: Option
, amount: token::Amount, ) { - let storage_key = match &sub_prefix { - Some(sp) => token::multitoken_balance_key(sp, target), - None => token::balance_key(token, target), - }; + let storage_key = token::balance_key(token, target); self.wl_storage .storage .write(&storage_key, amount.try_to_vec().unwrap()) diff --git a/tx_prelude/src/ibc.rs b/tx_prelude/src/ibc.rs index 12747e6a15..28694aa7f7 100644 --- a/tx_prelude/src/ibc.rs +++ b/tx_prelude/src/ibc.rs @@ -76,23 +76,22 @@ impl IbcStorageContext for Ctx { src: &Address, dest: &Address, token: &Address, - sub_prefix: Option
, amount: Amount, ) -> std::result::Result<(), Self::Error> { - transfer(self, src, dest, token, sub_prefix, amount, &None, &None) + transfer(self, src, dest, token, amount, &None, &None, &None) } fn mint_token( &mut self, target: &Address, - sub_prefix: &Address, + token: &Address, amount: Amount, ) -> Result<(), Self::Error> { mint( self, &Address::Internal(InternalAddress::Ibc), target, - sub_prefix, + token, amount, ) } @@ -100,10 +99,10 @@ impl IbcStorageContext for Ctx { fn burn_token( &mut self, target: &Address, - sub_prefix: &Address, + token: &Address, amount: Amount, ) -> Result<(), Self::Error> { - burn(self, target, sub_prefix, amount) + burn(self, target, token, amount) } fn get_height(&self) -> std::result::Result { diff --git a/tx_prelude/src/token.rs b/tx_prelude/src/token.rs index e85edf6b7a..3cf1d8ead3 100644 --- a/tx_prelude/src/token.rs +++ b/tx_prelude/src/token.rs @@ -14,21 +14,14 @@ pub fn transfer( src: &Address, dest: &Address, token: &Address, - sub_prefix: Option
, amount: DenominatedAmount, key: &Option, shielded_hash: &Option, shielded: &Option, ) -> TxResult { if amount.amount != Amount::default() { - let src_key = match &sub_prefix { - Some(sub_prefix) => token::multitoken_balance_key(sub_prefix, src), - None => token::balance_key(token, src), - }; - let dest_key = match &sub_prefix { - Some(sub_prefix) => token::multitoken_balance_key(sub_prefix, dest), - None => token::balance_key(token, dest), - }; + let src_key = token::balance_key(token, src); + let dest_key = token::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)); @@ -63,8 +56,6 @@ pub fn transfer( source: src.clone(), target: dest.clone(), token: token.clone(), - // todo: build asset types for multitokens - sub_prefix: None, amount, key: key.clone(), shielded: *shielded_hash, @@ -94,21 +85,21 @@ pub fn mint( ctx: &mut Ctx, minter: &Address, target: &Address, - sub_prefix: &Address, + token: &Address, amount: Amount, ) -> TxResult { - let target_key = token::multitoken_balance_key(sub_prefix, target); + let target_key = token::balance_key(token, target); let mut target_bal: Amount = ctx.read(&target_key)?.unwrap_or_default(); target_bal.receive(&amount); - let minted_key = token::multitoken_minted_key(sub_prefix); + let minted_key = token::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::multitoken_minter_key(sub_prefix); + let minter_key = token::minter_key(token); ctx.write(&minter_key, minter)?; Ok(()) @@ -118,15 +109,15 @@ pub fn mint( pub fn burn( ctx: &mut Ctx, target: &Address, - sub_prefix: &Address, + token: &Address, amount: Amount, ) -> TxResult { - let target_key = token::multitoken_balance_key(sub_prefix, target); + let target_key = token::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::multitoken_minted_key(sub_prefix); + let minted_key = token::minted_balance_key(token); let mut minted_bal: Amount = ctx.read(&minted_key)?.unwrap_or_default(); minted_bal.spend(&amount); diff --git a/wasm/wasm_source/src/tx_bond.rs b/wasm/wasm_source/src/tx_bond.rs index df55270ceb..a32455412e 100644 --- a/wasm/wasm_source/src/tx_bond.rs +++ b/wasm/wasm_source/src/tx_bond.rs @@ -102,7 +102,7 @@ mod tests { // Ensure that the bond's source has enough tokens for the bond let target = bond.source.as_ref().unwrap_or(&bond.validator); let native_token = tx_env.wl_storage.storage.native_token.clone(); - tx_env.credit_tokens(target, &native_token, None, bond.amount); + tx_env.credit_tokens(target, &native_token, bond.amount); native_token }); diff --git a/wasm/wasm_source/src/tx_transfer.rs b/wasm/wasm_source/src/tx_transfer.rs index d2e3dc314f..33899640c4 100644 --- a/wasm/wasm_source/src/tx_transfer.rs +++ b/wasm/wasm_source/src/tx_transfer.rs @@ -15,7 +15,6 @@ fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { source, target, token, - sub_prefix, amount, key, shielded: shielded_hash, @@ -34,7 +33,6 @@ fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { &source, &target, &token, - sub_prefix, amount, &key, &shielded_hash, diff --git a/wasm/wasm_source/src/tx_unbond.rs b/wasm/wasm_source/src/tx_unbond.rs index 7fb1de8f4f..be5dac3ea1 100644 --- a/wasm/wasm_source/src/tx_unbond.rs +++ b/wasm/wasm_source/src/tx_unbond.rs @@ -109,12 +109,7 @@ mod tests { // bond first. // First, credit the bond's source with the initial stake, // before we initialize the bond below - tx_env.credit_tokens( - source, - &native_token, - None, - initial_stake, - ); + tx_env.credit_tokens(source, &native_token, initial_stake); } native_token }); diff --git a/wasm/wasm_source/src/tx_withdraw.rs b/wasm/wasm_source/src/tx_withdraw.rs index 85ab2b3af4..20b202bdb6 100644 --- a/wasm/wasm_source/src/tx_withdraw.rs +++ b/wasm/wasm_source/src/tx_withdraw.rs @@ -113,12 +113,7 @@ mod tests { // bond first. // First, credit the bond's source with the initial stake, // before we initialize the bond below - tx_env.credit_tokens( - source, - &native_token, - None, - initial_stake, - ); + tx_env.credit_tokens(source, &native_token, initial_stake); } native_token }); diff --git a/wasm/wasm_source/src/vp_implicit.rs b/wasm/wasm_source/src/vp_implicit.rs index 70195a9c69..022a22700a 100644 --- a/wasm/wasm_source/src/vp_implicit.rs +++ b/wasm/wasm_source/src/vp_implicit.rs @@ -30,9 +30,7 @@ impl<'a> From<&'a storage::Key> for KeyType<'a> { fn from(key: &'a storage::Key) -> KeyType<'a> { if let Some(address) = key::is_pk_key(key) { Self::Pk(address) - } else if let Some(address) = token::is_any_token_balance_key(key) { - Self::Token(address) - } else if let Some((_, address)) = token::is_multitoken_balance_key(key) + } else if let Some((_, address)) = token::is_any_token_balance_key(key) { Self::Token { owner } } else if proof_of_stake::is_pos_key(key) { @@ -354,7 +352,7 @@ mod tests { // Credit the tokens to the source before running the transaction to be // able to transfer from it - tx_env.credit_tokens(&source, &token, None, amount); + tx_env.credit_tokens(&source, &token, amount); let amount = token::DenominatedAmount { amount, @@ -368,7 +366,6 @@ mod tests { &source, address, &token, - None, amount, &None, &None, @@ -433,15 +430,15 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.credit_tokens(&vp_owner, &token, amount); // write the denomination of NAM into storage storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); + // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |_address| { // Bond the tokens, then unbond some of them @@ -513,12 +510,11 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.credit_tokens(&vp_owner, &token, amount); // write the denomination of NAM into storage storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); @@ -574,12 +570,11 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.credit_tokens(&vp_owner, &token, amount); // write the denomination of NAM into storage storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); @@ -597,7 +592,6 @@ mod tests { address, &target, &token, - None, amount, &None, &None, @@ -637,12 +631,11 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.credit_tokens(&vp_owner, &token, amount); // write the denomination of NAM into storage storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); @@ -661,7 +654,6 @@ mod tests { address, &target, &token, - None, amount, &None, &None, @@ -709,12 +701,11 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&source, &token, None, amount); + tx_env.credit_tokens(&source, &token, amount); // write the denomination of NAM into storage storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); @@ -733,7 +724,6 @@ mod tests { &source, &target, &token, - None, amount, &None, &None, diff --git a/wasm/wasm_source/src/vp_testnet_faucet.rs b/wasm/wasm_source/src/vp_testnet_faucet.rs index 8e002663ee..7d5a4d6a0b 100644 --- a/wasm/wasm_source/src/vp_testnet_faucet.rs +++ b/wasm/wasm_source/src/vp_testnet_faucet.rs @@ -155,7 +155,7 @@ mod tests { // Credit the tokens to the source before running the transaction to be // able to transfer from it - tx_env.credit_tokens(&source, &token, None, amount); + tx_env.credit_tokens(&source, &token, amount); let amount = token::DenominatedAmount { amount, @@ -170,7 +170,6 @@ mod tests { &source, address, &token, - None, amount, &None, &None, @@ -315,7 +314,7 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.credit_tokens(&vp_owner, &token, amount); tx_env.commit_genesis(); let amount = token::DenominatedAmount { amount, @@ -325,7 +324,7 @@ mod tests { // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { // Apply transfer in a transaction - tx_host_env::token::transfer(tx::ctx(), address, &target, &token, None, amount, &None, &None, &None).unwrap(); + tx_host_env::token::transfer(tx::ctx(), address, &target, &token, amount, &None, &None, &None).unwrap(); }); let vp_env = vp_host_env::take(); @@ -361,9 +360,9 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.credit_tokens(&vp_owner, &token, amount); // write the denomination of NAM into storage - storage_api::token::write_denom(&mut tx_env.wl_storage, &token, None, token::NATIVE_MAX_DECIMAL_PLACES.into()).unwrap(); + storage_api::token::write_denom(&mut tx_env.wl_storage, &token, token::NATIVE_MAX_DECIMAL_PLACES.into()).unwrap(); tx_env.commit_genesis(); // Construct a PoW solution like a client would @@ -383,7 +382,7 @@ mod tests { let valid = solution.validate(tx::ctx(), address, target.clone()).unwrap(); assert!(valid); // Apply transfer in a transaction - tx_host_env::token::transfer(tx::ctx(), address, &target, &token, None, amount, &None, &None, &None).unwrap(); + tx_host_env::token::transfer(tx::ctx(), address, &target, &token, amount, &None, &None, &None).unwrap(); }); let mut vp_env = vp_host_env::take(); diff --git a/wasm/wasm_source/src/vp_token.rs b/wasm/wasm_source/src/vp_token.rs index e59b0864b7..98ad53d8d2 100644 --- a/wasm/wasm_source/src/vp_token.rs +++ b/wasm/wasm_source/src/vp_token.rs @@ -51,13 +51,19 @@ fn token_checks( keys_touched: &BTreeSet, verifiers: &BTreeSet
, ) -> VpResult { - let mut change = token::Change::default(); for key in keys_touched.iter() { let owner: Option<&Address> = token::is_balance_key(token, key); match owner { None => { - if token::is_total_supply_key(key, token) { + if key.segments.get(0) == Some(&token.to_db_key()) { + // Unknown changes to this address space are disallowed, but + // unknown changes anywhere else are permitted + return reject(); + } + } + Some(owner) => { + if token::is_minted_balance_key(token, key) { // check if total supply is changed, which it should never // be from a tx let total_pre: token::Amount = ctx.read_pre(key)?.unwrap(); @@ -66,30 +72,25 @@ fn token_checks( if total_pre != total_post { return reject(); } - } else if key.segments.get(0) == Some(&token.to_db_key()) { - // Unknown changes to this address space are disallowed, but - // unknown changes anywhere else are permitted - return reject(); - } - } - Some(owner) => { - // accumulate the change - let pre: token::Amount = ctx.read_pre(key)?.unwrap_or_default(); - let post: token::Amount = - ctx.read_post(key)?.unwrap_or_default(); - let this_change = post.change() - pre.change(); - change += this_change; - // make sure that the spender approved the transaction - if !(this_change.non_negative() - || verifiers.contains(owner) - || *owner == address::masp()) - { - return reject(); + } else { + // accumulate the change + let pre: token::Amount = + ctx.read_pre(key)?.unwrap_or_default(); + let post: token::Amount = + ctx.read_post(key)?.unwrap_or_default(); + // make sure that the spender approved the transaction + if post < pre + && !(verifiers.contains(owner) + || *owner == address::masp()) + { + return reject(); + } } } } } - Ok(change.is_zero()) + // The total change should be validated by multitoken VP + Ok(true) } #[cfg(test)] @@ -228,7 +229,7 @@ mod tests { // Commit the initial state tx_env.commit_tx_and_block(); - let total_supply_key = token::total_supply_key(&token); + let total_supply_key = token::minted_balance_key(&token); // Initialize VP environment from a transaction vp_host_env::init_from_tx(token.clone(), tx_env, |_address| { diff --git a/wasm/wasm_source/src/vp_user.rs b/wasm/wasm_source/src/vp_user.rs index dd86f09cbd..e605ce28d8 100644 --- a/wasm/wasm_source/src/vp_user.rs +++ b/wasm/wasm_source/src/vp_user.rs @@ -24,10 +24,7 @@ enum KeyType<'a> { impl<'a> From<&'a storage::Key> for KeyType<'a> { fn from(key: &'a storage::Key) -> KeyType<'a> { - if let Some(address) = token::is_any_token_balance_key(key) { - Self::Token(address) - } else if let Some((_, address)) = token::is_multitoken_balance_key(key) - { + if let Some([_, address]) = token::is_any_token_balance_key(key) { Self::Token { owner } } else if proof_of_stake::is_pos_key(key) { Self::PoS @@ -238,12 +235,11 @@ mod tests { // Credit the tokens to the source before running the transaction to be // able to transfer from it - tx_env.credit_tokens(&source, &token, None, amount); + tx_env.credit_tokens(&source, &token, amount); // write the denomination of NAM into storage storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); @@ -260,7 +256,6 @@ mod tests { &source, address, &token, - None, amount, &None, &None, @@ -306,7 +301,7 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.credit_tokens(&vp_owner, &token, amount); let amount = token::DenominatedAmount { amount, @@ -320,7 +315,6 @@ mod tests { address, &target, &token, - None, amount, &None, &None, @@ -360,12 +354,11 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.credit_tokens(&vp_owner, &token, amount); // write the denomination of NAM into storage storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); @@ -385,7 +378,6 @@ mod tests { address, &target, &token, - None, amount, &None, &None, @@ -465,7 +457,7 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.credit_tokens(&vp_owner, &token, amount); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |_address| { @@ -546,7 +538,7 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.credit_tokens(&vp_owner, &token, amount); tx_env.write_public_key(&vp_owner, &public_key); @@ -598,7 +590,7 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&source, &token, None, amount); + tx_env.credit_tokens(&source, &token, amount); let amount = token::DenominatedAmount { amount, @@ -614,7 +606,6 @@ mod tests { &source, &target, &token, - None, amount, &None, &None, diff --git a/wasm/wasm_source/src/vp_validator.rs b/wasm/wasm_source/src/vp_validator.rs index e79f51b629..768f025d78 100644 --- a/wasm/wasm_source/src/vp_validator.rs +++ b/wasm/wasm_source/src/vp_validator.rs @@ -26,11 +26,8 @@ enum KeyType<'a> { impl<'a> From<&'a storage::Key> for KeyType<'a> { fn from(key: &'a storage::Key) -> KeyType<'a> { - if let Some(address) = token::is_any_token_balance_key(key) { + if let Some([_, address]) = token::is_any_token_balance_key(key) { Self::Token(address) - } else if let Some((_, address)) = token::is_multitoken_balance_key(key) - { - Self::Token { owner } } else if proof_of_stake::is_pos_key(key) { Self::PoS } else if gov_storage::is_vote_key(key) { @@ -245,12 +242,11 @@ mod tests { // Credit the tokens to the source before running the transaction to be // able to transfer from it - tx_env.credit_tokens(&source, &token, None, amount); + tx_env.credit_tokens(&source, &token, amount); // write the denomination of NAM into storage storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); @@ -267,7 +263,6 @@ mod tests { &source, address, &token, - None, amount, &None, &None, @@ -305,7 +300,7 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.credit_tokens(&vp_owner, &token, amount); let amount = token::DenominatedAmount { amount, denom: token::NATIVE_MAX_DECIMAL_PLACES.into(), @@ -314,7 +309,6 @@ mod tests { storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); @@ -327,7 +321,6 @@ mod tests { address, &target, &token, - None, amount, &None, &None, @@ -367,12 +360,11 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.credit_tokens(&vp_owner, &token, amount); // write the denomination of NAM into storage storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); @@ -391,7 +383,6 @@ mod tests { address, &target, &token, - None, amount, &None, &None, @@ -463,12 +454,11 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.credit_tokens(&vp_owner, &token, amount); // write the denomination of NAM into storage storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); @@ -550,12 +540,11 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&vp_owner, &token, None, amount); + tx_env.credit_tokens(&vp_owner, &token, amount); // write the denomination of NAM into storage storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); @@ -616,7 +605,7 @@ mod tests { // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it - tx_env.credit_tokens(&source, &token, None, amount); + tx_env.credit_tokens(&source, &token, amount); let amount = token::DenominatedAmount { amount, denom: token::NATIVE_MAX_DECIMAL_PLACES.into(), @@ -631,7 +620,6 @@ mod tests { &source, &target, &token, - None, amount, &None, &None, diff --git a/wasm_for_tests/wasm_source/src/lib.rs b/wasm_for_tests/wasm_source/src/lib.rs index 3822a8a01f..561d43fc2e 100644 --- a/wasm_for_tests/wasm_source/src/lib.rs +++ b/wasm_for_tests/wasm_source/src/lib.rs @@ -16,7 +16,8 @@ pub mod main { #[transaction] fn apply_tx(_ctx: &mut Ctx, tx_data: Tx) -> TxResult { - let len = usize::try_from_slice(&tx_data.data().as_ref().unwrap()[..]).unwrap(); + let len = usize::try_from_slice(&tx_data.data().as_ref().unwrap()[..]) + .unwrap(); log_string(format!("allocate len {}", len)); let bytes: Vec = vec![6_u8; len]; // use the variable to prevent it from compiler optimizing it away @@ -51,7 +52,9 @@ pub mod main { #[transaction] fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { // Allocates a memory of size given from the `tx_data (usize)` - let key = storage::Key::try_from_slice(&tx_data.data().as_ref().unwrap()[..]).unwrap(); + let key = + storage::Key::try_from_slice(&tx_data.data().as_ref().unwrap()[..]) + .unwrap(); log_string(format!("key {}", key)); let _result: Vec = ctx.read(&key)?.unwrap(); Ok(()) @@ -64,7 +67,7 @@ pub mod main { use borsh::BorshDeserialize; use namada_test_utils::tx_data::TxWriteData; use namada_tx_prelude::{ - log_string, transaction, Ctx, StorageRead, StorageWrite, TxResult, Tx, + log_string, transaction, Ctx, StorageRead, StorageWrite, Tx, TxResult, }; const TX_NAME: &str = "tx_write"; @@ -129,29 +132,22 @@ pub mod main { /// token's VP. #[cfg(feature = "tx_mint_tokens")] pub mod main { + use namada_test_utils::tx_data::TxMintData; use namada_tx_prelude::*; #[transaction] fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { let signed = tx_data; - let transfer = - token::Transfer::try_from_slice(&signed.data().unwrap()[..]).unwrap(); - log_string(format!("apply_tx called to mint tokens: {:#?}", transfer)); - let token::Transfer { - source: _, + let mint_data = + TxMintData::try_from_slice(&signed.data().unwrap()[..]).unwrap(); + log_string(format!("apply_tx called to mint tokens: {:#?}", mint_data)); + let TxMintData { + minter, target, token, - sub_prefix: _, amount, - key: _, - shielded: _, - } = transfer; - let target_key = token::balance_key(&token, &target); - let mut target_bal: token::Amount = - ctx.read(&target_key)?.unwrap_or_default(); - target_bal.receive(&amount.amount); - ctx.write(&target_key, target_bal)?; - Ok(()) + } = mint_data; + token::mint(ctx, &minter, &target, &token, amount) } } @@ -204,8 +200,12 @@ pub mod main { _verifiers: BTreeSet
, ) -> VpResult { use validity_predicate::EvalVp; - let EvalVp { vp_code_hash, input }: EvalVp = - EvalVp::try_from_slice(&tx_data.data().as_ref().unwrap()[..]).unwrap(); + let EvalVp { + vp_code_hash, + input, + }: EvalVp = + EvalVp::try_from_slice(&tx_data.data().as_ref().unwrap()[..]) + .unwrap(); ctx.eval(vp_code_hash, input) } } @@ -224,7 +224,8 @@ pub mod main { _keys_changed: BTreeSet, _verifiers: BTreeSet
, ) -> VpResult { - let len = usize::try_from_slice(&tx_data.data().as_ref().unwrap()[..]).unwrap(); + let len = usize::try_from_slice(&tx_data.data().as_ref().unwrap()[..]) + .unwrap(); log_string(format!("allocate len {}", len)); let bytes: Vec = vec![6_u8; len]; // use the variable to prevent it from compiler optimizing it away @@ -248,7 +249,9 @@ pub mod main { _verifiers: BTreeSet
, ) -> VpResult { // Allocates a memory of size given from the `tx_data (usize)` - let key = storage::Key::try_from_slice(&tx_data.data().as_ref().unwrap()[..]).unwrap(); + let key = + storage::Key::try_from_slice(&tx_data.data().as_ref().unwrap()[..]) + .unwrap(); log_string(format!("key {}", key)); let _result: Vec = ctx.read_pre(&key)?.unwrap(); accept() From fc5eab4ad68ed95d95eb3a9afcdb2a7aa46001f5 Mon Sep 17 00:00:00 2001 From: yito88 Date: Fri, 9 Jun 2023 00:01:29 +0900 Subject: [PATCH 013/120] add multitoken VP file --- shared/src/ledger/native_vp/multitoken.rs | 95 +++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 shared/src/ledger/native_vp/multitoken.rs diff --git a/shared/src/ledger/native_vp/multitoken.rs b/shared/src/ledger/native_vp/multitoken.rs new file mode 100644 index 0000000000..c80e982afb --- /dev/null +++ b/shared/src/ledger/native_vp/multitoken.rs @@ -0,0 +1,95 @@ +//! Native VP for multitokens + +use std::collections::{BTreeSet, HashMap}; + +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; +use crate::types::token::{ + is_any_token_balance_key, is_minted_balance_key, minter_key, Amount, +}; +use crate::vm::WasmCacheAccess; + +#[allow(missing_docs)] +#[derive(Error, Debug)] +pub enum Error { + #[error("Native VP error: {0}")] + NativeVpError(#[from] native_vp::Error), +} + +/// Multitoken functions result +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, + CA: WasmCacheAccess, +{ + /// Context to interact with the host structures. + pub ctx: Ctx<'a, DB, H, CA>, +} + +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, + CA: 'static + WasmCacheAccess, +{ + type Error = Error; + + const ADDR: InternalAddress = InternalAddress::Multitoken; + + fn validate_tx( + &self, + _tx: &Tx, + keys_changed: &BTreeSet, + verifiers: &BTreeSet
, + ) -> Result { + let mut changes = HashMap::new(); + let mut mints = HashMap::new(); + for key in keys_changed { + if let Some((token, _)) = is_any_token_balance_key(key) { + let pre: Amount = self.ctx.read_pre(key)?.unwrap_or_default(); + let post: Amount = self.ctx.read_post(key)?.unwrap_or_default(); + let diff = post.change() - pre.change(); + + if is_minted_balance_key(token, key) { + match mints.get_mut(token) { + Some(mint) => *mint += diff, + None => _ = mints.insert(token, diff), + } + + // Check if the minter VP is called + let minter_key = minter_key(token); + let minter = match self.ctx.read_post(&minter_key)? { + Some(m) => m, + None => return Ok(false), + }; + if !verifiers.contains(&minter) { + return Ok(false); + } + } else { + match changes.get_mut(token) { + Some(change) => *change += diff, + None => _ = changes.insert(token, diff), + } + } + } + } + + Ok(changes.iter().all(|(token, change)| { + let mint = match mints.get(token) { + Some(mint) => *mint, + None => 0, + }; + *change == mint + })) + } +} From 9995a20a7e04f65cce7fa9c575a54af4dd8544ac Mon Sep 17 00:00:00 2001 From: yito88 Date: Tue, 13 Jun 2023 10:23:02 +0900 Subject: [PATCH 014/120] fix balance print --- apps/src/lib/client/rpc.rs | 95 ++++++++++--------- core/src/ledger/storage/masp_conversions.rs | 46 +++------ core/src/ledger/storage_api/token.rs | 2 - core/src/types/address.rs | 17 ++-- core/src/types/token.rs | 62 ++---------- .../transactions/ethereum_events/events.rs | 4 +- shared/src/ledger/ibc/vp/context.rs | 3 +- shared/src/ledger/rpc.rs | 5 +- tx_prelude/src/ibc.rs | 3 +- 9 files changed, 85 insertions(+), 152 deletions(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index b136ce17e0..a3863a9d82 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -302,7 +302,10 @@ pub async fn query_transparent_balance< wallet: &mut Wallet, args: args::QueryBalance, ) { - let tokens = wallet.get_addresses_with_vp_type(AddressVpType::Token); + let prefix = Key::from( + Address::Internal(namada::types::address::InternalAddress::Multitoken) + .to_db_key(), + ); match (args.token, args.owner) { (Some(token), Some(owner)) => { let balance_key = @@ -323,22 +326,16 @@ pub async fn query_transparent_balance< } } (None, Some(owner)) => { - for token in tokens { - let prefix = - token::balance_key(&token, &owner.address().unwrap()); - let balances = - query_storage_prefix::(client, &prefix) - .await; - if let Some(balances) = balances { - print_balances( - client, - wallet, - balances, - &token, - owner.address().as_ref(), - ) - .await; - } + let balances = + query_storage_prefix::(client, &prefix).await; + if let Some(balances) = balances { + print_balances( + client, + wallet, + balances, + None, + owner.address().as_ref(), + ); } } (Some(token), None) => { @@ -346,19 +343,14 @@ pub async fn query_transparent_balance< let balances = query_storage_prefix::(client, &prefix).await; if let Some(balances) = balances { - print_balances(client, wallet, balances, &token, None).await; + print_balances(client, wallet, balances, Some(&token), None); } } (None, None) => { - for token in tokens { - let key = token::balance_prefix(&token); - let balances = - query_storage_prefix::(client, &key) - .await; - if let Some(balances) = balances { - print_balances(client, wallet, balances, &token, None) - .await; - } + let balances = + query_storage_prefix::(client, &prefix).await; + if let Some(balances) = balances { + print_balances(client, wallet, balances, None, None); } } } @@ -521,19 +513,18 @@ async fn print_balances( client: &C, wallet: &Wallet, balances: impl Iterator, - token: &Address, + token: Option<&Address>, target: Option<&Address>, ) { let stdout = io::stdout(); let mut w = stdout.lock(); - let token_alias = lookup_alias(wallet, token); - writeln!(w, "Token {}", token_alias).unwrap(); - + let mut print_token = None; let print_num = balances .filter_map(|(key, balance)| { - token::is_balance_key(token, &key).map(|owner| { + token::is_any_token_balance_key(&key).map(|(token, owner)| { ( + token.clone(), owner.clone(), format!( ": {}, owned by {}", @@ -543,25 +534,43 @@ async fn print_balances( ) }) }) - .filter_map(|(o, s)| match target { - Some(t) if o == *t => Some(s), - Some(_) => None, - None => Some(s), + .filter_map(|(t, o, s)| match (token, target) { + (Some(token), Some(target)) if t == *token && o == *target => { + Some((t, s)) + } + (Some(token), None) if t == *token => Some((t, s)), + (None, Some(target)) if o == *target => Some((t, s)), + (None, None) => Some((t, s)), + _ => None, }) - .map(|s| { + .map(|(t, s)| { + match &print_token { + Some(token) if *token == t => { + // the token was already printed + } + Some(_) | None => { + let token_alias = lookup_alias(wallet, &t); + writeln!(w, "Token {}", token_alias).unwrap(); + print_token = Some(t); + } + } writeln!(w, "{}", s).unwrap(); }) .count(); if print_num == 0 { - match target { - Some(t) => { - writeln!(w, "No balances owned by {}", lookup_alias(wallet, t)) - .unwrap() - } - None => { + match (token, target) { + (Some(_), Some(target)) | (None, Some(target)) => writeln!( + w, + "No balances owned by {}", + lookup_alias(wallet, target) + ) + .unwrap(), + (Some(token), None) => { + let token_alias = lookup_alias(wallet, token); writeln!(w, "No balances for token {}", token_alias).unwrap() } + (None, None) => writeln!(w, "No balances").unwrap(), } } } diff --git a/core/src/ledger/storage/masp_conversions.rs b/core/src/ledger/storage/masp_conversions.rs index 4dec02d4f5..eefc6d7342 100644 --- a/core/src/ledger/storage/masp_conversions.rs +++ b/core/src/ledger/storage/masp_conversions.rs @@ -9,7 +9,7 @@ use masp_primitives::merkle_tree::FrozenCommitmentTree; use masp_primitives::sapling::Node; use crate::types::address::Address; -use crate::types::storage::{Epoch, Key}; +use crate::types::storage::Epoch; use crate::types::token::MaspDenom; /// A representation of the conversion state @@ -24,7 +24,7 @@ pub struct ConversionState { pub assets: BTreeMap< AssetType, ( - (Address, Option, MaspDenom), + (Address, MaspDenom), Epoch, AllowedConversion, usize, @@ -66,24 +66,16 @@ where // have to use. This trick works under the assumption that reward tokens // from different epochs are exactly equivalent. let reward_asset = - encode_asset_type(address::nam(), &None, MaspDenom::Zero, Epoch(0)); + encode_asset_type(address::nam(), MaspDenom::Zero, Epoch(0)); // Conversions from the previous to current asset for each address let mut current_convs = - BTreeMap::<(Address, Option, MaspDenom), AllowedConversion>::new(); + BTreeMap::<(Address, MaspDenom), AllowedConversion>::new(); // Reward all tokens according to above reward rates - for ((addr, sub_prefix), reward) in &masp_rewards { + for (addr, reward) in &masp_rewards { // Dispense a transparent reward in parallel to the shielded rewards - let addr_bal: token::Amount = match sub_prefix { - None => wl_storage - .read(&token::balance_key(addr, &masp_addr))? - .unwrap_or_default(), - Some(sub) => wl_storage - .read(&token::multitoken_balance_key( - &token::multitoken_balance_prefix(addr, sub), - &masp_addr, - ))? - .unwrap_or_default(), - }; + let addr_bal: token::Amount = wl_storage + .read(&token::balance_key(addr, &masp_addr))? + .unwrap_or_default(); // The reward for each reward.1 units of the current asset is // reward.0 units of the reward token // Since floor(a) + floor(b) <= floor(a+b), there will always be @@ -95,18 +87,16 @@ where // cancelled out/replaced with the new asset let old_asset = encode_asset_type( addr.clone(), - sub_prefix, denom, wl_storage.storage.last_epoch, ); let new_asset = encode_asset_type( addr.clone(), - sub_prefix, denom, wl_storage.storage.block.epoch, ); current_convs.insert( - (addr.clone(), sub_prefix.clone(), denom), + (addr.clone(), denom), (MaspAmount::from_pair(old_asset, -(reward.1 as i64)).unwrap() + MaspAmount::from_pair(new_asset, reward.1).unwrap() + MaspAmount::from_pair(reward_asset, reward.0).unwrap()) @@ -116,7 +106,7 @@ where wl_storage.storage.conversion_state.assets.insert( old_asset, ( - (addr.clone(), sub_prefix.clone(), denom), + (addr.clone(), denom), wl_storage.storage.last_epoch, MaspAmount::zero().into(), 0, @@ -188,20 +178,19 @@ where // Add purely decoding entries to the assets map. These will be // overwritten before the creation of the next commitment tree - for (addr, sub_prefix) in masp_rewards.keys() { + for addr in masp_rewards.keys() { for denom in token::MaspDenom::iter() { // Add the decoding entry for the new asset type. An uncommited // node position is used since this is not a conversion. let new_asset = encode_asset_type( addr.clone(), - sub_prefix, denom, wl_storage.storage.block.epoch, ); wl_storage.storage.conversion_state.assets.insert( new_asset, ( - (addr.clone(), sub_prefix.clone(), denom), + (addr.clone(), denom), wl_storage.storage.block.epoch, MaspAmount::zero().into(), wl_storage.storage.conversion_state.tree.size(), @@ -229,19 +218,10 @@ where /// Construct MASP asset type with given epoch for given token pub fn encode_asset_type( addr: Address, - sub_prefix: &Option, denom: MaspDenom, epoch: Epoch, ) -> AssetType { - let new_asset_bytes = ( - addr, - sub_prefix - .as_ref() - .map(|k| k.to_string()) - .unwrap_or_default(), - denom, - epoch.0, - ) + let new_asset_bytes = (addr, denom, epoch.0) .try_to_vec() .expect("unable to serialize address and epoch"); AssetType::new(new_asset_bytes.as_ref()) diff --git a/core/src/ledger/storage_api/token.rs b/core/src/ledger/storage_api/token.rs index 383911d09a..4609ab03df 100644 --- a/core/src/ledger/storage_api/token.rs +++ b/core/src/ledger/storage_api/token.rs @@ -3,8 +3,6 @@ use super::{StorageRead, StorageWrite}; use crate::ledger::storage_api; use crate::types::address::Address; -use crate::types::storage::DbKeySeg::StringSeg; -use crate::types::storage::Key; use crate::types::token; pub use crate::types::token::{ balance_key, is_balance_key, is_minted_balance_key, minted_balance_key, diff --git a/core/src/types/address.rs b/core/src/types/address.rs index c647b85238..d802b4b6ab 100644 --- a/core/src/types/address.rs +++ b/core/src/types/address.rs @@ -17,7 +17,6 @@ use crate::ibc::signer::Signer; use crate::types::ethereum_events::EthAddress; use crate::types::key; use crate::types::key::PublicKeyHash; -use crate::types::storage::Key; use crate::types::token::Denomination; /// The length of an established [`Address`] encoded with Borsh. @@ -641,15 +640,15 @@ pub fn tokens() -> HashMap { /// Temporary helper for testing, a hash map of tokens addresses with their /// MASP XAN incentive schedules. If the reward is (a, b) then a rewarded tokens /// are dispensed for every b possessed tokens. -pub fn masp_rewards() -> HashMap<(Address, Option), (u64, u64)> { +pub fn masp_rewards() -> HashMap { vec![ - ((nam(), None), (0, 100)), - ((btc(), None), (1, 100)), - ((eth(), None), (2, 100)), - ((dot(), None), (3, 100)), - ((schnitzel(), None), (4, 100)), - ((apfel(), None), (5, 100)), - ((kartoffel(), None), (6, 100)), + (nam(), (0, 100)), + (btc(), (1, 100)), + (eth(), (2, 100)), + (dot(), (3, 100)), + (schnitzel(), (4, 100)), + (apfel(), (5, 100)), + (kartoffel(), (6, 100)), ] .into_iter() .collect() diff --git a/core/src/types/token.rs b/core/src/types/token.rs index df50a5b1ba..97eb83b8e3 100644 --- a/core/src/types/token.rs +++ b/core/src/types/token.rs @@ -1,6 +1,6 @@ //! A basic fungible token -use std::fmt::{Display, Formatter}; +use std::fmt::Display; use std::iter::Sum; use std::ops::{Add, AddAssign, Div, Mul, Sub, SubAssign}; use std::str::FromStr; @@ -201,15 +201,13 @@ impl Amount { pub fn denominated( &self, token: &Address, - sub_prefix: Option<&Key>, storage: &impl StorageRead, ) -> storage_api::Result { - let denom = - read_denom(storage, token, sub_prefix)?.ok_or_else(|| { - storage_api::Error::SimpleMessage( - "No denomination found in storage for the given token", - ) - })?; + 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, @@ -780,54 +778,6 @@ pub const CONVERSION_KEY_PREFIX: &str = "conv"; /// Key segment prefix for pinned shielded transactions pub const PIN_KEY_PREFIX: &str = "pin-"; -/// A fully qualified (multi-) token address. -#[derive( - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - Debug, - Hash, - BorshSerialize, - BorshDeserialize, -)] -pub struct TokenAddress { - /// The address of the (multi-) token - pub address: Address, - /// If it is a mutli-token, this indicates the sub-token. - pub sub_prefix: Option, -} - -impl TokenAddress { - /// A function for displaying a [`TokenAddress`]. Takes a - /// human readable name of the token as input. - pub fn format_with_alias(&self, alias: &str) -> String { - format!( - "{}{}", - alias, - self.sub_prefix - .as_ref() - .map(|k| format!("/{}", k)) - .unwrap_or_default() - ) - } -} - -impl Display for TokenAddress { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let formatted = format!( - "{}{}", - self.address, - self.sub_prefix - .as_ref() - .map(|k| format!("/{}", k)) - .unwrap_or_default() - ); - f.write_str(&formatted) - } -} - /// Obtain a storage key for user's balance. pub fn balance_key(token_addr: &Address, owner: &Address) -> Key { balance_prefix(token_addr) diff --git a/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs b/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs index d4cd0370aa..1da9c5381c 100644 --- a/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs +++ b/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs @@ -26,9 +26,7 @@ use namada_core::types::ethereum_events::{ }; use namada_core::types::storage::{BlockHeight, Key, KeySeg}; use namada_core::types::token; -use namada_core::types::token::{ - balance_key, multitoken_balance_key, multitoken_balance_prefix, -}; +use namada_core::types::token::balance_key; use crate::parameters::read_native_erc20_address; use crate::protocol::transactions::update; diff --git a/shared/src/ledger/ibc/vp/context.rs b/shared/src/ledger/ibc/vp/context.rs index 17697f181c..d05eaebcd1 100644 --- a/shared/src/ledger/ibc/vp/context.rs +++ b/shared/src/ledger/ibc/vp/context.rs @@ -12,7 +12,8 @@ use namada_core::types::address::{Address, InternalAddress}; use namada_core::types::ibc::IbcEvent; use namada_core::types::storage::{BlockHeight, Header, Key}; use namada_core::types::token::{ - self, is_any_token_balance_key, is_any_token_or_multitoken_balance_key, Amount, + self, is_any_token_balance_key, is_any_token_or_multitoken_balance_key, + Amount, }; use super::Error; diff --git a/shared/src/ledger/rpc.rs b/shared/src/ledger/rpc.rs index 28431a7ffb..08111692e3 100644 --- a/shared/src/ledger/rpc.rs +++ b/shared/src/ledger/rpc.rs @@ -1065,10 +1065,7 @@ pub async fn format_denominated_amount< amount: token::Amount, ) -> String { let denom = unwrap_client_response::>( - RPC.vp() - .token() - .denomination(client, &token.address) - .await, + RPC.vp().token().denomination(client, &token.address).await, ) .unwrap_or_else(|| { println!( diff --git a/tx_prelude/src/ibc.rs b/tx_prelude/src/ibc.rs index 28694aa7f7..2edb13652b 100644 --- a/tx_prelude/src/ibc.rs +++ b/tx_prelude/src/ibc.rs @@ -12,7 +12,7 @@ use namada_core::ledger::tx_env::TxEnv; use namada_core::types::address::{Address, InternalAddress}; pub use namada_core::types::ibc::IbcEvent; use namada_core::types::storage::{BlockHeight, Header, Key}; -use namada_core::types::token::Amount; +use namada_core::types::token::{DenominatedAmount, Amount}; use crate::token::{burn, mint, transfer}; use crate::{Ctx, KeyValIterator}; @@ -78,6 +78,7 @@ impl IbcStorageContext for Ctx { token: &Address, amount: Amount, ) -> std::result::Result<(), Self::Error> { + let amount = DenominatedAmount::native(amount); transfer(self, src, dest, token, amount, &None, &None, &None) } From db117cfd141e419bf47c7f3cc0e061609a102cdc Mon Sep 17 00:00:00 2001 From: yito88 Date: Thu, 15 Jun 2023 16:15:53 +0900 Subject: [PATCH 015/120] fix token decoding --- core/src/ledger/ibc/context/transfer_mod.rs | 31 +++++++-------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/core/src/ledger/ibc/context/transfer_mod.rs b/core/src/ledger/ibc/context/transfer_mod.rs index 51ac801aec..8280f7c36b 100644 --- a/core/src/ledger/ibc/context/transfer_mod.rs +++ b/core/src/ledger/ibc/context/transfer_mod.rs @@ -444,13 +444,7 @@ where ) -> Result<(), TokenTransferError> { // Assumes that the coin denom is prefixed with "port-id/channel-id" or // has no prefix - let (token, amount) = get_token_amount(coin)?; - - let ibc_token = if coin.denom.trace_path.is_empty() { - token - } else { - storage::ibc_token(coin.denom.to_string()) - }; + let (ibc_token, amount) = get_token_amount(coin)?; self.ctx .borrow_mut() @@ -474,10 +468,8 @@ where account: &Self::AccountId, coin: &PrefixedCoin, ) -> Result<(), TokenTransferError> { - let (_, amount) = get_token_amount(coin)?; - // The trace path of the denom is already updated if receiving the token - let ibc_token = storage::ibc_token(coin.denom.to_string()); + let (ibc_token, amount) = get_token_amount(coin)?; self.ctx .borrow_mut() @@ -500,11 +492,9 @@ where account: &Self::AccountId, coin: &PrefixedCoin, ) -> Result<(), TokenTransferError> { - let (_, amount) = get_token_amount(coin)?; - - // The burn is unminting of the minted - let ibc_token = storage::ibc_token(coin.denom.to_string()); + let (ibc_token, amount) = get_token_amount(coin)?; + // The burn is "unminting" from the minted balance self.ctx .borrow_mut() .burn_token(account, &ibc_token, amount) @@ -559,16 +549,15 @@ where } } -/// Get the token address and the amount from PrefixedCoin +/// Get the token address and the amount from PrefixedCoin. If the base denom is +/// not an address, it returns `IbcToken` fn get_token_amount( coin: &PrefixedCoin, ) -> Result<(Address, token::Amount), TokenTransferError> { - let token = - Address::decode(coin.denom.base_denom.as_str()).map_err(|_| { - TokenTransferError::InvalidCoin { - coin: coin.denom.base_denom.to_string(), - } - })?; + let token = match Address::decode(coin.denom.base_denom.as_str()) { + Ok(token_addr) if coin.denom.trace_path.is_empty() => token_addr, + _ => storage::ibc_token(coin.denom.to_string()), + }; let amount = coin.amount.try_into().map_err(|_| { TokenTransferError::InvalidCoin { From 818cceda19974a4d2a9fd9b06d265e7af390c669 Mon Sep 17 00:00:00 2001 From: yito88 Date: Thu, 15 Jun 2023 16:44:46 +0900 Subject: [PATCH 016/120] rename --- core/src/types/address.rs | 20 +++++++++++--------- core/src/types/token.rs | 4 ++-- shared/src/ledger/protocol/mod.rs | 2 +- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/core/src/types/address.rs b/core/src/types/address.rs index d802b4b6ab..6d58cf049a 100644 --- a/core/src/types/address.rs +++ b/core/src/types/address.rs @@ -54,7 +54,7 @@ pub const FIXED_LEN_STRING_BYTES: usize = 45; /// Internal IBC address pub const IBC: Address = Address::Internal(InternalAddress::Ibc); /// Internal multitoken mint address -pub const MINT: Address = Address::Internal(InternalAddress::Mint); +pub const MINTED: Address = Address::Internal(InternalAddress::Minted); /// Internal ledger parameters address pub const PARAMETERS: Address = Address::Internal(InternalAddress::Parameters); /// Internal PoS address @@ -89,7 +89,7 @@ mod internal { "ano::Replay Protection "; pub const MULTITOKEN: &str = "ano::Multitoken "; - pub const MINT: &str = + pub const MINTED: &str = "ano::Multitoken Mint Address "; } @@ -240,7 +240,7 @@ impl Address { InternalAddress::Multitoken => { internal::MULTITOKEN.to_string() } - InternalAddress::Mint => internal::MINT.to_string(), + InternalAddress::Minted => internal::MINTED.to_string(), }; debug_assert_eq!(string.len(), FIXED_LEN_STRING_BYTES); string @@ -317,7 +317,9 @@ impl Address { internal::MULTITOKEN => { Ok(Address::Internal(InternalAddress::Multitoken)) } - internal::MINT => Ok(Address::Internal(InternalAddress::Mint)), + internal::MINTED => { + Ok(Address::Internal(InternalAddress::Minted)) + } _ => Err(Error::new( ErrorKind::InvalidData, "Invalid internal address", @@ -534,7 +536,7 @@ pub enum InternalAddress { /// Multitoken Multitoken, /// Minted multitoken address - Mint, + Minted, } impl Display for InternalAddress { @@ -554,7 +556,7 @@ impl Display for InternalAddress { Self::EthBridgePool => "EthBridgePool".to_string(), Self::ReplayProtection => "ReplayProtection".to_string(), Self::Multitoken => "Multitoken".to_string(), - Self::Mint => "Mint".to_string(), + Self::Minted => "Minted".to_string(), } ) } @@ -849,8 +851,8 @@ pub mod testing { InternalAddress::EthBridgePool => {} InternalAddress::ReplayProtection => {} InternalAddress::Multitoken => {} - InternalAddress::Mint => {} /* Add new addresses in the - * `prop_oneof` below. */ + InternalAddress::Minted => {} /* Add new addresses in the + * `prop_oneof` below. */ }; prop_oneof![ Just(InternalAddress::PoS), @@ -864,7 +866,7 @@ pub mod testing { Just(InternalAddress::EthBridgePool), Just(InternalAddress::ReplayProtection), Just(InternalAddress::Multitoken), - Just(InternalAddress::Mint) + Just(InternalAddress::Minted) ] } diff --git a/core/src/types/token.rs b/core/src/types/token.rs index 97eb83b8e3..f0ac2379a6 100644 --- a/core/src/types/token.rs +++ b/core/src/types/token.rs @@ -806,7 +806,7 @@ pub fn minter_key(token_addr: &Address) -> Key { /// Obtain a storage key for the minted multitoken balance. pub fn minted_balance_key(token_addr: &Address) -> Key { balance_prefix(token_addr) - .push(&Address::Internal(InternalAddress::Mint).to_db_key()) + .push(&Address::Internal(InternalAddress::Minted).to_db_key()) .expect("Cannot obtain a storage key") } @@ -880,7 +880,7 @@ pub fn is_masp_key(key: &Key) -> bool { /// Is storage key for total supply of a specific token? pub fn is_minted_balance_key(token_addr: &Address, key: &Key) -> bool { if let Some(owner) = is_balance_key(token_addr, key) { - *owner == Address::Internal(InternalAddress::Mint) + *owner == Address::Internal(InternalAddress::Minted) } else { false } diff --git a/shared/src/ledger/protocol/mod.rs b/shared/src/ledger/protocol/mod.rs index c1a8484e45..fbdc6b9bdd 100644 --- a/shared/src/ledger/protocol/mod.rs +++ b/shared/src/ledger/protocol/mod.rs @@ -586,7 +586,7 @@ where result } InternalAddress::IbcToken(_) - | InternalAddress::Mint => { + | InternalAddress::Minted => { // These addresses should be a part of a multitoken // key gas_meter = ctx.gas_meter.into_inner(); From 9759185361ebdc50319648b58a03e43e1a119b1e Mon Sep 17 00:00:00 2001 From: yito88 Date: Mon, 19 Jun 2023 22:26:14 +0900 Subject: [PATCH 017/120] remove InternalAddress::Minted --- core/src/ledger/storage_api/token.rs | 2 +- core/src/types/address.rs | 17 ++------- core/src/types/token.rs | 28 ++++++++++----- shared/src/ledger/native_vp/multitoken.rs | 42 +++++++++++----------- shared/src/ledger/protocol/mod.rs | 10 +++--- tests/src/e2e/helpers.rs | 3 +- wasm/wasm_source/src/vp_token.rs | 44 +++++++++++------------ 7 files changed, 71 insertions(+), 75 deletions(-) diff --git a/core/src/ledger/storage_api/token.rs b/core/src/ledger/storage_api/token.rs index 4609ab03df..1985d8325c 100644 --- a/core/src/ledger/storage_api/token.rs +++ b/core/src/ledger/storage_api/token.rs @@ -5,7 +5,7 @@ use crate::ledger::storage_api; use crate::types::address::Address; use crate::types::token; pub use crate::types::token::{ - balance_key, is_balance_key, is_minted_balance_key, minted_balance_key, + balance_key, is_any_minted_balance_key, is_balance_key, minted_balance_key, minter_key, Amount, Change, }; diff --git a/core/src/types/address.rs b/core/src/types/address.rs index 6d58cf049a..cb36593edc 100644 --- a/core/src/types/address.rs +++ b/core/src/types/address.rs @@ -53,8 +53,6 @@ pub const FIXED_LEN_STRING_BYTES: usize = 45; /// Internal IBC address pub const IBC: Address = Address::Internal(InternalAddress::Ibc); -/// Internal multitoken mint address -pub const MINTED: Address = Address::Internal(InternalAddress::Minted); /// Internal ledger parameters address pub const PARAMETERS: Address = Address::Internal(InternalAddress::Parameters); /// Internal PoS address @@ -89,8 +87,6 @@ mod internal { "ano::Replay Protection "; pub const MULTITOKEN: &str = "ano::Multitoken "; - pub const MINTED: &str = - "ano::Multitoken Mint Address "; } /// Fixed-length address strings prefix for established addresses. @@ -240,7 +236,6 @@ impl Address { InternalAddress::Multitoken => { internal::MULTITOKEN.to_string() } - InternalAddress::Minted => internal::MINTED.to_string(), }; debug_assert_eq!(string.len(), FIXED_LEN_STRING_BYTES); string @@ -317,9 +312,6 @@ impl Address { internal::MULTITOKEN => { Ok(Address::Internal(InternalAddress::Multitoken)) } - internal::MINTED => { - Ok(Address::Internal(InternalAddress::Minted)) - } _ => Err(Error::new( ErrorKind::InvalidData, "Invalid internal address", @@ -535,8 +527,6 @@ pub enum InternalAddress { ReplayProtection, /// Multitoken Multitoken, - /// Minted multitoken address - Minted, } impl Display for InternalAddress { @@ -556,7 +546,6 @@ impl Display for InternalAddress { Self::EthBridgePool => "EthBridgePool".to_string(), Self::ReplayProtection => "ReplayProtection".to_string(), Self::Multitoken => "Multitoken".to_string(), - Self::Minted => "Minted".to_string(), } ) } @@ -850,9 +839,8 @@ pub mod testing { InternalAddress::EthBridge => {} InternalAddress::EthBridgePool => {} InternalAddress::ReplayProtection => {} - InternalAddress::Multitoken => {} - InternalAddress::Minted => {} /* Add new addresses in the - * `prop_oneof` below. */ + InternalAddress::Multitoken => {} /* Add new addresses in the + * `prop_oneof` below. */ }; prop_oneof![ Just(InternalAddress::PoS), @@ -866,7 +854,6 @@ pub mod testing { Just(InternalAddress::EthBridgePool), Just(InternalAddress::ReplayProtection), Just(InternalAddress::Multitoken), - Just(InternalAddress::Minted) ] } diff --git a/core/src/types/token.rs b/core/src/types/token.rs index f0ac2379a6..16dcb6d424 100644 --- a/core/src/types/token.rs +++ b/core/src/types/token.rs @@ -769,6 +769,8 @@ pub const BALANCE_STORAGE_KEY: &str = "balance"; 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 @@ -806,12 +808,12 @@ pub fn minter_key(token_addr: &Address) -> Key { /// Obtain a storage key for the minted multitoken balance. pub fn minted_balance_key(token_addr: &Address) -> Key { balance_prefix(token_addr) - .push(&Address::Internal(InternalAddress::Minted).to_db_key()) + .push(&MINTED_STORAGE_KEY.to_owned()) .expect("Cannot obtain a storage key") } /// Check if the given storage key is balance key for the given token. If it is, -/// returns the owner. +/// returns the owner. For minted balances, use [`is_any_minted_balance_key()`]. pub fn is_balance_key<'a>( token_addr: &Address, key: &'a Key, @@ -877,12 +879,22 @@ pub fn is_masp_key(key: &Key) -> bool { || key.starts_with(PIN_KEY_PREFIX))) } -/// Is storage key for total supply of a specific token? -pub fn is_minted_balance_key(token_addr: &Address, key: &Key) -> bool { - if let Some(owner) = is_balance_key(token_addr, key) { - *owner == Address::Internal(InternalAddress::Minted) - } else { - false +/// 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, } } diff --git a/shared/src/ledger/native_vp/multitoken.rs b/shared/src/ledger/native_vp/multitoken.rs index c80e982afb..08e9437495 100644 --- a/shared/src/ledger/native_vp/multitoken.rs +++ b/shared/src/ledger/native_vp/multitoken.rs @@ -11,7 +11,7 @@ use crate::proto::Tx; use crate::types::address::{Address, InternalAddress}; use crate::types::storage::Key; use crate::types::token::{ - is_any_token_balance_key, is_minted_balance_key, minter_key, Amount, + is_any_minted_balance_key, is_any_token_balance_key, minter_key, Amount, }; use crate::vm::WasmCacheAccess; @@ -59,27 +59,27 @@ where let pre: Amount = self.ctx.read_pre(key)?.unwrap_or_default(); let post: Amount = self.ctx.read_post(key)?.unwrap_or_default(); let diff = post.change() - pre.change(); + match changes.get_mut(token) { + Some(change) => *change += diff, + None => _ = changes.insert(token, diff), + } + } else if let Some(token) = is_any_minted_balance_key(key) { + let pre: Amount = self.ctx.read_pre(key)?.unwrap_or_default(); + let post: Amount = self.ctx.read_post(key)?.unwrap_or_default(); + let diff = post.change() - pre.change(); + match mints.get_mut(token) { + Some(mint) => *mint += diff, + None => _ = mints.insert(token, diff), + } - if is_minted_balance_key(token, key) { - match mints.get_mut(token) { - Some(mint) => *mint += diff, - None => _ = mints.insert(token, diff), - } - - // Check if the minter VP is called - let minter_key = minter_key(token); - let minter = match self.ctx.read_post(&minter_key)? { - Some(m) => m, - None => return Ok(false), - }; - if !verifiers.contains(&minter) { - return Ok(false); - } - } else { - match changes.get_mut(token) { - Some(change) => *change += diff, - None => _ = changes.insert(token, diff), - } + // Check if the minter VP is called + let minter_key = minter_key(token); + let minter = match self.ctx.read_post(&minter_key)? { + Some(m) => m, + None => return Ok(false), + }; + if !verifiers.contains(&minter) { + return Ok(false); } } } diff --git a/shared/src/ledger/protocol/mod.rs b/shared/src/ledger/protocol/mod.rs index fbdc6b9bdd..b6920fb4f9 100644 --- a/shared/src/ledger/protocol/mod.rs +++ b/shared/src/ledger/protocol/mod.rs @@ -585,12 +585,12 @@ where replay_protection_vp.ctx.gas_meter.into_inner(); result } - InternalAddress::IbcToken(_) - | InternalAddress::Minted => { - // These addresses should be a part of a multitoken - // key + InternalAddress::IbcToken(_) => { + // The address should be a part of a multitoken key gas_meter = ctx.gas_meter.into_inner(); - Ok(true) + Ok(verifiers.contains(&Address::Internal( + InternalAddress::Multitoken, + ))) } }; diff --git a/tests/src/e2e/helpers.rs b/tests/src/e2e/helpers.rs index 7c79fdfdb6..c9042802d1 100644 --- a/tests/src/e2e/helpers.rs +++ b/tests/src/e2e/helpers.rs @@ -25,8 +25,7 @@ use namada_apps::config::{Config, TendermintMode}; use namada_core::types::token::NATIVE_MAX_DECIMAL_PLACES; use super::setup::{ - self, sleep, NamadaBgCmd, NamadaCmd, Test, ENV_VAR_DEBUG, - ENV_VAR_USE_PREBUILT_BINARIES, + sleep, NamadaCmd, Test, ENV_VAR_DEBUG, ENV_VAR_USE_PREBUILT_BINARIES, }; use crate::e2e::setup::{Bin, Who, APPS_PACKAGE}; use crate::{run, run_as}; diff --git a/wasm/wasm_source/src/vp_token.rs b/wasm/wasm_source/src/vp_token.rs index 98ad53d8d2..002553dd75 100644 --- a/wasm/wasm_source/src/vp_token.rs +++ b/wasm/wasm_source/src/vp_token.rs @@ -56,35 +56,33 @@ fn token_checks( match owner { None => { - if key.segments.get(0) == Some(&token.to_db_key()) { + if let Some(t) = token::is_any_minted_balance_key(key) { + if t == token { + // check if total supply is changed, which it should + // never be from a tx + let total_pre: token::Amount = + ctx.read_pre(key)?.unwrap(); + let total_post: token::Amount = + ctx.read_post(key)?.unwrap(); + if total_pre != total_post { + return reject(); + } + } + } else if key.segments.get(0) == Some(&token.to_db_key()) { // Unknown changes to this address space are disallowed, but // unknown changes anywhere else are permitted return reject(); } } Some(owner) => { - if token::is_minted_balance_key(token, key) { - // check if total supply is changed, which it should never - // be from a tx - let total_pre: token::Amount = ctx.read_pre(key)?.unwrap(); - let total_post: token::Amount = - ctx.read_post(key)?.unwrap(); - if total_pre != total_post { - return reject(); - } - } else { - // accumulate the change - let pre: token::Amount = - ctx.read_pre(key)?.unwrap_or_default(); - let post: token::Amount = - ctx.read_post(key)?.unwrap_or_default(); - // make sure that the spender approved the transaction - if post < pre - && !(verifiers.contains(owner) - || *owner == address::masp()) - { - return reject(); - } + let pre: token::Amount = ctx.read_pre(key)?.unwrap_or_default(); + let post: token::Amount = + ctx.read_post(key)?.unwrap_or_default(); + // make sure that the spender approved the transaction + if post < pre + && !(verifiers.contains(owner) || *owner == address::masp()) + { + return reject(); } } } From aa0cbbade92a730677baa44f27dbc9a303bcfbf4 Mon Sep 17 00:00:00 2001 From: yito88 Date: Thu, 29 Jun 2023 11:14:54 +0200 Subject: [PATCH 018/120] add unit tests --- shared/src/ledger/native_vp/multitoken.rs | 370 ++++++++++++++++++++++ 1 file changed, 370 insertions(+) diff --git a/shared/src/ledger/native_vp/multitoken.rs b/shared/src/ledger/native_vp/multitoken.rs index 08e9437495..9c985b9dab 100644 --- a/shared/src/ledger/native_vp/multitoken.rs +++ b/shared/src/ledger/native_vp/multitoken.rs @@ -93,3 +93,373 @@ where })) } } + +#[cfg(test)] +mod tests { + use std::collections::BTreeSet; + + use borsh::BorshSerialize; + + 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::proto::{Code, Data, Section, Signature, Tx}; + 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); + + fn dummy_tx(wl_storage: &TestWlStorage) -> Tx { + let tx_code = vec![]; + let tx_data = vec![]; + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = wl_storage.storage.chain_id.clone(); + tx.set_code(Code::new(tx_code)); + tx.set_data(Data::new(tx_data)); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &keypair_1(), + ))); + tx + } + + #[test] + fn test_valid_transfer() { + let mut wl_storage = TestWlStorage::default(); + let mut keys_changed = BTreeSet::new(); + + let sender = established_address_1(); + let sender_key = balance_key(&nam(), &sender); + let amount = Amount::whole(100); + wl_storage + .storage + .write(&sender_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + + // transfer 10 + let amount = Amount::whole(90); + wl_storage + .write_log + .write(&sender_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(sender_key); + let receiver = established_address_2(); + let receiver_key = balance_key(&nam(), &receiver); + let amount = Amount::whole(10); + wl_storage + .write_log + .write(&receiver_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(receiver_key); + + let tx_index = TxIndex::default(); + let tx = dummy_tx(&wl_storage); + let gas_meter = VpGasMeter::new(0); + let (vp_wasm_cache, _vp_cache_dir) = wasm_cache(); + let verifiers = BTreeSet::new(); + let ctx = Ctx::new( + &ADDRESS, + &wl_storage.storage, + &wl_storage.write_log, + &tx, + &tx_index, + gas_meter, + &keys_changed, + &verifiers, + vp_wasm_cache, + ); + + let vp = MultitokenVp { ctx }; + assert!( + vp.validate_tx(&tx, &keys_changed, &verifiers) + .expect("validation failed") + ); + } + + #[test] + fn test_invalid_transfer() { + let mut wl_storage = TestWlStorage::default(); + let mut keys_changed = BTreeSet::new(); + + let sender = established_address_1(); + let sender_key = balance_key(&nam(), &sender); + let amount = Amount::whole(100); + wl_storage + .storage + .write(&sender_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + + // transfer 10 + let amount = Amount::whole(90); + wl_storage + .write_log + .write(&sender_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(sender_key); + let receiver = established_address_2(); + let receiver_key = balance_key(&nam(), &receiver); + // receive more than 10 + let amount = Amount::whole(100); + wl_storage + .write_log + .write(&receiver_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(receiver_key); + + let tx_index = TxIndex::default(); + let tx = dummy_tx(&wl_storage); + let gas_meter = VpGasMeter::new(0); + let (vp_wasm_cache, _vp_cache_dir) = wasm_cache(); + let verifiers = BTreeSet::new(); + let ctx = Ctx::new( + &ADDRESS, + &wl_storage.storage, + &wl_storage.write_log, + &tx, + &tx_index, + gas_meter, + &keys_changed, + &verifiers, + vp_wasm_cache, + ); + + let vp = MultitokenVp { ctx }; + assert!( + !vp.validate_tx(&tx, &keys_changed, &verifiers) + .expect("validation failed") + ); + } + + #[test] + fn test_valid_mint() { + let mut wl_storage = TestWlStorage::default(); + let mut keys_changed = BTreeSet::new(); + + // mint 100 + let target = established_address_1(); + let target_key = balance_key(&nam(), &target); + let amount = Amount::whole(100); + wl_storage + .write_log + .write(&target_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(target_key); + let minted_key = minted_balance_key(&nam()); + let amount = Amount::whole(100); + wl_storage + .write_log + .write(&minted_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(minted_key); + + // minter + let minter = Address::Internal(InternalAddress::Ibc); + let minter_key = minter_key(&nam()); + wl_storage + .write_log + .write(&minter_key, minter.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(minter_key); + + let tx_index = TxIndex::default(); + let tx = dummy_tx(&wl_storage); + let gas_meter = VpGasMeter::new(0); + let (vp_wasm_cache, _vp_cache_dir) = wasm_cache(); + let mut verifiers = BTreeSet::new(); + // for the minter + verifiers.insert(minter); + let ctx = Ctx::new( + &ADDRESS, + &wl_storage.storage, + &wl_storage.write_log, + &tx, + &tx_index, + gas_meter, + &keys_changed, + &verifiers, + vp_wasm_cache, + ); + + let vp = MultitokenVp { ctx }; + assert!( + vp.validate_tx(&tx, &keys_changed, &verifiers) + .expect("validation failed") + ); + } + + #[test] + fn test_invalid_mint() { + let mut wl_storage = TestWlStorage::default(); + let mut keys_changed = BTreeSet::new(); + + // mint 100 + let target = established_address_1(); + let target_key = balance_key(&nam(), &target); + // mint more than 100 + let amount = Amount::whole(1000); + wl_storage + .write_log + .write(&target_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(target_key); + let minted_key = minted_balance_key(&nam()); + let amount = Amount::whole(100); + wl_storage + .write_log + .write(&minted_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(minted_key); + + // minter + let minter = Address::Internal(InternalAddress::Ibc); + let minter_key = minter_key(&nam()); + wl_storage + .write_log + .write(&minter_key, minter.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(minter_key); + + let tx_index = TxIndex::default(); + let tx = dummy_tx(&wl_storage); + let gas_meter = VpGasMeter::new(0); + let (vp_wasm_cache, _vp_cache_dir) = wasm_cache(); + let mut verifiers = BTreeSet::new(); + // for the minter + verifiers.insert(minter); + let ctx = Ctx::new( + &ADDRESS, + &wl_storage.storage, + &wl_storage.write_log, + &tx, + &tx_index, + gas_meter, + &keys_changed, + &verifiers, + vp_wasm_cache, + ); + + let vp = MultitokenVp { ctx }; + assert!( + !vp.validate_tx(&tx, &keys_changed, &verifiers) + .expect("validation failed") + ); + } + + #[test] + fn test_no_minter() { + let mut wl_storage = TestWlStorage::default(); + let mut keys_changed = BTreeSet::new(); + + // mint 100 + let target = established_address_1(); + let target_key = balance_key(&nam(), &target); + let amount = Amount::whole(100); + wl_storage + .write_log + .write(&target_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(target_key); + let minted_key = minted_balance_key(&nam()); + let amount = Amount::whole(100); + wl_storage + .write_log + .write(&minted_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(minted_key); + + // no minter is set + + let tx_index = TxIndex::default(); + let tx = dummy_tx(&wl_storage); + let gas_meter = VpGasMeter::new(0); + let (vp_wasm_cache, _vp_cache_dir) = wasm_cache(); + let verifiers = BTreeSet::new(); + let ctx = Ctx::new( + &ADDRESS, + &wl_storage.storage, + &wl_storage.write_log, + &tx, + &tx_index, + gas_meter, + &keys_changed, + &verifiers, + vp_wasm_cache, + ); + + let vp = MultitokenVp { ctx }; + assert!( + !vp.validate_tx(&tx, &keys_changed, &verifiers) + .expect("validation failed") + ); + } + + #[test] + fn test_no_minter_vp() { + let mut wl_storage = TestWlStorage::default(); + let mut keys_changed = BTreeSet::new(); + + // mint 100 + let target = established_address_1(); + let target_key = balance_key(&nam(), &target); + let amount = Amount::whole(100); + wl_storage + .write_log + .write(&target_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(target_key); + let minted_key = minted_balance_key(&nam()); + let amount = Amount::whole(100); + wl_storage + .write_log + .write(&minted_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(minted_key); + + // minter + let minter = Address::Internal(InternalAddress::Ibc); + let minter_key = minter_key(&nam()); + wl_storage + .write_log + .write(&minter_key, minter.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(minter_key); + + let tx_index = TxIndex::default(); + let tx = dummy_tx(&wl_storage); + let gas_meter = VpGasMeter::new(0); + let (vp_wasm_cache, _vp_cache_dir) = wasm_cache(); + let verifiers = BTreeSet::new(); + // the minter isn't included in the verifiers + let ctx = Ctx::new( + &ADDRESS, + &wl_storage.storage, + &wl_storage.write_log, + &tx, + &tx_index, + gas_meter, + &keys_changed, + &verifiers, + vp_wasm_cache, + ); + + let vp = MultitokenVp { ctx }; + assert!( + !vp.validate_tx(&tx, &keys_changed, &verifiers) + .expect("validation failed") + ); + } +} From eeb09af2929e734ff3a2fe85a43762f55737700d Mon Sep 17 00:00:00 2001 From: yito88 Date: Sun, 2 Jul 2023 15:32:40 +0200 Subject: [PATCH 019/120] change eth_bridge balance keys to multitoken keys --- apps/src/lib/client/rpc.rs | 115 ++----- .../lib/node/ledger/shell/finalize_block.rs | 18 +- core/src/ledger/eth_bridge/storage/mod.rs | 20 ++ .../eth_bridge/storage/wrapped_erc20s.rs | 291 +++++++----------- core/src/ledger/storage/masp_conversions.rs | 7 +- core/src/proto/types.rs | 4 +- core/src/types/address.rs | 35 ++- .../transactions/ethereum_events/events.rs | 106 +++---- .../transactions/ethereum_events/mod.rs | 24 +- shared/src/ledger/eth_bridge/bridge_pool.rs | 2 +- shared/src/ledger/ibc/vp/context.rs | 5 +- shared/src/ledger/masp.rs | 147 +++------ .../ethereum_bridge/bridge_pool_vp.rs | 22 +- .../ledger/native_vp/ethereum_bridge/vp.rs | 35 ++- shared/src/ledger/native_vp/multitoken.rs | 5 +- shared/src/ledger/protocol/mod.rs | 3 +- shared/src/ledger/queries/shell.rs | 6 +- shared/src/ledger/queries/shell/eth_bridge.rs | 10 +- shared/src/ledger/queries/vp/token.rs | 1 - shared/src/ledger/rpc.rs | 11 +- shared/src/ledger/signing.rs | 67 +--- shared/src/ledger/tx.rs | 26 +- tests/src/e2e/eth_bridge_tests.rs | 1 - tests/src/e2e/eth_bridge_tests/helpers.rs | 14 +- tests/src/e2e/helpers.rs | 3 +- tests/src/native_vp/eth_bridge_pool.rs | 24 +- tx_prelude/src/ibc.rs | 4 +- wasm/wasm_source/src/tx_bridge_pool.rs | 13 +- 28 files changed, 387 insertions(+), 632 deletions(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index a3863a9d82..95d0cb592d 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -46,7 +46,7 @@ use namada::types::hash::Hash; use namada::types::key::*; use namada::types::masp::{BalanceOwner, ExtendedViewingKey, PaymentAddress}; use namada::types::storage::{BlockHeight, BlockResults, Epoch, Key, KeySeg}; -use namada::types::token::{Change, Denomination, MaspDenom, TokenAddress}; +use namada::types::token::{Change, Denomination, MaspDenom}; use namada::types::{storage, token}; use tokio::time::Instant; @@ -119,7 +119,6 @@ pub async fn query_transfers< args: args::QueryTransfers, ) { let query_token = args.token; - let sub_prefix = args.sub_prefix.map(|s| Key::parse(s).unwrap()); let query_owner = args.owner.map_or_else( || Either::Right(wallet.get_addresses().into_values().collect()), Either::Left, @@ -173,10 +172,8 @@ pub async fn query_transfers< // Check if this transfer pertains to the supplied token relevant &= match &query_token { Some(token) => { - let check = |(tok, chg): (&TokenAddress, &Change)| { - tok.sub_prefix == sub_prefix - && &tok.address == token - && !chg.is_zero() + let check = |(tok, chg): (&Address, &Change)| { + &tok.address == token && !chg.is_zero() }; tfer_delta.values().cloned().any( |MaspChange { ref asset, change }| check((asset, &change)), @@ -431,13 +428,8 @@ pub async fn query_pinned_balance< (Ok((balance, epoch)), Some(token), sub_prefix) => { let token_alias = lookup_alias(wallet, token); - let token_address = TokenAddress { - address: token.clone(), - sub_prefix: sub_prefix - .map(|string| Key::parse(string).unwrap()), - }; let total_balance = balance - .get(&(epoch, token_address.clone())) + .get(&(epoch, token.clone())) .cloned() .unwrap_or_default(); @@ -447,12 +439,12 @@ pub async fn query_pinned_balance< Received no shielded {}", owner, epoch, - token_address.format_with_alias(&token_alias) + token.format_with_alias(&token_alias) ); } else { let formatted = format_denominated_amount( client, - &token_address, + &token, total_balance.into(), ) .await; @@ -462,7 +454,7 @@ pub async fn query_pinned_balance< owner, epoch, formatted, - token_address.format_with_alias(&token_alias), + token.format_with_alias(&token_alias), ); } } @@ -760,27 +752,22 @@ pub async fn query_shielded_balance< let token_alias = lookup_alias(wallet, &token); - let token_address = TokenAddress { - address: token, - sub_prefix: args.sub_prefix.map(|k| Key::parse(k).unwrap()), - }; - let total_balance = balance - .get(&(epoch, token_address.clone())) + .get(&(epoch, token.clone())) .cloned() .unwrap_or_default(); if total_balance.is_zero() { println!( "No shielded {} balance found for given key", - token_address.format_with_alias(&token_alias) + token.format_with_alias(&token_alias) ); } else { println!( "{}: {}", - token_address.format_with_alias(&token_alias), + token.format_with_alias(&token_alias), format_denominated_amount( client, - &token_address, + &token, token::Amount::from(total_balance) ) .await @@ -813,9 +800,6 @@ pub async fn query_shielded_balance< } } - // These are the asset types for which we have human-readable names - let mut read_tokens: HashMap>> = - HashMap::new(); // Print non-zero balances whose asset types can be decoded // TODO Implement a function for this @@ -835,72 +819,21 @@ pub async fn query_shielded_balance< } } } - for ( - ( - fvk, - TokenAddress { - address: addr, - sub_prefix, - }, - ), - token_balance, - ) in balance_map - { - read_tokens - .entry(addr.clone()) - .and_modify(|addr_vec| addr_vec.push(sub_prefix.clone())) - .or_insert_with(|| vec![sub_prefix.clone()]); - let token_address = TokenAddress { - address: addr, - sub_prefix, - }; + for ((fvk, token), token_balance) in balance_map { // Only assets with the current timestamp count let alias = tokens - .get(&token_address.address) + .get(&token) .map(|a| a.to_string()) - .unwrap_or_else(|| token_address.address.to_string()); - println!( - "Shielded Token {}:", - token_address.format_with_alias(&alias), - ); + .unwrap_or_else(|| token.to_string()); + println!("Shielded Token {}:", alias); let formatted = format_denominated_amount( client, - &token_address, + &token, token_balance.into(), ) .await; println!(" {}, owned by {}", formatted, fvk); } - // Print zero balances for remaining assets - for token in tokens { - if let Some(sub_addrs) = read_tokens.get(&token) { - let token_alias = lookup_alias(wallet, &token); - for sub_addr in sub_addrs { - match sub_addr { - // abstract out these prints - Some(sub_addr) => { - println!( - "Shielded Token {}/{}:", - token_alias, sub_addr - ); - println!( - "No shielded {}/{} balance found for any \ - wallet key", - token_alias, sub_addr - ); - } - None => { - println!("Shielded Token {}:", token_alias,); - println!( - "No shielded {} balance found for any \ - wallet key", - token_alias - ); - } - } - } - } - } } // Here the user wants to know the balance for a specific token across // users @@ -918,16 +851,9 @@ pub async fn query_shielded_balance< println!("Shielded Token {}:", token_alias); let mut found_any = false; let token_alias = lookup_alias(wallet, &token); - let token_address = TokenAddress { - address: token.clone(), - sub_prefix: args - .sub_prefix - .as_ref() - .map(|k| Key::parse(k).unwrap()), - }; println!( "Shielded Token {}:", - token_address.format_with_alias(&token_alias), + token.format_with_alias(&token_alias), ); for fvk in viewing_keys { // Query the multi-asset balance at the given spending key @@ -960,7 +886,7 @@ pub async fn query_shielded_balance< if !found_any { println!( "No shielded {} balance found for any wallet key", - token_address.format_with_alias(&token_alias), + token.format_with_alias(&token_alias), ); } } @@ -2270,10 +2196,7 @@ pub async fn validate_amount( InputAmount::Validated(amt) => return amt, }; let denom = unwrap_client_response::>( - RPC.vp() - .token() - .denomination(client, token, sub_prefix) - .await, + RPC.vp().token().denomination(client, token).await, ) .unwrap_or_else(|| { if force { diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 50e470c731..5c65930167 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -21,7 +21,7 @@ use namada::types::address::Address; use namada::types::dec::Dec; use namada::types::key::tm_raw_hash_to_string; use namada::types::storage::{BlockHash, BlockResults, Epoch, Header}; -use namada::types::token::{minted_balance_key, Amount}; +use namada::types::token::Amount; use namada::types::transaction::protocol::{ ethereum_tx_data_variants, ProtocolTxType, }; @@ -707,9 +707,9 @@ where .expect("PoS inflation amount should exist in storage"); // Read from PoS storage let total_tokens = self - .read_storage_key(&minted_balance_key(&staking_token_address( - &self.wl_storage, - ))) + .read_storage_key(&token::minted_balance_key( + &staking_token_address(&self.wl_storage), + )) .expect("Total NAM balance should exist in storage"); let pos_locked_supply = read_total_stake(&self.wl_storage, ¶ms, last_epoch)?; @@ -1628,10 +1628,12 @@ mod test_finalize_block { let bertha = crate::wallet::defaults::bertha_address(); // add bertha's escrowed `asset` to the pool { - let asset_key = wrapped_erc20s::Keys::from(&asset); - let owner_key = - asset_key.balance(&bridge_pool::BRIDGE_POOL_ADDRESS); - let supply_key = asset_key.supply(); + let token = wrapped_erc20s::token(&asset); + let owner_key = token::balance_key( + &token, + &bridge_pool::BRIDGE_POOL_ADDRESS, + ); + let supply_key = token::minted_balance_key(&token); let amt: Amount = 999_999_u64.into(); shell .wl_storage diff --git a/core/src/ledger/eth_bridge/storage/mod.rs b/core/src/ledger/eth_bridge/storage/mod.rs index 958b002af0..0906be3d5d 100644 --- a/core/src/ledger/eth_bridge/storage/mod.rs +++ b/core/src/ledger/eth_bridge/storage/mod.rs @@ -29,6 +29,7 @@ pub fn escrow_key(nam_addr: &Address) -> Key { 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 @@ -62,6 +63,7 @@ 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() { @@ -77,6 +79,17 @@ mod 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 = @@ -92,4 +105,11 @@ mod test { .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/wrapped_erc20s.rs b/core/src/ledger/eth_bridge/storage/wrapped_erc20s.rs index 6d2f6de4da..beb4a61723 100644 --- a/core/src/ledger/eth_bridge/storage/wrapped_erc20s.rs +++ b/core/src/ledger/eth_bridge/storage/wrapped_erc20s.rs @@ -1,68 +1,17 @@ //! Functionality for accessing the multitoken subspace -use std::str::FromStr; use eyre::eyre; -use crate::types::address::Address; +use crate::types::address::{Address, InternalAddress}; use crate::types::ethereum_events::EthAddress; -use crate::types::storage::{self, DbKeySeg, KeySeg}; - -#[allow(missing_docs)] -pub const MULTITOKEN_KEY_SEGMENT: &str = "ERC20"; - -/// Get the key prefix corresponding to the storage subspace that holds wrapped -/// ERC20 tokens -pub fn prefix() -> storage::Key { - super::prefix() - .push(&MULTITOKEN_KEY_SEGMENT.to_owned()) - .expect("should always be able to construct this key") -} - -const BALANCE_KEY_SEGMENT: &str = "balance"; -const SUPPLY_KEY_SEGMENT: &str = "supply"; - -/// Generator for the keys under which details of an ERC20 token are stored -pub struct Keys { - /// The prefix of keys under which the details for a specific ERC20 token - /// are stored - prefix: storage::Key, -} - -impl Keys { - /// Get the `balance` key for a specific owner - there should be a - /// [`crate::types::token::Amount`] stored here - pub fn balance(&self, owner: &Address) -> storage::Key { - self.prefix - .push(&BALANCE_KEY_SEGMENT.to_owned()) - .expect("should always be able to construct this key") - .push(&owner.to_db_key()) - .expect("should always be able to construct this key") - } - - /// Get the `supply` key - there should be a - /// [`crate::types::token::Amount`] stored here - pub fn supply(&self) -> storage::Key { - self.prefix - .push(&SUPPLY_KEY_SEGMENT.to_owned()) - .expect("should always be able to construct this key") - } -} - -impl From<&EthAddress> for Keys { - fn from(address: &EthAddress) -> Self { - Keys { - prefix: prefix() - .push(&address.to_canonical()) - .expect("should always be able to construct this key"), - } - } -} - -/// Construct a sub-prefix from an ERC20 address. -pub fn sub_prefix(address: &EthAddress) -> storage::Key { - storage::Key::from(MULTITOKEN_KEY_SEGMENT.to_owned().to_db_key()) - .push(&address.to_db_key()) - .expect("should always be able to construct this key") +use crate::types::storage::{self, DbKeySeg}; +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.clone())) } /// Represents the type of a key relating to a wrapped ERC20 @@ -88,19 +37,22 @@ pub struct Key { impl From<&Key> for storage::Key { fn from(mt_key: &Key) -> Self { - let keys = Keys::from(&mt_key.asset); + let token = token(&mt_key.asset); match &mt_key.suffix { - KeyType::Balance { owner } => keys.balance(owner), - KeyType::Supply => keys.supply(), + KeyType::Balance { owner } => balance_key(&token, owner), + KeyType::Supply => minted_balance_key(&token), } } } -fn has_erc20_segment(key: &storage::Key) -> bool { - matches!( - key.segments.get(1), - Some(segment) if segment == &DbKeySeg::StringSeg(MULTITOKEN_KEY_SEGMENT.to_owned()), - ) +/// Returns true if the given key has an ERC20 token +pub fn has_erc20_segment(key: &storage::Key) -> bool { + match key.segments.get(1) { + Some(DbKeySeg::AddressSeg(Address::Internal( + InternalAddress::Erc20(_addr), + ))) => true, + _ => false, + } } impl TryFrom<(&Address, &storage::Key)> for Key { @@ -116,52 +68,41 @@ impl TryFrom<(&Address, &storage::Key)> for Key { return Err(eyre!("key does not have ERC20 segment")); } - let asset = - if let Some(DbKeySeg::StringSeg(segment)) = key.segments.get(2) { - EthAddress::from_str(segment)? - } else { - return Err(eyre!( - "key has an incorrect segment at index #2, expected an \ - Ethereum address" - )); - }; - - let segment_3 = - if let Some(DbKeySeg::StringSeg(segment)) = key.segments.get(3) { - segment.to_owned() - } else { - return Err(eyre!( - "key has an incorrect segment at index #3, expected a \ - string segment" - )); - }; - - match segment_3.as_str() { - SUPPLY_KEY_SEGMENT => { - let supply_key = Key { + let asset = if let Some(DbKeySeg::AddressSeg(Address::Internal( + InternalAddress::Erc20(addr), + ))) = key.segments.get(1) + { + addr.clone() + } else { + return Err(eyre!( + "key has an incorrect segment at index #2, expected an \ + Ethereum address" + )); + }; + + match key.segments.get(3) { + Some(DbKeySeg::AddressSeg(owner)) => { + let balance_key = Key { asset, - suffix: KeyType::Supply, + suffix: KeyType::Balance { + owner: owner.clone(), + }, }; - Ok(supply_key) + Ok(balance_key) } - BALANCE_KEY_SEGMENT => { - let owner = if let Some(DbKeySeg::AddressSeg(address)) = - key.segments.get(4) - { - address.to_owned() - } else { - return Err(eyre!( - "key has an incorrect segment at index #4, expected \ - an address segment" - )); - }; - let balance_key = Key { + Some(DbKeySeg::StringSeg(segment)) + if segment == MINTED_STORAGE_KEY => + { + let supply_key = Key { asset, - suffix: KeyType::Balance { owner }, + suffix: KeyType::Supply, }; - Ok(balance_key) + Ok(supply_key) } - _ => Err(eyre!("key has unrecognized string segment at index #3")), + _ => Err(eyre!( + "key has an incorrect segment at index #3, expected a string \ + segment" + )), } } } @@ -174,97 +115,77 @@ mod test { use super::*; use crate::ledger::eth_bridge::ADDRESS; use crate::types::address::{nam, Address}; - use crate::types::ethereum_events::testing::{ - DAI_ERC20_ETH_ADDRESS, DAI_ERC20_ETH_ADDRESS_CHECKSUMMED, - }; + 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); const ARBITRARY_OWNER_ADDRESS: &str = "atest1d9khqw36x9zyxwfhgfpygv2pgc65gse4gy6rjs34gfzr2v69gy6y23zpggurjv2yx5m52sesu6r4y4"; - #[test] - fn test_prefix() { - assert_matches!( - &prefix().segments[..], - [ - DbKeySeg::AddressSeg(multitoken_addr), - DbKeySeg::StringSeg(multitoken_path), - ] if multitoken_addr == &ADDRESS && - multitoken_path == MULTITOKEN_KEY_SEGMENT - ) - } - - #[test] - fn test_keys_from_eth_address() { - let keys: Keys = (&DAI_ERC20_ETH_ADDRESS).into(); - assert_matches!( - &keys.prefix.segments[..], - [ - DbKeySeg::AddressSeg(multitoken_addr), - DbKeySeg::StringSeg(multitoken_path), - DbKeySeg::StringSeg(token_id), - ] if multitoken_addr == &ADDRESS && - multitoken_path == MULTITOKEN_KEY_SEGMENT && - token_id == &DAI_ERC20_ETH_ADDRESS_CHECKSUMMED.to_ascii_lowercase() - ) + fn dai_erc20_token() -> Address { + Address::Internal(InternalAddress::Erc20(DAI_ERC20_ETH_ADDRESS)) } #[test] fn test_keys_balance() { - let keys: Keys = (&DAI_ERC20_ETH_ADDRESS).into(); - let key = - keys.balance(&Address::from_str(ARBITRARY_OWNER_ADDRESS).unwrap()); + let token = token(&DAI_ERC20_ETH_ADDRESS); + let key = balance_key( + &token, + &Address::from_str(ARBITRARY_OWNER_ADDRESS).unwrap(), + ); assert_matches!( &key.segments[..], [ DbKeySeg::AddressSeg(multitoken_addr), - DbKeySeg::StringSeg(multitoken_path), - DbKeySeg::StringSeg(token_id), + DbKeySeg::AddressSeg(token_addr), DbKeySeg::StringSeg(balance_key_seg), DbKeySeg::AddressSeg(owner_addr), - ] if multitoken_addr == &ADDRESS && - multitoken_path == MULTITOKEN_KEY_SEGMENT && - token_id == &DAI_ERC20_ETH_ADDRESS_CHECKSUMMED.to_ascii_lowercase() && - balance_key_seg == BALANCE_KEY_SEGMENT && + ] if multitoken_addr == &MULTITOKEN_ADDRESS && + token_addr == &dai_erc20_token() && + balance_key_seg == BALANCE_STORAGE_KEY && owner_addr == &Address::decode(ARBITRARY_OWNER_ADDRESS).unwrap() ) } #[test] fn test_keys_balance_to_string() { - let keys: Keys = (&DAI_ERC20_ETH_ADDRESS).into(); - let key = - keys.balance(&Address::from_str(ARBITRARY_OWNER_ADDRESS).unwrap()); + let token = token(&DAI_ERC20_ETH_ADDRESS); + let key = balance_key( + &token, + &Address::from_str(ARBITRARY_OWNER_ADDRESS).unwrap(), + ); assert_eq!( - "#atest1v9hx7w36g42ysgzzwf5kgem9ypqkgerjv4ehxgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpq8f99ew/ERC20/0x6b175474e89094c44da98b954eedeac495271d0f/balance/#atest1d9khqw36x9zyxwfhgfpygv2pgc65gse4gy6rjs34gfzr2v69gy6y23zpggurjv2yx5m52sesu6r4y4", + "#atest1v9hx7w36f46kcarfw3hkketwyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpq4w0mck/#atest1v46xsw36xe3rzde4xsmngefc8ycrjdrrxs6xgcfe8p3rjdf5v4jkgetpvv6rjdfjxuckgvrxqhdj5x/balance/#atest1d9khqw36x9zyxwfhgfpygv2pgc65gse4gy6rjs34gfzr2v69gy6y23zpggurjv2yx5m52sesu6r4y4", key.to_string() ) } #[test] fn test_keys_supply() { - let keys: Keys = (&DAI_ERC20_ETH_ADDRESS).into(); - let key = keys.supply(); + let token = token(&DAI_ERC20_ETH_ADDRESS); + let key = minted_balance_key(&token); assert_matches!( &key.segments[..], [ DbKeySeg::AddressSeg(multitoken_addr), - DbKeySeg::StringSeg(multitoken_path), - DbKeySeg::StringSeg(token_id), + DbKeySeg::AddressSeg(token_addr), + DbKeySeg::StringSeg(balance_key_seg), DbKeySeg::StringSeg(supply_key_seg), - ] if multitoken_addr == &ADDRESS && - multitoken_path == MULTITOKEN_KEY_SEGMENT && - token_id == &DAI_ERC20_ETH_ADDRESS_CHECKSUMMED.to_ascii_lowercase() && - supply_key_seg == SUPPLY_KEY_SEGMENT + ] if multitoken_addr == &MULTITOKEN_ADDRESS && + token_addr == &dai_erc20_token() && + balance_key_seg == BALANCE_STORAGE_KEY && + supply_key_seg == MINTED_STORAGE_KEY ) } #[test] fn test_keys_supply_to_string() { - let keys: Keys = (&DAI_ERC20_ETH_ADDRESS).into(); - let key = keys.supply(); + let token = token(&DAI_ERC20_ETH_ADDRESS); + let key = minted_balance_key(&token); assert_eq!( - "#atest1v9hx7w36g42ysgzzwf5kgem9ypqkgerjv4ehxgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpq8f99ew/ERC20/0x6b175474e89094c44da98b954eedeac495271d0f/supply", + "#atest1v9hx7w36f46kcarfw3hkketwyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpq4w0mck/#atest1v46xsw36xe3rzde4xsmngefc8ycrjdrrxs6xgcfe8p3rjdf5v4jkgetpvv6rjdfjxuckgvrxqhdj5x/balance/minted", key.to_string(), ) } @@ -281,13 +202,13 @@ mod test { &key.segments[..], [ DbKeySeg::AddressSeg(multitoken_addr), - DbKeySeg::StringSeg(multitoken_path), - DbKeySeg::StringSeg(token_id), + DbKeySeg::AddressSeg(token_addr), + DbKeySeg::StringSeg(balance_key_seg), DbKeySeg::StringSeg(supply_key_seg), - ] if multitoken_addr == &ADDRESS && - multitoken_path == MULTITOKEN_KEY_SEGMENT && - token_id == &DAI_ERC20_ETH_ADDRESS_CHECKSUMMED.to_ascii_lowercase() && - supply_key_seg == SUPPLY_KEY_SEGMENT + ] if multitoken_addr == &MULTITOKEN_ADDRESS && + token_addr == &dai_erc20_token() && + balance_key_seg == &BALANCE_STORAGE_KEY && + supply_key_seg == MINTED_STORAGE_KEY ); // balance key @@ -302,14 +223,12 @@ mod test { &key.segments[..], [ DbKeySeg::AddressSeg(multitoken_addr), - DbKeySeg::StringSeg(multitoken_path), - DbKeySeg::StringSeg(token_id), + DbKeySeg::AddressSeg(token_addr), DbKeySeg::StringSeg(balance_key_seg), DbKeySeg::AddressSeg(owner_addr), - ] if multitoken_addr == &ADDRESS && - multitoken_path == MULTITOKEN_KEY_SEGMENT && - token_id == &DAI_ERC20_ETH_ADDRESS_CHECKSUMMED.to_ascii_lowercase() && - balance_key_seg == BALANCE_KEY_SEGMENT && + ] if multitoken_addr == &MULTITOKEN_ADDRESS && + token_addr == &dai_erc20_token() && + balance_key_seg == BALANCE_STORAGE_KEY && owner_addr == &Address::decode(ARBITRARY_OWNER_ADDRESS).unwrap() ); } @@ -318,9 +237,10 @@ mod test { fn test_try_from_key_for_multitoken_key_supply() { // supply key let key = storage::Key::from_str(&format!( - "#{}/ERC20/{}/supply", - ADDRESS, - DAI_ERC20_ETH_ADDRESS_CHECKSUMMED.to_ascii_lowercase(), + "#{}/#{}/balance/{}", + MULTITOKEN_ADDRESS, + dai_erc20_token(), + MINTED_STORAGE_KEY, )) .expect("Should be able to construct key for test"); @@ -344,9 +264,9 @@ mod test { fn test_try_from_key_for_multitoken_key_balance() { // supply key let key = storage::Key::from_str(&format!( - "#{}/ERC20/{}/balance/#{}", + "#{}/#{}/balance/#{}", ADDRESS, - DAI_ERC20_ETH_ADDRESS_CHECKSUMMED.to_ascii_lowercase(), + dai_erc20_token(), ARBITRARY_OWNER_ADDRESS )) .expect("Should be able to construct key for test"); @@ -375,9 +295,9 @@ mod test { #[test] fn test_has_erc20_segment() { let key = storage::Key::from_str(&format!( - "#{}/ERC20/{}/balance/#{}", + "#{}/#{}/balance/#{}", ADDRESS, - DAI_ERC20_ETH_ADDRESS_CHECKSUMMED.to_ascii_lowercase(), + dai_erc20_token(), ARBITRARY_OWNER_ADDRESS )) .expect("Should be able to construct key for test"); @@ -385,16 +305,21 @@ mod test { assert!(has_erc20_segment(&key)); let key = storage::Key::from_str(&format!( - "#{}/ERC20/{}/supply", + "#{}/#{}/balance/{}", ADDRESS, - DAI_ERC20_ETH_ADDRESS_CHECKSUMMED.to_ascii_lowercase(), + dai_erc20_token(), + MINTED_STORAGE_KEY, )) .expect("Should be able to construct key for test"); assert!(has_erc20_segment(&key)); - let key = storage::Key::from_str(&format!("#{}/ERC20", ADDRESS)) - .expect("Should be able to construct key for test"); + let key = storage::Key::from_str(&format!( + "#{}/#{}", + MULTITOKEN_ADDRESS, + dai_erc20_token() + )) + .expect("Should be able to construct key for test"); assert!(has_erc20_segment(&key)); } diff --git a/core/src/ledger/storage/masp_conversions.rs b/core/src/ledger/storage/masp_conversions.rs index eefc6d7342..bbb61ff50d 100644 --- a/core/src/ledger/storage/masp_conversions.rs +++ b/core/src/ledger/storage/masp_conversions.rs @@ -23,12 +23,7 @@ pub struct ConversionState { #[allow(clippy::type_complexity)] pub assets: BTreeMap< AssetType, - ( - (Address, MaspDenom), - Epoch, - AllowedConversion, - usize, - ), + ((Address, MaspDenom), Epoch, AllowedConversion, usize), >, } diff --git a/core/src/proto/types.rs b/core/src/proto/types.rs index 69da19f58d..610d453654 100644 --- a/core/src/proto/types.rs +++ b/core/src/proto/types.rs @@ -28,7 +28,7 @@ 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, Key}; +use crate::types::storage::Epoch; use crate::types::time::DateTimeUtc; use crate::types::token::MaspDenom; #[cfg(feature = "ferveo-tpke")] @@ -671,7 +671,7 @@ pub struct MaspBuilder { pub target: crate::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, Option, MaspDenom, Epoch)>, + pub asset_types: HashSet<(Address, MaspDenom, Epoch)>, /// Track how Info objects map to descriptors and outputs #[serde( serialize_with = "borsh_serde::", diff --git a/core/src/types/address.rs b/core/src/types/address.rs index cb36593edc..1a6611a2f5 100644 --- a/core/src/types/address.rs +++ b/core/src/types/address.rs @@ -97,6 +97,8 @@ const PREFIX_IMPLICIT: &str = "imp"; const PREFIX_INTERNAL: &str = "ano"; /// Fixed-length address strings prefix for IBC addresses. const PREFIX_IBC: &str = "ibc"; +/// Fixed-length address strings prefix for Ethereum addresses. +const PREFIX_ETH: &str = "eth"; #[allow(missing_docs)] #[derive(Error, Debug)] @@ -230,6 +232,11 @@ impl Address { InternalAddress::EthBridgePool => { internal::ETH_BRIDGE_POOL.to_string() } + InternalAddress::Erc20(eth_addr) => { + let eth_addr = + eth_addr.to_canonical().replace("0x", ""); + format!("{}::{}", PREFIX_ETH, eth_addr) + } InternalAddress::ReplayProtection => { internal::REPLAY_PROTECTION.to_string() } @@ -327,6 +334,23 @@ impl Address { "Invalid IBC internal address", )), }, + Some((PREFIX_ETH, raw)) => match string { + _ if raw.len() == HASH_HEX_LEN => { + match EthAddress::from_str(&format!("0x{}", raw)) { + Ok(eth_addr) => Ok(Address::Internal( + InternalAddress::Erc20(eth_addr), + )), + Err(e) => Err(Error::new( + ErrorKind::InvalidData, + e.to_string(), + )), + } + } + _ => Err(Error::new( + ErrorKind::InvalidData, + "Invalid ERC20 internal address", + )), + }, _ => Err(Error::new( ErrorKind::InvalidData, "Invalid address prefix", @@ -523,6 +547,8 @@ pub enum InternalAddress { EthBridge, /// The pool of transactions to be relayed to Ethereum EthBridgePool, + /// ERC20 token for Ethereum bridge + Erc20(EthAddress), /// Replay protection contains transactions' hash ReplayProtection, /// Multitoken @@ -544,6 +570,7 @@ impl Display for InternalAddress { Self::IbcToken(hash) => format!("IbcToken: {}", hash), Self::EthBridge => "EthBridge".to_string(), Self::EthBridgePool => "EthBridgePool".to_string(), + Self::Erc20(eth_addr) => format!("Erc20: {}", eth_addr), Self::ReplayProtection => "ReplayProtection".to_string(), Self::Multitoken => "Multitoken".to_string(), } @@ -838,6 +865,7 @@ pub mod testing { InternalAddress::IbcToken(_) => {} InternalAddress::EthBridge => {} InternalAddress::EthBridgePool => {} + InternalAddress::Erc20(_) => {} InternalAddress::ReplayProtection => {} InternalAddress::Multitoken => {} /* Add new addresses in the * `prop_oneof` below. */ @@ -852,13 +880,13 @@ pub mod testing { Just(InternalAddress::SlashFund), Just(InternalAddress::EthBridge), Just(InternalAddress::EthBridgePool), + Just(arb_erc20()), Just(InternalAddress::ReplayProtection), Just(InternalAddress::Multitoken), ] } fn arb_ibc_token() -> impl Strategy { - // use sha2::{Digest, Sha256}; ("[a-zA-Z0-9_]{2,128}", any::()).prop_map(|(id, counter)| { let mut hasher = sha2::Sha256::new(); let s = format!( @@ -873,4 +901,9 @@ pub mod testing { InternalAddress::IbcToken(hash) }) } + + fn arb_erc20() -> InternalAddress { + use crate::types::ethereum_events::testing::arbitrary_eth_address; + InternalAddress::Erc20(arbitrary_eth_address()) + } } diff --git a/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs b/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs index 1da9c5381c..8ef07a64a5 100644 --- a/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs +++ b/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs @@ -3,7 +3,7 @@ use std::collections::{BTreeSet, HashSet}; use std::str::FromStr; -use borsh::BorshDeserialize; +use borsh::{BorshDeserialize, BorshSerialize}; use eyre::{Result, WrapErr}; use namada_core::hints; use namada_core::ledger::eth_bridge::storage::bridge_pool::{ @@ -26,7 +26,7 @@ use namada_core::types::ethereum_events::{ }; use namada_core::types::storage::{BlockHeight, Key, KeySeg}; use namada_core::types::token; -use namada_core::types::token::balance_key; +use namada_core::types::token::{balance_key, minted_balance_key, minter_key}; use crate::parameters::read_native_erc20_address; use crate::protocol::transactions::update; @@ -232,8 +232,8 @@ where H: 'static + StorageHasher + Sync, { let mut changed_keys = BTreeSet::default(); - let keys: wrapped_erc20s::Keys = asset.into(); - let balance_key = keys.balance(receiver); + let token = wrapped_erc20s::token(asset); + let balance_key = balance_key(&token, receiver); update::amount(wl_storage, &balance_key, |balance| { tracing::debug!( %balance_key, @@ -249,7 +249,7 @@ where })?; _ = changed_keys.insert(balance_key); - let supply_key = keys.supply(); + let supply_key = minted_balance_key(&token); update::amount(wl_storage, &supply_key, |supply| { tracing::debug!( %supply_key, @@ -264,6 +264,11 @@ where ); })?; _ = changed_keys.insert(supply_key); + + let minter_key = minter_key(&token); + wl_storage.write_bytes(&minter_key, BRIDGE_POOL_ADDRESS.try_to_vec()?)?; + _ = changed_keys.insert(minter_key); + Ok(changed_keys) } @@ -475,12 +480,9 @@ where ); (escrow_balance_key, sender_balance_key) } else { - let sub_prefix = wrapped_erc20s::sub_prefix(&transfer.transfer.asset); - let prefix = multitoken_balance_prefix(&BRIDGE_ADDRESS, &sub_prefix); - let escrow_balance_key = - multitoken_balance_key(&prefix, &BRIDGE_POOL_ADDRESS); - let sender_balance_key = - multitoken_balance_key(&prefix, &transfer.transfer.sender); + let token = wrapped_erc20s::token(&transfer.transfer.asset); + let escrow_balance_key = balance_key(&token, &BRIDGE_POOL_ADDRESS); + let sender_balance_key = balance_key(&token, &transfer.transfer.sender); (escrow_balance_key, sender_balance_key) }; update::amount(wl_storage, &source, |balance| { @@ -516,15 +518,15 @@ where return Ok(changed_keys); } - let keys: wrapped_erc20s::Keys = (&transfer.transfer.asset).into(); + let token = wrapped_erc20s::token(&transfer.transfer.asset); - let escrow_balance_key = keys.balance(&BRIDGE_POOL_ADDRESS); + let escrow_balance_key = balance_key(&token, &BRIDGE_POOL_ADDRESS); update::amount(wl_storage, &escrow_balance_key, |balance| { balance.spend(&transfer.transfer.amount); })?; _ = changed_keys.insert(escrow_balance_key); - let supply_key = keys.supply(); + let supply_key = minted_balance_key(&token); update::amount(wl_storage, &supply_key, |supply| { supply.spend(&transfer.transfer.amount); })?; @@ -657,12 +659,8 @@ mod tests { ) .expect("Test failed"); } else { - let sub_prefix = - wrapped_erc20s::sub_prefix(&transfer.transfer.asset); - let prefix = - multitoken_balance_prefix(&BRIDGE_ADDRESS, &sub_prefix); - let sender_key = - multitoken_balance_key(&prefix, &transfer.transfer.sender); + let token = wrapped_erc20s::token(&transfer.transfer.asset); + let sender_key = balance_key(&token, &transfer.transfer.sender); let sender_balance = Amount::from(0); wl_storage .write_bytes( @@ -670,8 +668,7 @@ mod tests { sender_balance.try_to_vec().expect("Test failed"), ) .expect("Test failed"); - let escrow_key = - multitoken_balance_key(&prefix, &BRIDGE_POOL_ADDRESS); + let escrow_key = balance_key(&token, &BRIDGE_POOL_ADDRESS); let escrow_balance = Amount::from(10); wl_storage .write_bytes( @@ -679,11 +676,13 @@ mod tests { escrow_balance.try_to_vec().expect("Test failed"), ) .expect("Test failed"); - let asset_keys: wrapped_erc20s::Keys = - (&transfer.transfer.asset).into(); - update::amount(wl_storage, &asset_keys.supply(), |supply| { - supply.receive(&transfer.transfer.amount); - }) + update::amount( + wl_storage, + &minted_balance_key(&token), + |supply| { + supply.receive(&transfer.transfer.amount); + }, + ) .expect("Test failed"); }; let gas_fee = Amount::from(1); @@ -758,7 +757,7 @@ mod tests { assert_eq!( stored_keys_count(&wl_storage), - initial_stored_keys_count + 2 + initial_stored_keys_count + 3 ); } @@ -784,13 +783,13 @@ mod tests { ) .unwrap(); - let wdai: wrapped_erc20s::Keys = (&DAI_ERC20_ETH_ADDRESS).into(); - let receiver_balance_key = wdai.balance(&receiver); - let wdai_supply_key = wdai.supply(); + let wdai = wrapped_erc20s::token(&DAI_ERC20_ETH_ADDRESS); + let receiver_balance_key = balance_key(&wdai, &receiver); + let wdai_supply_key = minted_balance_key(&wdai); assert_eq!( stored_keys_count(&wl_storage), - initial_stored_keys_count + 2 + initial_stored_keys_count + 3 ); let expected_amount = amount.try_to_vec().unwrap(); @@ -812,7 +811,7 @@ mod tests { let native_erc20 = read_native_erc20_address(&wl_storage).expect("Test failed"); let random_erc20 = EthAddress([0xff; 20]); - let random_erc20_keys: wrapped_erc20s::Keys = (&random_erc20).into(); + let random_erc20_token = wrapped_erc20s::token(&random_erc20); let pending_transfers = init_bridge_pool_transfers( &mut wl_storage, [native_erc20, random_erc20], @@ -851,10 +850,12 @@ mod tests { let mut changed_keys = act_on(&mut wl_storage, event).unwrap(); assert!( - changed_keys - .remove(&random_erc20_keys.balance(&BRIDGE_POOL_ADDRESS)) + changed_keys.remove(&balance_key( + &random_erc20_token, + &BRIDGE_POOL_ADDRESS + )) ); - assert!(changed_keys.remove(&random_erc20_keys.supply())); + assert!(changed_keys.remove(&minted_balance_key(&random_erc20_token))); assert!(changed_keys.remove(&payer_balance_key)); assert!(changed_keys.remove(&pool_balance_key)); assert!(changed_keys.remove(&get_nonce_key())); @@ -985,20 +986,15 @@ mod tests { .expect("Test failed"); assert_eq!(escrow_balance, Amount::from(0)); } else { - let sub_prefix = - wrapped_erc20s::sub_prefix(&transfer.transfer.asset); - let prefix = - multitoken_balance_prefix(&BRIDGE_ADDRESS, &sub_prefix); - let sender_key = - multitoken_balance_key(&prefix, &transfer.transfer.sender); + let token = wrapped_erc20s::token(&transfer.transfer.asset); + let sender_key = balance_key(&token, &transfer.transfer.sender); let value = wl_storage.read_bytes(&sender_key).expect("Test failed"); let sender_balance = Amount::try_from_slice(&value.expect("Test failed")) .expect("Test failed"); assert_eq!(sender_balance, transfer.transfer.amount); - let escrow_key = - multitoken_balance_key(&prefix, &BRIDGE_POOL_ADDRESS); + let escrow_key = balance_key(&token, &BRIDGE_POOL_ADDRESS); let value = wl_storage.read_bytes(&escrow_key).expect("Test failed"); let escrow_balance = @@ -1127,12 +1123,12 @@ mod tests { if asset == &native_erc20 { return None; } - let asset_keys: wrapped_erc20s::Keys = asset.into(); + let erc20_token = wrapped_erc20s::token(asset); let prev_balance = wl_storage - .read(&asset_keys.balance(&BRIDGE_POOL_ADDRESS)) + .read(&balance_key(&erc20_token, &BRIDGE_POOL_ADDRESS)) .expect("Test failed"); let prev_supply = wl_storage - .read(&asset_keys.supply()) + .read(&minted_balance_key(&erc20_token)) .expect("Test failed"); Some(Delta { asset: *asset, @@ -1161,14 +1157,14 @@ mod tests { .checked_sub(sent_amount) .expect("Test failed"); - let asset_keys: wrapped_erc20s::Keys = asset.into(); + let erc20_token = wrapped_erc20s::token(asset); let balance: token::Amount = wl_storage - .read(&asset_keys.balance(&BRIDGE_POOL_ADDRESS)) + .read(&balance_key(&erc20_token, &BRIDGE_POOL_ADDRESS)) .expect("Read must succeed") .expect("Balance must exist"); let supply: token::Amount = wl_storage - .read(&asset_keys.supply()) + .read(&minted_balance_key(&erc20_token)) .expect("Read must succeed") .expect("Balance must exist"); @@ -1187,19 +1183,19 @@ mod tests { test_wrapped_erc20s_aux(|wl_storage, event| { let native_erc20 = read_native_erc20_address(wl_storage).expect("Test failed"); - let wnam_keys: wrapped_erc20s::Keys = (&native_erc20).into(); + let wnam = wrapped_erc20s::token(&native_erc20); let escrow_balance_key = balance_key(&nam(), &BRIDGE_ADDRESS); // check pre supply assert!( wl_storage - .read_bytes(&wnam_keys.balance(&BRIDGE_POOL_ADDRESS)) + .read_bytes(&balance_key(&wnam, &BRIDGE_POOL_ADDRESS)) .expect("Test failed") .is_none() ); assert!( wl_storage - .read_bytes(&wnam_keys.supply()) + .read_bytes(&minted_balance_key(&wnam)) .expect("Test failed") .is_none() ); @@ -1215,13 +1211,13 @@ mod tests { // check post supply assert!( wl_storage - .read_bytes(&wnam_keys.balance(&BRIDGE_POOL_ADDRESS)) + .read_bytes(&balance_key(&wnam, &BRIDGE_POOL_ADDRESS)) .expect("Test failed") .is_none() ); assert!( wl_storage - .read_bytes(&wnam_keys.supply()) + .read_bytes(&minted_balance_key(&wnam)) .expect("Test failed") .is_none() ); diff --git a/ethereum_bridge/src/protocol/transactions/ethereum_events/mod.rs b/ethereum_bridge/src/protocol/transactions/ethereum_events/mod.rs index bb7b614187..29fd07a8e7 100644 --- a/ethereum_bridge/src/protocol/transactions/ethereum_events/mod.rs +++ b/ethereum_bridge/src/protocol/transactions/ethereum_events/mod.rs @@ -284,7 +284,9 @@ mod tests { use namada_core::types::ethereum_events::{ EthereumEvent, TransferToNamada, }; - use namada_core::types::token::Amount; + use namada_core::types::token::{ + balance_key, minted_balance_key, minter_key, Amount, + }; use super::*; use crate::protocol::transactions::utils::GetVoters; @@ -335,7 +337,7 @@ mod tests { apply_updates(&mut wl_storage, updates, voting_powers)?; let eth_msg_keys: vote_tallies::Keys = (&body).into(); - let wrapped_erc20_keys: wrapped_erc20s::Keys = (&asset).into(); + let wrapped_erc20_token = wrapped_erc20s::token(&asset); assert_eq!( BTreeSet::from_iter(vec![ eth_msg_keys.body(), @@ -343,8 +345,9 @@ mod tests { eth_msg_keys.seen_by(), eth_msg_keys.voting_power(), eth_msg_keys.voting_started_epoch(), - wrapped_erc20_keys.balance(&receiver), - wrapped_erc20_keys.supply(), + balance_key(&wrapped_erc20_token, &receiver), + minted_balance_key(&wrapped_erc20_token), + minter_key(&wrapped_erc20_token), ]), changed_keys ); @@ -375,8 +378,8 @@ mod tests { let epoch_bytes = epoch_bytes.unwrap(); assert_eq!(Epoch::try_from_slice(&epoch_bytes)?, Epoch(0)); - let wrapped_erc20_balance_bytes = - wl_storage.read_bytes(&wrapped_erc20_keys.balance(&receiver))?; + let wrapped_erc20_balance_bytes = wl_storage + .read_bytes(&balance_key(&wrapped_erc20_token, &receiver))?; let wrapped_erc20_balance_bytes = wrapped_erc20_balance_bytes.unwrap(); assert_eq!( Amount::try_from_slice(&wrapped_erc20_balance_bytes)?, @@ -384,7 +387,7 @@ mod tests { ); let wrapped_erc20_supply_bytes = - wl_storage.read_bytes(&wrapped_erc20_keys.supply())?; + wl_storage.read_bytes(&minted_balance_key(&wrapped_erc20_token))?; let wrapped_erc20_supply_bytes = wrapped_erc20_supply_bytes.unwrap(); assert_eq!( Amount::try_from_slice(&wrapped_erc20_supply_bytes)?, @@ -435,7 +438,7 @@ mod tests { "No gas should be used for a derived transaction" ); let eth_msg_keys = vote_tallies::Keys::from(&event); - let dai_keys = wrapped_erc20s::Keys::from(&DAI_ERC20_ETH_ADDRESS); + let dai_token = wrapped_erc20s::token(&DAI_ERC20_ETH_ADDRESS); assert_eq!( tx_result.changed_keys, BTreeSet::from_iter(vec![ @@ -444,8 +447,9 @@ mod tests { eth_msg_keys.seen_by(), eth_msg_keys.voting_power(), eth_msg_keys.voting_started_epoch(), - dai_keys.balance(&receiver), - dai_keys.supply(), + balance_key(&dai_token, &receiver), + minted_balance_key(&dai_token), + minter_key(&dai_token), ]) ); assert!(tx_result.vps_result.accepted_vps.is_empty()); diff --git a/shared/src/ledger/eth_bridge/bridge_pool.rs b/shared/src/ledger/eth_bridge/bridge_pool.rs index a95d56d475..e06c141614 100644 --- a/shared/src/ledger/eth_bridge/bridge_pool.rs +++ b/shared/src/ledger/eth_bridge/bridge_pool.rs @@ -60,7 +60,7 @@ pub async fn build_bridge_pool_tx< let sub_prefix = Some(wrapped_erc20s::sub_prefix(&asset)); let DenominatedAmount { amount, .. } = - validate_amount(client, amount, &BRIDGE_ADDRESS, &sub_prefix, tx.force) + validate_amount(client, amount, &BRIDGE_ADDRESS, tx.force) .await .expect("Failed to validate amount"); let transfer = PendingTransfer { diff --git a/shared/src/ledger/ibc/vp/context.rs b/shared/src/ledger/ibc/vp/context.rs index d05eaebcd1..daf2246cbe 100644 --- a/shared/src/ledger/ibc/vp/context.rs +++ b/shared/src/ledger/ibc/vp/context.rs @@ -11,10 +11,7 @@ use namada_core::ledger::storage_api::StorageRead; use namada_core::types::address::{Address, InternalAddress}; use namada_core::types::ibc::IbcEvent; use namada_core::types::storage::{BlockHeight, Header, Key}; -use namada_core::types::token::{ - self, is_any_token_balance_key, is_any_token_or_multitoken_balance_key, - Amount, -}; +use namada_core::types::token::{self, Amount}; use super::Error; use crate::ledger::native_vp::CtxPreStorageRead; diff --git a/shared/src/ledger/masp.rs b/shared/src/ledger/masp.rs index b6ccd8672b..6fe972d4ac 100644 --- a/shared/src/ledger/masp.rs +++ b/shared/src/ledger/masp.rs @@ -50,7 +50,7 @@ use masp_proofs::bellman::groth16::PreparedVerifyingKey; use masp_proofs::bls12_381::Bls12; use masp_proofs::prover::LocalTxProver; use masp_proofs::sapling::SaplingVerificationContext; -use namada_core::types::token::{Change, MaspDenom, TokenAddress}; +use namada_core::types::token::{Change, MaspDenom}; use namada_core::types::transaction::AffineCurve; #[cfg(feature = "masp-tx-gen")] use rand_core::{CryptoRng, OsRng, RngCore}; @@ -407,7 +407,7 @@ pub enum PinnedBalanceError { // #[derive(BorshSerialize, BorshDeserialize, Debug, Clone)] // pub struct MaspAmount { -// pub asset: TokenAddress, +// pub asset: Address, // pub amount: token::Amount, // } @@ -415,7 +415,7 @@ pub enum PinnedBalanceError { #[derive(BorshSerialize, BorshDeserialize, Debug, Clone)] pub struct MaspChange { /// the token address - pub asset: TokenAddress, + pub asset: Address, /// the change in the token pub change: token::Change, } @@ -424,10 +424,10 @@ pub struct MaspChange { #[derive( BorshSerialize, BorshDeserialize, Debug, Clone, Default, PartialEq, Eq, )] -pub struct MaspAmount(HashMap<(Epoch, TokenAddress), token::Change>); +pub struct MaspAmount(HashMap<(Epoch, Address), token::Change>); impl std::ops::Deref for MaspAmount { - type Target = HashMap<(Epoch, TokenAddress), token::Change>; + type Target = HashMap<(Epoch, Address), token::Change>; fn deref(&self) -> &Self::Target { &self.0 @@ -495,14 +495,9 @@ impl std::ops::Mul for MaspAmount { impl<'a> From<&'a MaspAmount> for Amount { fn from(masp_amount: &'a MaspAmount) -> Amount { let mut res = Amount::zero(); - for ((epoch, key), val) in masp_amount.iter() { + for ((epoch, token), val) in masp_amount.iter() { for denom in MaspDenom::iter() { - let asset = make_asset_type( - Some(*epoch), - &key.address, - &key.sub_prefix, - denom, - ); + let asset = make_asset_type(Some(*epoch), token, denom); res += Amount::from_pair(asset, denom.denominate_i128(val)) .unwrap(); } @@ -558,8 +553,7 @@ pub struct ShieldedContext { /// The set of note positions that have been spent pub spents: HashSet, /// Maps asset types to their decodings - pub asset_types: - HashMap, MaspDenom, Epoch)>, + pub asset_types: HashMap, /// Maps note positions to their corresponding viewing keys pub vk_map: HashMap, } @@ -850,10 +844,7 @@ impl ShieldedContext { } // Record the changes to the transparent accounts let mut transfer_delta = TransferDelta::new(); - let token_addr = TokenAddress { - address: tx.token.clone(), - sub_prefix: tx.sub_prefix.clone(), - }; + let token_addr = tx.token.clone(); transfer_delta.insert( tx.source.clone(), MaspChange { @@ -917,23 +908,22 @@ impl ShieldedContext { &mut self, client: &U::C, asset_type: AssetType, - ) -> Option<(Address, Option, MaspDenom, Epoch)> { + ) -> Option<(Address, MaspDenom, Epoch)> { // Try to find the decoding in the cache if let decoded @ Some(_) = self.asset_types.get(&asset_type) { return decoded.cloned(); } // Query for the ID of the last accepted transaction - let (addr, sub_prefix, denom, ep, _conv, _path): ( + let (addr, denom, ep, _conv, _path): ( Address, - Option, MaspDenom, _, Amount, MerklePath, ) = rpc::query_conversion(client, asset_type).await?; self.asset_types - .insert(asset_type, (addr.clone(), sub_prefix.clone(), denom, ep)); - Some((addr, sub_prefix, denom, ep)) + .insert(asset_type, (addr.clone(), denom, ep)); + Some((addr, denom, ep)) } /// Query the ledger for the conversion that is allowed for the given asset @@ -946,11 +936,10 @@ impl ShieldedContext { ) { if let Entry::Vacant(conv_entry) = conversions.entry(asset_type) { // Query for the ID of the last accepted transaction - if let Some((addr, sub_prefix, denom, ep, conv, path)) = + if let Some((addr, denom, ep, conv, path)) = query_conversion(client, asset_type).await { - self.asset_types - .insert(asset_type, (addr, sub_prefix, denom, ep)); + self.asset_types.insert(asset_type, (addr, denom, ep)); // If the conversion is 0, then we just have a pure decoding if conv != Amount::zero() { conv_entry.insert((conv.into(), path, 0)); @@ -997,7 +986,7 @@ impl ShieldedContext { &mut self, client: &U::C, conv: AllowedConversion, - asset_type: (Epoch, TokenAddress, MaspDenom), + asset_type: (Epoch, Address, MaspDenom), value: i128, usage: &mut i128, input: &mut MaspAmount, @@ -1010,12 +999,8 @@ impl ShieldedContext { // If conversion if possible, accumulate the exchanged amount let conv: Amount = conv.into(); // The amount required of current asset to qualify for conversion - let masp_asset = make_asset_type( - Some(asset_type.0), - &asset_type.1.address, - &asset_type.1.sub_prefix, - asset_type.2, - ); + let masp_asset = + make_asset_type(Some(asset_type.0), &asset_type.1, asset_type.2); let threshold = -conv[&masp_asset]; if threshold == 0 { eprintln!( @@ -1063,18 +1048,10 @@ impl ShieldedContext { let asset_epoch = *asset_epoch; let token_addr = token_addr.clone(); for denom in MaspDenom::iter() { - let target_asset_type = make_asset_type( - Some(target_epoch), - &token_addr.address, - &token_addr.sub_prefix, - denom, - ); - let asset_type = make_asset_type( - Some(asset_epoch), - &token_addr.address, - &token_addr.sub_prefix, - denom, - ); + let target_asset_type = + make_asset_type(Some(target_epoch), &token_addr, denom); + let asset_type = + make_asset_type(Some(asset_epoch), &token_addr, denom); let at_target_asset_type = target_epoch == asset_epoch; let denom_value = denom.denominate_i128(&value); @@ -1139,9 +1116,9 @@ impl ShieldedContext { (asset_epoch, token_addr.clone()), denom_value.into(), ); - for ((e, key), val) in input.iter() { - if *key == token_addr && *e == asset_epoch { - comp.insert((*e, key.clone()), *val); + for ((e, token), val) in input.iter() { + if *token == token_addr && *e == asset_epoch { + comp.insert((*e, token.clone()), *val); } } output += comp.clone(); @@ -1327,24 +1304,19 @@ impl ShieldedContext { client: &U::C, amt: Amount, target_epoch: Epoch, - ) -> HashMap { + ) -> HashMap { let mut res = HashMap::new(); for (asset_type, val) in amt.components() { // Decode the asset type let decoded = self.decode_asset_type(client, *asset_type).await; // Only assets with the target timestamp count match decoded { - Some(asset_type @ (_, _, _, epoch)) - if epoch == target_epoch => - { + Some(asset_type @ (_, _, epoch)) if epoch == target_epoch => { decode_component( asset_type, *val, &mut res, - |address, sub_prefix, _| TokenAddress { - address, - sub_prefix, - }, + |address, _| address, ); } _ => {} @@ -1360,27 +1332,15 @@ impl ShieldedContext { client: &U::C, amt: Amount, ) -> MaspAmount { - let mut res: HashMap<(Epoch, TokenAddress), Change> = - HashMap::default(); + let mut res: HashMap<(Epoch, Address), Change> = HashMap::default(); for (asset_type, val) in amt.components() { // Decode the asset type if let Some(decoded) = self.decode_asset_type(client, *asset_type).await { - decode_component( - decoded, - *val, - &mut res, - |address, sub_prefix, epoch| { - ( - epoch, - TokenAddress { - address, - sub_prefix, - }, - ) - }, - ) + decode_component(decoded, *val, &mut res, |address, epoch| { + (epoch, address) + }) } } MaspAmount(res) @@ -1440,12 +1400,8 @@ impl ShieldedContext { unreachable!("The function `gen_shielded_transfer` is only called by `submit_tx` which validates amounts.") }; // Convert transaction amount into MASP types - let (asset_types, amount) = convert_amount( - epoch, - &args.token, - &args.sub_prefix.as_ref(), - amt.amount, - ); + let (asset_types, amount) = + convert_amount(epoch, &args.token, amt.amount); let tx_fee = // If there are shielded inputs @@ -1456,7 +1412,7 @@ impl ShieldedContext { // Transaction fees need to match the amount in the wrapper Transfer // when MASP source is used let (_, shielded_fee) = - convert_amount(epoch, &args.tx.fee_token, &None, fee.amount); + convert_amount(epoch, &args.tx.fee_token, fee.amount); let required_amt = if shielded_gas { amount + shielded_fee.clone() } else { @@ -1701,10 +1657,7 @@ impl ShieldedContext { let delta = TransferDelta::from([( transfer.source.clone(), MaspChange { - asset: TokenAddress { - address: transfer.token.clone(), - sub_prefix: transfer.sub_prefix.clone(), - }, + asset: transfer.token.clone(), change: -transfer.amount.amount.change(), }, )]); @@ -1744,33 +1697,15 @@ fn extract_payload( } /// Make asset type corresponding to given address and epoch -pub fn make_asset_type( +pub fn make_asset_type( epoch: Option, token: &Address, - sub_prefix: &Option, denom: MaspDenom, ) -> AssetType { // Typestamp the chosen token with the current epoch let token_bytes = match epoch { - None => ( - token, - sub_prefix - .as_ref() - .map(|k| k.to_string()) - .unwrap_or_default(), - denom, - ) - .try_to_vec() - .expect("token should serialize"), - Some(epoch) => ( - token, - sub_prefix - .as_ref() - .map(|k| k.to_string()) - .unwrap_or_default(), - denom, - epoch.0, - ) + None => (token, denom).try_to_vec().expect("token should serialize"), + Some(epoch) => (token, denom, epoch.0) .try_to_vec() .expect("token should serialize"), }; @@ -1782,14 +1717,12 @@ pub fn make_asset_type( fn convert_amount( epoch: Epoch, token: &Address, - sub_prefix: &Option<&String>, val: token::Amount, ) -> ([AssetType; 4], Amount) { let mut amount = Amount::zero(); let asset_types: [AssetType; 4] = MaspDenom::iter() .map(|denom| { - let asset_type = - make_asset_type(Some(epoch), token, sub_prefix, denom); + let asset_type = make_asset_type(Some(epoch), token, denom); // Combine the value and unit into one amount amount += Amount::from_nonnegative(asset_type, denom.denominate(&val)) 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 283cf52c58..63caf82a57 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 @@ -92,9 +92,9 @@ where transfer: &PendingTransfer, ) -> Result { // check that the assets to be transferred were escrowed - let asset_key = wrapped_erc20s::Keys::from(&transfer.transfer.asset); - let owner_key = asset_key.balance(&transfer.transfer.sender); - let escrow_key = asset_key.balance(&BRIDGE_POOL_ADDRESS); + let token = wrapped_erc20s::token(&transfer.transfer.asset); + let owner_key = balance_key(&token, &transfer.transfer.sender); + let escrow_key = balance_key(&token, &BRIDGE_POOL_ADDRESS); if keys_changed.contains(&owner_key) && keys_changed.contains(&escrow_key) { @@ -486,7 +486,7 @@ mod test_bridge_pool_vp { ) -> BTreeSet { // get the balance keys let token_key = - wrapped_erc20s::Keys::from(&ASSET).balance(&balance.owner); + balance_key(&wrapped_erc20s::token(&ASSET), &balance.owner); let account_key = balance_key(&nam(), &balance.owner); // update the balance of nam @@ -1028,12 +1028,14 @@ mod test_bridge_pool_vp { BTreeSet::from([get_pending_key(&transfer)]) }; // We escrow 0 tokens - keys_changed.insert( - wrapped_erc20s::Keys::from(&ASSET).balance(&bertha_address()), - ); - keys_changed.insert( - wrapped_erc20s::Keys::from(&ASSET).balance(&BRIDGE_POOL_ADDRESS), - ); + keys_changed.insert(balance_key( + &wrapped_erc20s::token(&ASSET), + &bertha_address(), + )); + keys_changed.insert(balance_key( + &wrapped_erc20s::token(&ASSET), + &BRIDGE_POOL_ADDRESS, + )); let verifiers = BTreeSet::default(); // create the data to be given to the vp diff --git a/shared/src/ledger/native_vp/ethereum_bridge/vp.rs b/shared/src/ledger/native_vp/ethereum_bridge/vp.rs index 47eed29f3c..de068f5e9e 100644 --- a/shared/src/ledger/native_vp/ethereum_bridge/vp.rs +++ b/shared/src/ledger/native_vp/ethereum_bridge/vp.rs @@ -437,6 +437,7 @@ mod tests { 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; @@ -563,10 +564,9 @@ mod tests { { let keys_changed = BTreeSet::from_iter(vec![ arbitrary_key(), - wrapped_erc20s::Keys::from( + minted_balance_key(&wrapped_erc20s::token( ðereum_events::testing::DAI_ERC20_ETH_ADDRESS, - ) - .supply(), + )), ]); let result = determine_check_type(&nam(), &keys_changed); @@ -577,10 +577,10 @@ mod tests { { let keys_changed = BTreeSet::from_iter(vec![ arbitrary_key(), - wrapped_erc20s::Keys::from( - ðereum_events::testing::DAI_ERC20_ETH_ADDRESS, - ) - .balance( + balance_key( + &wrapped_erc20s::token( + ðereum_events::testing::DAI_ERC20_ETH_ADDRESS, + ), &Address::decode(ARBITRARY_OWNER_A_ADDRESS) .expect("Couldn't set up test"), ), @@ -596,17 +596,17 @@ mod tests { fn test_rejects_if_multitoken_keys_for_different_assets() { { let keys_changed = BTreeSet::from_iter(vec![ - wrapped_erc20s::Keys::from( - ðereum_events::testing::DAI_ERC20_ETH_ADDRESS, - ) - .balance( + balance_key( + &wrapped_erc20s::token( + ðereum_events::testing::DAI_ERC20_ETH_ADDRESS, + ), &Address::decode(ARBITRARY_OWNER_A_ADDRESS) .expect("Couldn't set up test"), ), - wrapped_erc20s::Keys::from( - ðereum_events::testing::USDC_ERC20_ETH_ADDRESS, - ) - .balance( + balance_key( + &wrapped_erc20s::token( + ðereum_events::testing::USDC_ERC20_ETH_ADDRESS, + ), &Address::decode(ARBITRARY_OWNER_B_ADDRESS) .expect("Couldn't set up test"), ), @@ -623,8 +623,9 @@ mod tests { let asset = ðereum_events::testing::DAI_ERC20_ETH_ADDRESS; { let keys_changed = BTreeSet::from_iter(vec![ - wrapped_erc20s::Keys::from(asset).supply(), - wrapped_erc20s::Keys::from(asset).balance( + minted_balance_key(&wrapped_erc20s::token(asset)), + balance_key( + &wrapped_erc20s::token(asset), &Address::decode(ARBITRARY_OWNER_B_ADDRESS) .expect("Couldn't set up test"), ), diff --git a/shared/src/ledger/native_vp/multitoken.rs b/shared/src/ledger/native_vp/multitoken.rs index 9c985b9dab..22556b75a1 100644 --- a/shared/src/ledger/native_vp/multitoken.rs +++ b/shared/src/ledger/native_vp/multitoken.rs @@ -12,6 +12,7 @@ use crate::types::address::{Address, InternalAddress}; use crate::types::storage::Key; use crate::types::token::{ is_any_minted_balance_key, is_any_token_balance_key, minter_key, Amount, + Change, }; use crate::vm::WasmCacheAccess; @@ -55,7 +56,7 @@ where let mut changes = HashMap::new(); let mut mints = HashMap::new(); for key in keys_changed { - if let Some((token, _)) = is_any_token_balance_key(key) { + if let Some([token, _]) = is_any_token_balance_key(key) { let pre: Amount = self.ctx.read_pre(key)?.unwrap_or_default(); let post: Amount = self.ctx.read_post(key)?.unwrap_or_default(); let diff = post.change() - pre.change(); @@ -87,7 +88,7 @@ where Ok(changes.iter().all(|(token, change)| { let mint = match mints.get(token) { Some(mint) => *mint, - None => 0, + None => Change::zero(), }; *change == mint })) diff --git a/shared/src/ledger/protocol/mod.rs b/shared/src/ledger/protocol/mod.rs index b6920fb4f9..6cab156d7a 100644 --- a/shared/src/ledger/protocol/mod.rs +++ b/shared/src/ledger/protocol/mod.rs @@ -585,7 +585,8 @@ where replay_protection_vp.ctx.gas_meter.into_inner(); result } - InternalAddress::IbcToken(_) => { + InternalAddress::IbcToken(_) + | InternalAddress::Erc20(_) => { // The address should be a part of a multitoken key gas_meter = ctx.gas_meter.into_inner(); Ok(verifiers.contains(&Address::Internal( diff --git a/shared/src/ledger/queries/shell.rs b/shared/src/ledger/queries/shell.rs index 7583f8bcf2..3eb95c8e8c 100644 --- a/shared/src/ledger/queries/shell.rs +++ b/shared/src/ledger/queries/shell.rs @@ -7,7 +7,7 @@ use masp_primitives::sapling::Node; use namada_core::ledger::storage::LastBlock; use namada_core::types::address::Address; use namada_core::types::hash::Hash; -use namada_core::types::storage::{BlockHeight, BlockResults, Key, KeySeg}; +use namada_core::types::storage::{BlockHeight, BlockResults, KeySeg}; use namada_core::types::token::MaspDenom; use self::eth_bridge::{EthBridge, ETH_BRIDGE}; @@ -27,7 +27,6 @@ use crate::types::transaction::TxResult; type Conversion = ( Address, - Option, MaspDenom, Epoch, masp_primitives::transaction::components::Amount, @@ -163,7 +162,7 @@ where H: 'static + StorageHasher + Sync, { // Conversion values are constructed on request - if let Some(((addr, sub_prefix, denom), epoch, conv, pos)) = ctx + if let Some(((addr, denom), epoch, conv, pos)) = ctx .wl_storage .storage .conversion_state @@ -172,7 +171,6 @@ where { Ok(( addr.clone(), - sub_prefix.clone(), *denom, *epoch, Into::::into( diff --git a/shared/src/ledger/queries/shell/eth_bridge.rs b/shared/src/ledger/queries/shell/eth_bridge.rs index 42d0207d5b..55e5255b62 100644 --- a/shared/src/ledger/queries/shell/eth_bridge.rs +++ b/shared/src/ledger/queries/shell/eth_bridge.rs @@ -17,7 +17,7 @@ use namada_core::types::ethereum_events::{ }; use namada_core::types::ethereum_structs::RelayProof; use namada_core::types::storage::{BlockHeight, DbKeySeg, Key}; -use namada_core::types::token::Amount; +use namada_core::types::token::{minted_balance_key, Amount}; use namada_core::types::vote_extensions::validator_set_update::{ ValidatorSetArgs, VotingPowersMap, }; @@ -128,8 +128,8 @@ where "Wrapped NAM's supply is not kept track of", )); } - let keys: wrapped_erc20s::Keys = (&asset).into(); - ctx.wl_storage.read(&keys.supply()) + let token = wrapped_erc20s::token(&asset); + ctx.wl_storage.read(&minted_balance_key(&token)) } /// Helper function to read a smart contract from storage. @@ -1420,10 +1420,10 @@ mod test_ethbridge_router { // write tokens to storage let amount = Amount::native_whole(12345); - let keys: wrapped_erc20s::Keys = (&ERC20_TOKEN).into(); + let token = wrapped_erc20s::token(&ERC20_TOKEN); client .wl_storage - .write(&keys.supply(), amount) + .write(&minted_balance_key(&token), amount) .expect("Test failed"); // check that the supply was updated diff --git a/shared/src/ledger/queries/vp/token.rs b/shared/src/ledger/queries/vp/token.rs index 030bdddd53..4e6cdc6374 100644 --- a/shared/src/ledger/queries/vp/token.rs +++ b/shared/src/ledger/queries/vp/token.rs @@ -2,7 +2,6 @@ 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::storage::Key; use namada_core::types::token; use crate::ledger::queries::RequestCtx; diff --git a/shared/src/ledger/rpc.rs b/shared/src/ledger/rpc.rs index 08111692e3..56cc388f5a 100644 --- a/shared/src/ledger/rpc.rs +++ b/shared/src/ledger/rpc.rs @@ -13,7 +13,7 @@ use namada_core::ledger::testnet_pow; use namada_core::types::address::Address; use namada_core::types::storage::Key; use namada_core::types::token::{ - Amount, DenominatedAmount, Denomination, MaspDenom, TokenAddress, + Amount, DenominatedAmount, Denomination, MaspDenom, }; use namada_proof_of_stake::types::{BondsAndUnbondsDetails, CommissionPair}; use serde::Serialize; @@ -241,7 +241,6 @@ pub async fn query_conversion( asset_type: AssetType, ) -> Option<( Address, - Option, MaspDenom, Epoch, masp_primitives::transaction::components::Amount, @@ -954,7 +953,6 @@ pub async fn validate_amount( client: &C, amount: InputAmount, token: &Address, - sub_prefix: &Option, force: bool, ) -> Option { let input_amount = match amount { @@ -962,10 +960,7 @@ pub async fn validate_amount( InputAmount::Validated(amt) => return Some(amt), }; let denom = unwrap_client_response::>( - RPC.vp() - .token() - .denomination(client, token, sub_prefix) - .await, + RPC.vp().token().denomination(client, token).await, ) .or_else(|| { if force { @@ -1065,7 +1060,7 @@ pub async fn format_denominated_amount< amount: token::Amount, ) -> String { let denom = unwrap_client_response::>( - RPC.vp().token().denomination(client, &token.address).await, + RPC.vp().token().denomination(client, token).await, ) .unwrap_or_else(|| { println!( diff --git a/shared/src/ledger/signing.rs b/shared/src/ledger/signing.rs index b6b06f0dae..2f0fd0d56d 100644 --- a/shared/src/ledger/signing.rs +++ b/shared/src/ledger/signing.rs @@ -19,9 +19,7 @@ use namada_core::types::address::{ masp, masp_tx_key, Address, ImplicitAddress, }; use namada_core::types::storage::Key; -use namada_core::types::token::{ - self, Amount, DenominatedAmount, MaspDenom, TokenAddress, -}; +use namada_core::types::token::{self, Amount, DenominatedAmount, MaspDenom}; use namada_core::types::transaction::{pos, MIN_FEE}; use prost::Message; use serde::{Deserialize, Serialize}; @@ -252,10 +250,7 @@ pub async fn solve_pow_challenge( .unwrap_or_default(); let is_bal_sufficient = fee_amount <= balance; if !is_bal_sufficient { - let token_addr = TokenAddress { - address: args.fee_token.clone(), - sub_prefix: None, - }; + let token_addr = args.fee_token.clone(); let err_msg = format!( "The wrapper transaction source doesn't have enough balance to \ pay fee {}, got {}.", @@ -407,10 +402,7 @@ pub async fn sign_wrapper< .unwrap_or_default(); let is_bal_sufficient = fee_amount <= balance; if !is_bal_sufficient { - let token_addr = TokenAddress { - address: args.fee_token.clone(), - sub_prefix: None, - }; + let token_addr = args.fee_token.clone(); let err_msg = format!( "The wrapper transaction source doesn't have enough balance to \ pay fee {}, got {}.", @@ -537,23 +529,13 @@ fn make_ledger_amount_addr( output: &mut Vec, amount: DenominatedAmount, token: &Address, - sub_prefix: &Option, prefix: &str, ) { - let token_address = TokenAddress { - address: token.clone(), - sub_prefix: sub_prefix.clone(), - }; if let Some(token) = tokens.get(token) { - output.push(format!( - "{}Amount {}: {}", - prefix, - token_address.format_with_alias(token), - amount - )); + output.push(format!("{}Amount {}: {}", prefix, token, amount)); } else { output.extend(vec![ - format!("{}Token: {}", prefix, token_address), + format!("{}Token: {}", prefix, token), format!("{}Amount: {}", prefix, amount), ]); } @@ -567,27 +549,21 @@ async fn make_ledger_amount_asset( output: &mut Vec, amount: u64, token: &AssetType, - assets: &HashMap, MaspDenom, Epoch)>, + assets: &HashMap, prefix: &str, ) { - if let Some((token, sub_prefix, _, _epoch)) = assets.get(token) { + if let Some((token, _, _epoch)) = assets.get(token) { // If the AssetType can be decoded, then at least display Addressees - let token_addr = TokenAddress { - address: token.clone(), - sub_prefix: sub_prefix.clone(), - }; let formatted_amt = - format_denominated_amount(client, &token_addr, amount.into()).await; + format_denominated_amount(client, &token, amount.into()).await; if let Some(token) = tokens.get(token) { - output.push(format!( - "{}Amount: {} {}", - prefix, - token_addr.format_with_alias(token), - formatted_amt, - )); + output + .push( + format!("{}Amount: {} {}", prefix, token, formatted_amt,), + ); } else { output.extend(vec![ - format!("{}Token: {}", prefix, token_addr), + format!("{}Token: {}", prefix, token), format!("{}Amount: {}", prefix, formatted_amt), ]); } @@ -1022,16 +998,10 @@ pub async fn to_ledger_vector< Section::MaspBuilder(builder) if builder.target == shielded_hash => { - for (addr, sub_prefix, denom, epoch) in &builder.asset_types - { + for (addr, denom, epoch) in &builder.asset_types { asset_types.insert( - make_asset_type( - Some(*epoch), - addr, - sub_prefix, - *denom, - ), - (addr.clone(), sub_prefix.clone(), *denom, *epoch), + make_asset_type(Some(*epoch), addr, *denom), + (addr.clone(), *denom, *epoch), ); } Some(builder) @@ -1216,10 +1186,7 @@ pub async fn to_ledger_vector< } if let Some(wrapper) = tx.header.wrapper() { - let gas_token = TokenAddress { - address: wrapper.fee.token.clone(), - sub_prefix: None, - }; + let gas_token = wrapper.fee.token.clone(); let gas_limit = format_denominated_amount( client, &gas_token, diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index 6b676eb020..0a02f8dcf0 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -17,7 +17,6 @@ use masp_primitives::transaction::components::transparent::fees::{ use masp_primitives::transaction::components::Amount; use namada_core::types::address::{masp, masp_tx_key, Address}; use namada_core::types::dec::Dec; -use namada_core::types::storage::Key; use namada_core::types::token::MaspDenom; use namada_proof_of_stake::parameters::PosParams; use namada_proof_of_stake::types::CommissionPair; @@ -548,18 +547,18 @@ where /// decode components of a masp note pub fn decode_component( - (addr, sub, denom, epoch): (Address, Option, MaspDenom, Epoch), + (addr, denom, epoch): (Address, MaspDenom, Epoch), val: i128, res: &mut HashMap, mk_key: F, ) where - F: FnOnce(Address, Option, Epoch) -> K, + F: FnOnce(Address, Epoch) -> K, K: Eq + std::hash::Hash, { let decoded_change = token::Change::from_masp_denominated(val, denom) .expect("expected this to fit"); - res.entry(mk_key(addr, sub, epoch)) + res.entry(mk_key(addr, epoch)) .and_modify(|val| *val += decoded_change) .or_insert(decoded_change); } @@ -1231,7 +1230,7 @@ async fn add_asset_type< C: crate::ledger::queries::Client + Sync, U: ShieldedUtils, >( - asset_types: &mut HashSet<(Address, Option, MaspDenom, Epoch)>, + asset_types: &mut HashSet<(Address, MaspDenom, Epoch)>, shielded: &mut ShieldedContext, client: &C, asset_type: AssetType, @@ -1259,7 +1258,7 @@ async fn used_asset_types< shielded: &mut ShieldedContext, client: &C, builder: &Builder, -) -> Result, MaspDenom, Epoch)>, RpcError> { +) -> Result, RpcError> { let mut asset_types = HashSet::new(); // Collect all the asset types used in the Sapling inputs for input in builder.sapling_inputs() { @@ -1324,21 +1323,14 @@ pub async fn build_transfer< let balance_key = token::balance_key(&token, &source); // validate the amount given - let validated_amount = validate_amount( - client, - args.amount, - &token, - &sub_prefix, - args.tx.force, - ) - .await - .expect("expected to validate amount"); + let validated_amount = + validate_amount(client, args.amount, &token, args.tx.force) + .await + .expect("expected to validate amount"); let validate_fee = validate_amount( client, args.tx.fee_amount, &args.tx.fee_token, - // TODO: Currently multi-tokens cannot be used to pay fees - &None, args.tx.force, ) .await diff --git a/tests/src/e2e/eth_bridge_tests.rs b/tests/src/e2e/eth_bridge_tests.rs index 1d15c5323a..76756fea32 100644 --- a/tests/src/e2e/eth_bridge_tests.rs +++ b/tests/src/e2e/eth_bridge_tests.rs @@ -6,7 +6,6 @@ use std::str::FromStr; use borsh::{BorshDeserialize, BorshSerialize}; use color_eyre::eyre::{eyre, Result}; -use expectrl::ControlCode; use namada::eth_bridge::oracle; use namada::eth_bridge::storage::vote_tallies; use namada::ledger::eth_bridge::{ diff --git a/tests/src/e2e/eth_bridge_tests/helpers.rs b/tests/src/e2e/eth_bridge_tests/helpers.rs index 9fc7acd781..09df129e2b 100644 --- a/tests/src/e2e/eth_bridge_tests/helpers.rs +++ b/tests/src/e2e/eth_bridge_tests/helpers.rs @@ -14,7 +14,6 @@ use namada::ledger::eth_bridge::{ use namada::types::address::{wnam, Address}; use namada::types::ethereum_events::{EthAddress, Uint}; use namada_apps::config::ethereum_bridge; -use namada_core::ledger::eth_bridge; use namada_core::types::ethereum_events::{EthereumEvent, TransferToNamada}; use namada_core::types::token; @@ -173,16 +172,13 @@ pub fn attempt_wrapped_erc20_transfer( ) -> Result { let ledger_address = get_actor_rpc(test, node); - let eth_bridge_addr = eth_bridge::ADDRESS.to_string(); - let sub_prefix = wrapped_erc20s::sub_prefix(asset).to_string(); + let token = wrapped_erc20s::token(asset).to_string(); let amount = amount.to_string(); let transfer_args = vec![ "transfer", "--token", - ð_bridge_addr, - "--sub-prefix", - &sub_prefix, + &token, "--source", from, "--target", @@ -208,10 +204,8 @@ pub fn find_wrapped_erc20_balance( ) -> Result { let ledger_address = get_actor_rpc(test, node); - let sub_prefix = wrapped_erc20s::sub_prefix(asset); - let prefix = - token::multitoken_balance_prefix(ð_bridge::ADDRESS, &sub_prefix); - let balance_key = token::multitoken_balance_key(&prefix, owner); + let token = wrapped_erc20s::token(asset); + let balance_key = token::balance_key(&token, owner); let mut bytes = run!( test, Bin::Client, diff --git a/tests/src/e2e/helpers.rs b/tests/src/e2e/helpers.rs index c9042802d1..7c79fdfdb6 100644 --- a/tests/src/e2e/helpers.rs +++ b/tests/src/e2e/helpers.rs @@ -25,7 +25,8 @@ use namada_apps::config::{Config, TendermintMode}; use namada_core::types::token::NATIVE_MAX_DECIMAL_PLACES; use super::setup::{ - sleep, NamadaCmd, Test, ENV_VAR_DEBUG, ENV_VAR_USE_PREBUILT_BINARIES, + self, sleep, NamadaBgCmd, NamadaCmd, Test, ENV_VAR_DEBUG, + ENV_VAR_USE_PREBUILT_BINARIES, }; use crate::e2e::setup::{Bin, Who, APPS_PACKAGE}; use crate::{run, run_as}; diff --git a/tests/src/native_vp/eth_bridge_pool.rs b/tests/src/native_vp/eth_bridge_pool.rs index 2829029abf..46478acf48 100644 --- a/tests/src/native_vp/eth_bridge_pool.rs +++ b/tests/src/native_vp/eth_bridge_pool.rs @@ -6,7 +6,6 @@ mod test_bridge_pool_vp { use namada::core::ledger::eth_bridge::storage::bridge_pool::BRIDGE_POOL_ADDRESS; use namada::ledger::eth_bridge::{ wrapped_erc20s, Contracts, EthereumBridgeConfig, UpgradeableContract, - ADDRESS, }; use namada::ledger::native_vp::ethereum_bridge::bridge_pool_vp::BridgePoolVp; use namada::proto::{Code, Data, Section, Signature, Tx}; @@ -84,27 +83,12 @@ mod test_bridge_pool_vp { // initialize Bertha's account env.spawn_accounts([&albert_address(), &bertha_address(), &nam()]); // enrich Albert - env.credit_tokens( - &albert_address(), - &nam(), - None, - BERTHA_WEALTH.into(), - ); + env.credit_tokens(&albert_address(), &nam(), BERTHA_WEALTH.into()); // enrich Bertha - env.credit_tokens( - &bertha_address(), - &nam(), - None, - BERTHA_WEALTH.into(), - ); + env.credit_tokens(&bertha_address(), &nam(), BERTHA_WEALTH.into()); // Bertha has ERC20 tokens too. - let sub_prefix = wrapped_erc20s::sub_prefix(&ASSET); - env.credit_tokens( - &bertha_address(), - &ADDRESS, - Some(sub_prefix), - BERTHA_TOKENS.into(), - ); + let token = wrapped_erc20s::token(&ASSET); + env.credit_tokens(&bertha_address(), &token, BERTHA_TOKENS.into()); env } diff --git a/tx_prelude/src/ibc.rs b/tx_prelude/src/ibc.rs index 2edb13652b..d15a60a5af 100644 --- a/tx_prelude/src/ibc.rs +++ b/tx_prelude/src/ibc.rs @@ -12,7 +12,7 @@ use namada_core::ledger::tx_env::TxEnv; use namada_core::types::address::{Address, InternalAddress}; pub use namada_core::types::ibc::IbcEvent; use namada_core::types::storage::{BlockHeight, Header, Key}; -use namada_core::types::token::{DenominatedAmount, Amount}; +use namada_core::types::token::Amount; use crate::token::{burn, mint, transfer}; use crate::{Ctx, KeyValIterator}; @@ -78,7 +78,7 @@ impl IbcStorageContext for Ctx { token: &Address, amount: Amount, ) -> std::result::Result<(), Self::Error> { - let amount = DenominatedAmount::native(amount); + let amount = amount.denominated(token, self)?; transfer(self, src, dest, token, amount, &None, &None, &None) } diff --git a/wasm/wasm_source/src/tx_bridge_pool.rs b/wasm/wasm_source/src/tx_bridge_pool.rs index 001fdcffcf..1dbb86adb7 100644 --- a/wasm/wasm_source/src/tx_bridge_pool.rs +++ b/wasm/wasm_source/src/tx_bridge_pool.rs @@ -19,7 +19,6 @@ fn apply_tx(ctx: &mut Ctx, signed: Tx) -> TxResult { payer, &bridge_pool::BRIDGE_POOL_ADDRESS, &nam_addr, - None, amount.native_denominated(), &None, &None, @@ -39,7 +38,6 @@ fn apply_tx(ctx: &mut Ctx, signed: Tx) -> TxResult { sender, ð_bridge::ADDRESS, &nam_addr, - None, amount.native_denominated(), &None, &None, @@ -47,18 +45,13 @@ fn apply_tx(ctx: &mut Ctx, signed: Tx) -> TxResult { )?; } else { // Otherwise we escrow ERC20 tokens. - let sub_prefix = Some(wrapped_erc20s::sub_prefix(&asset)); - let amount = amount.denominated( - ð_bridge::ADDRESS, - sub_prefix.as_ref(), - ctx, - )?; + let token = wrapped_erc20s::token(&asset); + let amount = amount.denominated(ð_bridge::ADDRESS, ctx)?; token::transfer( ctx, sender, &bridge_pool::BRIDGE_POOL_ADDRESS, - ð_bridge::ADDRESS, - sub_prefix, + &token, amount, &None, &None, From ba65482b9a457f44172454f1a7098756f5f39509 Mon Sep 17 00:00:00 2001 From: yito88 Date: Sun, 2 Jul 2023 19:07:01 +0200 Subject: [PATCH 020/120] revert unexpected changes --- tests/src/e2e/helpers.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/src/e2e/helpers.rs b/tests/src/e2e/helpers.rs index 7c79fdfdb6..d1cb9b9f95 100644 --- a/tests/src/e2e/helpers.rs +++ b/tests/src/e2e/helpers.rs @@ -53,6 +53,7 @@ where /// and returns the [`Test`] handle and [`NamadaBgCmd`] for the validator node. /// It blocks until the node is ready to receive RPC requests from /// `namadac`. +#[allow(dead_code)] pub fn setup_single_node_test() -> Result<(Test, NamadaBgCmd)> { let test = setup::single_node_net()?; run_single_node_test_from(test) From 381dc7613350577fe7fbf794df92aa90c137077b Mon Sep 17 00:00:00 2001 From: yito88 Date: Tue, 4 Jul 2023 14:34:21 +0200 Subject: [PATCH 021/120] modify EthBridge VP and EthBridgePool VP for multitoken --- apps/src/lib/client/rpc.rs | 195 ++++++----------- apps/src/lib/node/ledger/shell/init_chain.rs | 9 +- shared/src/ledger/args.rs | 2 - shared/src/ledger/eth_bridge/bridge_pool.rs | 3 - .../native_vp/ethereum_bridge/authorize.rs | 56 ----- .../ethereum_bridge/bridge_pool_vp.rs | 37 +--- .../ledger/native_vp/ethereum_bridge/mod.rs | 1 - .../ledger/native_vp/ethereum_bridge/vp.rs | 204 ++++++------------ wasm/wasm_source/src/vp_implicit.rs | 3 +- wasm/wasm_source/src/vp_masp.rs | 21 +- wasm/wasm_source/src/vp_user.rs | 2 +- wasm/wasm_source/src/vp_validator.rs | 4 +- 12 files changed, 140 insertions(+), 397 deletions(-) delete mode 100644 shared/src/ledger/native_vp/ethereum_bridge/authorize.rs diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 95d0cb592d..62242d4584 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -16,7 +16,6 @@ use masp_primitives::merkle_tree::MerklePath; use masp_primitives::sapling::{Node, ViewingKey}; use masp_primitives::zip32::ExtendedFullViewingKey; use namada::core::types::transaction::governance::ProposalType; -use namada::ledger::args::InputAmount; use namada::ledger::events::Event; use namada::ledger::governance::parameters::GovParams; use namada::ledger::governance::storage as gov_storage; @@ -46,7 +45,7 @@ use namada::types::hash::Hash; use namada::types::key::*; use namada::types::masp::{BalanceOwner, ExtendedViewingKey, PaymentAddress}; use namada::types::storage::{BlockHeight, BlockResults, Epoch, Key, KeySeg}; -use namada::types::token::{Change, Denomination, MaspDenom}; +use namada::types::token::{Change, MaspDenom}; use namada::types::{storage, token}; use tokio::time::Instant; @@ -173,7 +172,7 @@ pub async fn query_transfers< relevant &= match &query_token { Some(token) => { let check = |(tok, chg): (&Address, &Change)| { - &tok.address == token && !chg.is_zero() + tok == token && !chg.is_zero() }; tfer_delta.values().cloned().any( |MaspChange { ref asset, change }| check((asset, &change)), @@ -193,7 +192,7 @@ pub async fn query_transfers< for (account, MaspChange { ref asset, change }) in tfer_delta { if account != masp() { print!(" {}:", account); - let token_alias = lookup_alias(wallet, &asset.address); + let token_alias = lookup_alias(wallet, &asset); let sign = match change.cmp(&Change::zero()) { Ordering::Greater => "+", Ordering::Less => "-", @@ -204,7 +203,7 @@ pub async fn query_transfers< sign, format_denominated_amount(client, asset, change.into(),) .await, - asset.format_with_alias(&token_alias) + token_alias ); } println!(); @@ -216,7 +215,7 @@ pub async fn query_transfers< if fvk_map.contains_key(&account) { print!(" {}:", fvk_map[&account]); for (token_addr, val) in masp_change { - let token_alias = lookup_alias(wallet, &token_addr.address); + let token_alias = lookup_alias(wallet, &token_addr); let sign = match val.cmp(&Change::zero()) { Ordering::Greater => "+", Ordering::Less => "-", @@ -231,7 +230,7 @@ pub async fn query_transfers< val.into(), ) .await, - token_addr.format_with_alias(&token_alias), + token_alias, ); } println!(); @@ -332,7 +331,8 @@ pub async fn query_transparent_balance< balances, None, owner.address().as_ref(), - ); + ) + .await; } } (Some(token), None) => { @@ -340,14 +340,15 @@ pub async fn query_transparent_balance< let balances = query_storage_prefix::(client, &prefix).await; if let Some(balances) = balances { - print_balances(client, wallet, balances, Some(&token), None); + print_balances(client, wallet, balances, Some(&token), None) + .await; } } (None, None) => { let balances = query_storage_prefix::(client, &prefix).await; if let Some(balances) = balances { - print_balances(client, wallet, balances, None, None); + print_balances(client, wallet, balances, None, None).await; } } } @@ -417,15 +418,15 @@ pub async fn query_pinned_balance< } // Now print out the received quantities according to CLI arguments - match (balance, args.token.as_ref(), args.sub_prefix.as_ref()) { - (Err(PinnedBalanceError::InvalidViewingKey), _, _) => println!( + match (balance, args.token.as_ref()) { + (Err(PinnedBalanceError::InvalidViewingKey), _) => println!( "Supplied viewing key cannot decode transactions to given \ payment address." ), - (Err(PinnedBalanceError::NoTransactionPinned), _, _) => { + (Err(PinnedBalanceError::NoTransactionPinned), _) => { println!("Payment address {} has not yet been consumed.", owner) } - (Ok((balance, epoch)), Some(token), sub_prefix) => { + (Ok((balance, epoch)), Some(token)) => { let token_alias = lookup_alias(wallet, token); let total_balance = balance @@ -437,9 +438,7 @@ pub async fn query_pinned_balance< println!( "Payment address {} was consumed during epoch {}. \ Received no shielded {}", - owner, - epoch, - token.format_with_alias(&token_alias) + owner, epoch, token_alias ); } else { let formatted = format_denominated_amount( @@ -451,14 +450,11 @@ pub async fn query_pinned_balance< println!( "Payment address {} was consumed during epoch {}. \ Received {} {}", - owner, - epoch, - formatted, - token.format_with_alias(&token_alias), + owner, epoch, formatted, token_alias, ); } } - (Ok((balance, epoch)), None, _) => { + (Ok((balance, epoch)), None) => { let mut found_any = false; for ((_, token_addr), value) in balance @@ -480,14 +476,10 @@ pub async fn query_pinned_balance< ) .await; let token_alias = tokens - .get(&token_addr.address) + .get(&token_addr) .map(|a| a.to_string()) - .unwrap_or_else(|| token_addr.address.to_string()); - println!( - " {}: {}", - token_addr.format_with_alias(&token_alias), - formatted, - ); + .unwrap_or_else(|| token_addr.to_string()); + println!(" {}: {}", token_alias, formatted,); } if !found_any { println!( @@ -511,44 +503,44 @@ async fn print_balances( let stdout = io::stdout(); let mut w = stdout.lock(); + let mut print_num = 0; let mut print_token = None; - let print_num = balances - .filter_map(|(key, balance)| { - token::is_any_token_balance_key(&key).map(|(token, owner)| { - ( - token.clone(), - owner.clone(), - format!( - ": {}, owned by {}", - format_denominated_amount(client, token, balance).await, - lookup_alias(wallet, owner) - ), - ) - }) - }) - .filter_map(|(t, o, s)| match (token, target) { + for (key, balance) in balances { + let (t, o, s) = match token::is_any_token_balance_key(&key) { + Some([tok, owner]) => ( + tok.clone(), + owner.clone(), + format!( + ": {}, owned by {}", + format_denominated_amount(client, tok, balance).await, + lookup_alias(wallet, owner) + ), + ), + None => continue, + }; + + let (t, s) = match (token, target) { (Some(token), Some(target)) if t == *token && o == *target => { - Some((t, s)) + (t, s) } - (Some(token), None) if t == *token => Some((t, s)), - (None, Some(target)) if o == *target => Some((t, s)), - (None, None) => Some((t, s)), - _ => None, - }) - .map(|(t, s)| { - match &print_token { - Some(token) if *token == t => { - // the token was already printed - } - Some(_) | None => { - let token_alias = lookup_alias(wallet, &t); - writeln!(w, "Token {}", token_alias).unwrap(); - print_token = Some(t); - } + (Some(token), None) if t == *token => (t, s), + (None, Some(target)) if o == *target => (t, s), + (None, None) => (t, s), + _ => continue, + }; + match &print_token { + Some(token) if *token == t => { + // the token was already printed } - writeln!(w, "{}", s).unwrap(); - }) - .count(); + _ => { + let token_alias = lookup_alias(wallet, &t); + writeln!(w, "Token {}", token_alias).unwrap(); + print_token = Some(t); + } + } + writeln!(w, "{}", s).unwrap(); + print_num += 1; + } if print_num == 0 { match (token, target) { @@ -759,12 +751,12 @@ pub async fn query_shielded_balance< if total_balance.is_zero() { println!( "No shielded {} balance found for given key", - token.format_with_alias(&token_alias) + token_alias ); } else { println!( "{}: {}", - token.format_with_alias(&token_alias), + token_alias, format_denominated_amount( client, &token, @@ -851,10 +843,7 @@ pub async fn query_shielded_balance< println!("Shielded Token {}:", token_alias); let mut found_any = false; let token_alias = lookup_alias(wallet, &token); - println!( - "Shielded Token {}:", - token.format_with_alias(&token_alias), - ); + println!("Shielded Token {}:", token_alias,); for fvk in viewing_keys { // Query the multi-asset balance at the given spending key let viewing_key = ExtendedFullViewingKey::from(fvk).fvk.vk; @@ -886,7 +875,7 @@ pub async fn query_shielded_balance< if !found_any { println!( "No shielded {} balance found for any wallet key", - token.format_with_alias(&token_alias), + token_alias, ); } } @@ -931,10 +920,7 @@ pub async fn print_decoded_balance< { println!( "{} : {}", - token_addr.format_with_alias(&lookup_alias( - wallet, - &token_addr.address - )), + lookup_alias(wallet, &token_addr), format_denominated_amount(client, token_addr, (*amount).into()) .await, ); @@ -956,12 +942,12 @@ pub async fn print_decoded_balance_with_epoch< for ((epoch, token_addr), value) in decoded_balance.iter() { let asset_value = (*value).into(); let alias = tokens - .get(&token_addr.address) + .get(&token_addr) .map(|a| a.to_string()) .unwrap_or_else(|| token_addr.to_string()); println!( "{} | {} : {}", - token_addr.format_with_alias(&alias), + alias, epoch, format_denominated_amount(client, token_addr, asset_value).await, ); @@ -1725,7 +1711,7 @@ pub async fn query_conversions( .expect("Conversions should be defined"); // Track whether any non-sentinel conversions are found let mut conversions_found = false; - for ((addr, sub, _), epoch, conv, _) in conv_state.assets.values() { + for ((addr, _), epoch, conv, _) in conv_state.assets.values() { let amt: masp_primitives::transaction::components::Amount = conv.clone().into(); // If the user has specified any targets, then meet them @@ -1739,9 +1725,8 @@ pub async fn query_conversions( conversions_found = true; // Print the asset to which the conversion applies print!( - "{}{}[{}]: ", + "{}[{}]: ", tokens.get(addr).cloned().unwrap_or_else(|| addr.clone()), - sub.as_ref().map(|k| format!("/{}", k)).unwrap_or_default(), epoch, ); // Now print out the components of the allowed conversion @@ -1749,14 +1734,13 @@ pub async fn query_conversions( for (asset_type, val) in amt.components() { // Look up the address and epoch of asset to facilitate pretty // printing - let ((addr, sub, _), epoch, _, _) = &conv_state.assets[asset_type]; + let ((addr, _), epoch, _, _) = &conv_state.assets[asset_type]; // Now print out this component of the conversion print!( - "{}{} {}{}[{}]", + "{}{} {}[{}]", prefix, val, tokens.get(addr).cloned().unwrap_or_else(|| addr.clone()), - sub.as_ref().map(|k| format!("/{}", k)).unwrap_or_default(), epoch ); // Future iterations need to be prefixed with + @@ -1776,7 +1760,6 @@ pub async fn query_conversion( asset_type: AssetType, ) -> Option<( Address, - Option, MaspDenom, Epoch, masp_primitives::transaction::components::Amount, @@ -2182,49 +2165,3 @@ fn unwrap_client_response( cli::safe_exit(1) }) } - -/// Get the correct representation of the amount given the token type. -pub async fn validate_amount( - client: &C, - amount: InputAmount, - token: &Address, - sub_prefix: &Option, - force: bool, -) -> token::DenominatedAmount { - let input_amount = match amount { - InputAmount::Unvalidated(amt) => amt.canonical(), - InputAmount::Validated(amt) => return amt, - }; - let denom = unwrap_client_response::>( - RPC.vp().token().denomination(client, token).await, - ) - .unwrap_or_else(|| { - if force { - println!( - "No denomination found for token: {token}, but --force was \ - passed. Defaulting to the provided denomination." - ); - input_amount.denom - } else { - println!( - "No denomination found for token: {token}, the input \ - arguments could not be parsed." - ); - cli::safe_exit(1); - } - }); - if denom < input_amount.denom && !force { - println!( - "The input amount contained a higher precision than allowed by \ - {token}." - ); - cli::safe_exit(1); - } else { - input_amount.increase_precision(denom).unwrap_or_else(|_| { - println!( - "The amount provided requires more the 256 bits to represent." - ); - cli::safe_exit(1); - }) - } -} diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index 91f48e3e05..4fa8889753 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -354,14 +354,7 @@ where } in accounts { // associate a token with its denomination. - write_denom( - &mut self.wl_storage, - &address, - // TODO: Should we support multi-tokens at genesis? - None, - denom, - ) - .unwrap(); + write_denom(&mut self.wl_storage, &address, denom).unwrap(); let vp_code_hash = read_wasm_hash(&self.wl_storage, vp_code_path.clone()) .unwrap() diff --git a/shared/src/ledger/args.rs b/shared/src/ledger/args.rs index ba1f071fb8..cb71e65b9c 100644 --- a/shared/src/ledger/args.rs +++ b/shared/src/ledger/args.rs @@ -324,8 +324,6 @@ pub struct QueryTransfers { pub owner: Option, /// Address of a token pub token: Option, - /// sub-prefix if querying a multi-token - pub sub_prefix: Option, } /// Query PoS bond(s) diff --git a/shared/src/ledger/eth_bridge/bridge_pool.rs b/shared/src/ledger/eth_bridge/bridge_pool.rs index e06c141614..30b1f4e171 100644 --- a/shared/src/ledger/eth_bridge/bridge_pool.rs +++ b/shared/src/ledger/eth_bridge/bridge_pool.rs @@ -8,7 +8,6 @@ use std::sync::Arc; use borsh::BorshSerialize; use ethbridge_bridge_contract::Bridge; use ethers::providers::Middleware; -use namada_core::ledger::eth_bridge::storage::wrapped_erc20s; use namada_core::ledger::eth_bridge::ADDRESS as BRIDGE_ADDRESS; use namada_core::types::key::common; use owo_colors::OwoColorize; @@ -57,8 +56,6 @@ pub async fn build_bridge_pool_tx< gas_payer, code_path: wasm_code, } = args; - - let sub_prefix = Some(wrapped_erc20s::sub_prefix(&asset)); let DenominatedAmount { amount, .. } = validate_amount(client, amount, &BRIDGE_ADDRESS, tx.force) .await diff --git a/shared/src/ledger/native_vp/ethereum_bridge/authorize.rs b/shared/src/ledger/native_vp/ethereum_bridge/authorize.rs deleted file mode 100644 index 8c998ad50b..0000000000 --- a/shared/src/ledger/native_vp/ethereum_bridge/authorize.rs +++ /dev/null @@ -1,56 +0,0 @@ -//! Functionality to do with checking whether a transaction is authorized by the -//! "owner" of some key under this account -use std::collections::BTreeSet; - -use namada_core::types::address::Address; - -/// For wrapped ERC20 transfers, checks that `verifiers` contains the `sender`'s -/// address - we delegate to the sender's VP to authorize the transfer (for -/// regular Namada accounts, this will be `vp_implicit` or `vp_user`). -pub(super) fn is_authorized( - verifiers: &BTreeSet
, - sender: &Address, - receiver: &Address, -) -> bool { - verifiers.contains(sender) && verifiers.contains(receiver) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::types::address; - - #[test] - fn test_is_authorized_passes() { - let sender = address::testing::established_address_1(); - let receiver = address::testing::established_address_2(); - let verifiers = BTreeSet::from([sender.clone(), receiver.clone()]); - - let authorized = is_authorized(&verifiers, &sender, &receiver); - - assert!(authorized); - } - - #[test] - fn test_is_authorized_fails() { - let sender = address::testing::established_address_1(); - let receiver = address::testing::established_address_2(); - let verifiers = BTreeSet::default(); - - let authorized = is_authorized(&verifiers, &sender, &receiver); - - assert!(!authorized); - - let verifiers = BTreeSet::from([sender.clone()]); - - let authorized = is_authorized(&verifiers, &sender, &receiver); - - assert!(!authorized); - - let verifiers = BTreeSet::from([receiver.clone()]); - - let authorized = is_authorized(&verifiers, &sender, &receiver); - - assert!(!authorized); - } -} 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 63caf82a57..e3cb7e24e8 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 @@ -98,24 +98,14 @@ where if keys_changed.contains(&owner_key) && keys_changed.contains(&escrow_key) { - match check_balance_changes( - &self.ctx, - (&self.ctx.storage.native_token, &escrow_key) - .try_into() - .expect("This should not fail"), - (&self.ctx.storage.native_token, &owner_key) - .try_into() - .expect("This should not fail"), - ) { - Ok(Some((sender, _, amount))) - if check_delta(&sender, &amount, transfer) => {} - other => { + match check_balance_changes(&self.ctx, &owner_key, &escrow_key)? { + Some(amount) if amount == transfer.transfer.amount => Ok(true), + _ => { tracing::debug!( "The assets of the transfer were not properly \ - escrowed into the Ethereum bridge pool: {:?}", - other + escrowed into the Ethereum bridge pool" ); - return Ok(false); + Ok(false) } } } else { @@ -123,14 +113,8 @@ where "The assets of the transfer were not properly escrowed into \ the Ethereum bridge pool." ); - return Ok(false); + Ok(false) } - - tracing::info!( - "The Ethereum bridge pool VP accepted the transfer {:?}.", - transfer - ); - Ok(true) } /// Check that the correct amount of Nam was sent @@ -235,15 +219,6 @@ where } } -/// Check if a delta matches the delta given by a transfer -fn check_delta( - sender: &Address, - amount: &Amount, - transfer: &PendingTransfer, -) -> bool { - *sender == transfer.transfer.sender && *amount == transfer.transfer.amount -} - /// Helper struct for handling the different escrow /// checking scenarios. struct EscrowDelta<'a> { diff --git a/shared/src/ledger/native_vp/ethereum_bridge/mod.rs b/shared/src/ledger/native_vp/ethereum_bridge/mod.rs index 7e5062a251..85df785e79 100644 --- a/shared/src/ledger/native_vp/ethereum_bridge/mod.rs +++ b/shared/src/ledger/native_vp/ethereum_bridge/mod.rs @@ -2,6 +2,5 @@ //! This includes both the bridge vp and the vp for the bridge //! pool. -mod authorize; pub mod bridge_pool_vp; pub mod vp; diff --git a/shared/src/ledger/native_vp/ethereum_bridge/vp.rs b/shared/src/ledger/native_vp/ethereum_bridge/vp.rs index de068f5e9e..01a9f7625d 100644 --- a/shared/src/ledger/native_vp/ethereum_bridge/vp.rs +++ b/shared/src/ledger/native_vp/ethereum_bridge/vp.rs @@ -13,7 +13,6 @@ use namada_core::types::address::{Address, InternalAddress}; use namada_core::types::storage::Key; use namada_core::types::token::{balance_key, Amount, Change}; -use crate::ledger::native_vp::ethereum_bridge::authorize; use crate::ledger::native_vp::{Ctx, NativeVp, StorageReader, VpEnv}; use crate::proto::Tx; use crate::vm::WasmCacheAccess; @@ -94,7 +93,7 @@ where #[derive(Debug)] enum CheckType { Escrow, - Erc20Transfer(wrapped_erc20s::Key, wrapped_erc20s::Key), + Erc20Transfer, } #[derive(thiserror::Error, Debug)] @@ -138,35 +137,14 @@ where "Ethereum Bridge VP triggered", ); - let (key_a, key_b) = match determine_check_type( + match determine_check_type( &self.ctx.storage.native_token, keys_changed, )? { - Some(CheckType::Erc20Transfer(key_a, key_b)) => (key_a, key_b), + // Multitoken VP checks the balance changes for the ERC20 transfer + Some(CheckType::Erc20Transfer) => Ok(true), Some(CheckType::Escrow) => return self.check_escrow(verifiers), - None => return Ok(false), - }; - let (sender, receiver, _) = - match check_balance_changes(&self.ctx, key_a, key_b)? { - Some(sender) => sender, - None => return Ok(false), - }; - if authorize::is_authorized(verifiers, &sender, &receiver) { - tracing::info!( - ?verifiers, - ?sender, - ?receiver, - "Ethereum Bridge VP authorized transfer" - ); - Ok(true) - } else { - tracing::info!( - ?verifiers, - ?sender, - ?receiver, - "Ethereum Bridge VP rejected unauthorized transfer" - ); - Ok(false) + None => Ok(false), } } } @@ -245,155 +223,95 @@ fn determine_check_type( ); return Ok(None); } - Ok(Some(CheckType::Erc20Transfer(key_a, key_b))) + Ok(Some(CheckType::Erc20Transfer)) } -/// Checks that the balances at both `key_a` and `key_b` have changed by some -/// amount, and that the changes balance each other out. If the balance changes -/// are invalid, the reason is logged and a `None` is returned. Otherwise, -/// return: -/// - the `Address` of the sender i.e. the owner of the balance which is -/// decreasing -/// - the `Address` of the receiver i.e. the owner of the balance which is -/// increasing -/// - the `Amount` of the transfer i.e. by how much the sender's balance -/// decreased, or equivalently by how much the receiver's balance increased +/// Checks that the balances at both `sender` and `receiver` have changed by +/// some amount, and that the changes balance each other out. If the balance +/// changes are invalid, the reason is logged and a `None` is returned. +/// Otherwise, return the `Amount` of the transfer i.e. by how much the sender's +/// balance decreased, or equivalently by how much the receiver's balance +/// increased pub(super) fn check_balance_changes( reader: impl StorageReader, - key_a: wrapped_erc20s::Key, - key_b: wrapped_erc20s::Key, -) -> Result> { - let (balance_a, balance_b) = - match (key_a.suffix.clone(), key_b.suffix.clone()) { - ( - wrapped_erc20s::KeyType::Balance { .. }, - wrapped_erc20s::KeyType::Balance { .. }, - ) => (Key::from(&key_a), Key::from(&key_b)), - ( - wrapped_erc20s::KeyType::Balance { .. }, - wrapped_erc20s::KeyType::Supply, - ) - | ( - wrapped_erc20s::KeyType::Supply, - wrapped_erc20s::KeyType::Balance { .. }, - ) => { - tracing::debug!( - ?key_a, - ?key_b, - "Rejecting transaction that is attempting to change a \ - supply key" - ); - return Ok(None); - } - ( - wrapped_erc20s::KeyType::Supply, - wrapped_erc20s::KeyType::Supply, - ) => { - // in theory, this should be unreachable!() as we would have - // already rejected if both supply keys were for - // the same asset - tracing::debug!( - ?key_a, - ?key_b, - "Rejecting transaction that is attempting to change two \ - supply keys" - ); - return Ok(None); - } - }; - let balance_a_pre = reader - .read_pre_value::(&balance_a)? + sender: &Key, + receiver: &Key, +) -> Result> { + let sender_balance_pre = reader + .read_pre_value::(&sender)? .unwrap_or_default() .change(); - let balance_a_post = match reader.read_post_value::(&balance_a)? { - Some(value) => value, - None => { - tracing::debug!( - ?balance_a, - "Rejecting transaction as could not read_post balance key" - ); - return Ok(None); + let sender_balance_post = + match reader.read_post_value::(&sender)? { + Some(value) => value, + None => { + return Err(eyre!( + "Rejecting transaction as could not read_post balance key \ + {}", + sender, + )); + } } - } - .change(); - let balance_b_pre = reader - .read_pre_value::(&balance_b)? + .change(); + let receiver_balance_pre = reader + .read_pre_value::(&receiver)? .unwrap_or_default() .change(); - let balance_b_post = match reader.read_post_value::(&balance_b)? { + let receiver_balance_post = match reader + .read_post_value::(&receiver)? + { Some(value) => value, None => { - tracing::debug!( - ?balance_b, - "Rejecting transaction as could not read_post balance key" - ); - return Ok(None); + return Err(eyre!( + "Rejecting transaction as could not read_post balance key {}", + receiver, + )); } } .change(); - let balance_a_delta = calculate_delta(balance_a_pre, balance_a_post)?; - let balance_b_delta = calculate_delta(balance_b_pre, balance_b_post)?; - if balance_a_delta != -balance_b_delta { + let sender_balance_delta = + calculate_delta(sender_balance_pre, sender_balance_post)?; + let receiver_balance_delta = + calculate_delta(receiver_balance_pre, receiver_balance_post)?; + if receiver_balance_delta != -sender_balance_delta { tracing::debug!( - ?balance_a_pre, - ?balance_b_pre, - ?balance_a_post, - ?balance_b_post, - ?balance_a_delta, - ?balance_b_delta, + ?sender_balance_pre, + ?receiver_balance_pre, + ?sender_balance_post, + ?receiver_balance_post, + ?sender_balance_delta, + ?receiver_balance_delta, "Rejecting transaction as balance changes do not match" ); return Ok(None); } - if balance_a_delta.is_zero() { - assert_eq!(balance_b_delta, Change::zero()); - tracing::debug!("Rejecting transaction as no balance change"); + if sender_balance_delta.is_zero() || sender_balance_delta > Change::zero() { + assert!( + receiver_balance_delta.is_zero() + || receiver_balance_delta < Change::zero() + ); + tracing::debug!( + "Rejecting transaction as no balance change or invalid change" + ); return Ok(None); } - if balance_a_post < Change::zero() { + if sender_balance_post < Change::zero() { tracing::debug!( - ?balance_a_post, + ?sender_balance_post, "Rejecting transaction as balance is negative" ); return Ok(None); } - if balance_b_post < Change::zero() { + if receiver_balance_post < Change::zero() { tracing::debug!( - ?balance_b_post, + ?receiver_balance_post, "Rejecting transaction as balance is negative" ); return Ok(None); } - if balance_a_delta < Change::zero() { - if let wrapped_erc20s::KeyType::Balance { owner: sender } = key_a.suffix - { - let wrapped_erc20s::KeyType::Balance { owner: receiver } = - key_b.suffix else { unreachable!() }; - Ok(Some(( - sender, - receiver, - Amount::from_change(balance_b_delta), - ))) - } else { - unreachable!() - } - } else { - assert!(balance_b_delta < Change::zero()); - if let wrapped_erc20s::KeyType::Balance { owner: sender } = key_b.suffix - { - let wrapped_erc20s::KeyType::Balance { owner: receiver } = - key_a.suffix else { unreachable!() }; - Ok(Some(( - sender, - receiver, - Amount::from_change(balance_a_delta), - ))) - } else { - unreachable!() - } - } + Ok(Some(Amount::from_change(receiver_balance_delta))) } /// Return the delta between `balance_pre` and `balance_post`, erroring if there diff --git a/wasm/wasm_source/src/vp_implicit.rs b/wasm/wasm_source/src/vp_implicit.rs index 022a22700a..1e4ff59852 100644 --- a/wasm/wasm_source/src/vp_implicit.rs +++ b/wasm/wasm_source/src/vp_implicit.rs @@ -30,8 +30,7 @@ impl<'a> From<&'a storage::Key> for KeyType<'a> { fn from(key: &'a storage::Key) -> KeyType<'a> { if let Some(address) = key::is_pk_key(key) { Self::Pk(address) - } else if let Some((_, address)) = token::is_any_token_balance_key(key) - { + } else if let Some([_, owner]) = token::is_any_token_balance_key(key) { Self::Token { owner } } else if proof_of_stake::is_pos_key(key) { Self::PoS diff --git a/wasm/wasm_source/src/vp_masp.rs b/wasm/wasm_source/src/vp_masp.rs index 39952339ea..1e43d93a25 100644 --- a/wasm/wasm_source/src/vp_masp.rs +++ b/wasm/wasm_source/src/vp_masp.rs @@ -13,11 +13,10 @@ use ripemd::{Digest, Ripemd160}; fn asset_type_from_epoched_address( epoch: Epoch, token: &Address, - sub_prefix: String, denom: token::MaspDenom, ) -> AssetType { // Timestamp the chosen token with the current epoch - let token_bytes = (token, sub_prefix, denom, epoch.0) + let token_bytes = (token, denom, epoch.0) .try_to_vec() .expect("token should serialize"); // Generate the unique asset identifier from the unique token address @@ -63,19 +62,10 @@ fn valid_transfer_amount( fn convert_amount( epoch: Epoch, token: &Address, - sub_prefix: &Option, val: token::Amount, denom: token::MaspDenom, ) -> (AssetType, Amount) { - let asset_type = asset_type_from_epoched_address( - epoch, - token, - sub_prefix - .as_ref() - .map(|k| k.to_string()) - .unwrap_or_default(), - denom, - ); + let asset_type = asset_type_from_epoched_address(epoch, token, denom); // Combine the value and unit into one amount let amount = Amount::from_nonnegative(asset_type, denom.denominate(&val)) .expect("invalid value or asset type for amount"); @@ -127,7 +117,6 @@ fn validate_tx( let (_transp_asset, transp_amt) = convert_amount( ctx.get_block_epoch().unwrap(), &transfer.token, - &transfer.sub_prefix, transfer.amount.into(), denom, ); @@ -191,11 +180,6 @@ fn validate_tx( asset_type_from_epoched_address( ctx.get_block_epoch().unwrap(), &transfer.token, - transfer - .sub_prefix - .as_ref() - .map(|k| k.to_string()) - .unwrap_or_default(), denom, ); @@ -215,7 +199,6 @@ fn validate_tx( let (_transp_asset, transp_amt) = convert_amount( ctx.get_block_epoch().unwrap(), &transfer.token, - &transfer.sub_prefix, transfer.amount.amount, denom, ); diff --git a/wasm/wasm_source/src/vp_user.rs b/wasm/wasm_source/src/vp_user.rs index e605ce28d8..e3fc1e1098 100644 --- a/wasm/wasm_source/src/vp_user.rs +++ b/wasm/wasm_source/src/vp_user.rs @@ -24,7 +24,7 @@ enum KeyType<'a> { impl<'a> From<&'a storage::Key> for KeyType<'a> { fn from(key: &'a storage::Key) -> KeyType<'a> { - if let Some([_, address]) = token::is_any_token_balance_key(key) { + if let Some([_, owner]) = token::is_any_token_balance_key(key) { Self::Token { owner } } else if proof_of_stake::is_pos_key(key) { Self::PoS diff --git a/wasm/wasm_source/src/vp_validator.rs b/wasm/wasm_source/src/vp_validator.rs index 768f025d78..eb80929626 100644 --- a/wasm/wasm_source/src/vp_validator.rs +++ b/wasm/wasm_source/src/vp_validator.rs @@ -26,8 +26,8 @@ enum KeyType<'a> { impl<'a> From<&'a storage::Key> for KeyType<'a> { fn from(key: &'a storage::Key) -> KeyType<'a> { - if let Some([_, address]) = token::is_any_token_balance_key(key) { - Self::Token(address) + if let Some([_, owner]) = token::is_any_token_balance_key(key) { + Self::Token { owner } } else if proof_of_stake::is_pos_key(key) { Self::PoS } else if gov_storage::is_vote_key(key) { From c441ad52a26d40d479a494d1694e28d84fb66c63 Mon Sep 17 00:00:00 2001 From: yito88 Date: Wed, 5 Jul 2023 11:02:15 +0200 Subject: [PATCH 022/120] for clippy --- .../eth_bridge/storage/wrapped_erc20s.rs | 14 +++++----- .../ledger/native_vp/ethereum_bridge/vp.rs | 28 +++++++++---------- tests/src/e2e/eth_bridge_tests.rs | 1 + 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/core/src/ledger/eth_bridge/storage/wrapped_erc20s.rs b/core/src/ledger/eth_bridge/storage/wrapped_erc20s.rs index beb4a61723..0062dd50c9 100644 --- a/core/src/ledger/eth_bridge/storage/wrapped_erc20s.rs +++ b/core/src/ledger/eth_bridge/storage/wrapped_erc20s.rs @@ -11,7 +11,7 @@ use crate::types::token::{ /// Construct a token address from an ERC20 address. pub fn token(address: &EthAddress) -> Address { - Address::Internal(InternalAddress::Erc20(address.clone())) + Address::Internal(InternalAddress::Erc20(*address)) } /// Represents the type of a key relating to a wrapped ERC20 @@ -47,12 +47,12 @@ impl From<&Key> for storage::Key { /// Returns true if the given key has an ERC20 token pub fn has_erc20_segment(key: &storage::Key) -> bool { - match key.segments.get(1) { + matches!( + key.segments.get(1), Some(DbKeySeg::AddressSeg(Address::Internal( InternalAddress::Erc20(_addr), - ))) => true, - _ => false, - } + ))) + ) } impl TryFrom<(&Address, &storage::Key)> for Key { @@ -72,7 +72,7 @@ impl TryFrom<(&Address, &storage::Key)> for Key { InternalAddress::Erc20(addr), ))) = key.segments.get(1) { - addr.clone() + *addr } else { return Err(eyre!( "key has an incorrect segment at index #2, expected an \ @@ -207,7 +207,7 @@ mod test { DbKeySeg::StringSeg(supply_key_seg), ] if multitoken_addr == &MULTITOKEN_ADDRESS && token_addr == &dai_erc20_token() && - balance_key_seg == &BALANCE_STORAGE_KEY && + balance_key_seg == BALANCE_STORAGE_KEY && supply_key_seg == MINTED_STORAGE_KEY ); diff --git a/shared/src/ledger/native_vp/ethereum_bridge/vp.rs b/shared/src/ledger/native_vp/ethereum_bridge/vp.rs index 01a9f7625d..f2b0f5245e 100644 --- a/shared/src/ledger/native_vp/ethereum_bridge/vp.rs +++ b/shared/src/ledger/native_vp/ethereum_bridge/vp.rs @@ -143,7 +143,7 @@ where )? { // Multitoken VP checks the balance changes for the ERC20 transfer Some(CheckType::Erc20Transfer) => Ok(true), - Some(CheckType::Escrow) => return self.check_escrow(verifiers), + Some(CheckType::Escrow) => self.check_escrow(verifiers), None => Ok(false), } } @@ -238,27 +238,25 @@ pub(super) fn check_balance_changes( receiver: &Key, ) -> Result> { let sender_balance_pre = reader - .read_pre_value::(&sender)? + .read_pre_value::(sender)? .unwrap_or_default() .change(); - let sender_balance_post = - match reader.read_post_value::(&sender)? { - Some(value) => value, - None => { - return Err(eyre!( - "Rejecting transaction as could not read_post balance key \ - {}", - sender, - )); - } + let sender_balance_post = match reader.read_post_value::(sender)? { + Some(value) => value, + None => { + return Err(eyre!( + "Rejecting transaction as could not read_post balance key {}", + sender, + )); } - .change(); + } + .change(); let receiver_balance_pre = reader - .read_pre_value::(&receiver)? + .read_pre_value::(receiver)? .unwrap_or_default() .change(); let receiver_balance_post = match reader - .read_post_value::(&receiver)? + .read_post_value::(receiver)? { Some(value) => value, None => { diff --git a/tests/src/e2e/eth_bridge_tests.rs b/tests/src/e2e/eth_bridge_tests.rs index 76756fea32..1d15c5323a 100644 --- a/tests/src/e2e/eth_bridge_tests.rs +++ b/tests/src/e2e/eth_bridge_tests.rs @@ -6,6 +6,7 @@ use std::str::FromStr; use borsh::{BorshDeserialize, BorshSerialize}; use color_eyre::eyre::{eyre, Result}; +use expectrl::ControlCode; use namada::eth_bridge::oracle; use namada::eth_bridge::storage::vote_tallies; use namada::ledger::eth_bridge::{ From d46513f79eb41e6bd22aa7adab83f90d09f62f62 Mon Sep 17 00:00:00 2001 From: yito88 Date: Wed, 5 Jul 2023 15:12:06 +0200 Subject: [PATCH 023/120] fix multitoken vp to check the minter --- apps/src/lib/client/tx.rs | 18 +- core/src/types/token.rs | 17 ++ shared/src/ledger/native_vp/multitoken.rs | 217 +++++++++++++++++++--- tests/src/e2e/helpers.rs | 1 + tests/src/e2e/ledger_tests.rs | 36 ++-- 5 files changed, 228 insertions(+), 61 deletions(-) diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index d70f8c7e6f..f361406643 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -1275,7 +1275,6 @@ mod test_tx { use masp_primitives::transaction::components::Amount; use namada::ledger::masp::{make_asset_type, MaspAmount}; use namada::types::address::testing::gen_established_address; - use namada::types::storage::DbKeySeg; use namada::types::token::MaspDenom; use super::*; @@ -1283,25 +1282,14 @@ mod test_tx { #[test] fn test_masp_add_amount() { let address_1 = gen_established_address(); - let prefix_1: Key = - DbKeySeg::StringSeg("eth_seg".parse().unwrap()).into(); - let prefix_2: Key = - DbKeySeg::StringSeg("crypto_kitty".parse().unwrap()).into(); let denom_1 = MaspDenom::One; let denom_2 = MaspDenom::Three; let epoch = Epoch::default(); let _masp_amount = MaspAmount::default(); - let asset_base = make_asset_type( - Some(epoch), - &address_1, - &Some(prefix_1.clone()), - denom_1, - ); - let _asset_denom = - make_asset_type(Some(epoch), &address_1, &Some(prefix_1), denom_2); - let _asset_prefix = - make_asset_type(Some(epoch), &address_1, &Some(prefix_2), denom_1); + let asset_base = make_asset_type(Some(epoch), &address_1, denom_1); + let _asset_denom = make_asset_type(Some(epoch), &address_1, denom_2); + let _asset_prefix = make_asset_type(Some(epoch), &address_1, denom_1); let _amount_base = Amount::from_pair(asset_base, 16).expect("Test failed"); diff --git a/core/src/types/token.rs b/core/src/types/token.rs index 16dcb6d424..bf4e23cbe3 100644 --- a/core/src/types/token.rs +++ b/core/src/types/token.rs @@ -879,6 +879,23 @@ pub fn is_masp_key(key: &Key) -> bool { || key.starts_with(PIN_KEY_PREFIX))) } +/// 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> { diff --git a/shared/src/ledger/native_vp/multitoken.rs b/shared/src/ledger/native_vp/multitoken.rs index 22556b75a1..ffb8731dbb 100644 --- a/shared/src/ledger/native_vp/multitoken.rs +++ b/shared/src/ledger/native_vp/multitoken.rs @@ -9,10 +9,10 @@ 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; +use crate::types::storage::{Key, KeySeg}; use crate::types::token::{ - is_any_minted_balance_key, is_any_token_balance_key, minter_key, Amount, - Change, + is_any_minted_balance_key, is_any_minter_key, is_any_token_balance_key, + minter_key, Amount, Change, }; use crate::vm::WasmCacheAccess; @@ -73,13 +73,25 @@ where None => _ = mints.insert(token, diff), } - // Check if the minter VP is called - let minter_key = minter_key(token); - let minter = match self.ctx.read_post(&minter_key)? { - Some(m) => m, + // Check if the minter is set + match self.check_minter(token)? { + Some(minter) if verifiers.contains(&minter) => {} + _ => return Ok(false), + } + } else if let Some(token) = is_any_minter_key(key) { + match self.check_minter(token)? { + Some(_) => {} None => return Ok(false), - }; - if !verifiers.contains(&minter) { + } + } else { + if key.segments.get(0) + == Some( + &Address::Internal(InternalAddress::Multitoken) + .to_db_key(), + ) + { + // Reject when trying to update an unexpected key under + // `#Multitoken/...` return Ok(false); } } @@ -95,6 +107,43 @@ 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, + CA: 'static + WasmCacheAccess, +{ + /// Return the minter if the minter is valid and the minter VP exists + pub fn check_minter(&self, token: &Address) -> Result> { + // Check if the minter is set + let minter_key = minter_key(token); + let minter = match self.ctx.read_post(&minter_key)? { + Some(m) => m, + None => return Ok(None), + }; + match token { + Address::Internal(InternalAddress::Erc20(_)) => { + if minter == Address::Internal(InternalAddress::EthBridge) { + return Ok(Some(minter)); + } + } + Address::Internal(InternalAddress::IbcToken(_)) => { + if minter == Address::Internal(InternalAddress::Ibc) { + return Ok(Some(minter)); + } + } + _ => { + // Check the minter VP exists + let vp_key = Key::validity_predicate(&minter); + if self.ctx.has_key_post(&vp_key)? { + return Ok(Some(minter)); + } + } + } + Ok(None) + } +} + #[cfg(test)] mod tests { use std::collections::BTreeSet; @@ -107,9 +156,11 @@ mod tests { use crate::core::types::address::testing::{ established_address_1, established_address_2, }; + use crate::eth_bridge::storage::wrapped_erc20s; use crate::ledger::gas::VpGasMeter; use crate::proto::{Code, Data, Section, Signature, Tx}; use crate::types::address::{Address, InternalAddress}; + use crate::types::ethereum_events::testing::arbitrary_eth_address; use crate::types::key::testing::keypair_1; use crate::types::storage::TxIndex; use crate::types::token::{ @@ -145,14 +196,14 @@ mod tests { let sender = established_address_1(); let sender_key = balance_key(&nam(), &sender); - let amount = Amount::whole(100); + let amount = Amount::native_whole(100); wl_storage .storage .write(&sender_key, amount.try_to_vec().unwrap()) .expect("write failed"); // transfer 10 - let amount = Amount::whole(90); + let amount = Amount::native_whole(90); wl_storage .write_log .write(&sender_key, amount.try_to_vec().unwrap()) @@ -160,7 +211,7 @@ mod tests { keys_changed.insert(sender_key); let receiver = established_address_2(); let receiver_key = balance_key(&nam(), &receiver); - let amount = Amount::whole(10); + let amount = Amount::native_whole(10); wl_storage .write_log .write(&receiver_key, amount.try_to_vec().unwrap()) @@ -198,14 +249,14 @@ mod tests { let sender = established_address_1(); let sender_key = balance_key(&nam(), &sender); - let amount = Amount::whole(100); + let amount = Amount::native_whole(100); wl_storage .storage .write(&sender_key, amount.try_to_vec().unwrap()) .expect("write failed"); // transfer 10 - let amount = Amount::whole(90); + let amount = Amount::native_whole(90); wl_storage .write_log .write(&sender_key, amount.try_to_vec().unwrap()) @@ -214,7 +265,7 @@ mod tests { let receiver = established_address_2(); let receiver_key = balance_key(&nam(), &receiver); // receive more than 10 - let amount = Amount::whole(100); + let amount = Amount::native_whole(100); wl_storage .write_log .write(&receiver_key, amount.try_to_vec().unwrap()) @@ -250,17 +301,20 @@ mod tests { let mut wl_storage = TestWlStorage::default(); let mut keys_changed = BTreeSet::new(); + // ERC20 token + let token = wrapped_erc20s::token(&arbitrary_eth_address()); + // mint 100 let target = established_address_1(); - let target_key = balance_key(&nam(), &target); - let amount = Amount::whole(100); + let target_key = balance_key(&token, &target); + let amount = Amount::native_whole(100); wl_storage .write_log .write(&target_key, amount.try_to_vec().unwrap()) .expect("write failed"); keys_changed.insert(target_key); - let minted_key = minted_balance_key(&nam()); - let amount = Amount::whole(100); + let minted_key = minted_balance_key(&token); + let amount = Amount::native_whole(100); wl_storage .write_log .write(&minted_key, amount.try_to_vec().unwrap()) @@ -268,8 +322,8 @@ mod tests { keys_changed.insert(minted_key); // minter - let minter = Address::Internal(InternalAddress::Ibc); - let minter_key = minter_key(&nam()); + let minter = Address::Internal(InternalAddress::EthBridge); + let minter_key = minter_key(&token); wl_storage .write_log .write(&minter_key, minter.try_to_vec().unwrap()) @@ -307,18 +361,25 @@ mod tests { let mut wl_storage = TestWlStorage::default(); let mut keys_changed = BTreeSet::new(); + // set the dummy nam vp + let vp_key = Key::validity_predicate(&nam()); + wl_storage + .storage + .write(&vp_key, vec![]) + .expect("write failed"); + // mint 100 let target = established_address_1(); let target_key = balance_key(&nam(), &target); // mint more than 100 - let amount = Amount::whole(1000); + let amount = Amount::native_whole(1000); wl_storage .write_log .write(&target_key, amount.try_to_vec().unwrap()) .expect("write failed"); keys_changed.insert(target_key); let minted_key = minted_balance_key(&nam()); - let amount = Amount::whole(100); + let amount = Amount::native_whole(100); wl_storage .write_log .write(&minted_key, amount.try_to_vec().unwrap()) @@ -326,7 +387,7 @@ mod tests { keys_changed.insert(minted_key); // minter - let minter = Address::Internal(InternalAddress::Ibc); + let minter = nam(); let minter_key = minter_key(&nam()); wl_storage .write_log @@ -368,14 +429,14 @@ mod tests { // mint 100 let target = established_address_1(); let target_key = balance_key(&nam(), &target); - let amount = Amount::whole(100); + let amount = Amount::native_whole(100); wl_storage .write_log .write(&target_key, amount.try_to_vec().unwrap()) .expect("write failed"); keys_changed.insert(target_key); let minted_key = minted_balance_key(&nam()); - let amount = Amount::whole(100); + let amount = Amount::native_whole(100); wl_storage .write_log .write(&minted_key, amount.try_to_vec().unwrap()) @@ -416,14 +477,14 @@ mod tests { // mint 100 let target = established_address_1(); let target_key = balance_key(&nam(), &target); - let amount = Amount::whole(100); + let amount = Amount::native_whole(100); wl_storage .write_log .write(&target_key, amount.try_to_vec().unwrap()) .expect("write failed"); keys_changed.insert(target_key); let minted_key = minted_balance_key(&nam()); - let amount = Amount::whole(100); + let amount = Amount::native_whole(100); wl_storage .write_log .write(&minted_key, amount.try_to_vec().unwrap()) @@ -463,4 +524,104 @@ mod tests { .expect("validation failed") ); } + + #[test] + fn test_invalid_minter() { + let mut wl_storage = TestWlStorage::default(); + let mut keys_changed = BTreeSet::new(); + + // ERC20 token + let token = wrapped_erc20s::token(&arbitrary_eth_address()); + + // mint 100 + let target = established_address_1(); + let target_key = balance_key(&token, &target); + let amount = Amount::native_whole(100); + wl_storage + .write_log + .write(&target_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(target_key); + let minted_key = minted_balance_key(&token); + let amount = Amount::native_whole(100); + wl_storage + .write_log + .write(&minted_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(minted_key); + + // invalid minter + let minter = established_address_1(); + let minter_key = minter_key(&token); + wl_storage + .write_log + .write(&minter_key, minter.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(minter_key); + + let tx_index = TxIndex::default(); + let tx = dummy_tx(&wl_storage); + let gas_meter = VpGasMeter::new(0); + let (vp_wasm_cache, _vp_cache_dir) = wasm_cache(); + let mut verifiers = BTreeSet::new(); + // for the minter + verifiers.insert(minter); + let ctx = Ctx::new( + &ADDRESS, + &wl_storage.storage, + &wl_storage.write_log, + &tx, + &tx_index, + gas_meter, + &keys_changed, + &verifiers, + vp_wasm_cache, + ); + + let vp = MultitokenVp { ctx }; + assert!( + !vp.validate_tx(&tx, &keys_changed, &verifiers) + .expect("validation failed") + ); + } + + #[test] + fn test_invalid_minter_update() { + let mut wl_storage = TestWlStorage::default(); + let mut keys_changed = BTreeSet::new(); + + let minter_key = minter_key(&nam()); + let minter = established_address_1(); + wl_storage + .write_log + .write(&minter_key, minter.try_to_vec().unwrap()) + .expect("write failed"); + + keys_changed.insert(minter_key); + + let tx_index = TxIndex::default(); + let tx = dummy_tx(&wl_storage); + let gas_meter = VpGasMeter::new(0); + let (vp_wasm_cache, _vp_cache_dir) = wasm_cache(); + let mut verifiers = BTreeSet::new(); + // for the minter + verifiers.insert(minter); + let ctx = Ctx::new( + &ADDRESS, + &wl_storage.storage, + &wl_storage.write_log, + &tx, + &tx_index, + gas_meter, + &keys_changed, + &verifiers, + vp_wasm_cache, + ); + + let vp = MultitokenVp { ctx }; + assert!( + !vp.validate_tx(&tx, &keys_changed, &verifiers) + .expect("validation failed") + ); + } } diff --git a/tests/src/e2e/helpers.rs b/tests/src/e2e/helpers.rs index d1cb9b9f95..8d9d23f626 100644 --- a/tests/src/e2e/helpers.rs +++ b/tests/src/e2e/helpers.rs @@ -72,6 +72,7 @@ pub fn run_single_node_test_from(test: Test) -> Result<(Test, NamadaBgCmd)> { } /// Initialize an established account. +#[allow(dead_code)] pub fn init_established_account( test: &Test, rpc_addr: &str, diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 9797dadf58..3889a1ae2c 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -1248,7 +1248,7 @@ fn masp_incentives() -> Result<()> { ], Some(60) )?; - let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep1.0 - ep0.0); + let amt = (amt20 * masp_rewards[&btc()]).0 * (ep1.0 - ep0.0); let denominated = DenominatedAmount { amount: amt, denom: NATIVE_MAX_DECIMAL_PLACES.into(), @@ -1271,7 +1271,7 @@ fn masp_incentives() -> Result<()> { ], Some(60) )?; - let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep1.0 - ep0.0); + let amt = (amt20 * masp_rewards[&btc()]).0 * (ep1.0 - ep0.0); let denominated = DenominatedAmount { amount: amt, denom: NATIVE_MAX_DECIMAL_PLACES.into(), @@ -1315,7 +1315,7 @@ fn masp_incentives() -> Result<()> { ], Some(60) )?; - let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep2.0 - ep0.0); + let amt = (amt20 * masp_rewards[&btc()]).0 * (ep2.0 - ep0.0); let denominated = DenominatedAmount { amount: amt, denom: NATIVE_MAX_DECIMAL_PLACES.into(), @@ -1338,7 +1338,7 @@ fn masp_incentives() -> Result<()> { ], Some(60) )?; - let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep2.0 - ep0.0); + let amt = (amt20 * masp_rewards[&btc()]).0 * (ep2.0 - ep0.0); let denominated = DenominatedAmount { amount: amt, denom: NATIVE_MAX_DECIMAL_PLACES.into(), @@ -1445,7 +1445,7 @@ fn masp_incentives() -> Result<()> { ], Some(60) )?; - let amt = (amt10 * masp_rewards[&(eth(), None)]).0 * (ep4.0 - ep3.0); + let amt = (amt10 * masp_rewards[ð()]).0 * (ep4.0 - ep3.0); let denominated = DenominatedAmount { amount: amt, denom: NATIVE_MAX_DECIMAL_PLACES.into(), @@ -1469,8 +1469,8 @@ fn masp_incentives() -> Result<()> { ], Some(60) )?; - let amt = ((amt20 * masp_rewards[&(btc(), None)]).0 * (ep4.0 - ep0.0)) - + ((amt10 * masp_rewards[&(eth(), None)]).0 * (ep4.0 - ep3.0)); + let amt = ((amt20 * masp_rewards[&btc()]).0 * (ep4.0 - ep0.0)) + + ((amt10 * masp_rewards[ð()]).0 * (ep4.0 - ep3.0)); let denominated = DenominatedAmount { amount: amt, denom: NATIVE_MAX_DECIMAL_PLACES.into(), @@ -1542,7 +1542,7 @@ fn masp_incentives() -> Result<()> { ], Some(60) )?; - let amt = (amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0); + let amt = (amt10 * masp_rewards[ð()]).0 * (ep5.0 - ep3.0); let denominated = DenominatedAmount { amount: amt, denom: NATIVE_MAX_DECIMAL_PLACES.into(), @@ -1567,7 +1567,7 @@ fn masp_incentives() -> Result<()> { ], Some(60) )?; - let amt = ((amt20 * masp_rewards[&(btc(), None)]).0 * (ep5.0 - ep0.0)) + let amt = ((amt20 * masp_rewards[&btc()]).0 * (ep5.0 - ep0.0)) + ((amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0)); let denominated = DenominatedAmount { amount: amt, @@ -1638,7 +1638,7 @@ fn masp_incentives() -> Result<()> { ], Some(60) )?; - let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep6.0 - ep0.0); + let amt = (amt20 * masp_rewards[&btc()]).0 * (ep6.0 - ep0.0); let denominated = DenominatedAmount { amount: amt, denom: NATIVE_MAX_DECIMAL_PLACES.into(), @@ -1662,8 +1662,8 @@ fn masp_incentives() -> Result<()> { ], Some(60) )?; - let amt = ((amt20 * masp_rewards[&(btc(), None)]).0 * (ep6.0 - ep0.0)) - + ((amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0)); + let amt = ((amt20 * masp_rewards[&btc()]).0 * (ep6.0 - ep0.0)) + + ((amt10 * masp_rewards[ð()]).0 * (ep5.0 - ep3.0)); let denominated = DenominatedAmount { amount: amt, denom: NATIVE_MAX_DECIMAL_PLACES.into(), @@ -1689,7 +1689,7 @@ fn masp_incentives() -> Result<()> { ], Some(60) )?; - let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep6.0 - ep0.0); + let amt = (amt20 * masp_rewards[&btc()]).0 * (ep6.0 - ep0.0); let denominated = DenominatedAmount { amount: amt, denom: NATIVE_MAX_DECIMAL_PLACES.into(), @@ -1712,7 +1712,7 @@ fn masp_incentives() -> Result<()> { ], Some(60) )?; - let amt = (amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0); + let amt = (amt10 * masp_rewards[ð()]).0 * (ep5.0 - ep3.0); let denominated = DenominatedAmount { amount: amt, denom: NATIVE_MAX_DECIMAL_PLACES.into(), @@ -1736,8 +1736,8 @@ fn masp_incentives() -> Result<()> { ], Some(60) )?; - let amt = ((amt20 * masp_rewards[&(btc(), None)]).0 * (ep6.0 - ep0.0)) - + ((amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0)); + let amt = ((amt20 * masp_rewards[&btc()]).0 * (ep6.0 - ep0.0)) + + ((amt10 * masp_rewards[ð()]).0 * (ep5.0 - ep3.0)); let denominated = DenominatedAmount { amount: amt, denom: NATIVE_MAX_DECIMAL_PLACES.into(), @@ -1762,7 +1762,7 @@ fn masp_incentives() -> Result<()> { "--token", NAM, "--amount", - &((amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0)) + &((amt10 * masp_rewards[ð()]).0 * (ep5.0 - ep3.0)) .to_string_native(), "--signer", BERTHA, @@ -1792,7 +1792,7 @@ fn masp_incentives() -> Result<()> { "--token", NAM, "--amount", - &((amt20 * masp_rewards[&(btc(), None)]).0 * (ep6.0 - ep0.0)) + &((amt20 * masp_rewards[&btc()]).0 * (ep6.0 - ep0.0)) .to_string_native(), "--signer", ALBERT, From 13f66dc2f43fe1ad0f5cc75ebf3de0c393c0c572 Mon Sep 17 00:00:00 2001 From: yito88 Date: Thu, 6 Jul 2023 17:19:47 +0200 Subject: [PATCH 024/120] for clippy --- apps/src/lib/client/rpc.rs | 10 +++++----- shared/src/ledger/native_vp/multitoken.rs | 19 ++++++++----------- shared/src/ledger/signing.rs | 2 +- wasm/wasm_source/src/vp_implicit.rs | 1 - wasm/wasm_source/src/vp_user.rs | 3 --- 5 files changed, 14 insertions(+), 21 deletions(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 62242d4584..f3b3ab5e44 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -192,7 +192,7 @@ pub async fn query_transfers< for (account, MaspChange { ref asset, change }) in tfer_delta { if account != masp() { print!(" {}:", account); - let token_alias = lookup_alias(wallet, &asset); + let token_alias = lookup_alias(wallet, asset); let sign = match change.cmp(&Change::zero()) { Ordering::Greater => "+", Ordering::Less => "-", @@ -443,7 +443,7 @@ pub async fn query_pinned_balance< } else { let formatted = format_denominated_amount( client, - &token, + token, total_balance.into(), ) .await; @@ -476,7 +476,7 @@ pub async fn query_pinned_balance< ) .await; let token_alias = tokens - .get(&token_addr) + .get(token_addr) .map(|a| a.to_string()) .unwrap_or_else(|| token_addr.to_string()); println!(" {}: {}", token_alias, formatted,); @@ -920,7 +920,7 @@ pub async fn print_decoded_balance< { println!( "{} : {}", - lookup_alias(wallet, &token_addr), + lookup_alias(wallet, token_addr), format_denominated_amount(client, token_addr, (*amount).into()) .await, ); @@ -942,7 +942,7 @@ pub async fn print_decoded_balance_with_epoch< for ((epoch, token_addr), value) in decoded_balance.iter() { let asset_value = (*value).into(); let alias = tokens - .get(&token_addr) + .get(token_addr) .map(|a| a.to_string()) .unwrap_or_else(|| token_addr.to_string()); println!( diff --git a/shared/src/ledger/native_vp/multitoken.rs b/shared/src/ledger/native_vp/multitoken.rs index ffb8731dbb..9e407e8602 100644 --- a/shared/src/ledger/native_vp/multitoken.rs +++ b/shared/src/ledger/native_vp/multitoken.rs @@ -83,17 +83,14 @@ where Some(_) => {} None => return Ok(false), } - } else { - if key.segments.get(0) - == Some( - &Address::Internal(InternalAddress::Multitoken) - .to_db_key(), - ) - { - // Reject when trying to update an unexpected key under - // `#Multitoken/...` - return Ok(false); - } + } else if key.segments.get(0) + == Some( + &Address::Internal(InternalAddress::Multitoken).to_db_key(), + ) + { + // Reject when trying to update an unexpected key under + // `#Multitoken/...` + return Ok(false); } } diff --git a/shared/src/ledger/signing.rs b/shared/src/ledger/signing.rs index 2f0fd0d56d..8a8e35a11c 100644 --- a/shared/src/ledger/signing.rs +++ b/shared/src/ledger/signing.rs @@ -555,7 +555,7 @@ async fn make_ledger_amount_asset( if let Some((token, _, _epoch)) = assets.get(token) { // If the AssetType can be decoded, then at least display Addressees let formatted_amt = - format_denominated_amount(client, &token, amount.into()).await; + format_denominated_amount(client, token, amount.into()).await; if let Some(token) = tokens.get(token) { output .push( diff --git a/wasm/wasm_source/src/vp_implicit.rs b/wasm/wasm_source/src/vp_implicit.rs index 1e4ff59852..f82c573bc1 100644 --- a/wasm/wasm_source/src/vp_implicit.rs +++ b/wasm/wasm_source/src/vp_implicit.rs @@ -344,7 +344,6 @@ mod tests { storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); diff --git a/wasm/wasm_source/src/vp_user.rs b/wasm/wasm_source/src/vp_user.rs index e3fc1e1098..6d6977997e 100644 --- a/wasm/wasm_source/src/vp_user.rs +++ b/wasm/wasm_source/src/vp_user.rs @@ -294,7 +294,6 @@ mod tests { storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); @@ -450,7 +449,6 @@ mod tests { storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); @@ -531,7 +529,6 @@ mod tests { storage_api::token::write_denom( &mut tx_env.wl_storage, &token, - None, token::NATIVE_MAX_DECIMAL_PLACES.into(), ) .unwrap(); From 74eee2dc18ea41cfd28ad441da4db0188cca1caf Mon Sep 17 00:00:00 2001 From: yito88 Date: Mon, 10 Jul 2023 11:06:39 +0200 Subject: [PATCH 025/120] fix for tx signing --- apps/src/lib/client/rpc.rs | 7 ++++++- shared/src/ledger/native_vp/multitoken.rs | 6 +----- shared/src/ledger/signing.rs | 6 +----- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index f3b3ab5e44..eb03de78d9 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -506,6 +506,8 @@ async fn print_balances( let mut print_num = 0; let mut print_token = None; 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) { Some([tok, owner]) => ( tok.clone(), @@ -519,6 +521,7 @@ async fn print_balances( None => continue, }; + // Get the token and the balance let (t, s) = match (token, target) { (Some(token), Some(target)) if t == *token && o == *target => { (t, s) @@ -528,9 +531,10 @@ async fn print_balances( (None, None) => (t, s), _ => continue, }; + // Print the token if it isn't printed yet match &print_token { Some(token) if *token == t => { - // the token was already printed + // the token has been already printed } _ => { let token_alias = lookup_alias(wallet, &t); @@ -538,6 +542,7 @@ async fn print_balances( print_token = Some(t); } } + // Print the balance writeln!(w, "{}", s).unwrap(); print_num += 1; } diff --git a/shared/src/ledger/native_vp/multitoken.rs b/shared/src/ledger/native_vp/multitoken.rs index 9e407e8602..3e2a0b84df 100644 --- a/shared/src/ledger/native_vp/multitoken.rs +++ b/shared/src/ledger/native_vp/multitoken.rs @@ -176,11 +176,7 @@ mod tests { tx.set_code(Code::new(tx_code)); tx.set_data(Data::new(tx_data)); tx.add_section(Section::Signature(Signature::new( - tx.code_sechash(), - &keypair_1(), - ))); - tx.add_section(Section::Signature(Signature::new( - tx.data_sechash(), + tx.sechashes(), &keypair_1(), ))); tx diff --git a/shared/src/ledger/signing.rs b/shared/src/ledger/signing.rs index 8a8e35a11c..4fdd426172 100644 --- a/shared/src/ledger/signing.rs +++ b/shared/src/ledger/signing.rs @@ -18,7 +18,6 @@ use masp_primitives::transaction::components::sapling::fees::{ use namada_core::types::address::{ masp, masp_tx_key, Address, ImplicitAddress, }; -use namada_core::types::storage::Key; use namada_core::types::token::{self, Amount, DenominatedAmount, MaspDenom}; use namada_core::types::transaction::{pos, MIN_FEE}; use prost::Message; @@ -647,7 +646,7 @@ pub async fn make_ledger_masp_endpoints< output: &mut Vec, transfer: &Transfer, builder: Option<&MaspBuilder>, - assets: &HashMap, MaspDenom, Epoch)>, + assets: &HashMap, ) { if transfer.source != masp() { output.push(format!("Sender : {}", transfer.source)); @@ -657,7 +656,6 @@ pub async fn make_ledger_masp_endpoints< output, transfer.amount, &transfer.token, - &transfer.sub_prefix, "Sending ", ); } @@ -685,7 +683,6 @@ pub async fn make_ledger_masp_endpoints< output, transfer.amount, &transfer.token, - &transfer.sub_prefix, "Receiving ", ); } @@ -711,7 +708,6 @@ pub async fn make_ledger_masp_endpoints< output, transfer.amount, &transfer.token, - &transfer.sub_prefix, "", ); } From 713dee1de6d9c9412d5dade6012853a7d6b702b3 Mon Sep 17 00:00:00 2001 From: yito88 Date: Mon, 10 Jul 2023 11:17:14 +0200 Subject: [PATCH 026/120] fix a test --- tests/src/e2e/ledger_tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 3889a1ae2c..3038371cba 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -1568,7 +1568,7 @@ fn masp_incentives() -> Result<()> { Some(60) )?; let amt = ((amt20 * masp_rewards[&btc()]).0 * (ep5.0 - ep0.0)) - + ((amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0)); + + ((amt10 * masp_rewards[ð()]).0 * (ep5.0 - ep3.0)); let denominated = DenominatedAmount { amount: amt, denom: NATIVE_MAX_DECIMAL_PLACES.into(), From 6e1852030cee4beff422ef242b1e1c57f156a9b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 7 Jul 2023 09:22:58 +0100 Subject: [PATCH 027/120] pos: add a function to get genesis validator consensus set for TM --- proof_of_stake/src/lib.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index 7f7265be91..289bc55dbe 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -2455,6 +2455,41 @@ where Ok((total, total_active)) } +/// Get the genesis consensus validators stake and consensus key for Tendermint, +/// converted from [`ValidatorSetUpdate`]s using the given function. +pub fn genesis_validator_set_tendermint( + storage: &S, + params: &PosParams, + current_epoch: Epoch, + mut f: impl FnMut(ValidatorSetUpdate) -> T, +) -> storage_api::Result> +where + S: StorageRead, +{ + let consensus_validator_handle = + consensus_validator_set_handle().at(¤t_epoch); + let iter = consensus_validator_handle.iter(storage)?; + + iter.map(|validator| { + let ( + NestedSubKey::Data { + key: new_stake, + nested_sub_key: _, + }, + address, + ) = validator?; + let consensus_key = validator_consensus_key_handle(&address) + .get(storage, current_epoch, params)? + .unwrap(); + let converted = f(ValidatorSetUpdate::Consensus(ConsensusValidator { + consensus_key, + bonded_stake: new_stake, + })); + Ok(converted) + }) + .collect() +} + /// Communicate imminent validator set updates to Tendermint. This function is /// called two blocks before the start of a new epoch because Tendermint /// validator updates become active two blocks after the updates are submitted. From feda19a8a8989fcbf326e97acde30965d91394d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 7 Jul 2023 09:23:50 +0100 Subject: [PATCH 028/120] app/ledger/init_chain: get genesis validator set using the new fn --- apps/src/lib/node/ledger/shell/init_chain.rs | 30 +++-------- apps/src/lib/node/ledger/shell/mod.rs | 54 ++++++++++++++++++++ 2 files changed, 61 insertions(+), 23 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index 91f48e3e05..cb696f665f 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -6,9 +6,7 @@ use std::hash::Hash; use namada::core::ledger::testnet_pow; use namada::ledger::eth_bridge::EthBridgeStatus; use namada::ledger::parameters::{self, Parameters}; -use namada::ledger::pos::{ - into_tm_voting_power, staking_token_address, PosParams, -}; +use namada::ledger::pos::{staking_token_address, PosParams}; use namada::ledger::storage::traits::StorageHasher; use namada::ledger::storage::{DBIter, DB}; use namada::ledger::storage_api::token::{ @@ -23,8 +21,6 @@ use namada::types::time::{DateTimeUtc, TimeZone, Utc}; use namada::types::token; use super::*; -use crate::facade::tendermint_proto::abci; -use crate::facade::tendermint_proto::crypto::PublicKey as TendermintPublicKey; use crate::facade::tendermint_proto::google::protobuf; use crate::facade::tower_abci::{request, response}; use crate::wasm_loader; @@ -473,10 +469,7 @@ where pos::init_genesis_storage( &mut self.wl_storage, pos_params, - validators - .clone() - .into_iter() - .map(|validator| validator.pos_data), + validators.into_iter().map(|validator| validator.pos_data), current_epoch, ); @@ -507,20 +500,11 @@ where ibc::init_genesis_storage(&mut self.wl_storage); // Set the initial validator set - for validator in validators { - let mut abci_validator = abci::ValidatorUpdate::default(); - let consensus_key: common::PublicKey = - validator.pos_data.consensus_key.clone(); - let pub_key = TendermintPublicKey { - sum: Some(key_to_tendermint(&consensus_key).unwrap()), - }; - abci_validator.pub_key = Some(pub_key); - abci_validator.power = into_tm_voting_power( - pos_params.tm_votes_per_token, - validator.pos_data.tokens, - ); - response.validators.push(abci_validator); - } + response.validators = self + .get_abci_validator_updates(true) + .expect("Must be able to set genesis validator set"); + debug_assert!(!response.validators.is_empty()); + response } } diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index e1b7f08883..23bc6fe64b 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -28,6 +28,7 @@ use namada::ledger::eth_bridge::{EthBridgeQueries, EthereumBridgeConfig}; use namada::ledger::events::log::EventLog; use namada::ledger::events::Event; use namada::ledger::gas::BlockGasMeter; +use namada::ledger::pos::into_tm_voting_power; use namada::ledger::pos::namada_proof_of_stake::types::{ ConsensusValidator, ValidatorSetUpdate, }; @@ -1374,6 +1375,59 @@ where } false } + + fn get_abci_validator_updates( + &self, + is_genesis: bool, + ) -> storage_api::Result> + { + use namada::ledger::pos::namada_proof_of_stake; + + use crate::facade::tendermint_proto::crypto::PublicKey as TendermintPublicKey; + + let (current_epoch, _gas) = self.wl_storage.storage.get_current_epoch(); + let pos_params = + namada_proof_of_stake::read_pos_params(&self.wl_storage) + .expect("Could not find the PoS parameters"); + + let validator_set_update_fn = if is_genesis { + namada_proof_of_stake::genesis_validator_set_tendermint + } else { + namada_proof_of_stake::validator_set_update_tendermint + }; + + validator_set_update_fn( + &self.wl_storage, + &pos_params, + current_epoch, + |update| { + let (consensus_key, power) = match update { + ValidatorSetUpdate::Consensus(ConsensusValidator { + consensus_key, + bonded_stake, + }) => { + let power: i64 = into_tm_voting_power( + pos_params.tm_votes_per_token, + bonded_stake, + ); + (consensus_key, power) + } + ValidatorSetUpdate::Deactivated(consensus_key) => { + // Any validators that have been dropped from the + // consensus set must have voting power set to 0 to + // remove them from the conensus set + let power = 0_i64; + (consensus_key, power) + } + }; + let pub_key = TendermintPublicKey { + sum: Some(key_to_tendermint(&consensus_key).unwrap()), + }; + let pub_key = Some(pub_key); + ValidatorUpdate { pub_key, power } + }, + ) + } } impl<'a, D, H> From<&'a mut Shell> From 5fead6af65abca37ac88ff60681d57d854e5282c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 7 Jul 2023 09:24:46 +0100 Subject: [PATCH 029/120] app/ledger/finalize_block: refactor validator set update for re-use --- .../lib/node/ledger/shell/finalize_block.rs | 44 ++----------------- 1 file changed, 3 insertions(+), 41 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 3af8ab9e3f..5c7e9d5b62 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -4,7 +4,6 @@ use std::collections::HashMap; use data_encoding::HEXUPPER; use namada::ledger::parameters::storage as params_storage; -use namada::ledger::pos::types::into_tm_voting_power; use namada::ledger::pos::{namada_proof_of_stake, staking_token_address}; use namada::ledger::storage::EPOCH_SWITCH_BLOCKS_DELAY; use namada::ledger::storage_api::token::credit_tokens; @@ -32,7 +31,6 @@ use super::*; use crate::facade::tendermint_proto::abci::{ Misbehavior as Evidence, VoteInfo, }; -use crate::facade::tendermint_proto::crypto::PublicKey as TendermintPublicKey; use crate::node::ledger::shell::stats::InternalStats; impl Shell @@ -634,45 +632,9 @@ where /// changes to the validator sets and consensus parameters fn update_epoch(&mut self, response: &mut shim::response::FinalizeBlock) { // Apply validator set update - let (current_epoch, _gas) = self.wl_storage.storage.get_current_epoch(); - let pos_params = - namada_proof_of_stake::read_pos_params(&self.wl_storage) - .expect("Could not find the PoS parameters"); - // TODO ABCI validator updates on block H affects the validator set - // on block H+2, do we need to update a block earlier? - response.validator_updates = - namada_proof_of_stake::validator_set_update_tendermint( - &self.wl_storage, - &pos_params, - current_epoch, - |update| { - let (consensus_key, power) = match update { - ValidatorSetUpdate::Consensus(ConsensusValidator { - consensus_key, - bonded_stake, - }) => { - let power: i64 = into_tm_voting_power( - pos_params.tm_votes_per_token, - bonded_stake, - ); - (consensus_key, power) - } - ValidatorSetUpdate::Deactivated(consensus_key) => { - // Any validators that have been dropped from the - // consensus set must have voting power set to 0 to - // remove them from the conensus set - let power = 0_i64; - (consensus_key, power) - } - }; - let pub_key = TendermintPublicKey { - sum: Some(key_to_tendermint(&consensus_key).unwrap()), - }; - let pub_key = Some(pub_key); - ValidatorUpdate { pub_key, power } - }, - ) - .expect("Must be able to update validator sets"); + response.validator_updates = self + .get_abci_validator_updates(false) + .expect("Must be able to update validator set"); } /// Calculate the new inflation rate, mint the new tokens to the PoS From eaebb7bdf91c1312e5989af9d57697c9ec475033 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 7 Jul 2023 09:43:33 +0100 Subject: [PATCH 030/120] changelog: add #1686 --- .changelog/unreleased/bug-fixes/1686-delete-prefix.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/unreleased/bug-fixes/1686-delete-prefix.md diff --git a/.changelog/unreleased/bug-fixes/1686-delete-prefix.md b/.changelog/unreleased/bug-fixes/1686-delete-prefix.md new file mode 100644 index 0000000000..dc8a2dd175 --- /dev/null +++ b/.changelog/unreleased/bug-fixes/1686-delete-prefix.md @@ -0,0 +1,3 @@ +- PoS: ensure that the size of genesis validator set + is limited by the `max_validator_slots` parameter. + ([\#1686](https://github.com/anoma/namada/pull/1686)) \ No newline at end of file From 1a4eb06ab6b77dcc5def29ab04a30964013b9e63 Mon Sep 17 00:00:00 2001 From: brentstone Date: Mon, 10 Jul 2023 10:30:09 -0400 Subject: [PATCH 031/120] improve error handling for `set_initial_validators` --- apps/src/lib/node/ledger/shell/init_chain.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index cb696f665f..9c41d54ad2 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -245,11 +245,11 @@ where &implicit_vp_code_path, ); // set the initial validators set - Ok(self.set_initial_validators( + self.set_initial_validators( &staking_token, genesis.validators, &genesis.pos_params, - )) + ) } /// Initialize genesis established accounts @@ -460,7 +460,7 @@ where staking_token: &Address, validators: Vec, pos_params: &PosParams, - ) -> response::InitChain { + ) -> Result { let mut response = response::InitChain::default(); // PoS system depends on epoch being initialized. Write the total // genesis staking token balance to storage after @@ -473,13 +473,11 @@ where current_epoch, ); - let total_nam = - read_total_supply(&self.wl_storage, staking_token).unwrap(); + let total_nam = read_total_supply(&self.wl_storage, staking_token)?; // At this stage in the chain genesis, the PoS address balance is the // same as the number of staked tokens let total_staked_nam = - read_balance(&self.wl_storage, staking_token, &address::POS) - .unwrap(); + read_balance(&self.wl_storage, staking_token, &address::POS)?; tracing::info!( "Genesis total native tokens: {}.", @@ -505,7 +503,7 @@ where .expect("Must be able to set genesis validator set"); debug_assert!(!response.validators.is_empty()); - response + Ok(response) } } From 8009548838d328a5799a0fb83f901edd7ec580dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Mon, 10 Jul 2023 16:29:04 +0100 Subject: [PATCH 032/120] deps: update sysinfo to latest 0.29.4 --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 568de9a745..5a6761f1ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4654,9 +4654,9 @@ checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7" [[package]] name = "ntapi" -version = "0.3.7" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" dependencies = [ "winapi", ] @@ -6840,9 +6840,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.21.1" +version = "0.29.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb6c2c4a6ca462f07ca89841a2618dca6e405304d19ae238997e64915d89f513" +checksum = "751e810399bba86e9326f5762b7f32ac5a085542df78da6a78d94e07d14d7c11" dependencies = [ "cfg-if 1.0.0", "core-foundation-sys", diff --git a/Cargo.toml b/Cargo.toml index a7bec0a2c4..14ecef3375 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -116,7 +116,7 @@ sha2 = "0.9.3" signal-hook = "0.3.9" slip10_ed25519 = "0.1.3" # sysinfo with disabled multithread feature -sysinfo = {version = "=0.21.1", default-features = false} +sysinfo = {version = "0.29.4", default-features = false} tar = "0.4.37" tempfile = {version = "3.2.0"} tendermint-config = {git = "https://github.com/heliaxdev/tendermint-rs.git", rev = "b7d1e5afc6f2ccb3fd1545c2174bab1cc48d7fa7"} From c265cc42789d97cc569d261d5fb10a8a5ac9b82d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Mon, 10 Jul 2023 16:32:15 +0100 Subject: [PATCH 033/120] changelog: add #1695 --- .changelog/unreleased/improvements/1695-update-sysinfo.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/improvements/1695-update-sysinfo.md diff --git a/.changelog/unreleased/improvements/1695-update-sysinfo.md b/.changelog/unreleased/improvements/1695-update-sysinfo.md new file mode 100644 index 0000000000..13dd88558c --- /dev/null +++ b/.changelog/unreleased/improvements/1695-update-sysinfo.md @@ -0,0 +1,2 @@ +- Updated sysinfo dependency. + ([\#1695](https://github.com/anoma/namada/pull/1695)) \ No newline at end of file From 5a0dd99260d5eefd16f09359cbe5dc5e90fe989a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 21 Jun 2023 12:40:46 +0200 Subject: [PATCH 034/120] apps: use fd-lock instead of file-lock for cross-platform support --- Cargo.lock | 49 ++++--------- Cargo.toml | 2 +- apps/Cargo.toml | 2 +- apps/src/lib/wallet/pre_genesis.rs | 112 +++++++++++++++-------------- apps/src/lib/wallet/store.rs | 49 +++++++------ 5 files changed, 99 insertions(+), 115 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 568de9a745..35940087d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2455,7 +2455,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b36f34b0325008d05b0e9be8361bfa8a0fb905f10de0d951c2621c59e811cb91" dependencies = [ "conpty", - "nix 0.26.2", + "nix", "ptyprocess", "regex", ] @@ -2491,6 +2491,17 @@ dependencies = [ "instant", ] +[[package]] +name = "fd-lock" +version = "3.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5" +dependencies = [ + "cfg-if 1.0.0", + "rustix 0.38.3", + "windows-sys 0.48.0", +] + [[package]] name = "ferveo" version = "0.1.1" @@ -2552,18 +2563,6 @@ dependencies = [ "subtle 2.4.1", ] -[[package]] -name = "file-lock" -version = "2.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0815fc2a1924e651b71ae6d13df07b356a671a09ecaf857dbd344a2ba937a496" -dependencies = [ - "cc", - "libc", - "mktemp", - "nix 0.24.2", -] - [[package]] name = "file-serve" version = "0.2.1" @@ -4157,15 +4156,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94c7128ba23c81f6471141b90f17654f89ef44a56e14b8a4dd0fddfccd655277" -[[package]] -name = "mktemp" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "975de676448231fcde04b9149d2543077e166b78fc29eae5aa219e7928410da2" -dependencies = [ - "uuid 0.8.2", -] - [[package]] name = "moka" version = "0.9.7" @@ -4316,9 +4306,9 @@ dependencies = [ "ethbridge-events", "ethbridge-governance-events", "eyre", + "fd-lock", "ferveo", "ferveo-common", - "file-lock", "flate2", "futures", "git2", @@ -4600,17 +4590,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "nix" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" -dependencies = [ - "bitflags 1.3.2", - "cfg-if 1.0.0", - "libc", -] - [[package]] name = "nix" version = "0.26.2" @@ -5537,7 +5516,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e05aef7befb11a210468a2d77d978dde2c6381a0381e33beb575e91f57fe8cf" dependencies = [ - "nix 0.26.2", + "nix", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index a7bec0a2c4..5ce4b613ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,9 +68,9 @@ ethabi = "18.0.0" ethers = "2.0.0" expectrl = "0.7.0" eyre = "0.6.5" +fd-lock = "3.0.12" ferveo = {git = "https://github.com/anoma/ferveo", rev = "e5abd0acc938da90140351a65a26472eb495ce4d"} ferveo-common = {git = "https://github.com/anoma/ferveo", rev = "e5abd0acc938da90140351a65a26472eb495ce4d"} -file-lock = "2.0.2" file-serve = "0.2.0" flate2 = "1.0.22" fs_extra = "1.2.0" diff --git a/apps/Cargo.toml b/apps/Cargo.toml index b82a4966b7..2d44bc0443 100644 --- a/apps/Cargo.toml +++ b/apps/Cargo.toml @@ -90,9 +90,9 @@ ethbridge-bridge-events = {git = "https://github.com/heliaxdev/ethbridge-rs", ta ethbridge-events = {git = "https://github.com/heliaxdev/ethbridge-rs", tag = "v0.18.0"} ethbridge-governance-events = {git = "https://github.com/heliaxdev/ethbridge-rs", tag = "v0.18.0"} eyre.workspace = true +fd-lock.workspace = true ferveo-common.workspace = true ferveo.workspace = true -file-lock.workspace = true flate2.workspace = true futures.workspace = true itertools.workspace = true diff --git a/apps/src/lib/wallet/pre_genesis.rs b/apps/src/lib/wallet/pre_genesis.rs index a0ec978887..3b05bb214f 100644 --- a/apps/src/lib/wallet/pre_genesis.rs +++ b/apps/src/lib/wallet/pre_genesis.rs @@ -2,7 +2,7 @@ use std::fs; use std::path::{Path, PathBuf}; use ark_serialize::{Read, Write}; -use file_lock::{FileLock, FileOptions}; +use fd_lock::RwLock; use namada::ledger::wallet::pre_genesis::{ ReadError, ValidatorStore, ValidatorWallet, }; @@ -36,10 +36,11 @@ pub fn gen_and_store( let wallet_dir = wallet_path.parent().unwrap(); fs::create_dir_all(wallet_dir)?; // Write the file - let options = FileOptions::new().create(true).write(true).truncate(true); - let mut filelock = - FileLock::lock(wallet_path.to_str().unwrap(), true, options)?; - filelock.file.write_all(&data)?; + let mut options = fs::OpenOptions::new(); + options.create(true).write(true).truncate(true); + let mut lock = RwLock::new(options.open(wallet_path)?); + let mut guard = lock.write()?; + guard.write_all(&data)?; Ok(validator) } @@ -47,59 +48,60 @@ pub fn gen_and_store( /// from a TOML file. pub fn load(store_dir: &Path) -> Result { let wallet_file = validator_file_name(store_dir); - match FileLock::lock( - wallet_file.to_str().unwrap(), - true, - FileOptions::new().read(true).write(false), - ) { - Ok(mut filelock) => { - let mut store = Vec::::new(); - filelock.file.read_to_end(&mut store).map_err(|err| { - ReadError::ReadWallet( - store_dir.to_str().unwrap().into(), - err.to_string(), - ) - })?; - let store = - ValidatorStore::decode(store).map_err(ReadError::Decode)?; + let mut options = fs::OpenOptions::new(); + options.read(true).write(false); + let lock = RwLock::new(options.open(&wallet_file).map_err(|err| { + ReadError::ReadWallet( + wallet_file.to_string_lossy().into_owned(), + err.to_string(), + ) + })?); + let guard = lock.read().map_err(|err| { + ReadError::ReadWallet( + wallet_file.to_string_lossy().into_owned(), + err.to_string(), + ) + })?; + let mut store = Vec::::new(); + (&*guard).read_to_end(&mut store).map_err(|err| { + ReadError::ReadWallet( + store_dir.to_str().unwrap().into(), + err.to_string(), + ) + })?; + let store = ValidatorStore::decode(store).map_err(ReadError::Decode)?; - let password = if store.account_key.is_encrypted() - || store.consensus_key.is_encrypted() - || store.account_key.is_encrypted() - { - Some(CliWalletUtils::read_decryption_password()) - } else { - None - }; + let password = if store.account_key.is_encrypted() + || store.consensus_key.is_encrypted() + || store.account_key.is_encrypted() + { + Some(CliWalletUtils::read_decryption_password()) + } else { + None + }; - let account_key = store - .account_key - .get::(true, password.clone())?; - let consensus_key = store - .consensus_key - .get::(true, password.clone())?; - let eth_cold_key = store - .eth_cold_key - .get::(true, password.clone())?; - let eth_hot_key = store.validator_keys.eth_bridge_keypair.clone(); - let tendermint_node_key = store - .tendermint_node_key - .get::(true, password)?; + let account_key = store + .account_key + .get::(true, password.clone())?; + let consensus_key = store + .consensus_key + .get::(true, password.clone())?; + let eth_cold_key = store + .eth_cold_key + .get::(true, password.clone())?; + let eth_hot_key = store.validator_keys.eth_bridge_keypair.clone(); + let tendermint_node_key = store + .tendermint_node_key + .get::(true, password)?; - Ok(ValidatorWallet { - store, - account_key, - consensus_key, - eth_cold_key, - eth_hot_key, - tendermint_node_key, - }) - } - Err(err) => Err(ReadError::ReadWallet( - wallet_file.to_string_lossy().into_owned(), - err.to_string(), - )), - } + Ok(ValidatorWallet { + store, + account_key, + consensus_key, + eth_cold_key, + eth_hot_key, + tendermint_node_key, + }) } /// Generate a new [`ValidatorWallet`] with required pre-genesis keys. Will diff --git a/apps/src/lib/wallet/store.rs b/apps/src/lib/wallet/store.rs index 925e9c6bf9..6ae0d023d9 100644 --- a/apps/src/lib/wallet/store.rs +++ b/apps/src/lib/wallet/store.rs @@ -7,7 +7,7 @@ use std::str::FromStr; use ark_std::rand::prelude::*; use ark_std::rand::SeedableRng; -use file_lock::{FileLock, FileOptions}; +use fd_lock::RwLock; #[cfg(not(feature = "dev"))] use namada::ledger::wallet::store::AddressVpType; #[cfg(feature = "dev")] @@ -48,10 +48,11 @@ pub fn save(store: &Store, store_dir: &Path) -> std::io::Result<()> { let wallet_dir = wallet_path.parent().unwrap(); fs::create_dir_all(wallet_dir)?; // Write the file - let options = FileOptions::new().create(true).write(true).truncate(true); - let mut filelock = - FileLock::lock(wallet_path.to_str().unwrap(), true, options)?; - filelock.file.write_all(&data) + let mut options = fs::OpenOptions::new(); + options.create(true).write(true).truncate(true); + let mut lock = RwLock::new(options.open(wallet_path)?); + let mut guard = lock.write()?; + guard.write_all(&data) } /// Load the store file or create a new one without any keys or addresses. @@ -88,26 +89,28 @@ pub fn load_or_new_from_genesis( /// Attempt to load the store file. pub fn load(store_dir: &Path) -> Result { let wallet_file = wallet_file(store_dir); - match FileLock::lock( - wallet_file.to_str().unwrap(), - true, - FileOptions::new().read(true).write(false), - ) { - Ok(mut filelock) => { - let mut store = Vec::::new(); - filelock.file.read_to_end(&mut store).map_err(|err| { - LoadStoreError::ReadWallet( - store_dir.to_str().unwrap().parse().unwrap(), - err.to_string(), - ) - })?; - Store::decode(store).map_err(LoadStoreError::Decode) - } - Err(err) => Err(LoadStoreError::ReadWallet( + let mut options = fs::OpenOptions::new(); + options.read(true).write(false); + let lock = RwLock::new(options.open(&wallet_file).map_err(|err| { + LoadStoreError::ReadWallet( wallet_file.to_string_lossy().into_owned(), err.to_string(), - )), - } + ) + })?); + let guard = lock.read().map_err(|err| { + LoadStoreError::ReadWallet( + wallet_file.to_string_lossy().into_owned(), + err.to_string(), + ) + })?; + let mut store = Vec::::new(); + (&*guard).read_to_end(&mut store).map_err(|err| { + LoadStoreError::ReadWallet( + store_dir.to_str().unwrap().parse().unwrap(), + err.to_string(), + ) + })?; + Store::decode(store).map_err(LoadStoreError::Decode) } /// Add addresses from a genesis configuration. From 8726bbed61ba07e6f4db8c1e9481292c4af75d64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Mon, 10 Jul 2023 16:41:03 +0100 Subject: [PATCH 035/120] changelog: add #1605 --- .changelog/unreleased/improvements/1605-win-build.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/improvements/1605-win-build.md diff --git a/.changelog/unreleased/improvements/1605-win-build.md b/.changelog/unreleased/improvements/1605-win-build.md new file mode 100644 index 0000000000..ee4a8d026e --- /dev/null +++ b/.changelog/unreleased/improvements/1605-win-build.md @@ -0,0 +1,2 @@ +- Replaced file-lock with fd-lock dependency to support Windows build. + ([\#1605](https://github.com/anoma/namada/pull/1605)) \ No newline at end of file From ce0e751df42fe0f07ff39d2370e5986c63f49115 Mon Sep 17 00:00:00 2001 From: bengtlofgren Date: Tue, 4 Jul 2023 11:31:47 +0200 Subject: [PATCH 036/120] query bonded-stake and order --- apps/src/lib/client/rpc.rs | 17 ++++++++++++++--- wasm/checksums.json | 2 +- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 44955967d0..fb1dbd6c33 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -10,7 +10,7 @@ use std::str::FromStr; use borsh::{BorshDeserialize, BorshSerialize}; use data_encoding::HEXLOWER; -use itertools::Either; +use itertools::{Either, Itertools}; use masp_primitives::asset_type::AssetType; use masp_primitives::merkle_tree::MerklePath; use masp_primitives::sapling::{Node, ViewingKey}; @@ -1580,13 +1580,19 @@ pub async fn query_bonded_stake( .below_capacity_validator_set(client, &Some(epoch)) .await, ); + + let sorted_consensus = consensus.into_iter(). + sorted_by_key(|v| + -(i128::try_from(v.bonded_stake.change())) + .expect("Failed to sort consensus validators") + ); // Iterate all validators let stdout = io::stdout(); let mut w = stdout.lock(); writeln!(w, "Consensus validators:").unwrap(); - for val in consensus { + for val in sorted_consensus { writeln!( w, " {}: {}", @@ -1596,8 +1602,13 @@ pub async fn query_bonded_stake( .unwrap(); } if !below_capacity.is_empty() { + let sorted_below_capacity = below_capacity.into_iter(). + sorted_by_key(|v| + -(i128::try_from(v.bonded_stake.change())) + .expect("Failed to sort consensus validators") + ); writeln!(w, "Below capacity validators:").unwrap(); - for val in &below_capacity { + for val in sorted_below_capacity { writeln!( w, " {}: {}", diff --git a/wasm/checksums.json b/wasm/checksums.json index 8397ffb0d6..c2d3d7e014 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -19,4 +19,4 @@ "vp_token.wasm": "vp_token.ec4f3914d074168f7ecbd43d5587e014381d409b3df4db4f63eaf73d3a56cdd6.wasm", "vp_user.wasm": "vp_user.fd9810232a2ec79ff3f9f8fc70a6427ce9d07a9ef51c1468a785247a9b08b337.wasm", "vp_validator.wasm": "vp_validator.8ce4c52a53aa451459e37ec560aa56ac4083d8829b0c29168c448e1e9d764c22.wasm" -} \ No newline at end of file +} From cb48c5133f527e3180a97082c45e52db0d440dc4 Mon Sep 17 00:00:00 2001 From: brentstone Date: Tue, 4 Jul 2023 14:18:33 +0200 Subject: [PATCH 037/120] CLI query a validator's state --- apps/src/bin/namada-client/cli.rs | 16 ++++++ apps/src/lib/cli.rs | 68 ++++++++++++++++++++++++-- apps/src/lib/client/rpc.rs | 75 ++++++++++++++++++++++++----- shared/src/ledger/args.rs | 11 +++++ shared/src/ledger/queries/vp/pos.rs | 26 +++++++++- 5 files changed, 179 insertions(+), 17 deletions(-) diff --git a/apps/src/bin/namada-client/cli.rs b/apps/src/bin/namada-client/cli.rs index 609588045d..be77d2b982 100644 --- a/apps/src/bin/namada-client/cli.rs +++ b/apps/src/bin/namada-client/cli.rs @@ -325,6 +325,22 @@ pub async fn main() -> Result<()> { let args = args.to_sdk(&mut ctx); rpc::query_bonded_stake(&client, args).await; } + Sub::QueryValidatorState(QueryValidatorState(mut args)) => { + let client = HttpClient::new(utils::take_config_address( + &mut args.query.ledger_address, + )) + .unwrap(); + wait_until_node_is_synched(&client) + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_and_print_validator_state( + &client, + &mut ctx.wallet, + args, + ) + .await; + } Sub::QueryCommissionRate(QueryCommissionRate(mut args)) => { let client = HttpClient::new(utils::take_config_address( &mut args.query.ledger_address, diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 06d6f3f406..a7a1e83350 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -381,6 +381,7 @@ pub mod cmds { QueryProposal(QueryProposal), QueryProposalResult(QueryProposalResult), QueryProtocolParameters(QueryProtocolParameters), + QueryValidatorState(QueryValidatorState), } #[allow(clippy::large_enum_variant)] @@ -1453,6 +1454,27 @@ pub mod cmds { } } + #[derive(Clone, Debug)] + pub struct QueryValidatorState( + pub args::QueryValidatorState, + ); + + impl SubCmd for QueryValidatorState { + const CMD: &'static str = "validator-state"; + + fn parse(matches: &ArgMatches) -> Option { + matches.subcommand_matches(Self::CMD).map(|matches| { + QueryValidatorState(args::QueryValidatorState::parse(matches)) + }) + } + + fn def() -> App { + App::new(Self::CMD) + .about("Query the state of a PoS validator.") + .add_args::>() + } + } + #[derive(Clone, Debug)] pub struct QueryTransfers(pub args::QueryTransfers); @@ -1488,7 +1510,7 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) - .about("Query commission rate.") + .about("Query a validator's commission rate.") .add_args::>() } } @@ -4016,8 +4038,44 @@ pub mod args { "The validator's address whose bonded stake to query.", )) .arg(EPOCH.def().help( - "The epoch at which to query (last committed, if not \ - specified).", + "The epoch at which to query (corresponding to the last \ + committed block, if not specified).", + )) + } + } + + impl CliToSdk> for QueryValidatorState { + fn to_sdk(self, ctx: &mut Context) -> QueryValidatorState { + QueryValidatorState:: { + query: self.query.to_sdk(ctx), + validator: ctx.get(&self.validator), + epoch: self.epoch, + } + } + } + + impl Args for QueryValidatorState { + fn parse(matches: &ArgMatches) -> Self { + let query = Query::parse(matches); + let validator = VALIDATOR.parse(matches); + let epoch = EPOCH.parse(matches); + Self { + query, + validator, + epoch, + } + } + + fn def(app: App) -> App { + app.add_args::>() + .arg( + VALIDATOR.def().help( + "The validator's address whose state is queried.", + ), + ) + .arg(EPOCH.def().help( + "The epoch at which to query (corresponding to the last \ + committed block, if not specified).", )) } } @@ -4127,8 +4185,8 @@ pub mod args { "The validator's address whose commission rate to query.", )) .arg(EPOCH.def().help( - "The epoch at which to query (last committed, if not \ - specified).", + "The epoch at which to query (corresponding to the last \ + committed block, if not specified).", )) } } diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index fb1dbd6c33..81d9d71009 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -36,7 +36,7 @@ use namada::ledger::rpc::{ }; use namada::ledger::storage::ConversionState; use namada::ledger::wallet::{AddressVpType, Wallet}; -use namada::proof_of_stake::types::WeightedValidator; +use namada::proof_of_stake::types::{ValidatorState, WeightedValidator}; use namada::types::address::{masp, Address}; use namada::types::control_flow::ProceedOrElse; use namada::types::governance::{ @@ -1580,12 +1580,11 @@ pub async fn query_bonded_stake( .below_capacity_validator_set(client, &Some(epoch)) .await, ); - - let sorted_consensus = consensus.into_iter(). - sorted_by_key(|v| + + let sorted_consensus = consensus.into_iter().sorted_by_key(|v| { -(i128::try_from(v.bonded_stake.change())) - .expect("Failed to sort consensus validators") - ); + .expect("Failed to sort consensus validators") + }); // Iterate all validators let stdout = io::stdout(); @@ -1602,11 +1601,11 @@ pub async fn query_bonded_stake( .unwrap(); } if !below_capacity.is_empty() { - let sorted_below_capacity = below_capacity.into_iter(). - sorted_by_key(|v| - -(i128::try_from(v.bonded_stake.change())) - .expect("Failed to sort consensus validators") - ); + let sorted_below_capacity = + below_capacity.into_iter().sorted_by_key(|v| { + -(i128::try_from(v.bonded_stake.change())) + .expect("Failed to sort consensus validators") + }); writeln!(w, "Below capacity validators:").unwrap(); for val in sorted_below_capacity { writeln!( @@ -1645,6 +1644,60 @@ pub async fn query_commission_rate< ) } +/// Query and return validator's state +pub async fn query_validator_state< + C: namada::ledger::queries::Client + Sync, +>( + client: &C, + validator: &Address, + epoch: Option, +) -> Option { + unwrap_client_response::>( + RPC.vp() + .pos() + .validator_state(client, validator, &epoch) + .await, + ) +} + +/// Query a validator's state information +pub async fn query_and_print_validator_state< + C: namada::ledger::queries::Client + Sync, +>( + client: &C, + _wallet: &mut Wallet, + args: args::QueryValidatorState, +) { + let validator = args.validator; + let state: Option = + query_validator_state(client, &validator, args.epoch).await; + + match state { + Some(state) => match state { + ValidatorState::Consensus => { + println!("Validator {validator} is in the consensus set") + } + ValidatorState::BelowCapacity => { + println!("Validator {validator} is in the below-capacity set") + } + ValidatorState::BelowThreshold => { + println!("Validator {validator} is in the below-threshold set") + } + ValidatorState::Inactive => { + println!("Validator {validator} is inactive") + } + ValidatorState::Jailed => { + println!("Validator {validator} is jailed") + } + }, + None => println!( + "Validator {validator} is either not a validator, or an epoch \ + before the current epoch has been queried (and the validator \ + state information is no longer stored)" + ), + } +} + /// Query PoS validator's commission rate information pub async fn query_and_print_commission_rate< C: namada::ledger::queries::Client + Sync, diff --git a/shared/src/ledger/args.rs b/shared/src/ledger/args.rs index 35081d3283..47117ba594 100644 --- a/shared/src/ledger/args.rs +++ b/shared/src/ledger/args.rs @@ -356,6 +356,17 @@ pub struct QueryBondedStake { pub epoch: Option, } +/// Query the state of a validator (its validator set or if it is jailed) +#[derive(Clone, Debug)] +pub struct QueryValidatorState { + /// Common query args + pub query: Query, + /// Address of a validator + pub validator: C::Address, + /// Epoch in which to find the validator state + pub epoch: Option, +} + #[derive(Clone, Debug)] /// Commission rate change args pub struct CommissionRateChange { diff --git a/shared/src/ledger/queries/vp/pos.rs b/shared/src/ledger/queries/vp/pos.rs index d872aa5002..9934a71271 100644 --- a/shared/src/ledger/queries/vp/pos.rs +++ b/shared/src/ledger/queries/vp/pos.rs @@ -7,7 +7,7 @@ use namada_core::ledger::storage_api::collections::lazy_map; use namada_core::ledger::storage_api::OptionExt; use namada_proof_of_stake::types::{ BondId, BondsAndUnbondsDetail, BondsAndUnbondsDetails, CommissionPair, - Slash, WeightedValidator, + Slash, ValidatorState, WeightedValidator, }; use namada_proof_of_stake::{ self, below_capacity_validator_set_handle, bond_amount, bond_handle, @@ -16,6 +16,7 @@ use namada_proof_of_stake::{ read_pos_params, read_total_stake, read_validator_max_commission_rate_change, read_validator_stake, unbond_handle, validator_commission_rate_handle, validator_slashes_handle, + validator_state_handle, }; use crate::ledger::queries::types::RequestCtx; @@ -43,6 +44,9 @@ router! {POS, ( "commission" / [validator: Address] / [epoch: opt Epoch] ) -> Option = validator_commission, + + ( "state" / [validator: Address] / [epoch: opt Epoch] ) + -> Option = validator_state, }, ( "validator_set" ) = { @@ -203,6 +207,26 @@ where } } +/// Get the validator state +fn validator_state( + ctx: RequestCtx<'_, D, H>, + validator: Address, + epoch: Option, +) -> storage_api::Result> +where + D: 'static + DB + for<'iter> DBIter<'iter> + Sync, + H: 'static + StorageHasher + Sync, +{ + let epoch = epoch.unwrap_or(ctx.wl_storage.storage.last_epoch); + let params = read_pos_params(ctx.wl_storage)?; + let state = validator_state_handle(&validator).get( + ctx.wl_storage, + epoch, + ¶ms, + )?; + Ok(state) +} + /// Get the total stake of a validator at the given epoch or current when /// `None`. The total stake is a sum of validator's self-bonds and delegations /// to their address. From 23a78b224fea2e0b20eca1a991bab4a156eaeade Mon Sep 17 00:00:00 2001 From: brentstone Date: Thu, 6 Jul 2023 11:59:19 +0200 Subject: [PATCH 038/120] Expanding and fixing slashes query --- apps/src/lib/client/rpc.rs | 92 +++++++++++++++++++++++++---- proof_of_stake/src/lib.rs | 36 +++++++++++ shared/src/ledger/queries/vp/pos.rs | 25 ++++++-- 3 files changed, 135 insertions(+), 18 deletions(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 81d9d71009..626c946f3d 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -1,7 +1,7 @@ //! Client RPC queries use std::cmp::Ordering; -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeMap, HashMap, HashSet}; use std::fs::File; use std::io::{self, Write}; use std::iter::Iterator; @@ -1738,11 +1738,6 @@ pub async fn query_slashes( _wallet: &mut Wallet, args: args::QuerySlashes, ) { - let params_key = pos::params_key(); - let params = query_storage_value::(client, ¶ms_key) - .await - .expect("Parameter should be defined."); - match args.validator { Some(validator) => { let validator = validator; @@ -1751,18 +1746,54 @@ pub async fn query_slashes( RPC.vp().pos().validator_slashes(client, &validator).await, ); if !slashes.is_empty() { + println!("Processed slashes:"); let stdout = io::stdout(); let mut w = stdout.lock(); for slash in slashes { writeln!( w, - "Slash epoch {}, type {}, rate {}", - slash.epoch, slash.r#type, slash.rate + "Infraction epoch {}, block height {}, type {}, rate \ + {}", + slash.epoch, + slash.block_height, + slash.r#type, + slash.rate ) .unwrap(); } } else { - println!("No slashes found for {}", validator.encode()) + println!( + "No processed slashes found for {}", + validator.encode() + ) + } + // Find enqueued slashes to be processed in the future for the given + // validator + let enqueued_slashes: HashMap< + Address, + BTreeMap>, + > = unwrap_client_response::< + C, + HashMap>>, + >(RPC.vp().pos().enqueued_slashes(client).await); + let enqueued_slashes = enqueued_slashes.get(&validator).cloned(); + if let Some(enqueued) = enqueued_slashes { + println!("\nEnqueued slashes for future processing"); + for (epoch, slashes) in enqueued { + println!("To be processed in epoch {}", epoch); + for slash in slashes { + let stdout = io::stdout(); + let mut w = stdout.lock(); + writeln!( + w, + "Infraction epoch {}, block height {}, type {}", + slash.epoch, slash.block_height, slash.r#type, + ) + .unwrap(); + } + } + } else { + println!("No enqueued slashes found for {}", validator.encode()) } } None => { @@ -1774,15 +1805,16 @@ pub async fn query_slashes( if !all_slashes.is_empty() { let stdout = io::stdout(); let mut w = stdout.lock(); + println!("Processed slashes:"); for (validator, slashes) in all_slashes.into_iter() { for slash in slashes { writeln!( w, - "Slash epoch {}, block height {}, rate {}, type \ - {}, validator {}", + "Infraction epoch {}, block height {}, rate {}, \ + type {}, validator {}", slash.epoch, slash.block_height, - slash.r#type.get_slash_rate(¶ms), + slash.rate, slash.r#type, validator, ) @@ -1790,7 +1822,41 @@ pub async fn query_slashes( } } } else { - println!("No slashes found") + println!("No processed slashes found") + } + + // Find enqueued slashes to be processed in the future for the given + // validator + let enqueued_slashes: HashMap< + Address, + BTreeMap>, + > = unwrap_client_response::< + C, + HashMap>>, + >(RPC.vp().pos().enqueued_slashes(client).await); + if !enqueued_slashes.is_empty() { + println!("\nEnqueued slashes for future processing"); + for (validator, slashes_by_epoch) in enqueued_slashes { + for (epoch, slashes) in slashes_by_epoch { + println!("\nTo be processed in epoch {}", epoch); + for slash in slashes { + let stdout = io::stdout(); + let mut w = stdout.lock(); + writeln!( + w, + "Infraction epoch {}, block height {}, type \ + {}, validator {}", + slash.epoch, + slash.block_height, + slash.r#type, + validator + ) + .unwrap(); + } + } + } + } else { + println!("\nNo enqueued slashes found for future processing") } } } diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index 7f7265be91..050334debc 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -2753,6 +2753,42 @@ where } } +/// Collect the details of all of the enqueued slashes to be processed in future +/// epochs into a nested map +pub fn find_all_enqueued_slashes( + storage: &S, + epoch: Epoch, +) -> storage_api::Result>>> +where + S: StorageRead, +{ + let mut enqueued = HashMap::>>::new(); + for res in enqueued_slashes_handle().get_data_handler().iter(storage)? { + let ( + NestedSubKey::Data { + key: processing_epoch, + nested_sub_key: + NestedSubKey::Data { + key: address, + nested_sub_key: _, + }, + }, + slash, + ) = res?; + if processing_epoch <= epoch { + continue; + } + + let slashes = enqueued + .entry(address) + .or_default() + .entry(processing_epoch) + .or_default(); + slashes.push(slash); + } + Ok(enqueued) +} + /// Find all slashes and the associated validators in the PoS system pub fn find_all_slashes( storage: &S, diff --git a/shared/src/ledger/queries/vp/pos.rs b/shared/src/ledger/queries/vp/pos.rs index 9934a71271..94c93f6a54 100644 --- a/shared/src/ledger/queries/vp/pos.rs +++ b/shared/src/ledger/queries/vp/pos.rs @@ -1,6 +1,6 @@ //! Queries router and handlers for PoS validity predicate -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeMap, HashMap, HashSet}; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use namada_core::ledger::storage_api::collections::lazy_map; @@ -11,9 +11,9 @@ use namada_proof_of_stake::types::{ }; use namada_proof_of_stake::{ self, below_capacity_validator_set_handle, bond_amount, bond_handle, - consensus_validator_set_handle, find_all_slashes, - find_delegation_validators, find_delegations, read_all_validator_addresses, - read_pos_params, read_total_stake, + consensus_validator_set_handle, find_all_enqueued_slashes, + find_all_slashes, find_delegation_validators, find_delegations, + read_all_validator_addresses, read_pos_params, read_total_stake, read_validator_max_commission_rate_change, read_validator_stake, unbond_handle, validator_commission_rate_handle, validator_slashes_handle, validator_state_handle, @@ -86,6 +86,9 @@ router! {POS, ( "bonds_and_unbonds" / [source: opt Address] / [validator: opt Address] ) -> BondsAndUnbondsDetails = bonds_and_unbonds, + ( "enqueued_slashes" ) + -> HashMap>> = enqueued_slashes, + ( "all_slashes" ) -> HashMap> = slashes, ( "is_delegator" / [addr: Address ] / [epoch: opt Epoch] ) -> bool = is_delegator, @@ -520,7 +523,19 @@ where find_all_slashes(ctx.wl_storage) } -/// All slashes +/// Enqueued slashes +fn enqueued_slashes( + ctx: RequestCtx<'_, D, H>, +) -> storage_api::Result>>> +where + D: 'static + DB + for<'iter> DBIter<'iter> + Sync, + H: 'static + StorageHasher + Sync, +{ + let current_epoch = ctx.wl_storage.storage.last_epoch; + find_all_enqueued_slashes(ctx.wl_storage, current_epoch) +} + +/// Native validator address by looking up the Tendermint address fn validator_by_tm_addr( ctx: RequestCtx<'_, D, H>, tm_addr: String, From c2cfd34de6aa08a6fe4ec4066128e0707906e220 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 5 Jul 2023 14:12:51 +0200 Subject: [PATCH 039/120] feat: store total consensus stake; garbage collect validator sets --- .../lib/node/ledger/shell/finalize_block.rs | 10 +- proof_of_stake/src/epoched.rs | 25 ++++ proof_of_stake/src/lib.rs | 125 +++++++++++++----- proof_of_stake/src/storage.rs | 16 +++ proof_of_stake/src/tests.rs | 16 ++- proof_of_stake/src/types.rs | 4 + 6 files changed, 157 insertions(+), 39 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 3af8ab9e3f..e8e7f1bf56 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -105,8 +105,14 @@ where &mut self.wl_storage, current_epoch, current_epoch + pos_params.pipeline_len, - &namada_proof_of_stake::consensus_validator_set_handle(), - &namada_proof_of_stake::below_capacity_validator_set_handle(), + )?; + namada_proof_of_stake::store_total_consensus_stake( + &mut self.wl_storage, + current_epoch, + )?; + namada_proof_of_stake::purge_validator_sets_for_old_epoch( + &mut self.wl_storage, + current_epoch, )?; } diff --git a/proof_of_stake/src/epoched.rs b/proof_of_stake/src/epoched.rs index 4899ae1e1d..40c8c8f50d 100644 --- a/proof_of_stake/src/epoched.rs +++ b/proof_of_stake/src/epoched.rs @@ -659,6 +659,29 @@ where } } +/// Zero offset +#[derive( + Debug, + Clone, + BorshDeserialize, + BorshSerialize, + BorshSchema, + PartialEq, + Eq, + PartialOrd, + Ord, +)] +pub struct OffsetZero; +impl EpochOffset for OffsetZero { + fn value(_paras: &PosParams) -> u64 { + 0 + } + + fn dyn_offset() -> DynEpochOffset { + DynEpochOffset::Zero + } +} + /// Offset at pipeline length. #[derive( Debug, @@ -731,6 +754,8 @@ impl EpochOffset for OffsetPipelinePlusUnbondingLen { /// Offset length dynamic choice. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum DynEpochOffset { + /// Zero offset + Zero, /// Offset at pipeline length - 1 PipelineLenMinusOne, /// Offset at pipeline length. diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index 7f7265be91..d8de70ad0c 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -56,7 +56,7 @@ use storage::{ validator_address_raw_hash_key, validator_last_slash_key, validator_max_commission_rate_change_key, BondDetails, BondsAndUnbondsDetail, BondsAndUnbondsDetails, EpochedSlashes, - ReverseOrdTokenAmount, RewardsAccumulator, SlashedAmount, UnbondDetails, + ReverseOrdTokenAmount, RewardsAccumulator, SlashedAmount, TotalConsensusStakes, UnbondDetails, ValidatorAddresses, ValidatorUnbondRecords, }; use thiserror::Error; @@ -84,6 +84,10 @@ pub fn staking_token_address(storage: &impl StorageRead) -> Address { .expect("Must be able to read native token address") } +/// Number of epochs below the current epoch for which full validator sets are +/// stored +const STORE_VALIDATOR_SETS_LEN: u64 = 2; + #[allow(missing_docs)] #[derive(Error, Debug)] pub enum GenesisError { @@ -274,6 +278,12 @@ pub fn validator_eth_cold_key_handle( ValidatorEthColdKeys::open(key) } +/// Get the storage handle to the total consensus validator stake +pub fn total_consensus_stake_key_handle() -> TotalConsensusStakes { + let key = storage::total_consensus_stake_key(); + TotalConsensusStakes::open(key) +} + /// Get the storage handle to a PoS validator's state pub fn validator_state_handle(validator: &Address) -> ValidatorStates { let key = storage::validator_state_key(validator); @@ -476,6 +486,9 @@ where )?; } + // Store the total consensus validator stake to storage + store_total_consensus_stake(storage, current_epoch)?; + // Write total deltas to storage total_deltas_handle().init_at_genesis( storage, @@ -488,13 +501,7 @@ where credit_tokens(storage, &staking_token, &ADDRESS, total_bonded)?; // Copy the genesis validator set into the pipeline epoch as well for epoch in (current_epoch.next()).iter_range(params.pipeline_len) { - copy_validator_sets_and_positions( - storage, - current_epoch, - epoch, - &consensus_validator_set_handle(), - &below_capacity_validator_set_handle(), - )?; + copy_validator_sets_and_positions(storage, current_epoch, epoch)?; } tracing::debug!("Genesis initialized"); @@ -1528,14 +1535,15 @@ pub fn copy_validator_sets_and_positions( storage: &mut S, current_epoch: Epoch, target_epoch: Epoch, - consensus_validator_set: &ConsensusValidatorSets, - below_capacity_validator_set: &BelowCapacityValidatorSets, ) -> storage_api::Result<()> where S: StorageRead + StorageWrite, { let prev_epoch = target_epoch.prev(); + let consensus_validator_set = consensus_validator_set_handle(); + let below_capacity_validator_set = below_capacity_validator_set_handle(); + let (consensus, below_capacity) = ( consensus_validator_set.at(&prev_epoch), below_capacity_validator_set.at(&prev_epoch), @@ -1597,28 +1605,31 @@ where // Copy validator positions let mut positions = HashMap::::default(); - let positions_handle = validator_set_positions_handle().at(&prev_epoch); + let validator_set_positions_handle = validator_set_positions_handle(); + let positions_handle = validator_set_positions_handle.at(&prev_epoch); + for result in positions_handle.iter(storage)? { let (validator, position) = result?; positions.insert(validator, position); } - let new_positions_handle = - validator_set_positions_handle().at(&target_epoch); + + let new_positions_handle = validator_set_positions_handle.at(&target_epoch); for (validator, position) in positions { let prev = new_positions_handle.insert(storage, validator, position)?; debug_assert!(prev.is_none()); } - validator_set_positions_handle().set_last_update(storage, current_epoch)?; + validator_set_positions_handle.set_last_update(storage, current_epoch)?; // Copy set of all validator addresses let mut all_validators = HashSet::
::default(); - let all_validators_handle = validator_addresses_handle().at(&prev_epoch); + let validator_addresses_handle = validator_addresses_handle(); + let all_validators_handle = validator_addresses_handle.at(&prev_epoch); for result in all_validators_handle.iter(storage)? { let validator = result?; all_validators.insert(validator); } let new_all_validators_handle = - validator_addresses_handle().at(&target_epoch); + validator_addresses_handle.at(&target_epoch); for validator in all_validators { let was_in = new_all_validators_handle.insert(storage, validator)?; debug_assert!(!was_in); @@ -1627,6 +1638,68 @@ where Ok(()) } +/// Compute total validator stake for the current epoch +fn compute_total_consensus_stake( + storage: &S, + epoch: Epoch, +) -> storage_api::Result +where + S: StorageRead, +{ + consensus_validator_set_handle() + .at(&epoch) + .iter(storage)? + .fold(Ok(token::Amount::zero()), |acc, entry| { + let acc = acc?; + let ( + NestedSubKey::Data { + key: amount, + nested_sub_key: _, + }, + _validator, + ) = entry?; + Ok(acc + amount) + }) +} + +/// Store total consensus stake +pub fn store_total_consensus_stake( + storage: &mut S, + epoch: Epoch, +) -> storage_api::Result<()> +where + S: StorageRead + StorageWrite, +{ + let total = compute_total_consensus_stake(storage, epoch)?; + tracing::debug!( + "Computed total consensus stake for epoch {}: {}", + epoch, + total.to_string_native() + ); + total_consensus_stake_key_handle().set(storage, total, epoch, 0) +} + +/// Purge the validator sets from the epochs older than the current epoch minus +/// `STORE_VALIDATOR_SETS_LEN` +pub fn purge_validator_sets_for_old_epoch( + storage: &mut S, + epoch: Epoch, +) -> storage_api::Result<()> +where + S: StorageRead + StorageWrite, +{ + if Epoch(STORE_VALIDATOR_SETS_LEN) < epoch { + let old_epoch = epoch - STORE_VALIDATOR_SETS_LEN - 1; + consensus_validator_set_handle() + .get_data_handler() + .remove_all(storage, &old_epoch)?; + below_capacity_validator_set_handle() + .get_data_handler() + .remove_all(storage, &old_epoch)?; + } + Ok(()) +} + /// Read the position of the validator in the subset of validators that have the /// same bonded stake. This information is held in its own epoched structure in /// addition to being inside the validator sets. @@ -3252,9 +3325,9 @@ where for epoch in Epoch::iter_bounds_inclusive(start_epoch, end_epoch) { let consensus_stake = - Dec::from(get_total_consensus_stake(storage, epoch)?); + Dec::from(get_total_consensus_stake(storage, epoch, params)?); tracing::debug!( - "Consensus stake in epoch {}: {}", + "Total consensus stake in epoch {}: {}", epoch, consensus_stake ); @@ -3856,22 +3929,14 @@ where fn get_total_consensus_stake( storage: &S, epoch: Epoch, + params: &PosParams, ) -> storage_api::Result where S: StorageRead, { - let mut total = token::Amount::default(); - for res in consensus_validator_set_handle().at(&epoch).iter(storage)? { - let ( - NestedSubKey::Data { - key: bonded_stake, - nested_sub_key: _, - }, - _validator, - ) = res?; - total += bonded_stake; - } - Ok(total) + total_consensus_stake_key_handle() + .get(storage, epoch, params) + .map(|o| o.expect("Total consensus stake could not be retrieved.")) } /// Find slashes applicable to a validator with inclusive `start` and exclusive diff --git a/proof_of_stake/src/storage.rs b/proof_of_stake/src/storage.rs index 79124fe3bb..54bd7cfe6b 100644 --- a/proof_of_stake/src/storage.rs +++ b/proof_of_stake/src/storage.rs @@ -36,6 +36,7 @@ const VALIDATOR_TOTAL_UNBONDED_STORAGE_KEY: &str = "total_unbonded"; const VALIDATOR_SETS_STORAGE_PREFIX: &str = "validator_sets"; const CONSENSUS_VALIDATOR_SET_STORAGE_KEY: &str = "consensus"; const BELOW_CAPACITY_VALIDATOR_SET_STORAGE_KEY: &str = "below_capacity"; +const TOTAL_CONSENSUS_STAKE_STORAGE_KEY: &str = "total_consensus_stake"; const TOTAL_DELTAS_STORAGE_KEY: &str = "total_deltas"; const VALIDATOR_SET_POSITIONS_KEY: &str = "validator_set_positions"; const CONSENSUS_KEYS: &str = "consensus_keys"; @@ -584,6 +585,21 @@ pub fn is_below_capacity_validator_set_key(key: &Key) -> bool { matches!(&key.segments[..], [DbKeySeg::AddressSeg(addr), DbKeySeg::StringSeg(key), DbKeySeg::StringSeg(set_type), DbKeySeg::StringSeg(lazy_map), DbKeySeg::StringSeg(data), DbKeySeg::StringSeg(_epoch), DbKeySeg::StringSeg(_), DbKeySeg::StringSeg(_amount), DbKeySeg::StringSeg(_), DbKeySeg::StringSeg(_position)] if addr == &ADDRESS && key == VALIDATOR_SETS_STORAGE_PREFIX && set_type == BELOW_CAPACITY_VALIDATOR_SET_STORAGE_KEY && lazy_map == LAZY_MAP_SUB_KEY && data == lazy_map::DATA_SUBKEY) } +/// Storage key for total consensus stake +pub fn total_consensus_stake_key() -> Key { + Key::from(ADDRESS.to_db_key()) + .push(&TOTAL_CONSENSUS_STAKE_STORAGE_KEY.to_owned()) + .expect("Cannot obtain a total consensus stake key") +} + +/// Is storage key for the total consensus stake? +pub fn is_total_consensus_stake_key(key: &Key) -> bool { + matches!(&key.segments[..], [ + DbKeySeg::AddressSeg(addr), + DbKeySeg::StringSeg(key) + ] if addr == &ADDRESS && key == TOTAL_CONSENSUS_STAKE_STORAGE_KEY) +} + /// Storage key for total deltas of all validators. pub fn total_deltas_key() -> Key { Key::from(ADDRESS.to_db_key()) diff --git a/proof_of_stake/src/tests.rs b/proof_of_stake/src/tests.rs index 451a6880fe..6136b7a94b 100644 --- a/proof_of_stake/src/tests.rs +++ b/proof_of_stake/src/tests.rs @@ -43,15 +43,17 @@ use crate::{ copy_validator_sets_and_positions, find_validator_by_raw_hash, get_num_consensus_validators, init_genesis, insert_validator_into_validator_set, is_validator, process_slashes, + purge_validator_sets_for_old_epoch, 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_delta_value, read_validator_stake, slash, - staking_token_address, total_deltas_handle, unbond_handle, unbond_tokens, - unjail_validator, update_validator_deltas, update_validator_set, - validator_consensus_key_handle, validator_set_update_tendermint, - validator_slashes_handle, validator_state_handle, withdraw_tokens, - write_validator_address_raw_hash, BecomeValidator, + staking_token_address, store_total_consensus_stake, total_deltas_handle, + unbond_handle, unbond_tokens, unjail_validator, update_validator_deltas, + update_validator_set, validator_consensus_key_handle, + validator_set_update_tendermint, validator_slashes_handle, + validator_state_handle, withdraw_tokens, write_validator_address_raw_hash, + BecomeValidator, }; proptest! { @@ -2003,14 +2005,14 @@ fn get_tendermint_set_updates( 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; + store_total_consensus_stake(s, current_epoch).unwrap(); copy_validator_sets_and_positions( s, current_epoch, current_epoch + params.pipeline_len, - &consensus_validator_set_handle(), - &below_capacity_validator_set_handle(), ) .unwrap(); + purge_validator_sets_for_old_epoch(s, current_epoch).unwrap(); // process_slashes(s, current_epoch).unwrap(); // dbg!(current_epoch); current_epoch diff --git a/proof_of_stake/src/types.rs b/proof_of_stake/src/types.rs index caf705fce3..53f09a5d13 100644 --- a/proof_of_stake/src/types.rs +++ b/proof_of_stake/src/types.rs @@ -121,6 +121,10 @@ pub type BelowCapacityValidatorSets = crate::epoched::NestedEpoched< crate::epoched::OffsetPipelineLen, >; +/// Epoched total consensus validator stake +pub type TotalConsensusStakes = + crate::epoched::Epoched; + /// Epoched validator's deltas. pub type ValidatorDeltas = crate::epoched::EpochedDelta< token::Change, From 363aa470eee38ae1e634e465bb02452583a87270 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Sun, 9 Jul 2023 21:14:32 +0200 Subject: [PATCH 040/120] add test case --- proof_of_stake/src/tests.rs | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/proof_of_stake/src/tests.rs b/proof_of_stake/src/tests.rs index 6136b7a94b..6476827417 100644 --- a/proof_of_stake/src/tests.rs +++ b/proof_of_stake/src/tests.rs @@ -53,7 +53,7 @@ use crate::{ update_validator_set, validator_consensus_key_handle, validator_set_update_tendermint, validator_slashes_handle, validator_state_handle, withdraw_tokens, write_validator_address_raw_hash, - BecomeValidator, + BecomeValidator, STORE_VALIDATOR_SETS_LEN, }; proptest! { @@ -1161,8 +1161,7 @@ fn test_validator_sets() { .unwrap(); }; - // Start with two genesis validators with 1 NAM stake - let epoch = Epoch::default(); + // Create genesis validators let ((val1, pk1), stake1) = (gen_validator(), token::Amount::native_whole(1)); let ((val2, pk2), stake2) = @@ -1185,6 +1184,9 @@ fn test_validator_sets() { 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; + init_genesis( &mut s, ¶ms, @@ -1751,6 +1753,28 @@ fn test_validator_sets() { }) ); assert_eq!(tm_updates[1], ValidatorSetUpdate::Deactivated(pk4)); + + // Check that the validator sets were purged for the old epochs + let last_epoch = epoch; + for e in Epoch::iter_bounds_inclusive( + start_epoch, + last_epoch + .sub_or_default(Epoch(STORE_VALIDATOR_SETS_LEN)) + .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 From c7dab48e7b917591889fe054ad852331757cc958 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Sun, 9 Jul 2023 21:16:02 +0200 Subject: [PATCH 041/120] refactor: introduce name for magic constant --- proof_of_stake/src/types.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/proof_of_stake/src/types.rs b/proof_of_stake/src/types.rs index 53f09a5d13..736ffe7a46 100644 --- a/proof_of_stake/src/types.rs +++ b/proof_of_stake/src/types.rs @@ -29,6 +29,10 @@ use crate::parameters::PosParams; // core::types::token::NATIVE_MAX_DECIMAL_PLACES?? const U64_MAX: u64 = u64::MAX; +/// Number of epochs below the current epoch for which validator deltas and +/// slashes are stored +const VALIDATOR_DELTAS_SLASHES_LEN: u64 = 23; + // TODO: add this to the spec /// Stored positions of validators in validator sets pub type ValidatorSetPositions = crate::epoched::NestedEpoched< @@ -129,14 +133,14 @@ pub type TotalConsensusStakes = pub type ValidatorDeltas = crate::epoched::EpochedDelta< token::Change, crate::epoched::OffsetUnbondingLen, - 23, + VALIDATOR_DELTAS_SLASHES_LEN, >; /// Epoched total deltas. pub type TotalDeltas = crate::epoched::EpochedDelta< token::Change, crate::epoched::OffsetUnbondingLen, - 23, + VALIDATOR_DELTAS_SLASHES_LEN, >; /// Epoched validator commission rate @@ -168,7 +172,7 @@ pub type ValidatorSlashes = NestedMap; pub type EpochedSlashes = crate::epoched::NestedEpoched< ValidatorSlashes, crate::epoched::OffsetUnbondingLen, - 23, + VALIDATOR_DELTAS_SLASHES_LEN, >; /// Epoched validator's unbonds From e43d5f7e086976d16e3c91995dc8f50dc336ba4c Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Sun, 9 Jul 2023 21:27:21 +0200 Subject: [PATCH 042/120] refactor: simplify code --- proof_of_stake/src/lib.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index d8de70ad0c..204df78176 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -3336,6 +3336,7 @@ where let infracting_stake = slashes.iter(storage)?.fold( Ok(Dec::zero()), |acc: storage_api::Result, res| { + let acc = acc?; let ( NestedSubKey::Data { key: validator, @@ -3349,11 +3350,7 @@ where .unwrap_or_default(); // println!("Val {} stake: {}", &validator, validator_stake); - if let Ok(inner) = acc { - Ok(inner + Dec::from(validator_stake)) - } else { - acc - } + Ok(acc + Dec::from(validator_stake)) // TODO: does something more complex need to be done // here in the event some of these slashes correspond to // the same validator? From 9dc5fff4d72d3f9290114df5c3806a5d87a62237 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Mon, 10 Jul 2023 16:12:09 +0200 Subject: [PATCH 043/120] add changelog --- .../1129-clear-out-validator-sets-for-old-epochs.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/improvements/1129-clear-out-validator-sets-for-old-epochs.md diff --git a/.changelog/unreleased/improvements/1129-clear-out-validator-sets-for-old-epochs.md b/.changelog/unreleased/improvements/1129-clear-out-validator-sets-for-old-epochs.md new file mode 100644 index 0000000000..e7fb6e9063 --- /dev/null +++ b/.changelog/unreleased/improvements/1129-clear-out-validator-sets-for-old-epochs.md @@ -0,0 +1,2 @@ +- PoS: purge validator sets for old epochs from the storage; store total + validator stake ([\#1129](https://github.com/anoma/namada/issues/1129)) \ No newline at end of file From 4abcc9b9c713de10f4343eac6e9559fbee4fbf0c Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Mon, 10 Jul 2023 16:25:53 +0200 Subject: [PATCH 044/120] refactor: fix formatting --- proof_of_stake/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index 204df78176..805d79443c 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -56,8 +56,9 @@ use storage::{ validator_address_raw_hash_key, validator_last_slash_key, validator_max_commission_rate_change_key, BondDetails, BondsAndUnbondsDetail, BondsAndUnbondsDetails, EpochedSlashes, - ReverseOrdTokenAmount, RewardsAccumulator, SlashedAmount, TotalConsensusStakes, UnbondDetails, - ValidatorAddresses, ValidatorUnbondRecords, + ReverseOrdTokenAmount, RewardsAccumulator, SlashedAmount, + TotalConsensusStakes, UnbondDetails, ValidatorAddresses, + ValidatorUnbondRecords, }; use thiserror::Error; use types::{ From 2a908a00a4dd5cd781fca6212c98903b4e19852c Mon Sep 17 00:00:00 2001 From: yito88 Date: Mon, 10 Jul 2023 21:42:51 +0200 Subject: [PATCH 045/120] fix according to feedback --- core/src/ledger/ibc/actions.rs | 1605 ----------------- .../transactions/ethereum_events/events.rs | 13 +- .../transactions/ethereum_events/mod.rs | 6 +- shared/src/ledger/native_vp/multitoken.rs | 12 +- shared/src/ledger/queries/shell/eth_bridge.rs | 37 +- wasm/wasm_source/src/tx_bridge_pool.rs | 3 +- 6 files changed, 33 insertions(+), 1643 deletions(-) delete mode 100644 core/src/ledger/ibc/actions.rs diff --git a/core/src/ledger/ibc/actions.rs b/core/src/ledger/ibc/actions.rs deleted file mode 100644 index e925a98d92..0000000000 --- a/core/src/ledger/ibc/actions.rs +++ /dev/null @@ -1,1605 +0,0 @@ -//! Functions to handle IBC modules - -use std::str::FromStr; - -use sha2::Digest; -use thiserror::Error; - -use crate::ibc::applications::ics20_fungible_token_transfer::msgs::transfer::MsgTransfer; -use crate::ibc::clients::ics07_tendermint::consensus_state::ConsensusState as TmConsensusState; -use crate::ibc::core::ics02_client::client_consensus::{ - AnyConsensusState, ConsensusState, -}; -use crate::ibc::core::ics02_client::client_state::{ - AnyClientState, ClientState, -}; -use crate::ibc::core::ics02_client::client_type::ClientType; -use crate::ibc::core::ics02_client::events::{ - Attributes as ClientAttributes, CreateClient, UpdateClient, UpgradeClient, -}; -use crate::ibc::core::ics02_client::header::{AnyHeader, Header}; -use crate::ibc::core::ics02_client::height::Height; -use crate::ibc::core::ics02_client::msgs::create_client::MsgCreateAnyClient; -use crate::ibc::core::ics02_client::msgs::update_client::MsgUpdateAnyClient; -use crate::ibc::core::ics02_client::msgs::upgrade_client::MsgUpgradeAnyClient; -use crate::ibc::core::ics02_client::msgs::ClientMsg; -use crate::ibc::core::ics03_connection::connection::{ - ConnectionEnd, Counterparty as ConnCounterparty, State as ConnState, -}; -use crate::ibc::core::ics03_connection::events::{ - Attributes as ConnectionAttributes, OpenAck as ConnOpenAck, - OpenConfirm as ConnOpenConfirm, OpenInit as ConnOpenInit, - OpenTry as ConnOpenTry, -}; -use crate::ibc::core::ics03_connection::msgs::conn_open_ack::MsgConnectionOpenAck; -use crate::ibc::core::ics03_connection::msgs::conn_open_confirm::MsgConnectionOpenConfirm; -use crate::ibc::core::ics03_connection::msgs::conn_open_init::MsgConnectionOpenInit; -use crate::ibc::core::ics03_connection::msgs::conn_open_try::MsgConnectionOpenTry; -use crate::ibc::core::ics03_connection::msgs::ConnectionMsg; -use crate::ibc::core::ics04_channel::channel::{ - ChannelEnd, Counterparty as ChanCounterparty, Order, State as ChanState, -}; -use crate::ibc::core::ics04_channel::commitment::PacketCommitment; -use crate::ibc::core::ics04_channel::events::{ - AcknowledgePacket, CloseConfirm as ChanCloseConfirm, - CloseInit as ChanCloseInit, OpenAck as ChanOpenAck, - OpenConfirm as ChanOpenConfirm, OpenInit as ChanOpenInit, - OpenTry as ChanOpenTry, SendPacket, TimeoutPacket, WriteAcknowledgement, -}; -use crate::ibc::core::ics04_channel::msgs::acknowledgement::MsgAcknowledgement; -use crate::ibc::core::ics04_channel::msgs::chan_close_confirm::MsgChannelCloseConfirm; -use crate::ibc::core::ics04_channel::msgs::chan_close_init::MsgChannelCloseInit; -use crate::ibc::core::ics04_channel::msgs::chan_open_ack::MsgChannelOpenAck; -use crate::ibc::core::ics04_channel::msgs::chan_open_confirm::MsgChannelOpenConfirm; -use crate::ibc::core::ics04_channel::msgs::chan_open_init::MsgChannelOpenInit; -use crate::ibc::core::ics04_channel::msgs::chan_open_try::MsgChannelOpenTry; -use crate::ibc::core::ics04_channel::msgs::recv_packet::MsgRecvPacket; -use crate::ibc::core::ics04_channel::msgs::timeout::MsgTimeout; -use crate::ibc::core::ics04_channel::msgs::timeout_on_close::MsgTimeoutOnClose; -use crate::ibc::core::ics04_channel::msgs::{ChannelMsg, PacketMsg}; -use crate::ibc::core::ics04_channel::packet::{Packet, Sequence}; -use crate::ibc::core::ics23_commitment::commitment::CommitmentPrefix; -use crate::ibc::core::ics24_host::error::ValidationError as Ics24Error; -use crate::ibc::core::ics24_host::identifier::{ - ChannelId, ClientId, ConnectionId, PortChannelId, PortId, -}; -use crate::ibc::core::ics26_routing::msgs::Ics26Envelope; -use crate::ibc::events::IbcEvent; -#[cfg(any(feature = "ibc-mocks-abcipp", feature = "ibc-mocks"))] -use crate::ibc::mock::client_state::{MockClientState, MockConsensusState}; -use crate::ibc::timestamp::Timestamp; -use crate::ledger::ibc::data::{ - Error as IbcDataError, FungibleTokenPacketData, IbcMessage, PacketAck, - PacketReceipt, -}; -use crate::ledger::ibc::storage; -use crate::ledger::storage_api; -use crate::tendermint::Time; -use crate::tendermint_proto::{Error as ProtoError, Protobuf}; -use crate::types::address::{Address, InternalAddress}; -use crate::types::ibc::IbcEvent as NamadaIbcEvent; -use crate::types::storage::{BlockHeight, Key}; -use crate::types::time::Rfc3339String; -use crate::types::token::{self, Amount}; - -const COMMITMENT_PREFIX: &[u8] = b"ibc"; - -#[allow(missing_docs)] -#[derive(Error, Debug)] -pub enum Error { - #[error("Invalid client error: {0}")] - ClientId(Ics24Error), - #[error("Invalid port error: {0}")] - PortId(Ics24Error), - #[error("Updating a client error: {0}")] - ClientUpdate(String), - #[error("IBC data error: {0}")] - IbcData(IbcDataError), - #[error("Decoding IBC data error: {0}")] - Decoding(ProtoError), - #[error("Client error: {0}")] - Client(String), - #[error("Connection error: {0}")] - Connection(String), - #[error("Channel error: {0}")] - Channel(String), - #[error("Counter error: {0}")] - Counter(String), - #[error("Sequence error: {0}")] - Sequence(String), - #[error("Time error: {0}")] - Time(String), - #[error("Invalid transfer message: {0}")] - TransferMessage(token::TransferError), - #[error("Sending a token error: {0}")] - SendingToken(String), - #[error("Receiving a token error: {0}")] - ReceivingToken(String), - #[error("IBC storage error: {0}")] - IbcStorage(storage::Error), -} - -// This is needed to use `ibc::Handler::Error` with `IbcActions` in -// `tx_prelude/src/ibc.rs` -impl From for storage_api::Error { - fn from(err: Error) -> Self { - storage_api::Error::new(err) - } -} - -/// for handling IBC modules -pub type Result = std::result::Result; - -/// IBC trait to be implemented in integration that can read and write -pub trait IbcActions { - /// IBC action error - type Error: From; - - /// Read IBC-related data - fn read_ibc_data( - &self, - key: &Key, - ) -> std::result::Result>, Self::Error>; - - /// Write IBC-related data - fn write_ibc_data( - &mut self, - key: &Key, - data: impl AsRef<[u8]>, - ) -> std::result::Result<(), Self::Error>; - - /// Delete IBC-related data - fn delete_ibc_data( - &mut self, - key: &Key, - ) -> std::result::Result<(), Self::Error>; - - /// Emit an IBC event - fn emit_ibc_event( - &mut self, - event: NamadaIbcEvent, - ) -> std::result::Result<(), Self::Error>; - - /// Transfer token - fn transfer_token( - &mut self, - src: &Key, - dest: &Key, - amount: Amount, - ) -> std::result::Result<(), Self::Error>; - - /// Get the current height of this chain - fn get_height(&self) -> std::result::Result; - - /// Get the current time of the tendermint header of this chain - fn get_header_time( - &self, - ) -> std::result::Result; - - /// dispatch according to ICS26 routing - fn dispatch_ibc_action( - &mut self, - tx_data: &[u8], - ) -> std::result::Result<(), Self::Error> { - let ibc_msg = IbcMessage::decode(tx_data).map_err(Error::IbcData)?; - match &ibc_msg.0 { - Ics26Envelope::Ics2Msg(ics02_msg) => match ics02_msg { - ClientMsg::CreateClient(msg) => self.create_client(msg), - ClientMsg::UpdateClient(msg) => self.update_client(msg), - ClientMsg::Misbehaviour(_msg) => todo!(), - ClientMsg::UpgradeClient(msg) => self.upgrade_client(msg), - }, - Ics26Envelope::Ics3Msg(ics03_msg) => match ics03_msg { - ConnectionMsg::ConnectionOpenInit(msg) => { - self.init_connection(msg) - } - ConnectionMsg::ConnectionOpenTry(msg) => { - self.try_connection(msg) - } - ConnectionMsg::ConnectionOpenAck(msg) => { - self.ack_connection(msg) - } - ConnectionMsg::ConnectionOpenConfirm(msg) => { - self.confirm_connection(msg) - } - }, - Ics26Envelope::Ics4ChannelMsg(ics04_msg) => match ics04_msg { - ChannelMsg::ChannelOpenInit(msg) => self.init_channel(msg), - ChannelMsg::ChannelOpenTry(msg) => self.try_channel(msg), - ChannelMsg::ChannelOpenAck(msg) => self.ack_channel(msg), - ChannelMsg::ChannelOpenConfirm(msg) => { - self.confirm_channel(msg) - } - ChannelMsg::ChannelCloseInit(msg) => { - self.close_init_channel(msg) - } - ChannelMsg::ChannelCloseConfirm(msg) => { - self.close_confirm_channel(msg) - } - }, - Ics26Envelope::Ics4PacketMsg(ics04_msg) => match ics04_msg { - PacketMsg::AckPacket(msg) => self.acknowledge_packet(msg), - PacketMsg::RecvPacket(msg) => self.receive_packet(msg), - PacketMsg::ToPacket(msg) => self.timeout_packet(msg), - PacketMsg::ToClosePacket(msg) => { - self.timeout_on_close_packet(msg) - } - }, - Ics26Envelope::Ics20Msg(msg) => self.send_token(msg), - } - } - - /// Create a new client - fn create_client( - &mut self, - msg: &MsgCreateAnyClient, - ) -> std::result::Result<(), Self::Error> { - let counter_key = storage::client_counter_key(); - let counter = self.get_and_inc_counter(&counter_key)?; - let client_type = msg.client_state.client_type(); - let client_id = client_id(client_type, counter)?; - // client type - let client_type_key = storage::client_type_key(&client_id); - self.write_ibc_data(&client_type_key, client_type.as_str().as_bytes())?; - // client state - let client_state_key = storage::client_state_key(&client_id); - self.write_ibc_data( - &client_state_key, - msg.client_state - .encode_vec() - .expect("encoding shouldn't fail"), - )?; - // consensus state - let height = msg.client_state.latest_height(); - let consensus_state_key = - storage::consensus_state_key(&client_id, height); - self.write_ibc_data( - &consensus_state_key, - msg.consensus_state - .encode_vec() - .expect("encoding shouldn't fail"), - )?; - - self.set_client_update_time(&client_id)?; - - let event = make_create_client_event(&client_id, msg) - .try_into() - .unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Update a client - fn update_client( - &mut self, - msg: &MsgUpdateAnyClient, - ) -> std::result::Result<(), Self::Error> { - // get and update the client - let client_id = msg.client_id.clone(); - let client_state_key = storage::client_state_key(&client_id); - let value = - self.read_ibc_data(&client_state_key)?.ok_or_else(|| { - Error::Client(format!( - "The client to be updated doesn't exist: ID {}", - client_id - )) - })?; - let client_state = - AnyClientState::decode_vec(&value).map_err(Error::Decoding)?; - let (new_client_state, new_consensus_state) = - update_client(client_state, msg.header.clone())?; - - let height = new_client_state.latest_height(); - self.write_ibc_data( - &client_state_key, - new_client_state - .encode_vec() - .expect("encoding shouldn't fail"), - )?; - let consensus_state_key = - storage::consensus_state_key(&client_id, height); - self.write_ibc_data( - &consensus_state_key, - new_consensus_state - .encode_vec() - .expect("encoding shouldn't fail"), - )?; - - self.set_client_update_time(&client_id)?; - - let event = make_update_client_event(&client_id, msg) - .try_into() - .unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Upgrade a client - fn upgrade_client( - &mut self, - msg: &MsgUpgradeAnyClient, - ) -> std::result::Result<(), Self::Error> { - let client_state_key = storage::client_state_key(&msg.client_id); - let height = msg.client_state.latest_height(); - let consensus_state_key = - storage::consensus_state_key(&msg.client_id, height); - self.write_ibc_data( - &client_state_key, - msg.client_state - .encode_vec() - .expect("encoding shouldn't fail"), - )?; - self.write_ibc_data( - &consensus_state_key, - msg.consensus_state - .encode_vec() - .expect("encoding shouldn't fail"), - )?; - - self.set_client_update_time(&msg.client_id)?; - - let event = make_upgrade_client_event(&msg.client_id, msg) - .try_into() - .unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Initialize a connection for ConnectionOpenInit - fn init_connection( - &mut self, - msg: &MsgConnectionOpenInit, - ) -> std::result::Result<(), Self::Error> { - let counter_key = storage::connection_counter_key(); - let counter = self.get_and_inc_counter(&counter_key)?; - // new connection - let conn_id = connection_id(counter); - let conn_key = storage::connection_key(&conn_id); - let connection = init_connection(msg); - self.write_ibc_data( - &conn_key, - connection.encode_vec().expect("encoding shouldn't fail"), - )?; - - let event = make_open_init_connection_event(&conn_id, msg) - .try_into() - .unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Initialize a connection for ConnectionOpenTry - fn try_connection( - &mut self, - msg: &MsgConnectionOpenTry, - ) -> std::result::Result<(), Self::Error> { - let counter_key = storage::connection_counter_key(); - let counter = self.get_and_inc_counter(&counter_key)?; - // new connection - let conn_id = connection_id(counter); - let conn_key = storage::connection_key(&conn_id); - let connection = try_connection(msg); - self.write_ibc_data( - &conn_key, - connection.encode_vec().expect("encoding shouldn't fail"), - )?; - - let event = make_open_try_connection_event(&conn_id, msg) - .try_into() - .unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Open the connection for ConnectionOpenAck - fn ack_connection( - &mut self, - msg: &MsgConnectionOpenAck, - ) -> std::result::Result<(), Self::Error> { - let conn_key = storage::connection_key(&msg.connection_id); - let value = self.read_ibc_data(&conn_key)?.ok_or_else(|| { - Error::Connection(format!( - "The connection to be opened doesn't exist: ID {}", - msg.connection_id - )) - })?; - let mut connection = - ConnectionEnd::decode_vec(&value).map_err(Error::Decoding)?; - open_connection(&mut connection); - let mut counterparty = connection.counterparty().clone(); - counterparty.connection_id = - Some(msg.counterparty_connection_id.clone()); - connection.set_counterparty(counterparty); - self.write_ibc_data( - &conn_key, - connection.encode_vec().expect("encoding shouldn't fail"), - )?; - - let event = make_open_ack_connection_event(msg).try_into().unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Open the connection for ConnectionOpenConfirm - fn confirm_connection( - &mut self, - msg: &MsgConnectionOpenConfirm, - ) -> std::result::Result<(), Self::Error> { - let conn_key = storage::connection_key(&msg.connection_id); - let value = self.read_ibc_data(&conn_key)?.ok_or_else(|| { - Error::Connection(format!( - "The connection to be opend doesn't exist: ID {}", - msg.connection_id - )) - })?; - let mut connection = - ConnectionEnd::decode_vec(&value).map_err(Error::Decoding)?; - open_connection(&mut connection); - self.write_ibc_data( - &conn_key, - connection.encode_vec().expect("encoding shouldn't fail"), - )?; - - let event = make_open_confirm_connection_event(msg).try_into().unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Initialize a channel for ChannelOpenInit - fn init_channel( - &mut self, - msg: &MsgChannelOpenInit, - ) -> std::result::Result<(), Self::Error> { - self.bind_port(&msg.port_id)?; - let counter_key = storage::channel_counter_key(); - let counter = self.get_and_inc_counter(&counter_key)?; - let channel_id = channel_id(counter); - let port_channel_id = port_channel_id(msg.port_id.clone(), channel_id); - let channel_key = storage::channel_key(&port_channel_id); - self.write_ibc_data( - &channel_key, - msg.channel.encode_vec().expect("encoding shouldn't fail"), - )?; - - let event = make_open_init_channel_event(&channel_id, msg) - .try_into() - .unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Initialize a channel for ChannelOpenTry - fn try_channel( - &mut self, - msg: &MsgChannelOpenTry, - ) -> std::result::Result<(), Self::Error> { - self.bind_port(&msg.port_id)?; - let counter_key = storage::channel_counter_key(); - let counter = self.get_and_inc_counter(&counter_key)?; - let channel_id = channel_id(counter); - let port_channel_id = port_channel_id(msg.port_id.clone(), channel_id); - let channel_key = storage::channel_key(&port_channel_id); - self.write_ibc_data( - &channel_key, - msg.channel.encode_vec().expect("encoding shouldn't fail"), - )?; - - let event = make_open_try_channel_event(&channel_id, msg) - .try_into() - .unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Open the channel for ChannelOpenAck - fn ack_channel( - &mut self, - msg: &MsgChannelOpenAck, - ) -> std::result::Result<(), Self::Error> { - let port_channel_id = - port_channel_id(msg.port_id.clone(), msg.channel_id); - let channel_key = storage::channel_key(&port_channel_id); - let value = self.read_ibc_data(&channel_key)?.ok_or_else(|| { - Error::Channel(format!( - "The channel to be opened doesn't exist: Port/Channel {}", - port_channel_id - )) - })?; - let mut channel = - ChannelEnd::decode_vec(&value).map_err(Error::Decoding)?; - channel.set_counterparty_channel_id(msg.counterparty_channel_id); - open_channel(&mut channel); - self.write_ibc_data( - &channel_key, - channel.encode_vec().expect("encoding shouldn't fail"), - )?; - - let event = make_open_ack_channel_event(msg, &channel)? - .try_into() - .unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Open the channel for ChannelOpenConfirm - fn confirm_channel( - &mut self, - msg: &MsgChannelOpenConfirm, - ) -> std::result::Result<(), Self::Error> { - let port_channel_id = - port_channel_id(msg.port_id.clone(), msg.channel_id); - let channel_key = storage::channel_key(&port_channel_id); - let value = self.read_ibc_data(&channel_key)?.ok_or_else(|| { - Error::Channel(format!( - "The channel to be opened doesn't exist: Port/Channel {}", - port_channel_id - )) - })?; - let mut channel = - ChannelEnd::decode_vec(&value).map_err(Error::Decoding)?; - open_channel(&mut channel); - self.write_ibc_data( - &channel_key, - channel.encode_vec().expect("encoding shouldn't fail"), - )?; - - let event = make_open_confirm_channel_event(msg, &channel)? - .try_into() - .unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Close the channel for ChannelCloseInit - fn close_init_channel( - &mut self, - msg: &MsgChannelCloseInit, - ) -> std::result::Result<(), Self::Error> { - let port_channel_id = - port_channel_id(msg.port_id.clone(), msg.channel_id); - let channel_key = storage::channel_key(&port_channel_id); - let value = self.read_ibc_data(&channel_key)?.ok_or_else(|| { - Error::Channel(format!( - "The channel to be closed doesn't exist: Port/Channel {}", - port_channel_id - )) - })?; - let mut channel = - ChannelEnd::decode_vec(&value).map_err(Error::Decoding)?; - close_channel(&mut channel); - self.write_ibc_data( - &channel_key, - channel.encode_vec().expect("encoding shouldn't fail"), - )?; - - let event = make_close_init_channel_event(msg, &channel)? - .try_into() - .unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Close the channel for ChannelCloseConfirm - fn close_confirm_channel( - &mut self, - msg: &MsgChannelCloseConfirm, - ) -> std::result::Result<(), Self::Error> { - let port_channel_id = - port_channel_id(msg.port_id.clone(), msg.channel_id); - let channel_key = storage::channel_key(&port_channel_id); - let value = self.read_ibc_data(&channel_key)?.ok_or_else(|| { - Error::Channel(format!( - "The channel to be closed doesn't exist: Port/Channel {}", - port_channel_id - )) - })?; - let mut channel = - ChannelEnd::decode_vec(&value).map_err(Error::Decoding)?; - close_channel(&mut channel); - self.write_ibc_data( - &channel_key, - channel.encode_vec().expect("encoding shouldn't fail"), - )?; - - let event = make_close_confirm_channel_event(msg, &channel)? - .try_into() - .unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Send a packet - fn send_packet( - &mut self, - port_channel_id: PortChannelId, - data: Vec, - timeout_height: Height, - timeout_timestamp: Timestamp, - ) -> std::result::Result<(), Self::Error> { - // get and increment the next sequence send - let seq_key = storage::next_sequence_send_key(&port_channel_id); - let sequence = self.get_and_inc_sequence(&seq_key)?; - - // get the channel for the destination info. - let channel_key = storage::channel_key(&port_channel_id); - let channel = self - .read_ibc_data(&channel_key)? - .expect("cannot get the channel to be closed"); - let channel = - ChannelEnd::decode_vec(&channel).expect("cannot get the channel"); - let counterparty = channel.counterparty(); - - // make a packet - let packet = Packet { - sequence, - source_port: port_channel_id.port_id.clone(), - source_channel: port_channel_id.channel_id, - destination_port: counterparty.port_id.clone(), - destination_channel: *counterparty - .channel_id() - .expect("the counterparty channel should exist"), - data, - timeout_height, - timeout_timestamp, - }; - // store the commitment of the packet - let commitment_key = storage::commitment_key( - &port_channel_id.port_id, - &port_channel_id.channel_id, - packet.sequence, - ); - let commitment = commitment(&packet); - self.write_ibc_data(&commitment_key, commitment.into_vec())?; - - let event = make_send_packet_event(packet).try_into().unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Receive a packet - fn receive_packet( - &mut self, - msg: &MsgRecvPacket, - ) -> std::result::Result<(), Self::Error> { - // check the packet data - let packet_ack = - if let Ok(data) = serde_json::from_slice(&msg.packet.data) { - match self.receive_token(&msg.packet, &data) { - Ok(_) => PacketAck::result_success(), - Err(_) => PacketAck::result_error( - "receiving a token failed".to_string(), - ), - } - } else { - PacketAck::result_error("unknown packet data".to_string()) - }; - - // store the receipt - let receipt_key = storage::receipt_key( - &msg.packet.destination_port, - &msg.packet.destination_channel, - msg.packet.sequence, - ); - self.write_ibc_data(&receipt_key, PacketReceipt::default().as_bytes())?; - - // store the ack - let ack_key = storage::ack_key( - &msg.packet.destination_port, - &msg.packet.destination_channel, - msg.packet.sequence, - ); - let ack = packet_ack.encode_to_vec(); - let ack_commitment = sha2::Sha256::digest(&ack).to_vec(); - self.write_ibc_data(&ack_key, ack_commitment)?; - - // increment the next sequence receive - let port_channel_id = port_channel_id( - msg.packet.destination_port.clone(), - msg.packet.destination_channel, - ); - let seq_key = storage::next_sequence_recv_key(&port_channel_id); - self.get_and_inc_sequence(&seq_key)?; - - let event = make_write_ack_event(msg.packet.clone(), ack) - .try_into() - .unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Receive a acknowledgement - fn acknowledge_packet( - &mut self, - msg: &MsgAcknowledgement, - ) -> std::result::Result<(), Self::Error> { - let ack = PacketAck::try_from(msg.acknowledgement.clone()) - .map_err(Error::IbcData)?; - if !ack.is_success() { - if let Ok(data) = serde_json::from_slice(&msg.packet.data) { - self.refund_token(&msg.packet, &data)?; - } - } - - let commitment_key = storage::commitment_key( - &msg.packet.source_port, - &msg.packet.source_channel, - msg.packet.sequence, - ); - self.delete_ibc_data(&commitment_key)?; - - // get and increment the next sequence ack - let port_channel_id = port_channel_id( - msg.packet.source_port.clone(), - msg.packet.source_channel, - ); - let seq_key = storage::next_sequence_ack_key(&port_channel_id); - self.get_and_inc_sequence(&seq_key)?; - - let event = make_ack_event(msg.packet.clone()).try_into().unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Receive a timeout - fn timeout_packet( - &mut self, - msg: &MsgTimeout, - ) -> std::result::Result<(), Self::Error> { - // check the packet data - if let Ok(data) = serde_json::from_slice(&msg.packet.data) { - self.refund_token(&msg.packet, &data)?; - } - - // delete the commitment of the packet - let commitment_key = storage::commitment_key( - &msg.packet.source_port, - &msg.packet.source_channel, - msg.packet.sequence, - ); - self.delete_ibc_data(&commitment_key)?; - - // close the channel - let port_channel_id = port_channel_id( - msg.packet.source_port.clone(), - msg.packet.source_channel, - ); - let channel_key = storage::channel_key(&port_channel_id); - let value = self.read_ibc_data(&channel_key)?.ok_or_else(|| { - Error::Channel(format!( - "The channel to be closed doesn't exist: Port/Channel {}", - port_channel_id - )) - })?; - let mut channel = - ChannelEnd::decode_vec(&value).map_err(Error::Decoding)?; - if channel.order_matches(&Order::Ordered) { - close_channel(&mut channel); - self.write_ibc_data( - &channel_key, - channel.encode_vec().expect("encoding shouldn't fail"), - )?; - } - - let event = make_timeout_event(msg.packet.clone()).try_into().unwrap(); - self.emit_ibc_event(event)?; - - Ok(()) - } - - /// Receive a timeout for TimeoutOnClose - fn timeout_on_close_packet( - &mut self, - msg: &MsgTimeoutOnClose, - ) -> std::result::Result<(), Self::Error> { - // check the packet data - if let Ok(data) = serde_json::from_slice(&msg.packet.data) { - self.refund_token(&msg.packet, &data)?; - } - - // delete the commitment of the packet - let commitment_key = storage::commitment_key( - &msg.packet.source_port, - &msg.packet.source_channel, - msg.packet.sequence, - ); - self.delete_ibc_data(&commitment_key)?; - - // close the channel - let port_channel_id = port_channel_id( - msg.packet.source_port.clone(), - msg.packet.source_channel, - ); - let channel_key = storage::channel_key(&port_channel_id); - let value = self.read_ibc_data(&channel_key)?.ok_or_else(|| { - Error::Channel(format!( - "The channel to be closed doesn't exist: Port/Channel {}", - port_channel_id - )) - })?; - let mut channel = - ChannelEnd::decode_vec(&value).map_err(Error::Decoding)?; - if channel.order_matches(&Order::Ordered) { - close_channel(&mut channel); - self.write_ibc_data( - &channel_key, - channel.encode_vec().expect("encoding shouldn't fail"), - )?; - } - - Ok(()) - } - - /// Set the timestamp and the height for the client update - fn set_client_update_time( - &mut self, - client_id: &ClientId, - ) -> std::result::Result<(), Self::Error> { - let time = Time::parse_from_rfc3339(&self.get_header_time()?.0) - .map_err(|e| { - Error::Time(format!("The time of the header is invalid: {}", e)) - })?; - let key = storage::client_update_timestamp_key(client_id); - self.write_ibc_data( - &key, - time.encode_vec().expect("encoding shouldn't fail"), - )?; - - // the revision number is always 0 - let height = Height::new(0, self.get_height()?.0); - let height_key = storage::client_update_height_key(client_id); - // write the current height as u64 - self.write_ibc_data( - &height_key, - height.encode_vec().expect("Encoding shouldn't fail"), - )?; - - Ok(()) - } - - /// Get and increment the counter - fn get_and_inc_counter( - &mut self, - key: &Key, - ) -> std::result::Result { - let value = self.read_ibc_data(key)?.ok_or_else(|| { - Error::Counter(format!("The counter doesn't exist: {}", key)) - })?; - let value: [u8; 8] = value.try_into().map_err(|_| { - Error::Counter(format!("The counter value wasn't u64: Key {}", key)) - })?; - let counter = u64::from_be_bytes(value); - self.write_ibc_data(key, (counter + 1).to_be_bytes())?; - Ok(counter) - } - - /// Get and increment the sequence - fn get_and_inc_sequence( - &mut self, - key: &Key, - ) -> std::result::Result { - let index = match self.read_ibc_data(key)? { - Some(v) => { - let index: [u8; 8] = v.try_into().map_err(|_| { - Error::Sequence(format!( - "The sequence index wasn't u64: Key {}", - key - )) - })?; - u64::from_be_bytes(index) - } - // when the sequence has never been used, returns the initial value - None => 1, - }; - self.write_ibc_data(key, (index + 1).to_be_bytes())?; - Ok(index.into()) - } - - /// Bind a new port - fn bind_port( - &mut self, - port_id: &PortId, - ) -> std::result::Result<(), Self::Error> { - let port_key = storage::port_key(port_id); - match self.read_ibc_data(&port_key)? { - Some(_) => {} - None => { - // create a new capability and claim it - let index_key = storage::capability_index_key(); - let cap_index = self.get_and_inc_counter(&index_key)?; - self.write_ibc_data(&port_key, cap_index.to_be_bytes())?; - let cap_key = storage::capability_key(cap_index); - self.write_ibc_data(&cap_key, port_id.as_bytes())?; - } - } - Ok(()) - } - - /// Send the specified token by escrowing or burning - fn send_token( - &mut self, - msg: &MsgTransfer, - ) -> std::result::Result<(), Self::Error> { - let mut data = FungibleTokenPacketData::from(msg.clone()); - if let Some(hash) = storage::token_hash_from_denom(&data.denom) - .map_err(Error::IbcStorage)? - { - let denom_key = storage::ibc_denom_key(hash); - let denom_bytes = - self.read_ibc_data(&denom_key)?.ok_or_else(|| { - Error::SendingToken(format!( - "No original denom: denom_key {}", - denom_key - )) - })?; - let denom = std::str::from_utf8(&denom_bytes).map_err(|e| { - Error::SendingToken(format!( - "Decoding the denom failed: denom_key {}, error {}", - denom_key, e - )) - })?; - data.denom = denom.to_string(); - } - let token = storage::token(&data.denom).map_err(Error::IbcStorage)?; - let amount = Amount::from_str(&data.amount, 0).map_err(|e| { - Error::SendingToken(format!( - "Invalid amount: amount {}, error {}", - data.amount, e - )) - })?; - - let source_addr = Address::decode(&data.sender).map_err(|e| { - Error::SendingToken(format!( - "Invalid sender address: sender {}, error {}", - data.sender, e - )) - })?; - - // check the denomination field - let prefix = format!( - "{}/{}/", - msg.source_port.clone(), - msg.source_channel.clone() - ); - let (source, target) = if data.denom.starts_with(&prefix) { - // the receiver's chain was the source - // transfer from the origin-specific account of the token - let key_prefix = storage::ibc_token_prefix(&data.denom) - .map_err(Error::IbcStorage)?; - let src = token::multitoken_balance_key(&key_prefix, &source_addr); - - let key_prefix = storage::ibc_account_prefix( - &msg.source_port, - &msg.source_channel, - &token, - ); - let burn = token::multitoken_balance_key( - &key_prefix, - &Address::Internal(InternalAddress::IbcBurn), - ); - (src, burn) - } else { - // this chain is the source - // escrow the amount of the token - let src = if data.denom == token.to_string() { - token::balance_key(&token, &source_addr) - } else { - let key_prefix = storage::ibc_token_prefix(&data.denom) - .map_err(Error::IbcStorage)?; - token::multitoken_balance_key(&key_prefix, &source_addr) - }; - - let key_prefix = storage::ibc_account_prefix( - &msg.source_port, - &msg.source_channel, - &token, - ); - let escrow = token::multitoken_balance_key( - &key_prefix, - &Address::Internal(InternalAddress::IbcEscrow), - ); - (src, escrow) - }; - self.transfer_token(&source, &target, amount)?; - - // send a packet - let port_channel_id = - port_channel_id(msg.source_port.clone(), msg.source_channel); - let packet_data = serde_json::to_vec(&data) - .expect("encoding the packet data shouldn't fail"); - self.send_packet( - port_channel_id, - packet_data, - msg.timeout_height, - msg.timeout_timestamp, - ) - } - - /// Receive the specified token by unescrowing or minting - fn receive_token( - &mut self, - packet: &Packet, - data: &FungibleTokenPacketData, - ) -> std::result::Result<(), Self::Error> { - let token = storage::token(&data.denom).map_err(Error::IbcStorage)?; - let amount = Amount::from_str(&data.amount, 0).map_err(|e| { - Error::ReceivingToken(format!( - "Invalid amount: amount {}, error {}", - data.amount, e - )) - })?; - // The receiver should be an address because the origin-specific account - // key should be assigned internally - let dest_addr = Address::decode(&data.receiver).map_err(|e| { - Error::ReceivingToken(format!( - "Invalid receiver address: receiver {}, error {}", - data.receiver, e - )) - })?; - - let prefix = format!( - "{}/{}/", - packet.source_port.clone(), - packet.source_channel.clone() - ); - let (source, target) = match data.denom.strip_prefix(&prefix) { - Some(denom) => { - // unescrow the token because this chain was the source - let escrow_prefix = storage::ibc_account_prefix( - &packet.destination_port, - &packet.destination_channel, - &token, - ); - let escrow = token::multitoken_balance_key( - &escrow_prefix, - &Address::Internal(InternalAddress::IbcEscrow), - ); - let dest = if denom == token.to_string() { - token::balance_key(&token, &dest_addr) - } else { - let key_prefix = storage::ibc_token_prefix(denom) - .map_err(Error::IbcStorage)?; - token::multitoken_balance_key(&key_prefix, &dest_addr) - }; - (escrow, dest) - } - None => { - // mint the token because the sender chain is the source - let key_prefix = storage::ibc_account_prefix( - &packet.destination_port, - &packet.destination_channel, - &token, - ); - let mint = token::multitoken_balance_key( - &key_prefix, - &Address::Internal(InternalAddress::IbcMint), - ); - - // prefix the denom with the this chain port and channel - let denom = format!( - "{}/{}/{}", - &packet.destination_port, - &packet.destination_channel, - &data.denom - ); - let key_prefix = storage::ibc_token_prefix(&denom) - .map_err(Error::IbcStorage)?; - let dest = - token::multitoken_balance_key(&key_prefix, &dest_addr); - - // store the prefixed denom - let token_hash = storage::calc_hash(&denom); - let denom_key = storage::ibc_denom_key(token_hash); - self.write_ibc_data(&denom_key, denom.as_bytes())?; - - (mint, dest) - } - }; - self.transfer_token(&source, &target, amount)?; - - Ok(()) - } - - /// Refund the specified token by unescrowing or minting - fn refund_token( - &mut self, - packet: &Packet, - data: &FungibleTokenPacketData, - ) -> std::result::Result<(), Self::Error> { - let token = storage::token(&data.denom).map_err(Error::IbcStorage)?; - let amount = Amount::from_str(&data.amount, 0).map_err(|e| { - Error::ReceivingToken(format!( - "Invalid amount: amount {}, error {}", - data.amount, e - )) - })?; - - let dest_addr = Address::decode(&data.sender).map_err(|e| { - Error::SendingToken(format!( - "Invalid sender address: sender {}, error {}", - data.sender, e - )) - })?; - - let prefix = format!( - "{}/{}/", - packet.source_port.clone(), - packet.source_channel.clone() - ); - let (source, target) = if data.denom.starts_with(&prefix) { - // mint the token because the amount was burned - let key_prefix = storage::ibc_account_prefix( - &packet.source_port, - &packet.source_channel, - &token, - ); - let mint = token::multitoken_balance_key( - &key_prefix, - &Address::Internal(InternalAddress::IbcMint), - ); - let key_prefix = storage::ibc_token_prefix(&data.denom) - .map_err(Error::IbcStorage)?; - let dest = token::multitoken_balance_key(&key_prefix, &dest_addr); - (mint, dest) - } else { - // unescrow the token because the acount was escrowed - let dest = if data.denom == token.to_string() { - token::balance_key(&token, &dest_addr) - } else { - let key_prefix = storage::ibc_token_prefix(&data.denom) - .map_err(Error::IbcStorage)?; - token::multitoken_balance_key(&key_prefix, &dest_addr) - }; - - let key_prefix = storage::ibc_account_prefix( - &packet.source_port, - &packet.source_channel, - &token, - ); - let escrow = token::multitoken_balance_key( - &key_prefix, - &Address::Internal(InternalAddress::IbcEscrow), - ); - (escrow, dest) - }; - self.transfer_token(&source, &target, amount)?; - - Ok(()) - } -} - -/// Update a client with the given state and headers -pub fn update_client( - client_state: AnyClientState, - header: AnyHeader, -) -> Result<(AnyClientState, AnyConsensusState)> { - match client_state { - AnyClientState::Tendermint(cs) => match header { - AnyHeader::Tendermint(h) => { - let new_client_state = cs.with_header(h.clone()).wrap_any(); - let new_consensus_state = TmConsensusState::from(h).wrap_any(); - Ok((new_client_state, new_consensus_state)) - } - #[cfg(any(feature = "ibc-mocks-abcipp", feature = "ibc-mocks"))] - _ => Err(Error::ClientUpdate( - "The header type is mismatched".to_owned(), - )), - }, - #[cfg(any(feature = "ibc-mocks-abcipp", feature = "ibc-mocks"))] - AnyClientState::Mock(_) => match header { - AnyHeader::Mock(h) => Ok(( - MockClientState::new(h).wrap_any(), - MockConsensusState::new(h).wrap_any(), - )), - _ => Err(Error::ClientUpdate( - "The header type is mismatched".to_owned(), - )), - }, - } -} - -/// Returns a new client ID -pub fn client_id(client_type: ClientType, counter: u64) -> Result { - ClientId::new(client_type, counter).map_err(Error::ClientId) -} - -/// Returns a new connection ID -pub fn connection_id(counter: u64) -> ConnectionId { - ConnectionId::new(counter) -} - -/// Make a connection end from the init message -pub fn init_connection(msg: &MsgConnectionOpenInit) -> ConnectionEnd { - ConnectionEnd::new( - ConnState::Init, - msg.client_id.clone(), - msg.counterparty.clone(), - vec![msg.version.clone().unwrap_or_default()], - msg.delay_period, - ) -} - -/// Make a connection end from the try message -pub fn try_connection(msg: &MsgConnectionOpenTry) -> ConnectionEnd { - ConnectionEnd::new( - ConnState::TryOpen, - msg.client_id.clone(), - msg.counterparty.clone(), - msg.counterparty_versions.clone(), - msg.delay_period, - ) -} - -/// Open the connection -pub fn open_connection(conn: &mut ConnectionEnd) { - conn.set_state(ConnState::Open); -} - -/// Returns a new channel ID -pub fn channel_id(counter: u64) -> ChannelId { - ChannelId::new(counter) -} - -/// Open the channel -pub fn open_channel(channel: &mut ChannelEnd) { - channel.set_state(ChanState::Open); -} - -/// Close the channel -pub fn close_channel(channel: &mut ChannelEnd) { - channel.set_state(ChanState::Closed); -} - -/// Returns a port ID -pub fn port_id(id: &str) -> Result { - PortId::from_str(id).map_err(Error::PortId) -} - -/// Returns a pair of port ID and channel ID -pub fn port_channel_id( - port_id: PortId, - channel_id: ChannelId, -) -> PortChannelId { - PortChannelId { - port_id, - channel_id, - } -} - -/// Returns a sequence -pub fn sequence(index: u64) -> Sequence { - Sequence::from(index) -} - -/// Make a packet from MsgTransfer -pub fn packet_from_message( - msg: &MsgTransfer, - sequence: Sequence, - counterparty: &ChanCounterparty, -) -> Packet { - Packet { - sequence, - source_port: msg.source_port.clone(), - source_channel: msg.source_channel, - destination_port: counterparty.port_id.clone(), - destination_channel: *counterparty - .channel_id() - .expect("the counterparty channel should exist"), - data: serde_json::to_vec(&FungibleTokenPacketData::from(msg.clone())) - .expect("encoding the packet data shouldn't fail"), - timeout_height: msg.timeout_height, - timeout_timestamp: msg.timeout_timestamp, - } -} - -/// Returns a commitment from the given packet -pub fn commitment(packet: &Packet) -> PacketCommitment { - let timeout = packet.timeout_timestamp.nanoseconds().to_be_bytes(); - let revision_number = packet.timeout_height.revision_number.to_be_bytes(); - let revision_height = packet.timeout_height.revision_height.to_be_bytes(); - let data = sha2::Sha256::digest(&packet.data); - let input = [ - &timeout, - &revision_number, - &revision_height, - data.as_slice(), - ] - .concat(); - sha2::Sha256::digest(&input).to_vec().into() -} - -/// Returns a counterparty of a connection -pub fn connection_counterparty( - client_id: ClientId, - conn_id: ConnectionId, -) -> ConnCounterparty { - ConnCounterparty::new(client_id, Some(conn_id), commitment_prefix()) -} - -/// Returns a counterparty of a channel -pub fn channel_counterparty( - port_id: PortId, - channel_id: ChannelId, -) -> ChanCounterparty { - ChanCounterparty::new(port_id, Some(channel_id)) -} - -/// Returns Namada commitment prefix -pub fn commitment_prefix() -> CommitmentPrefix { - CommitmentPrefix::try_from(COMMITMENT_PREFIX.to_vec()) - .expect("the conversion shouldn't fail") -} - -/// Makes CreateClient event -pub fn make_create_client_event( - client_id: &ClientId, - msg: &MsgCreateAnyClient, -) -> IbcEvent { - let attributes = ClientAttributes { - client_id: client_id.clone(), - client_type: msg.client_state.client_type(), - consensus_height: msg.client_state.latest_height(), - ..Default::default() - }; - IbcEvent::CreateClient(CreateClient::from(attributes)) -} - -/// Makes UpdateClient event -pub fn make_update_client_event( - client_id: &ClientId, - msg: &MsgUpdateAnyClient, -) -> IbcEvent { - let attributes = ClientAttributes { - client_id: client_id.clone(), - client_type: msg.header.client_type(), - consensus_height: msg.header.height(), - ..Default::default() - }; - IbcEvent::UpdateClient(UpdateClient::from(attributes)) -} - -/// Makes UpgradeClient event -pub fn make_upgrade_client_event( - client_id: &ClientId, - msg: &MsgUpgradeAnyClient, -) -> IbcEvent { - let attributes = ClientAttributes { - client_id: client_id.clone(), - client_type: msg.client_state.client_type(), - consensus_height: msg.client_state.latest_height(), - ..Default::default() - }; - IbcEvent::UpgradeClient(UpgradeClient::from(attributes)) -} - -/// Makes OpenInitConnection event -pub fn make_open_init_connection_event( - conn_id: &ConnectionId, - msg: &MsgConnectionOpenInit, -) -> IbcEvent { - let attributes = ConnectionAttributes { - connection_id: Some(conn_id.clone()), - client_id: msg.client_id.clone(), - counterparty_connection_id: msg.counterparty.connection_id().cloned(), - counterparty_client_id: msg.counterparty.client_id().clone(), - ..Default::default() - }; - ConnOpenInit::from(attributes).into() -} - -/// Makes OpenTryConnection event -pub fn make_open_try_connection_event( - conn_id: &ConnectionId, - msg: &MsgConnectionOpenTry, -) -> IbcEvent { - let attributes = ConnectionAttributes { - connection_id: Some(conn_id.clone()), - client_id: msg.client_id.clone(), - counterparty_connection_id: msg.counterparty.connection_id().cloned(), - counterparty_client_id: msg.counterparty.client_id().clone(), - ..Default::default() - }; - ConnOpenTry::from(attributes).into() -} - -/// Makes OpenAckConnection event -pub fn make_open_ack_connection_event(msg: &MsgConnectionOpenAck) -> IbcEvent { - let attributes = ConnectionAttributes { - connection_id: Some(msg.connection_id.clone()), - counterparty_connection_id: Some( - msg.counterparty_connection_id.clone(), - ), - ..Default::default() - }; - ConnOpenAck::from(attributes).into() -} - -/// Makes OpenConfirmConnection event -pub fn make_open_confirm_connection_event( - msg: &MsgConnectionOpenConfirm, -) -> IbcEvent { - let attributes = ConnectionAttributes { - connection_id: Some(msg.connection_id.clone()), - ..Default::default() - }; - ConnOpenConfirm::from(attributes).into() -} - -/// Makes OpenInitChannel event -pub fn make_open_init_channel_event( - channel_id: &ChannelId, - msg: &MsgChannelOpenInit, -) -> IbcEvent { - let connection_id = match msg.channel.connection_hops().get(0) { - Some(c) => c.clone(), - None => ConnectionId::default(), - }; - let attributes = ChanOpenInit { - height: Height::default(), - port_id: msg.port_id.clone(), - channel_id: Some(*channel_id), - connection_id, - counterparty_port_id: msg.channel.counterparty().port_id().clone(), - counterparty_channel_id: msg - .channel - .counterparty() - .channel_id() - .cloned(), - }; - attributes.into() -} - -/// Makes OpenTryChannel event -pub fn make_open_try_channel_event( - channel_id: &ChannelId, - msg: &MsgChannelOpenTry, -) -> IbcEvent { - let connection_id = match msg.channel.connection_hops().get(0) { - Some(c) => c.clone(), - None => ConnectionId::default(), - }; - let attributes = ChanOpenTry { - height: Height::default(), - port_id: msg.port_id.clone(), - channel_id: Some(*channel_id), - connection_id, - counterparty_port_id: msg.channel.counterparty().port_id().clone(), - counterparty_channel_id: msg - .channel - .counterparty() - .channel_id() - .cloned(), - }; - attributes.into() -} - -/// Makes OpenAckChannel event -pub fn make_open_ack_channel_event( - msg: &MsgChannelOpenAck, - channel: &ChannelEnd, -) -> Result { - let conn_id = get_connection_id_from_channel(channel)?; - let counterparty = channel.counterparty(); - let attributes = ChanOpenAck { - height: Height::default(), - port_id: msg.port_id.clone(), - channel_id: Some(msg.channel_id), - counterparty_channel_id: Some(msg.counterparty_channel_id), - connection_id: conn_id.clone(), - counterparty_port_id: counterparty.port_id().clone(), - }; - Ok(attributes.into()) -} - -/// Makes OpenConfirmChannel event -pub fn make_open_confirm_channel_event( - msg: &MsgChannelOpenConfirm, - channel: &ChannelEnd, -) -> Result { - let conn_id = get_connection_id_from_channel(channel)?; - let counterparty = channel.counterparty(); - let attributes = ChanOpenConfirm { - height: Height::default(), - port_id: msg.port_id.clone(), - channel_id: Some(msg.channel_id), - connection_id: conn_id.clone(), - counterparty_port_id: counterparty.port_id().clone(), - counterparty_channel_id: counterparty.channel_id().cloned(), - }; - Ok(attributes.into()) -} - -/// Makes CloseInitChannel event -pub fn make_close_init_channel_event( - msg: &MsgChannelCloseInit, - channel: &ChannelEnd, -) -> Result { - let conn_id = get_connection_id_from_channel(channel)?; - let counterparty = channel.counterparty(); - let attributes = ChanCloseInit { - height: Height::default(), - port_id: msg.port_id.clone(), - channel_id: msg.channel_id, - connection_id: conn_id.clone(), - counterparty_port_id: counterparty.port_id().clone(), - counterparty_channel_id: counterparty.channel_id().cloned(), - }; - Ok(attributes.into()) -} - -/// Makes CloseConfirmChannel event -pub fn make_close_confirm_channel_event( - msg: &MsgChannelCloseConfirm, - channel: &ChannelEnd, -) -> Result { - let conn_id = get_connection_id_from_channel(channel)?; - let counterparty = channel.counterparty(); - let attributes = ChanCloseConfirm { - height: Height::default(), - port_id: msg.port_id.clone(), - channel_id: Some(msg.channel_id), - connection_id: conn_id.clone(), - counterparty_port_id: counterparty.port_id.clone(), - counterparty_channel_id: counterparty.channel_id().cloned(), - }; - Ok(attributes.into()) -} - -fn get_connection_id_from_channel( - channel: &ChannelEnd, -) -> Result<&ConnectionId> { - channel.connection_hops().get(0).ok_or_else(|| { - Error::Channel("No connection for the channel".to_owned()) - }) -} - -/// Makes SendPacket event -pub fn make_send_packet_event(packet: Packet) -> IbcEvent { - IbcEvent::SendPacket(SendPacket { - height: packet.timeout_height, - packet, - }) -} - -/// Makes WriteAcknowledgement event -pub fn make_write_ack_event(packet: Packet, ack: Vec) -> IbcEvent { - IbcEvent::WriteAcknowledgement(WriteAcknowledgement { - // this height is not used - height: Height::default(), - packet, - ack, - }) -} - -/// Makes AcknowledgePacket event -pub fn make_ack_event(packet: Packet) -> IbcEvent { - IbcEvent::AcknowledgePacket(AcknowledgePacket { - // this height is not used - height: Height::default(), - packet, - }) -} - -/// Makes TimeoutPacket event -pub fn make_timeout_event(packet: Packet) -> IbcEvent { - IbcEvent::TimeoutPacket(TimeoutPacket { - // this height is not used - height: Height::default(), - packet, - }) -} diff --git a/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs b/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs index 8ef07a64a5..0052fb01b1 100644 --- a/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs +++ b/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs @@ -3,7 +3,7 @@ use std::collections::{BTreeSet, HashSet}; use std::str::FromStr; -use borsh::{BorshDeserialize, BorshSerialize}; +use borsh::BorshDeserialize; use eyre::{Result, WrapErr}; use namada_core::hints; use namada_core::ledger::eth_bridge::storage::bridge_pool::{ @@ -26,7 +26,7 @@ use namada_core::types::ethereum_events::{ }; use namada_core::types::storage::{BlockHeight, Key, KeySeg}; use namada_core::types::token; -use namada_core::types::token::{balance_key, minted_balance_key, minter_key}; +use namada_core::types::token::{balance_key, minted_balance_key}; use crate::parameters::read_native_erc20_address; use crate::protocol::transactions::update; @@ -265,9 +265,8 @@ where })?; _ = changed_keys.insert(supply_key); - let minter_key = minter_key(&token); - wl_storage.write_bytes(&minter_key, BRIDGE_POOL_ADDRESS.try_to_vec()?)?; - _ = changed_keys.insert(minter_key); + // mint the token without a minter because a protocol tx doesn't need to + // trigger a VP Ok(changed_keys) } @@ -757,7 +756,7 @@ mod tests { assert_eq!( stored_keys_count(&wl_storage), - initial_stored_keys_count + 3 + initial_stored_keys_count + 2 ); } @@ -789,7 +788,7 @@ mod tests { assert_eq!( stored_keys_count(&wl_storage), - initial_stored_keys_count + 3 + initial_stored_keys_count + 2 ); let expected_amount = amount.try_to_vec().unwrap(); diff --git a/ethereum_bridge/src/protocol/transactions/ethereum_events/mod.rs b/ethereum_bridge/src/protocol/transactions/ethereum_events/mod.rs index 29fd07a8e7..d3cd32972d 100644 --- a/ethereum_bridge/src/protocol/transactions/ethereum_events/mod.rs +++ b/ethereum_bridge/src/protocol/transactions/ethereum_events/mod.rs @@ -284,9 +284,7 @@ mod tests { use namada_core::types::ethereum_events::{ EthereumEvent, TransferToNamada, }; - use namada_core::types::token::{ - balance_key, minted_balance_key, minter_key, Amount, - }; + use namada_core::types::token::{balance_key, minted_balance_key, Amount}; use super::*; use crate::protocol::transactions::utils::GetVoters; @@ -347,7 +345,6 @@ mod tests { eth_msg_keys.voting_started_epoch(), balance_key(&wrapped_erc20_token, &receiver), minted_balance_key(&wrapped_erc20_token), - minter_key(&wrapped_erc20_token), ]), changed_keys ); @@ -449,7 +446,6 @@ mod tests { eth_msg_keys.voting_started_epoch(), balance_key(&dai_token, &receiver), minted_balance_key(&dai_token), - minter_key(&dai_token), ]) ); assert!(tx_result.vps_result.accepted_vps.is_empty()); diff --git a/shared/src/ledger/native_vp/multitoken.rs b/shared/src/ledger/native_vp/multitoken.rs index 3e2a0b84df..b3d93e5170 100644 --- a/shared/src/ledger/native_vp/multitoken.rs +++ b/shared/src/ledger/native_vp/multitoken.rs @@ -120,9 +120,8 @@ where }; match token { Address::Internal(InternalAddress::Erc20(_)) => { - if minter == Address::Internal(InternalAddress::EthBridge) { - return Ok(Some(minter)); - } + // ERC20 token should not be minted by a wasm transaction + return Ok(None); } Address::Internal(InternalAddress::IbcToken(_)) => { if minter == Address::Internal(InternalAddress::Ibc) { @@ -155,6 +154,7 @@ mod tests { }; use crate::eth_bridge::storage::wrapped_erc20s; use crate::ledger::gas::VpGasMeter; + use crate::ledger::ibc::storage::ibc_token; use crate::proto::{Code, Data, Section, Signature, Tx}; use crate::types::address::{Address, InternalAddress}; use crate::types::ethereum_events::testing::arbitrary_eth_address; @@ -294,8 +294,8 @@ mod tests { let mut wl_storage = TestWlStorage::default(); let mut keys_changed = BTreeSet::new(); - // ERC20 token - let token = wrapped_erc20s::token(&arbitrary_eth_address()); + // IBC token + let token = ibc_token("/port-42/channel-42/denom"); // mint 100 let target = established_address_1(); @@ -315,7 +315,7 @@ mod tests { keys_changed.insert(minted_key); // minter - let minter = Address::Internal(InternalAddress::EthBridge); + let minter = Address::Internal(InternalAddress::Ibc); let minter_key = minter_key(&token); wl_storage .write_log diff --git a/shared/src/ledger/queries/shell/eth_bridge.rs b/shared/src/ledger/queries/shell/eth_bridge.rs index 55e5255b62..d7d0ed249e 100644 --- a/shared/src/ledger/queries/shell/eth_bridge.rs +++ b/shared/src/ledger/queries/shell/eth_bridge.rs @@ -123,12 +123,11 @@ where "The Ethereum bridge storage is not initialized", )); }; - if asset == native_erc20 { - return Err(storage_api::Error::SimpleMessage( - "Wrapped NAM's supply is not kept track of", - )); - } - let token = wrapped_erc20s::token(&asset); + let token = if asset == native_erc20 { + ctx.wl_storage.storage.native_token.clone() + } else { + wrapped_erc20s::token(&asset) + }; ctx.wl_storage.read(&minted_balance_key(&token)) } @@ -1365,15 +1364,26 @@ mod test_ethbridge_router { assert!(resp.is_err()); } - /// Test that reading the wrapped NAM supply fails. + /// Test reading the wrapped NAM supply #[tokio::test] - async fn test_read_wnam_supply_fails() { + async fn test_read_wnam_supply() { let mut client = TestClient::new(RPC); assert_eq!(client.wl_storage.storage.last_epoch.0, 0); // initialize storage test_utils::init_default_storage(&mut client.wl_storage); + let native_erc20 = + read_native_erc20_address(&client.wl_storage).expect("Test failed"); + + // write tokens to storage + let amount = Amount::native_whole(12345); + let token = &client.wl_storage.storage.native_token; + client + .wl_storage + .write(&minted_balance_key(token), amount) + .expect("Test failed"); + // commit the changes client .wl_storage @@ -1382,21 +1392,12 @@ mod test_ethbridge_router { .expect("Test failed"); // check that reading wrapped NAM fails - let native_erc20 = - read_native_erc20_address(&client.wl_storage).expect("Test failed"); let result = RPC .shell() .eth_bridge() .read_erc20_supply(&client, &native_erc20) .await; - let Err(err) = result else { - panic!("Test failed"); - }; - - assert_eq!( - err.to_string(), - "Wrapped NAM's supply is not kept track of" - ); + assert_matches!(result, Ok(Some(a)) if a == amount); } /// Test reading the supply of an ERC20 token. diff --git a/wasm/wasm_source/src/tx_bridge_pool.rs b/wasm/wasm_source/src/tx_bridge_pool.rs index 1dbb86adb7..bf73e83f7d 100644 --- a/wasm/wasm_source/src/tx_bridge_pool.rs +++ b/wasm/wasm_source/src/tx_bridge_pool.rs @@ -46,13 +46,12 @@ fn apply_tx(ctx: &mut Ctx, signed: Tx) -> TxResult { } else { // Otherwise we escrow ERC20 tokens. let token = wrapped_erc20s::token(&asset); - let amount = amount.denominated(ð_bridge::ADDRESS, ctx)?; token::transfer( ctx, sender, &bridge_pool::BRIDGE_POOL_ADDRESS, &token, - amount, + amount.native_denominated(), &None, &None, &None, From 93b6679c5f74d1ce361f86b2619a0244742d83f0 Mon Sep 17 00:00:00 2001 From: yito88 Date: Mon, 10 Jul 2023 22:29:21 +0200 Subject: [PATCH 046/120] add comments --- apps/src/lib/client/rpc.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index eb03de78d9..dd18aa8520 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -520,15 +520,20 @@ async fn print_balances( ), None => continue, }; - // Get the token and the balance let (t, s) = match (token, target) { + // the given token and the given target are the same as the + // retrieved ones (Some(token), Some(target)) if t == *token && o == *target => { (t, s) } + // the given token is the same as the retrieved one (Some(token), None) if t == *token => (t, s), + // the given target is the same as the retrieved one (None, Some(target)) if o == *target => (t, s), + // no specified token or target (None, None) => (t, s), + // otherwise, this balance will not be printed _ => continue, }; // Print the token if it isn't printed yet From b3b1f261049f03cf1a70da7f378e806c47204500 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 11 Jul 2023 09:25:19 +0100 Subject: [PATCH 047/120] pos: return sorted validator sets and code re-use for queries --- apps/src/lib/client/rpc.rs | 22 +++------- proof_of_stake/src/lib.rs | 4 +- shared/src/ledger/queries/vp/pos.rs | 68 ++++++++--------------------- wasm/wasm_source/src/tx_bond.rs | 6 +-- wasm/wasm_source/src/tx_unbond.rs | 4 +- 5 files changed, 30 insertions(+), 74 deletions(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 626c946f3d..2895238071 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -1,7 +1,7 @@ //! Client RPC queries use std::cmp::Ordering; -use std::collections::{BTreeMap, HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::fs::File; use std::io::{self, Write}; use std::iter::Iterator; @@ -10,7 +10,7 @@ use std::str::FromStr; use borsh::{BorshDeserialize, BorshSerialize}; use data_encoding::HEXLOWER; -use itertools::{Either, Itertools}; +use itertools::Either; use masp_primitives::asset_type::AssetType; use masp_primitives::merkle_tree::MerklePath; use masp_primitives::sapling::{Node, ViewingKey}; @@ -1567,31 +1567,26 @@ pub async fn query_bonded_stake( } None => { let consensus = - unwrap_client_response::>( + unwrap_client_response::>( RPC.vp() .pos() .consensus_validator_set(client, &Some(epoch)) .await, ); let below_capacity = - unwrap_client_response::>( + unwrap_client_response::>( RPC.vp() .pos() .below_capacity_validator_set(client, &Some(epoch)) .await, ); - let sorted_consensus = consensus.into_iter().sorted_by_key(|v| { - -(i128::try_from(v.bonded_stake.change())) - .expect("Failed to sort consensus validators") - }); - // Iterate all validators let stdout = io::stdout(); let mut w = stdout.lock(); writeln!(w, "Consensus validators:").unwrap(); - for val in sorted_consensus { + for val in consensus.into_iter().rev() { writeln!( w, " {}: {}", @@ -1601,13 +1596,8 @@ pub async fn query_bonded_stake( .unwrap(); } if !below_capacity.is_empty() { - let sorted_below_capacity = - below_capacity.into_iter().sorted_by_key(|v| { - -(i128::try_from(v.bonded_stake.change())) - .expect("Failed to sort consensus validators") - }); writeln!(w, "Below capacity validators:").unwrap(); - for val in sorted_below_capacity { + for val in below_capacity.into_iter().rev() { writeln!( w, " {}: {}", diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index 050334debc..4022d49ffc 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -748,7 +748,7 @@ where pub fn read_consensus_validator_set_addresses_with_stake( storage: &S, epoch: namada_core::types::storage::Epoch, -) -> storage_api::Result> +) -> storage_api::Result> where S: StorageRead, { @@ -792,7 +792,7 @@ where pub fn read_below_capacity_validator_set_addresses_with_stake( storage: &S, epoch: namada_core::types::storage::Epoch, -) -> storage_api::Result> +) -> storage_api::Result> where S: StorageRead, { diff --git a/shared/src/ledger/queries/vp/pos.rs b/shared/src/ledger/queries/vp/pos.rs index 94c93f6a54..177b31ed87 100644 --- a/shared/src/ledger/queries/vp/pos.rs +++ b/shared/src/ledger/queries/vp/pos.rs @@ -1,6 +1,6 @@ //! Queries router and handlers for PoS validity predicate -use std::collections::{BTreeMap, HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use namada_core::ledger::storage_api::collections::lazy_map; @@ -10,13 +10,14 @@ use namada_proof_of_stake::types::{ Slash, ValidatorState, WeightedValidator, }; use namada_proof_of_stake::{ - self, below_capacity_validator_set_handle, bond_amount, bond_handle, - consensus_validator_set_handle, find_all_enqueued_slashes, + self, bond_amount, bond_handle, find_all_enqueued_slashes, find_all_slashes, find_delegation_validators, find_delegations, - read_all_validator_addresses, read_pos_params, read_total_stake, - read_validator_max_commission_rate_change, read_validator_stake, - unbond_handle, validator_commission_rate_handle, validator_slashes_handle, - validator_state_handle, + read_all_validator_addresses, + read_below_capacity_validator_set_addresses_with_stake, + read_consensus_validator_set_addresses_with_stake, read_pos_params, + read_total_stake, read_validator_max_commission_rate_change, + read_validator_stake, unbond_handle, validator_commission_rate_handle, + validator_slashes_handle, validator_state_handle, }; use crate::ledger::queries::types::RequestCtx; @@ -51,10 +52,10 @@ router! {POS, ( "validator_set" ) = { ( "consensus" / [epoch: opt Epoch] ) - -> HashSet = consensus_validator_set, + -> BTreeSet = consensus_validator_set, ( "below_capacity" / [epoch: opt Epoch] ) - -> HashSet = below_capacity_validator_set, + -> BTreeSet = below_capacity_validator_set, // TODO: add "below_threshold" }, @@ -253,64 +254,29 @@ where fn consensus_validator_set( ctx: RequestCtx<'_, D, H>, epoch: Option, -) -> storage_api::Result> +) -> storage_api::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { let epoch = epoch.unwrap_or(ctx.wl_storage.storage.last_epoch); - consensus_validator_set_handle() - .at(&epoch) - .iter(ctx.wl_storage)? - .map(|next_result| { - next_result.map( - |( - lazy_map::NestedSubKey::Data { - key: bonded_stake, - nested_sub_key: _position, - }, - address, - )| { - WeightedValidator { - bonded_stake, - address, - } - }, - ) - }) - .collect() + read_consensus_validator_set_addresses_with_stake(ctx.wl_storage, epoch) } /// Get all the validator in the below-capacity set with their bonded stake. fn below_capacity_validator_set( ctx: RequestCtx<'_, D, H>, epoch: Option, -) -> storage_api::Result> +) -> storage_api::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { let epoch = epoch.unwrap_or(ctx.wl_storage.storage.last_epoch); - below_capacity_validator_set_handle() - .at(&epoch) - .iter(ctx.wl_storage)? - .map(|next_result| { - next_result.map( - |( - lazy_map::NestedSubKey::Data { - key: bonded_stake, - nested_sub_key: _position, - }, - address, - )| { - WeightedValidator { - bonded_stake: bonded_stake.into(), - address, - } - }, - ) - }) - .collect() + read_below_capacity_validator_set_addresses_with_stake( + ctx.wl_storage, + epoch, + ) } /// Get the total stake in PoS system at the given epoch or current when `None`. diff --git a/wasm/wasm_source/src/tx_bond.rs b/wasm/wasm_source/src/tx_bond.rs index df55270ceb..9340592bb0 100644 --- a/wasm/wasm_source/src/tx_bond.rs +++ b/wasm/wasm_source/src/tx_bond.rs @@ -15,7 +15,7 @@ fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { #[cfg(test)] mod tests { - use std::collections::HashSet; + use std::collections::BTreeSet; use namada::ledger::pos::{GenesisValidator, PosParams, PosVP}; use namada::proof_of_stake::{ @@ -132,7 +132,7 @@ mod tests { // Read some data before the tx is executed let mut epoched_total_stake_pre: Vec = Vec::new(); let mut epoched_validator_stake_pre: Vec = Vec::new(); - let mut epoched_validator_set_pre: Vec> = + let mut epoched_validator_set_pre: Vec> = Vec::new(); for epoch in 0..=pos_params.unbonding_len { @@ -163,7 +163,7 @@ mod tests { // Read the data after the tx is executed. let mut epoched_total_stake_post: Vec = Vec::new(); let mut epoched_validator_stake_post: Vec = Vec::new(); - let mut epoched_validator_set_post: Vec> = + let mut epoched_validator_set_post: Vec> = Vec::new(); println!("\nFILLING POST STATE\n"); diff --git a/wasm/wasm_source/src/tx_unbond.rs b/wasm/wasm_source/src/tx_unbond.rs index 7fb1de8f4f..fc69294b2b 100644 --- a/wasm/wasm_source/src/tx_unbond.rs +++ b/wasm/wasm_source/src/tx_unbond.rs @@ -15,7 +15,7 @@ fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { #[cfg(test)] mod tests { - use std::collections::HashSet; + use std::collections::BTreeSet; use namada::ledger::pos::{GenesisValidator, PosParams, PosVP}; use namada::proof_of_stake::types::WeightedValidator; @@ -164,7 +164,7 @@ mod tests { let mut epoched_total_stake_pre: Vec = Vec::new(); let mut epoched_validator_stake_pre: Vec = Vec::new(); let mut epoched_bonds_pre: Vec> = Vec::new(); - let mut epoched_validator_set_pre: Vec> = + let mut epoched_validator_set_pre: Vec> = Vec::new(); for epoch in 0..=pos_params.unbonding_len { From 8f7a1b08601ba9e93cb89808d325a9e3edcd79ae Mon Sep 17 00:00:00 2001 From: "Raymond E. Pasco" Date: Tue, 27 Jun 2023 14:19:55 -0400 Subject: [PATCH 048/120] apps: add namadac epoch-sleep Add the epoch-sleep helper, which queries the current epoch and then sleeps until the epoch is greater (polling once a second). This can replace the multiple namadac invocations used in e2e tests. --- apps/src/bin/namada-client/cli.rs | 11 ++++++++ apps/src/lib/cli.rs | 28 +++++++++++++++++++++ apps/src/lib/client/rpc.rs | 15 +++++++++++ tests/src/e2e/ledger_tests.rs | 42 +++++++++++++++++++++++++++++++ 4 files changed, 96 insertions(+) diff --git a/apps/src/bin/namada-client/cli.rs b/apps/src/bin/namada-client/cli.rs index 609588045d..11e35fa789 100644 --- a/apps/src/bin/namada-client/cli.rs +++ b/apps/src/bin/namada-client/cli.rs @@ -433,6 +433,17 @@ pub async fn main() -> Result<()> { let args = args.to_sdk(&mut ctx); rpc::query_protocol_parameters(&client, args).await; } + Sub::EpochSleep(EpochSleep(mut args)) => { + let client = HttpClient::new(utils::take_config_address( + &mut args.ledger_address, + )) + .unwrap(); + wait_until_node_is_synched(&client).await; + let client = + HttpClient::new(args.ledger_address.clone()).unwrap(); + let args = args.to_sdk(&mut ctx); + rpc::epoch_sleep(&client, args).await; + } } } cli::NamadaClient::WithoutContext(cmd, global_args) => match cmd { diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 06d6f3f406..b585ac4fa0 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -238,6 +238,9 @@ pub mod cmds { .subcommand(QueryProposal::def().display_order(4)) .subcommand(QueryProposalResult::def().display_order(4)) .subcommand(QueryProtocolParameters::def().display_order(4)) + // wait until next epoch (can't be in utils, needs the ledger + // address) + .subcommand(EpochSleep::def().display_order(4)) // Utils .subcommand(Utils::def().display_order(5)) } @@ -284,6 +287,7 @@ pub mod cmds { Self::parse_with_ctx(matches, QueryProtocolParameters); let add_to_eth_bridge_pool = Self::parse_with_ctx(matches, AddToEthBridgePool); + let epoch_sleep = Self::parse_with_ctx(matches, EpochSleep); let utils = SubCmd::parse(matches).map(Self::WithoutContext); tx_custom .or(tx_transfer) @@ -314,6 +318,7 @@ pub mod cmds { .or(query_proposal) .or(query_proposal_result) .or(query_protocol_parameters) + .or(epoch_sleep) .or(utils) } } @@ -381,6 +386,7 @@ pub mod cmds { QueryProposal(QueryProposal), QueryProposalResult(QueryProposalResult), QueryProtocolParameters(QueryProtocolParameters), + EpochSleep(EpochSleep), } #[allow(clippy::large_enum_variant)] @@ -1678,6 +1684,28 @@ pub mod cmds { } } + #[derive(Clone, Debug)] + pub struct EpochSleep(pub args::Query); + + impl SubCmd for EpochSleep { + const CMD: &'static str = "epoch-sleep"; + + fn parse(matches: &ArgMatches) -> Option { + matches + .subcommand_matches(Self::CMD) + .map(|matches| Self(args::Query::parse(matches))) + } + + fn def() -> App { + App::new(Self::CMD) + .about( + "Query for the current epoch, then sleep until the next \ + epoch.", + ) + .add_args::>() + } + } + #[derive(Clone, Debug)] pub enum Utils { JoinNetwork(JoinNetwork), diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 44955967d0..2b0b71650e 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -2030,6 +2030,21 @@ pub async fn query_result( } } +pub async fn epoch_sleep( + client: &C, + _args: args::Query, +) { + let start_epoch = query_and_print_epoch(client).await; + loop { + tokio::time::sleep(core::time::Duration::from_secs(1)).await; + let current_epoch = query_epoch(client).await; + if current_epoch > start_epoch { + println!("Reached epoch {}", current_epoch); + break; + } + } +} + pub async fn get_proposal_votes( client: &C, epoch: Epoch, diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index ca7bd974c7..cfddcb2442 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -4663,6 +4663,48 @@ fn implicit_account_reveal_pk() -> Result<()> { Ok(()) } +#[test] +fn test_epoch_sleep() -> Result<()> { + // Use slightly longer epochs to give us time to sleep + let test = setup::network( + |genesis| { + let parameters = ParametersConfig { + epochs_per_year: epochs_per_year_from_min_duration(30), + min_num_of_blocks: 1, + ..genesis.parameters + }; + GenesisConfig { + parameters, + ..genesis + } + }, + None, + )?; + + // 1. Run the ledger node + let mut ledger = + run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; + wait_for_wasm_pre_compile(&mut ledger)?; + + let _bg_ledger = ledger.background(); + + let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); + + // 2. Query the current epoch + let start_epoch = get_epoch(&test, &validator_one_rpc).unwrap(); + + // 3. Use epoch-sleep to sleep for an epoch + let args = ["epoch-sleep", "--node", &validator_one_rpc]; + let mut client = run!(test, Bin::Client, &args, None)?; + client.assert_success(); + + // 4. Confirm the current epoch is larger + let current_epoch = get_epoch(&test, &validator_one_rpc).unwrap(); + assert!(current_epoch > start_epoch); + + Ok(()) +} + /// Prepare proposal data in the test's temp dir from the given source address. /// This can be submitted with "init-proposal" command. fn prepare_proposal_data( From ab083952c9a252b0f4f2c1312973798518b1bcdb Mon Sep 17 00:00:00 2001 From: "Raymond E. Pasco" Date: Tue, 27 Jun 2023 16:20:08 -0400 Subject: [PATCH 049/120] tests/e2e: change epoch_sleep to use namadac epoch-sleep Instead of manually querying, we now epoch-sleep until the next epoch, limiting the number of clients invoked to 1. Also update the epoch sleep e2e test to check the return value (parsed from the output of epoch-sleep). --- tests/src/e2e/helpers.rs | 37 ++++++++++++++++++++++------------- tests/src/e2e/ledger_tests.rs | 5 +++++ 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/tests/src/e2e/helpers.rs b/tests/src/e2e/helpers.rs index 7c79fdfdb6..ac76e3f1a9 100644 --- a/tests/src/e2e/helpers.rs +++ b/tests/src/e2e/helpers.rs @@ -424,20 +424,29 @@ pub fn epoch_sleep( ledger_address: &str, timeout_secs: u64, ) -> Result { - let old_epoch = get_epoch(test, ledger_address)?; - let start = Instant::now(); - let loop_timeout = Duration::new(timeout_secs, 0); - loop { - if Instant::now().duration_since(start) > loop_timeout { - panic!("Timed out waiting for the next epoch"); - } - let epoch = get_epoch(test, ledger_address)?; - if epoch > old_epoch { - break Ok(epoch); - } else { - sleep(10); - } - } + let mut find = run!( + test, + Bin::Client, + &["epoch-sleep", "--node", ledger_address], + Some(timeout_secs) + )?; + parse_reached_epoch(&mut find) +} + +pub fn parse_reached_epoch(find: &mut NamadaCmd) -> Result { + let (unread, matched) = find.exp_regex("Reached epoch .*")?; + let epoch_str = strip_trailing_newline(&matched) + .trim() + .rsplit_once(' ') + .unwrap() + .1; + let epoch = u64::from_str(epoch_str).map_err(|e| { + eyre!(format!( + "Epoch: {} parsed from {}, Error: {}\n\nOutput: {}", + epoch_str, matched, e, unread + )) + })?; + Ok(Epoch(epoch)) } /// Wait for txs and VPs WASM compilations to finish. This is useful to avoid a diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index cfddcb2442..d24aa9e2a5 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -41,6 +41,7 @@ use super::helpers::{ use super::setup::{get_all_wasms_hashes, set_ethereum_bridge_mode}; use crate::e2e::helpers::{ epoch_sleep, find_address, find_bonded_stake, get_actor_rpc, get_epoch, + parse_reached_epoch, }; use crate::e2e::setup::{self, default_port_offset, sleep, Bin, Who}; use crate::{run, run_as}; @@ -4696,11 +4697,15 @@ fn test_epoch_sleep() -> Result<()> { // 3. Use epoch-sleep to sleep for an epoch let args = ["epoch-sleep", "--node", &validator_one_rpc]; let mut client = run!(test, Bin::Client, &args, None)?; + let reached_epoch = parse_reached_epoch(&mut client)?; client.assert_success(); // 4. Confirm the current epoch is larger + // possibly badly, we assume we get here within 30 seconds of the last step + // should be fine haha (future debuggers: sorry) let current_epoch = get_epoch(&test, &validator_one_rpc).unwrap(); assert!(current_epoch > start_epoch); + assert_eq!(current_epoch, reached_epoch); Ok(()) } From 4de68f7660bf5d938cf027b2a18cab54252ff158 Mon Sep 17 00:00:00 2001 From: "Raymond E. Pasco" Date: Tue, 27 Jun 2023 16:21:51 -0400 Subject: [PATCH 050/120] tests/e2e: replace PoS test epoch waits with epoch-sleeps PoS hammered the node as fast as possible with epoch queries in its tests to wait for a specified epoch. Instead, epoch-sleep repeatedly until the target epoch has been reached. This could be improved further by adding an --until parameter to epoch-sleep. --- tests/src/e2e/ledger_tests.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index d24aa9e2a5..034a9503f1 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -2151,7 +2151,7 @@ fn pos_bonds() -> Result<()> { delegation_withdrawable_epoch ); } - let epoch = get_epoch(&test, &validator_one_rpc)?; + let epoch = epoch_sleep(&test, &validator_one_rpc, 40)?; if epoch >= delegation_withdrawable_epoch { break; } @@ -2358,7 +2358,7 @@ fn pos_rewards() -> Result<()> { if Instant::now().duration_since(start) > loop_timeout { panic!("Timed out waiting for epoch: {}", wait_epoch); } - let epoch = get_epoch(&test, &validator_zero_rpc)?; + let epoch = epoch_sleep(&test, &validator_zero_rpc, 40)?; if dbg!(epoch) >= wait_epoch { break; } @@ -2441,7 +2441,7 @@ fn test_bond_queries() -> Result<()> { if Instant::now().duration_since(start) > loop_timeout { panic!("Timed out waiting for epoch: {}", 1); } - let epoch = get_epoch(&test, &validator_one_rpc)?; + let epoch = epoch_sleep(&test, &validator_one_rpc, 40)?; if epoch >= Epoch(4) { break; } @@ -2500,7 +2500,7 @@ fn test_bond_queries() -> Result<()> { // 6. Wait for withdraw_epoch loop { - let epoch = epoch_sleep(&test, &validator_one_rpc, 120)?; + let epoch = epoch_sleep(&test, &validator_one_rpc, 40)?; if epoch >= withdraw_epoch { break; } @@ -2740,7 +2740,7 @@ fn pos_init_validator() -> Result<()> { if Instant::now().duration_since(start) > loop_timeout { panic!("Timed out waiting for epoch: {}", earliest_update_epoch); } - let epoch = get_epoch(&test, &non_validator_rpc)?; + let epoch = epoch_sleep(&test, &non_validator_rpc, 40)?; if epoch >= earliest_update_epoch { break; } From 9702dd90f89bc6a8de129c15746fe57e335af89a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 11 Jul 2023 12:16:11 +0100 Subject: [PATCH 051/120] changelog: add #1656 --- .changelog/unreleased/improvements/1656-pos-cli-queries.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/improvements/1656-pos-cli-queries.md diff --git a/.changelog/unreleased/improvements/1656-pos-cli-queries.md b/.changelog/unreleased/improvements/1656-pos-cli-queries.md new file mode 100644 index 0000000000..a114c6f2f6 --- /dev/null +++ b/.changelog/unreleased/improvements/1656-pos-cli-queries.md @@ -0,0 +1,2 @@ +- Added a client query for `validator-state` and improved the slashes query to + show more info. ([\#1656](https://github.com/anoma/namada/pull/1656)) \ No newline at end of file From 1f27630081756954e2939eee65cdbd68ab10cb36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 11 Jul 2023 11:26:04 +0100 Subject: [PATCH 052/120] apps: move epoch-sleep client cmd under utils --- apps/src/bin/namada-client/cli.rs | 23 ++++++++++++----------- apps/src/lib/cli.rs | 10 ++++------ tests/src/e2e/helpers.rs | 2 +- tests/src/e2e/ledger_tests.rs | 2 +- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/apps/src/bin/namada-client/cli.rs b/apps/src/bin/namada-client/cli.rs index 11e35fa789..50e307d5c6 100644 --- a/apps/src/bin/namada-client/cli.rs +++ b/apps/src/bin/namada-client/cli.rs @@ -433,17 +433,6 @@ pub async fn main() -> Result<()> { let args = args.to_sdk(&mut ctx); rpc::query_protocol_parameters(&client, args).await; } - Sub::EpochSleep(EpochSleep(mut args)) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client).await; - let client = - HttpClient::new(args.ledger_address.clone()).unwrap(); - let args = args.to_sdk(&mut ctx); - rpc::epoch_sleep(&client, args).await; - } } } cli::NamadaClient::WithoutContext(cmd, global_args) => match cmd { @@ -466,6 +455,18 @@ pub async fn main() -> Result<()> { Utils::DefaultBaseDir(DefaultBaseDir(args)) => { utils::default_base_dir(global_args, args) } + Utils::EpochSleep(EpochSleep(mut args)) => { + let mut ctx = cli::Context::new(global_args)?; + let ledger_address = args.ledger_address.clone(); + let client = HttpClient::new(utils::take_config_address( + &mut args.ledger_address, + )) + .unwrap(); + wait_until_node_is_synched(&client).await; + let client = HttpClient::new(ledger_address).unwrap(); + let args = args.to_sdk(&mut ctx); + rpc::epoch_sleep(&client, args).await; + } }, } Ok(()) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index b585ac4fa0..40f459d10a 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -238,9 +238,6 @@ pub mod cmds { .subcommand(QueryProposal::def().display_order(4)) .subcommand(QueryProposalResult::def().display_order(4)) .subcommand(QueryProtocolParameters::def().display_order(4)) - // wait until next epoch (can't be in utils, needs the ledger - // address) - .subcommand(EpochSleep::def().display_order(4)) // Utils .subcommand(Utils::def().display_order(5)) } @@ -287,7 +284,6 @@ pub mod cmds { Self::parse_with_ctx(matches, QueryProtocolParameters); let add_to_eth_bridge_pool = Self::parse_with_ctx(matches, AddToEthBridgePool); - let epoch_sleep = Self::parse_with_ctx(matches, EpochSleep); let utils = SubCmd::parse(matches).map(Self::WithoutContext); tx_custom .or(tx_transfer) @@ -318,7 +314,6 @@ pub mod cmds { .or(query_proposal) .or(query_proposal_result) .or(query_protocol_parameters) - .or(epoch_sleep) .or(utils) } } @@ -386,7 +381,6 @@ pub mod cmds { QueryProposal(QueryProposal), QueryProposalResult(QueryProposalResult), QueryProtocolParameters(QueryProtocolParameters), - EpochSleep(EpochSleep), } #[allow(clippy::large_enum_variant)] @@ -1714,6 +1708,7 @@ pub mod cmds { InitGenesisValidator(InitGenesisValidator), PkToTmAddress(PkToTmAddress), DefaultBaseDir(DefaultBaseDir), + EpochSleep(EpochSleep), } impl SubCmd for Utils { @@ -1732,12 +1727,14 @@ pub mod cmds { SubCmd::parse(matches).map(Self::PkToTmAddress); let default_base_dir = SubCmd::parse(matches).map(Self::DefaultBaseDir); + let epoch_sleep = SubCmd::parse(matches).map(Self::EpochSleep); join_network .or(fetch_wasms) .or(init_network) .or(init_genesis) .or(pk_to_tm_address) .or(default_base_dir) + .or(epoch_sleep) }) } @@ -1750,6 +1747,7 @@ pub mod cmds { .subcommand(InitGenesisValidator::def()) .subcommand(PkToTmAddress::def()) .subcommand(DefaultBaseDir::def()) + .subcommand(EpochSleep::def()) .subcommand_required(true) .arg_required_else_help(true) } diff --git a/tests/src/e2e/helpers.rs b/tests/src/e2e/helpers.rs index ac76e3f1a9..b4235c7347 100644 --- a/tests/src/e2e/helpers.rs +++ b/tests/src/e2e/helpers.rs @@ -427,7 +427,7 @@ pub fn epoch_sleep( let mut find = run!( test, Bin::Client, - &["epoch-sleep", "--node", ledger_address], + &["utils", "epoch-sleep", "--node", ledger_address], Some(timeout_secs) )?; parse_reached_epoch(&mut find) diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 034a9503f1..f21e7bc053 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -4695,7 +4695,7 @@ fn test_epoch_sleep() -> Result<()> { let start_epoch = get_epoch(&test, &validator_one_rpc).unwrap(); // 3. Use epoch-sleep to sleep for an epoch - let args = ["epoch-sleep", "--node", &validator_one_rpc]; + let args = ["utils", "epoch-sleep", "--node", &validator_one_rpc]; let mut client = run!(test, Bin::Client, &args, None)?; let reached_epoch = parse_reached_epoch(&mut client)?; client.assert_success(); From 28a636227b25f8ceacdfbe15ff6239ffde91ce56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 11 Jul 2023 11:29:41 +0100 Subject: [PATCH 053/120] changelog: add #1621 --- .changelog/unreleased/improvements/1621-utils-next-epoch.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/improvements/1621-utils-next-epoch.md diff --git a/.changelog/unreleased/improvements/1621-utils-next-epoch.md b/.changelog/unreleased/improvements/1621-utils-next-epoch.md new file mode 100644 index 0000000000..5f663a8e00 --- /dev/null +++ b/.changelog/unreleased/improvements/1621-utils-next-epoch.md @@ -0,0 +1,2 @@ +- Added a command to wait for the next epoch: `client utils epoch-sleep`. + ([\#1621](https://github.com/anoma/namada/pull/1621)) \ No newline at end of file From 23de497339ded227ad04af27dcd31a2bcbb54590 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 11 Jul 2023 12:36:55 +0100 Subject: [PATCH 054/120] ci/test/e2e: add new test --- .github/workflows/scripts/e2e.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/scripts/e2e.json b/.github/workflows/scripts/e2e.json index 74b08e0fb8..bf91fc1588 100644 --- a/.github/workflows/scripts/e2e.json +++ b/.github/workflows/scripts/e2e.json @@ -20,6 +20,7 @@ "e2e::ledger_tests::test_namada_shuts_down_if_tendermint_dies": 2, "e2e::ledger_tests::test_genesis_validators": 14, "e2e::ledger_tests::test_node_connectivity_and_consensus": 28, + "e2e::ledger_tests::test_epoch_sleep": 12, "e2e::wallet_tests::wallet_address_cmds": 1, "e2e::wallet_tests::wallet_encrypted_key_cmds": 1, "e2e::wallet_tests::wallet_encrypted_key_cmds_env_var": 1, From 62ce5593f42496e8f243be257102d503815e559e Mon Sep 17 00:00:00 2001 From: "Raymond E. Pasco" Date: Tue, 11 Jul 2023 10:53:59 -0400 Subject: [PATCH 055/120] Revert "Merge branch 'ray/utils-next-epoch' (#1621)" This reverts commit 119210b1ad9b07184f618e31ff659fae026fc4f5, reversing changes made to 141ce4b6795e1e29e43a3929e1fd171897591d52. --- .../improvements/1621-utils-next-epoch.md | 2 - .github/workflows/scripts/e2e.json | 1 - apps/src/bin/namada-client/cli.rs | 12 ---- apps/src/lib/cli.rs | 26 --------- apps/src/lib/client/rpc.rs | 15 ----- tests/src/e2e/helpers.rs | 37 +++++------- tests/src/e2e/ledger_tests.rs | 57 ++----------------- 7 files changed, 19 insertions(+), 131 deletions(-) delete mode 100644 .changelog/unreleased/improvements/1621-utils-next-epoch.md diff --git a/.changelog/unreleased/improvements/1621-utils-next-epoch.md b/.changelog/unreleased/improvements/1621-utils-next-epoch.md deleted file mode 100644 index 5f663a8e00..0000000000 --- a/.changelog/unreleased/improvements/1621-utils-next-epoch.md +++ /dev/null @@ -1,2 +0,0 @@ -- Added a command to wait for the next epoch: `client utils epoch-sleep`. - ([\#1621](https://github.com/anoma/namada/pull/1621)) \ No newline at end of file diff --git a/.github/workflows/scripts/e2e.json b/.github/workflows/scripts/e2e.json index bf91fc1588..74b08e0fb8 100644 --- a/.github/workflows/scripts/e2e.json +++ b/.github/workflows/scripts/e2e.json @@ -20,7 +20,6 @@ "e2e::ledger_tests::test_namada_shuts_down_if_tendermint_dies": 2, "e2e::ledger_tests::test_genesis_validators": 14, "e2e::ledger_tests::test_node_connectivity_and_consensus": 28, - "e2e::ledger_tests::test_epoch_sleep": 12, "e2e::wallet_tests::wallet_address_cmds": 1, "e2e::wallet_tests::wallet_encrypted_key_cmds": 1, "e2e::wallet_tests::wallet_encrypted_key_cmds_env_var": 1, diff --git a/apps/src/bin/namada-client/cli.rs b/apps/src/bin/namada-client/cli.rs index dc901f3446..be77d2b982 100644 --- a/apps/src/bin/namada-client/cli.rs +++ b/apps/src/bin/namada-client/cli.rs @@ -471,18 +471,6 @@ pub async fn main() -> Result<()> { Utils::DefaultBaseDir(DefaultBaseDir(args)) => { utils::default_base_dir(global_args, args) } - Utils::EpochSleep(EpochSleep(mut args)) => { - let mut ctx = cli::Context::new(global_args)?; - let ledger_address = args.ledger_address.clone(); - let client = HttpClient::new(utils::take_config_address( - &mut args.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client).await; - let client = HttpClient::new(ledger_address).unwrap(); - let args = args.to_sdk(&mut ctx); - rpc::epoch_sleep(&client, args).await; - } }, } Ok(()) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index e55f79a930..a7a1e83350 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -1700,28 +1700,6 @@ pub mod cmds { } } - #[derive(Clone, Debug)] - pub struct EpochSleep(pub args::Query); - - impl SubCmd for EpochSleep { - const CMD: &'static str = "epoch-sleep"; - - fn parse(matches: &ArgMatches) -> Option { - matches - .subcommand_matches(Self::CMD) - .map(|matches| Self(args::Query::parse(matches))) - } - - fn def() -> App { - App::new(Self::CMD) - .about( - "Query for the current epoch, then sleep until the next \ - epoch.", - ) - .add_args::>() - } - } - #[derive(Clone, Debug)] pub enum Utils { JoinNetwork(JoinNetwork), @@ -1730,7 +1708,6 @@ pub mod cmds { InitGenesisValidator(InitGenesisValidator), PkToTmAddress(PkToTmAddress), DefaultBaseDir(DefaultBaseDir), - EpochSleep(EpochSleep), } impl SubCmd for Utils { @@ -1749,14 +1726,12 @@ pub mod cmds { SubCmd::parse(matches).map(Self::PkToTmAddress); let default_base_dir = SubCmd::parse(matches).map(Self::DefaultBaseDir); - let epoch_sleep = SubCmd::parse(matches).map(Self::EpochSleep); join_network .or(fetch_wasms) .or(init_network) .or(init_genesis) .or(pk_to_tm_address) .or(default_base_dir) - .or(epoch_sleep) }) } @@ -1769,7 +1744,6 @@ pub mod cmds { .subcommand(InitGenesisValidator::def()) .subcommand(PkToTmAddress::def()) .subcommand(DefaultBaseDir::def()) - .subcommand(EpochSleep::def()) .subcommand_required(true) .arg_required_else_help(true) } diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 6f2be4a9c5..2895238071 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -2150,21 +2150,6 @@ pub async fn query_result( } } -pub async fn epoch_sleep( - client: &C, - _args: args::Query, -) { - let start_epoch = query_and_print_epoch(client).await; - loop { - tokio::time::sleep(core::time::Duration::from_secs(1)).await; - let current_epoch = query_epoch(client).await; - if current_epoch > start_epoch { - println!("Reached epoch {}", current_epoch); - break; - } - } -} - pub async fn get_proposal_votes( client: &C, epoch: Epoch, diff --git a/tests/src/e2e/helpers.rs b/tests/src/e2e/helpers.rs index b4235c7347..7c79fdfdb6 100644 --- a/tests/src/e2e/helpers.rs +++ b/tests/src/e2e/helpers.rs @@ -424,29 +424,20 @@ pub fn epoch_sleep( ledger_address: &str, timeout_secs: u64, ) -> Result { - let mut find = run!( - test, - Bin::Client, - &["utils", "epoch-sleep", "--node", ledger_address], - Some(timeout_secs) - )?; - parse_reached_epoch(&mut find) -} - -pub fn parse_reached_epoch(find: &mut NamadaCmd) -> Result { - let (unread, matched) = find.exp_regex("Reached epoch .*")?; - let epoch_str = strip_trailing_newline(&matched) - .trim() - .rsplit_once(' ') - .unwrap() - .1; - let epoch = u64::from_str(epoch_str).map_err(|e| { - eyre!(format!( - "Epoch: {} parsed from {}, Error: {}\n\nOutput: {}", - epoch_str, matched, e, unread - )) - })?; - Ok(Epoch(epoch)) + let old_epoch = get_epoch(test, ledger_address)?; + let start = Instant::now(); + let loop_timeout = Duration::new(timeout_secs, 0); + loop { + if Instant::now().duration_since(start) > loop_timeout { + panic!("Timed out waiting for the next epoch"); + } + let epoch = get_epoch(test, ledger_address)?; + if epoch > old_epoch { + break Ok(epoch); + } else { + sleep(10); + } + } } /// Wait for txs and VPs WASM compilations to finish. This is useful to avoid a diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 373feebff2..3f22402c2c 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -41,7 +41,6 @@ use super::helpers::{ use super::setup::{get_all_wasms_hashes, set_ethereum_bridge_mode}; use crate::e2e::helpers::{ epoch_sleep, find_address, find_bonded_stake, get_actor_rpc, get_epoch, - parse_reached_epoch, }; use crate::e2e::setup::{self, default_port_offset, sleep, Bin, Who}; use crate::{run, run_as}; @@ -2151,7 +2150,7 @@ fn pos_bonds() -> Result<()> { delegation_withdrawable_epoch ); } - let epoch = epoch_sleep(&test, &validator_one_rpc, 40)?; + let epoch = get_epoch(&test, &validator_one_rpc)?; if epoch >= delegation_withdrawable_epoch { break; } @@ -2358,7 +2357,7 @@ fn pos_rewards() -> Result<()> { if Instant::now().duration_since(start) > loop_timeout { panic!("Timed out waiting for epoch: {}", wait_epoch); } - let epoch = epoch_sleep(&test, &validator_zero_rpc, 40)?; + let epoch = get_epoch(&test, &validator_zero_rpc)?; if dbg!(epoch) >= wait_epoch { break; } @@ -2441,7 +2440,7 @@ fn test_bond_queries() -> Result<()> { if Instant::now().duration_since(start) > loop_timeout { panic!("Timed out waiting for epoch: {}", 1); } - let epoch = epoch_sleep(&test, &validator_one_rpc, 40)?; + let epoch = get_epoch(&test, &validator_one_rpc)?; if epoch >= Epoch(4) { break; } @@ -2500,7 +2499,7 @@ fn test_bond_queries() -> Result<()> { // 6. Wait for withdraw_epoch loop { - let epoch = epoch_sleep(&test, &validator_one_rpc, 40)?; + let epoch = epoch_sleep(&test, &validator_one_rpc, 120)?; if epoch >= withdraw_epoch { break; } @@ -2740,7 +2739,7 @@ fn pos_init_validator() -> Result<()> { if Instant::now().duration_since(start) > loop_timeout { panic!("Timed out waiting for epoch: {}", earliest_update_epoch); } - let epoch = epoch_sleep(&test, &non_validator_rpc, 40)?; + let epoch = get_epoch(&test, &non_validator_rpc)?; if epoch >= earliest_update_epoch { break; } @@ -4664,52 +4663,6 @@ fn implicit_account_reveal_pk() -> Result<()> { Ok(()) } -#[test] -fn test_epoch_sleep() -> Result<()> { - // Use slightly longer epochs to give us time to sleep - let test = setup::network( - |genesis| { - let parameters = ParametersConfig { - epochs_per_year: epochs_per_year_from_min_duration(30), - min_num_of_blocks: 1, - ..genesis.parameters - }; - GenesisConfig { - parameters, - ..genesis - } - }, - None, - )?; - - // 1. Run the ledger node - let mut ledger = - run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; - wait_for_wasm_pre_compile(&mut ledger)?; - - let _bg_ledger = ledger.background(); - - let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); - - // 2. Query the current epoch - let start_epoch = get_epoch(&test, &validator_one_rpc).unwrap(); - - // 3. Use epoch-sleep to sleep for an epoch - let args = ["utils", "epoch-sleep", "--node", &validator_one_rpc]; - let mut client = run!(test, Bin::Client, &args, None)?; - let reached_epoch = parse_reached_epoch(&mut client)?; - client.assert_success(); - - // 4. Confirm the current epoch is larger - // possibly badly, we assume we get here within 30 seconds of the last step - // should be fine haha (future debuggers: sorry) - let current_epoch = get_epoch(&test, &validator_one_rpc).unwrap(); - assert!(current_epoch > start_epoch); - assert_eq!(current_epoch, reached_epoch); - - Ok(()) -} - /// Prepare proposal data in the test's temp dir from the given source address. /// This can be submitted with "init-proposal" command. fn prepare_proposal_data( From b06b60374fa13dcc28455d27fd0831c0bc525006 Mon Sep 17 00:00:00 2001 From: brentstone Date: Tue, 4 Jul 2023 14:18:33 +0200 Subject: [PATCH 056/120] CLI query a validator's state --- apps/src/bin/namada-client/cli.rs | 16 ++++++ apps/src/lib/cli.rs | 72 +++++++++++++++++++++++++-- apps/src/lib/client/rpc.rs | 75 ++++++++++++++++++++++++----- shared/src/ledger/args.rs | 11 +++++ shared/src/ledger/queries/vp/pos.rs | 26 +++++++++- 5 files changed, 183 insertions(+), 17 deletions(-) diff --git a/apps/src/bin/namada-client/cli.rs b/apps/src/bin/namada-client/cli.rs index 609588045d..be77d2b982 100644 --- a/apps/src/bin/namada-client/cli.rs +++ b/apps/src/bin/namada-client/cli.rs @@ -325,6 +325,22 @@ pub async fn main() -> Result<()> { let args = args.to_sdk(&mut ctx); rpc::query_bonded_stake(&client, args).await; } + Sub::QueryValidatorState(QueryValidatorState(mut args)) => { + let client = HttpClient::new(utils::take_config_address( + &mut args.query.ledger_address, + )) + .unwrap(); + wait_until_node_is_synched(&client) + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_and_print_validator_state( + &client, + &mut ctx.wallet, + args, + ) + .await; + } Sub::QueryCommissionRate(QueryCommissionRate(mut args)) => { let client = HttpClient::new(utils::take_config_address( &mut args.query.ledger_address, diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 06d6f3f406..00a24c45b2 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -238,6 +238,7 @@ pub mod cmds { .subcommand(QueryProposal::def().display_order(4)) .subcommand(QueryProposalResult::def().display_order(4)) .subcommand(QueryProtocolParameters::def().display_order(4)) + .subcommand(QueryValidatorState::def().display_order(4)) // Utils .subcommand(Utils::def().display_order(5)) } @@ -282,6 +283,8 @@ pub mod cmds { Self::parse_with_ctx(matches, QueryProposalResult); let query_protocol_parameters = Self::parse_with_ctx(matches, QueryProtocolParameters); + let query_validator_state = + Self::parse_with_ctx(matches, QueryValidatorState); let add_to_eth_bridge_pool = Self::parse_with_ctx(matches, AddToEthBridgePool); let utils = SubCmd::parse(matches).map(Self::WithoutContext); @@ -314,6 +317,7 @@ pub mod cmds { .or(query_proposal) .or(query_proposal_result) .or(query_protocol_parameters) + .or(query_validator_state) .or(utils) } } @@ -381,6 +385,7 @@ pub mod cmds { QueryProposal(QueryProposal), QueryProposalResult(QueryProposalResult), QueryProtocolParameters(QueryProtocolParameters), + QueryValidatorState(QueryValidatorState), } #[allow(clippy::large_enum_variant)] @@ -1453,6 +1458,27 @@ pub mod cmds { } } + #[derive(Clone, Debug)] + pub struct QueryValidatorState( + pub args::QueryValidatorState, + ); + + impl SubCmd for QueryValidatorState { + const CMD: &'static str = "validator-state"; + + fn parse(matches: &ArgMatches) -> Option { + matches.subcommand_matches(Self::CMD).map(|matches| { + QueryValidatorState(args::QueryValidatorState::parse(matches)) + }) + } + + fn def() -> App { + App::new(Self::CMD) + .about("Query the state of a PoS validator.") + .add_args::>() + } + } + #[derive(Clone, Debug)] pub struct QueryTransfers(pub args::QueryTransfers); @@ -1488,7 +1514,7 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) - .about("Query commission rate.") + .about("Query a validator's commission rate.") .add_args::>() } } @@ -4016,8 +4042,44 @@ pub mod args { "The validator's address whose bonded stake to query.", )) .arg(EPOCH.def().help( - "The epoch at which to query (last committed, if not \ - specified).", + "The epoch at which to query (corresponding to the last \ + committed block, if not specified).", + )) + } + } + + impl CliToSdk> for QueryValidatorState { + fn to_sdk(self, ctx: &mut Context) -> QueryValidatorState { + QueryValidatorState:: { + query: self.query.to_sdk(ctx), + validator: ctx.get(&self.validator), + epoch: self.epoch, + } + } + } + + impl Args for QueryValidatorState { + fn parse(matches: &ArgMatches) -> Self { + let query = Query::parse(matches); + let validator = VALIDATOR.parse(matches); + let epoch = EPOCH.parse(matches); + Self { + query, + validator, + epoch, + } + } + + fn def(app: App) -> App { + app.add_args::>() + .arg( + VALIDATOR.def().help( + "The validator's address whose state is queried.", + ), + ) + .arg(EPOCH.def().help( + "The epoch at which to query (corresponding to the last \ + committed block, if not specified).", )) } } @@ -4127,8 +4189,8 @@ pub mod args { "The validator's address whose commission rate to query.", )) .arg(EPOCH.def().help( - "The epoch at which to query (last committed, if not \ - specified).", + "The epoch at which to query (corresponding to the last \ + committed block, if not specified).", )) } } diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index fb1dbd6c33..81d9d71009 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -36,7 +36,7 @@ use namada::ledger::rpc::{ }; use namada::ledger::storage::ConversionState; use namada::ledger::wallet::{AddressVpType, Wallet}; -use namada::proof_of_stake::types::WeightedValidator; +use namada::proof_of_stake::types::{ValidatorState, WeightedValidator}; use namada::types::address::{masp, Address}; use namada::types::control_flow::ProceedOrElse; use namada::types::governance::{ @@ -1580,12 +1580,11 @@ pub async fn query_bonded_stake( .below_capacity_validator_set(client, &Some(epoch)) .await, ); - - let sorted_consensus = consensus.into_iter(). - sorted_by_key(|v| + + let sorted_consensus = consensus.into_iter().sorted_by_key(|v| { -(i128::try_from(v.bonded_stake.change())) - .expect("Failed to sort consensus validators") - ); + .expect("Failed to sort consensus validators") + }); // Iterate all validators let stdout = io::stdout(); @@ -1602,11 +1601,11 @@ pub async fn query_bonded_stake( .unwrap(); } if !below_capacity.is_empty() { - let sorted_below_capacity = below_capacity.into_iter(). - sorted_by_key(|v| - -(i128::try_from(v.bonded_stake.change())) - .expect("Failed to sort consensus validators") - ); + let sorted_below_capacity = + below_capacity.into_iter().sorted_by_key(|v| { + -(i128::try_from(v.bonded_stake.change())) + .expect("Failed to sort consensus validators") + }); writeln!(w, "Below capacity validators:").unwrap(); for val in sorted_below_capacity { writeln!( @@ -1645,6 +1644,60 @@ pub async fn query_commission_rate< ) } +/// Query and return validator's state +pub async fn query_validator_state< + C: namada::ledger::queries::Client + Sync, +>( + client: &C, + validator: &Address, + epoch: Option, +) -> Option { + unwrap_client_response::>( + RPC.vp() + .pos() + .validator_state(client, validator, &epoch) + .await, + ) +} + +/// Query a validator's state information +pub async fn query_and_print_validator_state< + C: namada::ledger::queries::Client + Sync, +>( + client: &C, + _wallet: &mut Wallet, + args: args::QueryValidatorState, +) { + let validator = args.validator; + let state: Option = + query_validator_state(client, &validator, args.epoch).await; + + match state { + Some(state) => match state { + ValidatorState::Consensus => { + println!("Validator {validator} is in the consensus set") + } + ValidatorState::BelowCapacity => { + println!("Validator {validator} is in the below-capacity set") + } + ValidatorState::BelowThreshold => { + println!("Validator {validator} is in the below-threshold set") + } + ValidatorState::Inactive => { + println!("Validator {validator} is inactive") + } + ValidatorState::Jailed => { + println!("Validator {validator} is jailed") + } + }, + None => println!( + "Validator {validator} is either not a validator, or an epoch \ + before the current epoch has been queried (and the validator \ + state information is no longer stored)" + ), + } +} + /// Query PoS validator's commission rate information pub async fn query_and_print_commission_rate< C: namada::ledger::queries::Client + Sync, diff --git a/shared/src/ledger/args.rs b/shared/src/ledger/args.rs index 35081d3283..47117ba594 100644 --- a/shared/src/ledger/args.rs +++ b/shared/src/ledger/args.rs @@ -356,6 +356,17 @@ pub struct QueryBondedStake { pub epoch: Option, } +/// Query the state of a validator (its validator set or if it is jailed) +#[derive(Clone, Debug)] +pub struct QueryValidatorState { + /// Common query args + pub query: Query, + /// Address of a validator + pub validator: C::Address, + /// Epoch in which to find the validator state + pub epoch: Option, +} + #[derive(Clone, Debug)] /// Commission rate change args pub struct CommissionRateChange { diff --git a/shared/src/ledger/queries/vp/pos.rs b/shared/src/ledger/queries/vp/pos.rs index d872aa5002..9934a71271 100644 --- a/shared/src/ledger/queries/vp/pos.rs +++ b/shared/src/ledger/queries/vp/pos.rs @@ -7,7 +7,7 @@ use namada_core::ledger::storage_api::collections::lazy_map; use namada_core::ledger::storage_api::OptionExt; use namada_proof_of_stake::types::{ BondId, BondsAndUnbondsDetail, BondsAndUnbondsDetails, CommissionPair, - Slash, WeightedValidator, + Slash, ValidatorState, WeightedValidator, }; use namada_proof_of_stake::{ self, below_capacity_validator_set_handle, bond_amount, bond_handle, @@ -16,6 +16,7 @@ use namada_proof_of_stake::{ read_pos_params, read_total_stake, read_validator_max_commission_rate_change, read_validator_stake, unbond_handle, validator_commission_rate_handle, validator_slashes_handle, + validator_state_handle, }; use crate::ledger::queries::types::RequestCtx; @@ -43,6 +44,9 @@ router! {POS, ( "commission" / [validator: Address] / [epoch: opt Epoch] ) -> Option = validator_commission, + + ( "state" / [validator: Address] / [epoch: opt Epoch] ) + -> Option = validator_state, }, ( "validator_set" ) = { @@ -203,6 +207,26 @@ where } } +/// Get the validator state +fn validator_state( + ctx: RequestCtx<'_, D, H>, + validator: Address, + epoch: Option, +) -> storage_api::Result> +where + D: 'static + DB + for<'iter> DBIter<'iter> + Sync, + H: 'static + StorageHasher + Sync, +{ + let epoch = epoch.unwrap_or(ctx.wl_storage.storage.last_epoch); + let params = read_pos_params(ctx.wl_storage)?; + let state = validator_state_handle(&validator).get( + ctx.wl_storage, + epoch, + ¶ms, + )?; + Ok(state) +} + /// Get the total stake of a validator at the given epoch or current when /// `None`. The total stake is a sum of validator's self-bonds and delegations /// to their address. From 5a6a92a31bcdeb0a9cbfe22dbac32e31df52437d Mon Sep 17 00:00:00 2001 From: brentstone Date: Thu, 6 Jul 2023 11:59:19 +0200 Subject: [PATCH 057/120] Expanding and fixing slashes query --- apps/src/lib/client/rpc.rs | 92 +++++++++++++++++++++++++---- proof_of_stake/src/lib.rs | 36 +++++++++++ shared/src/ledger/queries/vp/pos.rs | 25 ++++++-- 3 files changed, 135 insertions(+), 18 deletions(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 81d9d71009..626c946f3d 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -1,7 +1,7 @@ //! Client RPC queries use std::cmp::Ordering; -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeMap, HashMap, HashSet}; use std::fs::File; use std::io::{self, Write}; use std::iter::Iterator; @@ -1738,11 +1738,6 @@ pub async fn query_slashes( _wallet: &mut Wallet, args: args::QuerySlashes, ) { - let params_key = pos::params_key(); - let params = query_storage_value::(client, ¶ms_key) - .await - .expect("Parameter should be defined."); - match args.validator { Some(validator) => { let validator = validator; @@ -1751,18 +1746,54 @@ pub async fn query_slashes( RPC.vp().pos().validator_slashes(client, &validator).await, ); if !slashes.is_empty() { + println!("Processed slashes:"); let stdout = io::stdout(); let mut w = stdout.lock(); for slash in slashes { writeln!( w, - "Slash epoch {}, type {}, rate {}", - slash.epoch, slash.r#type, slash.rate + "Infraction epoch {}, block height {}, type {}, rate \ + {}", + slash.epoch, + slash.block_height, + slash.r#type, + slash.rate ) .unwrap(); } } else { - println!("No slashes found for {}", validator.encode()) + println!( + "No processed slashes found for {}", + validator.encode() + ) + } + // Find enqueued slashes to be processed in the future for the given + // validator + let enqueued_slashes: HashMap< + Address, + BTreeMap>, + > = unwrap_client_response::< + C, + HashMap>>, + >(RPC.vp().pos().enqueued_slashes(client).await); + let enqueued_slashes = enqueued_slashes.get(&validator).cloned(); + if let Some(enqueued) = enqueued_slashes { + println!("\nEnqueued slashes for future processing"); + for (epoch, slashes) in enqueued { + println!("To be processed in epoch {}", epoch); + for slash in slashes { + let stdout = io::stdout(); + let mut w = stdout.lock(); + writeln!( + w, + "Infraction epoch {}, block height {}, type {}", + slash.epoch, slash.block_height, slash.r#type, + ) + .unwrap(); + } + } + } else { + println!("No enqueued slashes found for {}", validator.encode()) } } None => { @@ -1774,15 +1805,16 @@ pub async fn query_slashes( if !all_slashes.is_empty() { let stdout = io::stdout(); let mut w = stdout.lock(); + println!("Processed slashes:"); for (validator, slashes) in all_slashes.into_iter() { for slash in slashes { writeln!( w, - "Slash epoch {}, block height {}, rate {}, type \ - {}, validator {}", + "Infraction epoch {}, block height {}, rate {}, \ + type {}, validator {}", slash.epoch, slash.block_height, - slash.r#type.get_slash_rate(¶ms), + slash.rate, slash.r#type, validator, ) @@ -1790,7 +1822,41 @@ pub async fn query_slashes( } } } else { - println!("No slashes found") + println!("No processed slashes found") + } + + // Find enqueued slashes to be processed in the future for the given + // validator + let enqueued_slashes: HashMap< + Address, + BTreeMap>, + > = unwrap_client_response::< + C, + HashMap>>, + >(RPC.vp().pos().enqueued_slashes(client).await); + if !enqueued_slashes.is_empty() { + println!("\nEnqueued slashes for future processing"); + for (validator, slashes_by_epoch) in enqueued_slashes { + for (epoch, slashes) in slashes_by_epoch { + println!("\nTo be processed in epoch {}", epoch); + for slash in slashes { + let stdout = io::stdout(); + let mut w = stdout.lock(); + writeln!( + w, + "Infraction epoch {}, block height {}, type \ + {}, validator {}", + slash.epoch, + slash.block_height, + slash.r#type, + validator + ) + .unwrap(); + } + } + } + } else { + println!("\nNo enqueued slashes found for future processing") } } } diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index 7f7265be91..050334debc 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -2753,6 +2753,42 @@ where } } +/// Collect the details of all of the enqueued slashes to be processed in future +/// epochs into a nested map +pub fn find_all_enqueued_slashes( + storage: &S, + epoch: Epoch, +) -> storage_api::Result>>> +where + S: StorageRead, +{ + let mut enqueued = HashMap::>>::new(); + for res in enqueued_slashes_handle().get_data_handler().iter(storage)? { + let ( + NestedSubKey::Data { + key: processing_epoch, + nested_sub_key: + NestedSubKey::Data { + key: address, + nested_sub_key: _, + }, + }, + slash, + ) = res?; + if processing_epoch <= epoch { + continue; + } + + let slashes = enqueued + .entry(address) + .or_default() + .entry(processing_epoch) + .or_default(); + slashes.push(slash); + } + Ok(enqueued) +} + /// Find all slashes and the associated validators in the PoS system pub fn find_all_slashes( storage: &S, diff --git a/shared/src/ledger/queries/vp/pos.rs b/shared/src/ledger/queries/vp/pos.rs index 9934a71271..94c93f6a54 100644 --- a/shared/src/ledger/queries/vp/pos.rs +++ b/shared/src/ledger/queries/vp/pos.rs @@ -1,6 +1,6 @@ //! Queries router and handlers for PoS validity predicate -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeMap, HashMap, HashSet}; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use namada_core::ledger::storage_api::collections::lazy_map; @@ -11,9 +11,9 @@ use namada_proof_of_stake::types::{ }; use namada_proof_of_stake::{ self, below_capacity_validator_set_handle, bond_amount, bond_handle, - consensus_validator_set_handle, find_all_slashes, - find_delegation_validators, find_delegations, read_all_validator_addresses, - read_pos_params, read_total_stake, + consensus_validator_set_handle, find_all_enqueued_slashes, + find_all_slashes, find_delegation_validators, find_delegations, + read_all_validator_addresses, read_pos_params, read_total_stake, read_validator_max_commission_rate_change, read_validator_stake, unbond_handle, validator_commission_rate_handle, validator_slashes_handle, validator_state_handle, @@ -86,6 +86,9 @@ router! {POS, ( "bonds_and_unbonds" / [source: opt Address] / [validator: opt Address] ) -> BondsAndUnbondsDetails = bonds_and_unbonds, + ( "enqueued_slashes" ) + -> HashMap>> = enqueued_slashes, + ( "all_slashes" ) -> HashMap> = slashes, ( "is_delegator" / [addr: Address ] / [epoch: opt Epoch] ) -> bool = is_delegator, @@ -520,7 +523,19 @@ where find_all_slashes(ctx.wl_storage) } -/// All slashes +/// Enqueued slashes +fn enqueued_slashes( + ctx: RequestCtx<'_, D, H>, +) -> storage_api::Result>>> +where + D: 'static + DB + for<'iter> DBIter<'iter> + Sync, + H: 'static + StorageHasher + Sync, +{ + let current_epoch = ctx.wl_storage.storage.last_epoch; + find_all_enqueued_slashes(ctx.wl_storage, current_epoch) +} + +/// Native validator address by looking up the Tendermint address fn validator_by_tm_addr( ctx: RequestCtx<'_, D, H>, tm_addr: String, From 214735c63e4432fd262ac88a552e9e356d317989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 11 Jul 2023 09:25:19 +0100 Subject: [PATCH 058/120] pos: return sorted validator sets and code re-use for queries --- apps/src/lib/client/rpc.rs | 22 +++------- proof_of_stake/src/lib.rs | 4 +- shared/src/ledger/queries/vp/pos.rs | 68 ++++++++--------------------- wasm/wasm_source/src/tx_bond.rs | 6 +-- wasm/wasm_source/src/tx_unbond.rs | 4 +- 5 files changed, 30 insertions(+), 74 deletions(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 626c946f3d..2895238071 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -1,7 +1,7 @@ //! Client RPC queries use std::cmp::Ordering; -use std::collections::{BTreeMap, HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::fs::File; use std::io::{self, Write}; use std::iter::Iterator; @@ -10,7 +10,7 @@ use std::str::FromStr; use borsh::{BorshDeserialize, BorshSerialize}; use data_encoding::HEXLOWER; -use itertools::{Either, Itertools}; +use itertools::Either; use masp_primitives::asset_type::AssetType; use masp_primitives::merkle_tree::MerklePath; use masp_primitives::sapling::{Node, ViewingKey}; @@ -1567,31 +1567,26 @@ pub async fn query_bonded_stake( } None => { let consensus = - unwrap_client_response::>( + unwrap_client_response::>( RPC.vp() .pos() .consensus_validator_set(client, &Some(epoch)) .await, ); let below_capacity = - unwrap_client_response::>( + unwrap_client_response::>( RPC.vp() .pos() .below_capacity_validator_set(client, &Some(epoch)) .await, ); - let sorted_consensus = consensus.into_iter().sorted_by_key(|v| { - -(i128::try_from(v.bonded_stake.change())) - .expect("Failed to sort consensus validators") - }); - // Iterate all validators let stdout = io::stdout(); let mut w = stdout.lock(); writeln!(w, "Consensus validators:").unwrap(); - for val in sorted_consensus { + for val in consensus.into_iter().rev() { writeln!( w, " {}: {}", @@ -1601,13 +1596,8 @@ pub async fn query_bonded_stake( .unwrap(); } if !below_capacity.is_empty() { - let sorted_below_capacity = - below_capacity.into_iter().sorted_by_key(|v| { - -(i128::try_from(v.bonded_stake.change())) - .expect("Failed to sort consensus validators") - }); writeln!(w, "Below capacity validators:").unwrap(); - for val in sorted_below_capacity { + for val in below_capacity.into_iter().rev() { writeln!( w, " {}: {}", diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index 050334debc..4022d49ffc 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -748,7 +748,7 @@ where pub fn read_consensus_validator_set_addresses_with_stake( storage: &S, epoch: namada_core::types::storage::Epoch, -) -> storage_api::Result> +) -> storage_api::Result> where S: StorageRead, { @@ -792,7 +792,7 @@ where pub fn read_below_capacity_validator_set_addresses_with_stake( storage: &S, epoch: namada_core::types::storage::Epoch, -) -> storage_api::Result> +) -> storage_api::Result> where S: StorageRead, { diff --git a/shared/src/ledger/queries/vp/pos.rs b/shared/src/ledger/queries/vp/pos.rs index 94c93f6a54..177b31ed87 100644 --- a/shared/src/ledger/queries/vp/pos.rs +++ b/shared/src/ledger/queries/vp/pos.rs @@ -1,6 +1,6 @@ //! Queries router and handlers for PoS validity predicate -use std::collections::{BTreeMap, HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use namada_core::ledger::storage_api::collections::lazy_map; @@ -10,13 +10,14 @@ use namada_proof_of_stake::types::{ Slash, ValidatorState, WeightedValidator, }; use namada_proof_of_stake::{ - self, below_capacity_validator_set_handle, bond_amount, bond_handle, - consensus_validator_set_handle, find_all_enqueued_slashes, + self, bond_amount, bond_handle, find_all_enqueued_slashes, find_all_slashes, find_delegation_validators, find_delegations, - read_all_validator_addresses, read_pos_params, read_total_stake, - read_validator_max_commission_rate_change, read_validator_stake, - unbond_handle, validator_commission_rate_handle, validator_slashes_handle, - validator_state_handle, + read_all_validator_addresses, + read_below_capacity_validator_set_addresses_with_stake, + read_consensus_validator_set_addresses_with_stake, read_pos_params, + read_total_stake, read_validator_max_commission_rate_change, + read_validator_stake, unbond_handle, validator_commission_rate_handle, + validator_slashes_handle, validator_state_handle, }; use crate::ledger::queries::types::RequestCtx; @@ -51,10 +52,10 @@ router! {POS, ( "validator_set" ) = { ( "consensus" / [epoch: opt Epoch] ) - -> HashSet = consensus_validator_set, + -> BTreeSet = consensus_validator_set, ( "below_capacity" / [epoch: opt Epoch] ) - -> HashSet = below_capacity_validator_set, + -> BTreeSet = below_capacity_validator_set, // TODO: add "below_threshold" }, @@ -253,64 +254,29 @@ where fn consensus_validator_set( ctx: RequestCtx<'_, D, H>, epoch: Option, -) -> storage_api::Result> +) -> storage_api::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { let epoch = epoch.unwrap_or(ctx.wl_storage.storage.last_epoch); - consensus_validator_set_handle() - .at(&epoch) - .iter(ctx.wl_storage)? - .map(|next_result| { - next_result.map( - |( - lazy_map::NestedSubKey::Data { - key: bonded_stake, - nested_sub_key: _position, - }, - address, - )| { - WeightedValidator { - bonded_stake, - address, - } - }, - ) - }) - .collect() + read_consensus_validator_set_addresses_with_stake(ctx.wl_storage, epoch) } /// Get all the validator in the below-capacity set with their bonded stake. fn below_capacity_validator_set( ctx: RequestCtx<'_, D, H>, epoch: Option, -) -> storage_api::Result> +) -> storage_api::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { let epoch = epoch.unwrap_or(ctx.wl_storage.storage.last_epoch); - below_capacity_validator_set_handle() - .at(&epoch) - .iter(ctx.wl_storage)? - .map(|next_result| { - next_result.map( - |( - lazy_map::NestedSubKey::Data { - key: bonded_stake, - nested_sub_key: _position, - }, - address, - )| { - WeightedValidator { - bonded_stake: bonded_stake.into(), - address, - } - }, - ) - }) - .collect() + read_below_capacity_validator_set_addresses_with_stake( + ctx.wl_storage, + epoch, + ) } /// Get the total stake in PoS system at the given epoch or current when `None`. diff --git a/wasm/wasm_source/src/tx_bond.rs b/wasm/wasm_source/src/tx_bond.rs index df55270ceb..9340592bb0 100644 --- a/wasm/wasm_source/src/tx_bond.rs +++ b/wasm/wasm_source/src/tx_bond.rs @@ -15,7 +15,7 @@ fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { #[cfg(test)] mod tests { - use std::collections::HashSet; + use std::collections::BTreeSet; use namada::ledger::pos::{GenesisValidator, PosParams, PosVP}; use namada::proof_of_stake::{ @@ -132,7 +132,7 @@ mod tests { // Read some data before the tx is executed let mut epoched_total_stake_pre: Vec = Vec::new(); let mut epoched_validator_stake_pre: Vec = Vec::new(); - let mut epoched_validator_set_pre: Vec> = + let mut epoched_validator_set_pre: Vec> = Vec::new(); for epoch in 0..=pos_params.unbonding_len { @@ -163,7 +163,7 @@ mod tests { // Read the data after the tx is executed. let mut epoched_total_stake_post: Vec = Vec::new(); let mut epoched_validator_stake_post: Vec = Vec::new(); - let mut epoched_validator_set_post: Vec> = + let mut epoched_validator_set_post: Vec> = Vec::new(); println!("\nFILLING POST STATE\n"); diff --git a/wasm/wasm_source/src/tx_unbond.rs b/wasm/wasm_source/src/tx_unbond.rs index 7fb1de8f4f..fc69294b2b 100644 --- a/wasm/wasm_source/src/tx_unbond.rs +++ b/wasm/wasm_source/src/tx_unbond.rs @@ -15,7 +15,7 @@ fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { #[cfg(test)] mod tests { - use std::collections::HashSet; + use std::collections::BTreeSet; use namada::ledger::pos::{GenesisValidator, PosParams, PosVP}; use namada::proof_of_stake::types::WeightedValidator; @@ -164,7 +164,7 @@ mod tests { let mut epoched_total_stake_pre: Vec = Vec::new(); let mut epoched_validator_stake_pre: Vec = Vec::new(); let mut epoched_bonds_pre: Vec> = Vec::new(); - let mut epoched_validator_set_pre: Vec> = + let mut epoched_validator_set_pre: Vec> = Vec::new(); for epoch in 0..=pos_params.unbonding_len { From ce14a4c812aced85b2e6b5f5aca78b9856833baa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 11 Jul 2023 12:16:11 +0100 Subject: [PATCH 059/120] changelog: add #1656 --- .changelog/unreleased/improvements/1656-pos-cli-queries.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/improvements/1656-pos-cli-queries.md diff --git a/.changelog/unreleased/improvements/1656-pos-cli-queries.md b/.changelog/unreleased/improvements/1656-pos-cli-queries.md new file mode 100644 index 0000000000..a114c6f2f6 --- /dev/null +++ b/.changelog/unreleased/improvements/1656-pos-cli-queries.md @@ -0,0 +1,2 @@ +- Added a client query for `validator-state` and improved the slashes query to + show more info. ([\#1656](https://github.com/anoma/namada/pull/1656)) \ No newline at end of file From 532659d159889be26b1e6711e498f96d3862c84f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 11 Jul 2023 11:26:04 +0100 Subject: [PATCH 060/120] apps: move epoch-sleep client cmd under utils --- apps/src/bin/namada-client/cli.rs | 16 +++++++++------- apps/src/lib/cli.rs | 10 ++++------ tests/src/e2e/helpers.rs | 2 +- tests/src/e2e/ledger_tests.rs | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/apps/src/bin/namada-client/cli.rs b/apps/src/bin/namada-client/cli.rs index 1b1514ad52..c0db01fdf9 100644 --- a/apps/src/bin/namada-client/cli.rs +++ b/apps/src/bin/namada-client/cli.rs @@ -307,13 +307,6 @@ pub async fn main() -> Result<()> { let args = args.to_sdk(&mut ctx); rpc::query_protocol_parameters(&client, args).await; } - Sub::EpochSleep(EpochSleep(args)) => { - wait_until_node_is_synched(&args.ledger_address).await; - let client = - HttpClient::new(args.ledger_address.clone()).unwrap(); - let args = args.to_sdk(&mut ctx); - rpc::epoch_sleep(&client, args).await; - } } } cli::NamadaClient::WithoutContext(cmd, global_args) => match cmd { @@ -336,6 +329,15 @@ pub async fn main() -> Result<()> { Utils::DefaultBaseDir(DefaultBaseDir(args)) => { utils::default_base_dir(global_args, args) } + Utils::EpochSleep(EpochSleep(args)) => { + let mut ctx = cli::Context::new(global_args) + .expect("expected to construct a context"); + let ledger_address = args.ledger_address.clone(); + wait_until_node_is_synched(&ledger_address).await; + let client = HttpClient::new(ledger_address).unwrap(); + let args = args.to_sdk(&mut ctx); + rpc::epoch_sleep(&client, args).await; + } }, } Ok(()) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index a060688b28..d9dc843381 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -181,9 +181,6 @@ pub mod cmds { .subcommand(QueryProposal::def().display_order(3)) .subcommand(QueryProposalResult::def().display_order(3)) .subcommand(QueryProtocolParameters::def().display_order(3)) - // wait until next epoch (can't be in utils, needs the ledger - // address) - .subcommand(EpochSleep::def().display_order(4)) // Utils .subcommand(Utils::def().display_order(5)) } @@ -226,7 +223,6 @@ pub mod cmds { Self::parse_with_ctx(matches, QueryProposalResult); let query_protocol_parameters = Self::parse_with_ctx(matches, QueryProtocolParameters); - let epoch_sleep = Self::parse_with_ctx(matches, EpochSleep); let utils = SubCmd::parse(matches).map(Self::WithoutContext); tx_custom .or(tx_transfer) @@ -255,7 +251,6 @@ pub mod cmds { .or(query_proposal) .or(query_proposal_result) .or(query_protocol_parameters) - .or(epoch_sleep) .or(utils) } } @@ -320,7 +315,6 @@ pub mod cmds { QueryProposal(QueryProposal), QueryProposalResult(QueryProposalResult), QueryProtocolParameters(QueryProtocolParameters), - EpochSleep(EpochSleep), } #[allow(clippy::large_enum_variant)] @@ -1617,6 +1611,7 @@ pub mod cmds { InitGenesisValidator(InitGenesisValidator), PkToTmAddress(PkToTmAddress), DefaultBaseDir(DefaultBaseDir), + EpochSleep(EpochSleep), } impl SubCmd for Utils { @@ -1635,12 +1630,14 @@ pub mod cmds { SubCmd::parse(matches).map(Self::PkToTmAddress); let default_base_dir = SubCmd::parse(matches).map(Self::DefaultBaseDir); + let epoch_sleep = SubCmd::parse(matches).map(Self::EpochSleep); join_network .or(fetch_wasms) .or(init_network) .or(init_genesis) .or(pk_to_tm_address) .or(default_base_dir) + .or(epoch_sleep) }) } @@ -1653,6 +1650,7 @@ pub mod cmds { .subcommand(InitGenesisValidator::def()) .subcommand(PkToTmAddress::def()) .subcommand(DefaultBaseDir::def()) + .subcommand(EpochSleep::def()) .setting(AppSettings::SubcommandRequiredElseHelp) } } diff --git a/tests/src/e2e/helpers.rs b/tests/src/e2e/helpers.rs index 19a3f9a373..0634cdac1c 100644 --- a/tests/src/e2e/helpers.rs +++ b/tests/src/e2e/helpers.rs @@ -361,7 +361,7 @@ pub fn epoch_sleep( let mut find = run!( test, Bin::Client, - &["epoch-sleep", "--node", ledger_address], + &["utils", "epoch-sleep", "--node", ledger_address], Some(timeout_secs) )?; parse_reached_epoch(&mut find) diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 30c71afa70..070ee0092a 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -4431,7 +4431,7 @@ fn test_epoch_sleep() -> Result<()> { let start_epoch = get_epoch(&test, &validator_one_rpc).unwrap(); // 3. Use epoch-sleep to sleep for an epoch - let args = ["epoch-sleep", "--node", &validator_one_rpc]; + let args = ["utils", "epoch-sleep", "--node", &validator_one_rpc]; let mut client = run!(test, Bin::Client, &args, None)?; let reached_epoch = parse_reached_epoch(&mut client)?; client.assert_success(); From ff692abfd798dab01d9ba3aeb39e9c71aed599bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 11 Jul 2023 12:36:55 +0100 Subject: [PATCH 061/120] ci/test/e2e: add new test --- .github/workflows/scripts/e2e.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/scripts/e2e.json b/.github/workflows/scripts/e2e.json index df2b75369c..80d1a8f6cf 100644 --- a/.github/workflows/scripts/e2e.json +++ b/.github/workflows/scripts/e2e.json @@ -19,6 +19,7 @@ "e2e::ledger_tests::test_namada_shuts_down_if_tendermint_dies": 2, "e2e::ledger_tests::test_genesis_validators": 14, "e2e::ledger_tests::test_node_connectivity_and_consensus": 28, + "e2e::ledger_tests::test_epoch_sleep": 12, "e2e::wallet_tests::wallet_address_cmds": 1, "e2e::wallet_tests::wallet_encrypted_key_cmds": 1, "e2e::wallet_tests::wallet_encrypted_key_cmds_env_var": 1, From 45d0813e88e87922dc10d31877b984fe9825a7a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 11 Jul 2023 11:29:41 +0100 Subject: [PATCH 062/120] changelog: add #1621 --- .changelog/unreleased/improvements/1621-utils-next-epoch.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/improvements/1621-utils-next-epoch.md diff --git a/.changelog/unreleased/improvements/1621-utils-next-epoch.md b/.changelog/unreleased/improvements/1621-utils-next-epoch.md new file mode 100644 index 0000000000..5f663a8e00 --- /dev/null +++ b/.changelog/unreleased/improvements/1621-utils-next-epoch.md @@ -0,0 +1,2 @@ +- Added a command to wait for the next epoch: `client utils epoch-sleep`. + ([\#1621](https://github.com/anoma/namada/pull/1621)) \ No newline at end of file From 49bb2634a3f3f8829f3126d9b3090e28be0ea6eb Mon Sep 17 00:00:00 2001 From: "Raymond E. Pasco" Date: Tue, 11 Jul 2023 12:36:30 -0400 Subject: [PATCH 063/120] fixup! Merge branch 'ray/utils-next-epoch' (#1621) into draft evil changes: - wait_until_node_is_synched now takes a &client - qualified core::time::Duration in rpc epoch_sleep --- apps/src/bin/namada-client/cli.rs | 2 +- apps/src/lib/client/rpc.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/src/bin/namada-client/cli.rs b/apps/src/bin/namada-client/cli.rs index dd75d9c82b..60ad4a04c1 100644 --- a/apps/src/bin/namada-client/cli.rs +++ b/apps/src/bin/namada-client/cli.rs @@ -475,8 +475,8 @@ pub async fn main() -> Result<()> { let mut ctx = cli::Context::new(global_args) .expect("expected to construct a context"); let ledger_address = args.ledger_address.clone(); - wait_until_node_is_synched(&ledger_address).await; let client = HttpClient::new(ledger_address).unwrap(); + wait_until_node_is_synched(&client).await; let args = args.to_sdk(&mut ctx); rpc::epoch_sleep(&client, args).await; } diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 0e93a8f05a..6f2be4a9c5 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -2156,7 +2156,7 @@ pub async fn epoch_sleep( ) { let start_epoch = query_and_print_epoch(client).await; loop { - tokio::time::sleep(Duration::from_secs(1)).await; + tokio::time::sleep(core::time::Duration::from_secs(1)).await; let current_epoch = query_epoch(client).await; if current_epoch > start_epoch { println!("Reached epoch {}", current_epoch); From e0287ea3e1685c97262dc22d8688dd6298197234 Mon Sep 17 00:00:00 2001 From: bengtlofgren Date: Tue, 4 Jul 2023 11:52:28 +0200 Subject: [PATCH 064/120] add unjail tx at CLI --- apps/src/bin/namada-client/cli.rs | 14 ++++++++++++ apps/src/lib/cli.rs | 37 ++++++++++++++++++++++++------- shared/src/ledger/args.rs | 2 +- shared/src/ledger/tx.rs | 10 +++++---- 4 files changed, 50 insertions(+), 13 deletions(-) diff --git a/apps/src/bin/namada-client/cli.rs b/apps/src/bin/namada-client/cli.rs index be77d2b982..dd041d4794 100644 --- a/apps/src/bin/namada-client/cli.rs +++ b/apps/src/bin/namada-client/cli.rs @@ -234,6 +234,20 @@ pub async fn main() -> Result<()> { sdk_tx::process_tx(&client, &mut ctx.wallet, &tx_args, tx) .await?; } + Sub::TxUnjailValidator(TxUnjailValidator(mut args)) => { + let client = HttpClient::new(utils::take_config_address( + &mut args.tx.ledger_address, + )) + .unwrap(); + wait_until_node_is_synched(&client) + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + tx::submit_unjail_validator::( + &client, ctx, args, + ) + .await?; + } // Ledger queries Sub::QueryEpoch(QueryEpoch(mut args)) => { let client = HttpClient::new(utils::take_config_address( diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 00a24c45b2..4e3bc9c88c 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -27,6 +27,7 @@ const WALLET_CMD: &str = "wallet"; const RELAYER_CMD: &str = "relayer"; pub mod cmds { + use super::utils::*; use super::{ args, ArgMatches, CLIENT_CMD, NODE_CMD, RELAYER_CMD, WALLET_CMD, @@ -216,6 +217,7 @@ pub mod cmds { .subcommand(TxVoteProposal::def().display_order(1)) // PoS transactions .subcommand(TxInitValidator::def().display_order(2)) + .subcommand(TxUnjailValidator::def().display_order(2)) .subcommand(Bond::def().display_order(2)) .subcommand(Unbond::def().display_order(2)) .subcommand(Withdraw::def().display_order(2)) @@ -252,6 +254,8 @@ pub mod cmds { let tx_init_account = Self::parse_with_ctx(matches, TxInitAccount); let tx_init_validator = Self::parse_with_ctx(matches, TxInitValidator); + let tx_unjail_validator = + Self::parse_with_ctx(matches, TxUnjailValidator); let tx_reveal_pk = Self::parse_with_ctx(matches, TxRevealPk); let tx_init_proposal = Self::parse_with_ctx(matches, TxInitProposal); @@ -298,6 +302,7 @@ pub mod cmds { .or(tx_vote_proposal) .or(tx_init_validator) .or(tx_commission_rate_change) + .or(tx_unjail_validator) .or(bond) .or(unbond) .or(withdraw) @@ -363,6 +368,7 @@ pub mod cmds { TxInitAccount(TxInitAccount), TxInitValidator(TxInitValidator), TxCommissionRateChange(TxCommissionRateChange), + TxUnjailValidator(TxUnjailValidator), TxInitProposal(TxInitProposal), TxVoteProposal(TxVoteProposal), TxRevealPk(TxRevealPk), @@ -1287,6 +1293,27 @@ pub mod cmds { } } + #[derive(Clone, Debug)] + pub struct TxUnjailValidator(pub args::TxUnjailValidator); + + impl SubCmd for TxUnjailValidator { + const CMD: &'static str = "unjail-validator"; + + fn parse(matches: &ArgMatches) -> Option { + matches.subcommand_matches(Self::CMD).map(|matches| { + TxUnjailValidator(args::TxUnjailValidator::parse(matches)) + }) + } + + fn def() -> App { + App::new(Self::CMD) + .about( + "Send a signed transaction to unjail a jailed validator.", + ) + .add_args::>() + } + } + #[derive(Clone, Debug)] pub struct Bond(pub args::Bond); @@ -4126,16 +4153,10 @@ pub mod args { impl CliToSdk> for TxUnjailValidator { fn to_sdk(self, ctx: &mut Context) -> TxUnjailValidator { - TxUnjailValidator { + TxUnjailValidator:: { tx: self.tx.to_sdk(ctx), validator: ctx.get(&self.validator), - tx_code_path: self - .tx_code_path - .as_path() - .to_str() - .unwrap() - .to_string() - .into_bytes(), + tx_code_path: self.tx_code_path.to_path_buf(), } } } diff --git a/shared/src/ledger/args.rs b/shared/src/ledger/args.rs index 47117ba594..73b2b665dc 100644 --- a/shared/src/ledger/args.rs +++ b/shared/src/ledger/args.rs @@ -388,7 +388,7 @@ pub struct TxUnjailValidator { /// Validator address (should be self) pub validator: C::Address, /// Path to the TX WASM code file - pub tx_code_path: C::Data, + pub tx_code_path: PathBuf, } /// Query PoS commission rate diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index 36644a3e14..edb42ce920 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -725,9 +725,10 @@ pub async fn build_unjail_validator< } } - let tx_code_path = String::from_utf8(args.tx_code_path).unwrap(); let tx_code_hash = - query_wasm_code_hash(client, tx_code_path).await.unwrap(); + query_wasm_code_hash(client, args.tx_code_path.to_str().unwrap()) + .await + .unwrap(); let data = args .validator @@ -770,9 +771,10 @@ pub async fn submit_unjail_validator< } } - let tx_code_path = String::from_utf8(args.tx_code_path).unwrap(); let tx_code_hash = - query_wasm_code_hash(client, tx_code_path).await.unwrap(); + query_wasm_code_hash(client, args.tx_code_path.to_str().unwrap()) + .await + .unwrap(); let data = args .validator From fc528cc0261b9ab80c1e9d26ddc68af9e92ab84c Mon Sep 17 00:00:00 2001 From: brentstone Date: Tue, 4 Jul 2023 15:57:35 +0200 Subject: [PATCH 065/120] expand and fix e2e test `double_signing_gets_slashed` --- tests/src/e2e/ledger_tests.rs | 146 ++++++++++++++++++++++++++++++++-- 1 file changed, 141 insertions(+), 5 deletions(-) diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index ca7bd974c7..eb5b4196ce 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -4377,9 +4377,20 @@ fn double_signing_gets_slashed() -> Result<()> { use namada_apps::client; use namada_apps::config::Config; + let mut pipeline_len = 0; + let mut unbonding_len = 0; + let mut cubic_offset = 0; + // Setup 2 genesis validator nodes let test = setup::network( - |genesis| setup::set_validators(2, genesis, default_port_offset), + |genesis| { + (pipeline_len, unbonding_len, cubic_offset) = ( + genesis.pos_params.pipeline_len, + genesis.pos_params.unbonding_len, + genesis.pos_params.cubic_slashing_window_length, + ); + setup::set_validators(4, genesis, default_port_offset) + }, None, )?; @@ -4397,6 +4408,7 @@ fn double_signing_gets_slashed() -> Result<()> { ethereum_bridge::ledger::Mode::Off, None, ); + println!("pipeline_len: {}", pipeline_len); // 1. Run 2 genesis validator ledger nodes let args = ["ledger"]; @@ -4405,12 +4417,25 @@ fn double_signing_gets_slashed() -> Result<()> { validator_0.exp_string("Namada ledger node started")?; validator_0.exp_string("This node is a validator")?; let _bg_validator_0 = validator_0.background(); + let mut validator_1 = run_as!(test, Who::Validator(1), Bin::Node, args, Some(40))?; validator_1.exp_string("Namada ledger node started")?; validator_1.exp_string("This node is a validator")?; let bg_validator_1 = validator_1.background(); + let mut validator_2 = + run_as!(test, Who::Validator(2), Bin::Node, &["ledger"], Some(40))?; + validator_2.exp_string("Namada ledger node started")?; + validator_2.exp_string("This node is a validator")?; + let _bg_validator_2 = validator_2.background(); + + let mut validator_3 = + run_as!(test, Who::Validator(3), Bin::Node, &["ledger"], Some(40))?; + validator_3.exp_string("Namada ledger node started")?; + validator_3.exp_string("This node is a validator")?; + let _bg_validator_3 = validator_3.background(); + // 2. Copy the first genesis validator base-dir let validator_0_base_dir = test.get_base_dir(&Who::Validator(0)); let validator_0_base_dir_copy = @@ -4432,7 +4457,7 @@ fn double_signing_gets_slashed() -> Result<()> { let net_address_port_0 = net_address_0.port(); let update_config = |ix: u8, mut config: Config| { - let first_port = net_address_port_0 + 6 * (ix as u16 + 1); + let first_port = net_address_port_0 + 26 * (ix as u16 + 1); let p2p_addr = convert_tm_addr_to_socket_addr(&config.ledger.cometbft.p2p.laddr) .ip() @@ -4489,14 +4514,14 @@ fn double_signing_gets_slashed() -> Result<()> { let mut validator_0_copy = setup::run_cmd( Bin::Node, args, - Some(40), + Some(180), &test.working_dir, validator_0_base_dir_copy, loc, )?; validator_0_copy.exp_string("Namada ledger node started")?; validator_0_copy.exp_string("This node is a validator")?; - wait_for_wasm_pre_compile(&mut validator_0_copy)?; + // wait_for_wasm_pre_compile(&mut validator_0_copy)?; let _bg_validator_0_copy = validator_0_copy.background(); // 5. Submit a valid token transfer tx to validator 0 @@ -4528,7 +4553,118 @@ fn double_signing_gets_slashed() -> Result<()> { // 6. Wait for double signing evidence let mut validator_1 = bg_validator_1.foreground(); validator_1.exp_string("Processing evidence")?; - validator_1.exp_string("Slashing")?; + // validator_1.exp_string("Slashing")?; + + println!("\nPARSING SLASH MESSAGE\n"); + let (_, res) = validator_1 + .exp_regex(r"Slashing [a-z0-9]+ for Duplicate vote in epoch [0-9]+") + .unwrap(); + println!("\n{res}\n"); + let _bg_validator_1 = validator_1.background(); + + let exp_processing_epoch = Epoch::from_str(res.split(' ').last().unwrap()) + .unwrap() + + unbonding_len + + cubic_offset + + 1u64; + + // Query slashes + // let tx_args = ["slashes", "--node", &validator_one_rpc]; + // let client = run!(test, Bin::Client, tx_args, Some(40))?; + + let mut client = run!( + test, + Bin::Client, + &["slashes", "--node", &validator_one_rpc], + Some(40) + )?; + client.exp_string("No processed slashes found")?; + client.exp_string("Enqueued slashes for future processing")?; + let (_, res) = client + .exp_regex(r"To be processed in epoch [0-9]+") + .unwrap(); + let processing_epoch = + Epoch::from_str(res.split(' ').last().unwrap()).unwrap(); + + assert_eq!(processing_epoch, exp_processing_epoch); + + println!("\n{processing_epoch}\n"); + + // 6. Wait for processing epoch + loop { + let epoch = epoch_sleep(&test, &validator_one_rpc, 240)?; + println!("\nCurrent epoch: {}", epoch); + if epoch > processing_epoch { + break; + } + } + + let mut client = run!( + test, + Bin::Client, + &[ + "validator-state", + "--validator", + "validator-0", + "--node", + &validator_one_rpc + ], + Some(40) + )?; + let _ = client.exp_regex(r"Validator [a-z0-9]+ is jailed").unwrap(); + + let mut client = run!( + test, + Bin::Client, + &["slashes", "--node", &validator_one_rpc], + Some(40) + )?; + client.exp_string("Processed slashes:")?; + client.exp_string("No enqueued slashes found")?; + + let tx_args = vec![ + "unjail-validator", + "--validator", + "validator-0", + "--gas-amount", + "0", + "--gas-limit", + "0", + "--gas-token", + NAM, + "--node", + &validator_one_rpc, + ]; + let mut client = + run_as!(test, Who::Validator(0), Bin::Client, tx_args, Some(40))?; + client.exp_string("Transaction applied with result:")?; + client.exp_string("Transaction is valid.")?; + client.assert_success(); + + // Wait until pipeline epoch to see if the validator is back in consensus + let cur_epoch = epoch_sleep(&test, &validator_one_rpc, 240)?; + loop { + let epoch = epoch_sleep(&test, &validator_one_rpc, 240)?; + println!("\nCurrent epoch: {}", epoch); + if epoch > cur_epoch + pipeline_len + 1u64 { + break; + } + } + let mut client = run!( + test, + Bin::Client, + &[ + "validator-state", + "--validator", + "validator-0", + "--node", + &validator_one_rpc + ], + Some(40) + )?; + let _ = client + .exp_regex(r"Validator [a-z0-9]+ is in the .* set") + .unwrap(); Ok(()) } From a5d223787b67dcf3a6895ce2071a7c20b571043c Mon Sep 17 00:00:00 2001 From: brentstone Date: Thu, 6 Jul 2023 15:28:58 +0200 Subject: [PATCH 066/120] handle errors for unjail-validator tx in the client --- shared/src/ledger/queries/vp/pos.rs | 12 ++++++ shared/src/ledger/rpc.rs | 26 +++++++++++- shared/src/ledger/tx.rs | 63 ++++++++++++++++++++++++++--- 3 files changed, 94 insertions(+), 7 deletions(-) diff --git a/shared/src/ledger/queries/vp/pos.rs b/shared/src/ledger/queries/vp/pos.rs index 177b31ed87..075b936e25 100644 --- a/shared/src/ledger/queries/vp/pos.rs +++ b/shared/src/ledger/queries/vp/pos.rs @@ -5,6 +5,7 @@ use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use namada_core::ledger::storage_api::collections::lazy_map; use namada_core::ledger::storage_api::OptionExt; +use namada_proof_of_stake::parameters::PosParams; use namada_proof_of_stake::types::{ BondId, BondsAndUnbondsDetail, BondsAndUnbondsDetails, CommissionPair, Slash, ValidatorState, WeightedValidator, @@ -60,6 +61,8 @@ router! {POS, // TODO: add "below_threshold" }, + ( "pos_params") -> PosParams = pos_params, + ( "total_stake" / [epoch: opt Epoch] ) -> token::Amount = total_stake, @@ -141,6 +144,15 @@ impl Enriched { // Handlers that implement the functions via `trait StorageRead`: +/// Get the PoS parameters +fn pos_params(ctx: RequestCtx<'_, D, H>) -> storage_api::Result +where + D: 'static + DB + for<'iter> DBIter<'iter> + Sync, + H: 'static + StorageHasher + Sync, +{ + read_pos_params(ctx.wl_storage) +} + /// Find if the given address belongs to a validator account. fn is_validator( ctx: RequestCtx<'_, D, H>, diff --git a/shared/src/ledger/rpc.rs b/shared/src/ledger/rpc.rs index 90d05ebd13..38e8777c0a 100644 --- a/shared/src/ledger/rpc.rs +++ b/shared/src/ledger/rpc.rs @@ -15,7 +15,10 @@ use namada_core::types::storage::Key; use namada_core::types::token::{ Amount, DenominatedAmount, Denomination, MaspDenom, TokenAddress, }; -use namada_proof_of_stake::types::{BondsAndUnbondsDetails, CommissionPair}; +use namada_proof_of_stake::parameters::PosParams; +use namada_proof_of_stake::types::{ + BondsAndUnbondsDetails, CommissionPair, ValidatorState, +}; use serde::Serialize; use crate::ledger::args::InputAmount; @@ -696,6 +699,13 @@ pub async fn get_proposal_votes( } } +/// Get the PoS parameters +pub async fn get_pos_params( + client: &C, +) -> PosParams { + unwrap_client_response::(RPC.vp().pos().pos_params(client).await) +} + /// Get all validators in the given epoch pub async fn get_all_validators( client: &C, @@ -736,6 +746,20 @@ pub async fn get_validator_stake( .unwrap_or_default() } +/// Query and return a validator's state +pub async fn get_validator_state( + client: &C, + validator: &Address, + epoch: Option, +) -> Option { + unwrap_client_response::>( + RPC.vp() + .pos() + .validator_state(client, validator, &epoch) + .await, + ) +} + /// Get the delegator's delegation pub async fn get_delegators_delegation< C: crate::ledger::queries::Client + Sync, diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index edb42ce920..b82372eb7c 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -20,7 +20,7 @@ use namada_core::types::dec::Dec; use namada_core::types::storage::Key; use namada_core::types::token::MaspDenom; use namada_proof_of_stake::parameters::PosParams; -use namada_proof_of_stake::types::CommissionPair; +use namada_proof_of_stake::types::{CommissionPair, ValidatorState}; use prost::EncodeError; use thiserror::Error; @@ -110,6 +110,18 @@ pub enum Error { /// Invalid validator address #[error("The address {0} doesn't belong to any known validator account.")] InvalidValidatorAddress(Address), + /// Not jailed at pipeline epoch + #[error( + "The validator address {0} is not jailed at epoch when it would be \ + restored." + )] + ValidatorNotCurrentlyJailed(Address), + /// Validator still frozen and ineligible to be unjailed + #[error( + "The validator address {0} is currently frozen and ineligible to be \ + unjailed." + )] + ValidatorFrozenFromUnjailing(Address), /// Rate of epoch change too large for current epoch #[error( "New rate, {0}, is too large of a change with respect to the \ @@ -631,11 +643,7 @@ pub async fn build_validator_commission_change< .await .unwrap(); - // TODO: put following two let statements in its own function - let params_key = crate::ledger::pos::params_key(); - let params = rpc::query_storage_value::(client, ¶ms_key) - .await - .expect("Parameter should be defined."); + let params: PosParams = rpc::get_pos_params(client).await; let validator = args.validator.clone(); if rpc::is_validator(client, &validator).await { @@ -771,6 +779,49 @@ pub async fn submit_unjail_validator< } } + let params: PosParams = rpc::get_pos_params(client).await; + let current_epoch = rpc::query_epoch(client).await; + let pipeline_epoch = current_epoch + params.pipeline_len; + + let validator_state_at_pipeline = + rpc::get_validator_state(client, &args.validator, Some(pipeline_epoch)) + .await + .expect("Validator state should be defined."); + if validator_state_at_pipeline != ValidatorState::Jailed { + eprintln!( + "The given validator address {} is not jailed at the pipeline \ + epoch when it would be restored to one of the validator sets.", + &args.validator + ); + if !args.tx.force { + return Err(Error::ValidatorNotCurrentlyJailed( + args.validator.clone(), + )); + } + } + + let last_slash_epoch_key = + crate::ledger::pos::validator_last_slash_key(&args.validator); + let last_slash_epoch = + rpc::query_storage_value::(client, &last_slash_epoch_key) + .await; + if let Some(last_slash_epoch) = last_slash_epoch { + let eligible_epoch = + last_slash_epoch + params.slash_processing_epoch_offset(); + if current_epoch < eligible_epoch { + eprintln!( + "The given validator address {} is currently frozen and not \ + yet eligible to be unjailed.", + &args.validator + ); + if !args.tx.force { + return Err(Error::ValidatorNotCurrentlyJailed( + args.validator.clone(), + )); + } + } + } + let tx_code_hash = query_wasm_code_hash(client, args.tx_code_path.to_str().unwrap()) .await From 079d5c133bd18067ec28ec0df88e1492aefd587b Mon Sep 17 00:00:00 2001 From: yito88 Date: Tue, 11 Jul 2023 20:53:29 +0200 Subject: [PATCH 067/120] fix client_connections encoding --- core/src/ledger/ibc/context/execution.rs | 6 ++++-- shared/src/ledger/ibc/vp/mod.rs | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/core/src/ledger/ibc/context/execution.rs b/core/src/ledger/ibc/context/execution.rs index ddf289ce08..ae84afa614 100644 --- a/core/src/ledger/ibc/context/execution.rs +++ b/core/src/ledger/ibc/context/execution.rs @@ -1,5 +1,7 @@ //! ExecutionContext implementation for IBC +use borsh::{BorshDeserialize, BorshSerialize}; + use super::super::{IbcActions, IbcCommonContext}; use crate::ibc::core::ics02_client::client_state::ClientState; use crate::ibc::core::ics02_client::client_type::ClientType; @@ -180,7 +182,7 @@ where .expect("Creating a key for the client state shouldn't fail"); let list = match self.ctx.borrow().read(&key) { Ok(Some(value)) => { - let list = String::from_utf8(value).map_err(|e| { + let list = String::try_from_slice(&value).map_err(|e| { ContextError::ConnectionError(ConnectionError::Other { description: format!( "Decoding the connection list failed: Key {}, \ @@ -201,7 +203,7 @@ where }))? } }; - let bytes = list.as_bytes().to_vec(); + let bytes = list.try_to_vec().expect("encoding shouldn't fail"); self.ctx.borrow_mut().write(&key, bytes).map_err(|_| { ContextError::ConnectionError(ConnectionError::Other { description: format!( diff --git a/shared/src/ledger/ibc/vp/mod.rs b/shared/src/ledger/ibc/vp/mod.rs index 619e484ba2..04c980a21b 100644 --- a/shared/src/ledger/ibc/vp/mod.rs +++ b/shared/src/ledger/ibc/vp/mod.rs @@ -1047,7 +1047,7 @@ mod tests { // client connection list let client_conn_key = client_connections_key(&msg.client_id_on_a); let conn_list = conn_id.to_string(); - let bytes = conn_list.as_bytes().to_vec(); + let bytes = conn_list.try_to_vec().expect("encoding failed"); wl_storage .write_log .write(&client_conn_key, bytes) @@ -1154,7 +1154,7 @@ mod tests { // client connection list let client_conn_key = client_connections_key(&msg.client_id_on_a); let conn_list = conn_id.to_string(); - let bytes = conn_list.as_bytes().to_vec(); + let bytes = conn_list.try_to_vec().expect("encoding failed"); wl_storage .write_log .write(&client_conn_key, bytes) @@ -1270,7 +1270,7 @@ mod tests { // client connection list let client_conn_key = client_connections_key(&msg.client_id_on_b); let conn_list = conn_id.to_string(); - let bytes = conn_list.as_bytes().to_vec(); + let bytes = conn_list.try_to_vec().expect("encoding failed"); wl_storage .write_log .write(&client_conn_key, bytes) From 7f45cb27bc1eef3a82b10a5a174d13211f6350bb Mon Sep 17 00:00:00 2001 From: brentstone Date: Tue, 11 Jul 2023 21:00:36 -0400 Subject: [PATCH 068/120] Revert "Merge branch 'bengt/pos-cli-queries' (#1656)" This reverts commit 3dd72457ad6f992561d9301b2b7063691187c121, reversing changes made to 119210b1ad9b07184f618e31ff659fae026fc4f5. --- .../improvements/1656-pos-cli-queries.md | 2 - apps/src/bin/namada-client/cli.rs | 16 -- apps/src/lib/cli.rs | 68 +------- apps/src/lib/client/rpc.rs | 156 ++---------------- proof_of_stake/src/lib.rs | 40 +---- shared/src/ledger/args.rs | 11 -- shared/src/ledger/queries/vp/pos.rs | 111 ++++++------- wasm/checksums.json | 2 +- wasm/wasm_source/src/tx_bond.rs | 6 +- wasm/wasm_source/src/tx_unbond.rs | 4 +- 10 files changed, 84 insertions(+), 332 deletions(-) delete mode 100644 .changelog/unreleased/improvements/1656-pos-cli-queries.md diff --git a/.changelog/unreleased/improvements/1656-pos-cli-queries.md b/.changelog/unreleased/improvements/1656-pos-cli-queries.md deleted file mode 100644 index a114c6f2f6..0000000000 --- a/.changelog/unreleased/improvements/1656-pos-cli-queries.md +++ /dev/null @@ -1,2 +0,0 @@ -- Added a client query for `validator-state` and improved the slashes query to - show more info. ([\#1656](https://github.com/anoma/namada/pull/1656)) \ No newline at end of file diff --git a/apps/src/bin/namada-client/cli.rs b/apps/src/bin/namada-client/cli.rs index 60ad4a04c1..dbdb4bc516 100644 --- a/apps/src/bin/namada-client/cli.rs +++ b/apps/src/bin/namada-client/cli.rs @@ -325,22 +325,6 @@ pub async fn main() -> Result<()> { let args = args.to_sdk(&mut ctx); rpc::query_bonded_stake(&client, args).await; } - Sub::QueryValidatorState(QueryValidatorState(mut args)) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk(&mut ctx); - rpc::query_and_print_validator_state( - &client, - &mut ctx.wallet, - args, - ) - .await; - } Sub::QueryCommissionRate(QueryCommissionRate(mut args)) => { let client = HttpClient::new(utils::take_config_address( &mut args.query.ledger_address, diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index e55f79a930..40f459d10a 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -381,7 +381,6 @@ pub mod cmds { QueryProposal(QueryProposal), QueryProposalResult(QueryProposalResult), QueryProtocolParameters(QueryProtocolParameters), - QueryValidatorState(QueryValidatorState), } #[allow(clippy::large_enum_variant)] @@ -1454,27 +1453,6 @@ pub mod cmds { } } - #[derive(Clone, Debug)] - pub struct QueryValidatorState( - pub args::QueryValidatorState, - ); - - impl SubCmd for QueryValidatorState { - const CMD: &'static str = "validator-state"; - - fn parse(matches: &ArgMatches) -> Option { - matches.subcommand_matches(Self::CMD).map(|matches| { - QueryValidatorState(args::QueryValidatorState::parse(matches)) - }) - } - - fn def() -> App { - App::new(Self::CMD) - .about("Query the state of a PoS validator.") - .add_args::>() - } - } - #[derive(Clone, Debug)] pub struct QueryTransfers(pub args::QueryTransfers); @@ -1510,7 +1488,7 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) - .about("Query a validator's commission rate.") + .about("Query commission rate.") .add_args::>() } } @@ -4064,44 +4042,8 @@ pub mod args { "The validator's address whose bonded stake to query.", )) .arg(EPOCH.def().help( - "The epoch at which to query (corresponding to the last \ - committed block, if not specified).", - )) - } - } - - impl CliToSdk> for QueryValidatorState { - fn to_sdk(self, ctx: &mut Context) -> QueryValidatorState { - QueryValidatorState:: { - query: self.query.to_sdk(ctx), - validator: ctx.get(&self.validator), - epoch: self.epoch, - } - } - } - - impl Args for QueryValidatorState { - fn parse(matches: &ArgMatches) -> Self { - let query = Query::parse(matches); - let validator = VALIDATOR.parse(matches); - let epoch = EPOCH.parse(matches); - Self { - query, - validator, - epoch, - } - } - - fn def(app: App) -> App { - app.add_args::>() - .arg( - VALIDATOR.def().help( - "The validator's address whose state is queried.", - ), - ) - .arg(EPOCH.def().help( - "The epoch at which to query (corresponding to the last \ - committed block, if not specified).", + "The epoch at which to query (last committed, if not \ + specified).", )) } } @@ -4211,8 +4153,8 @@ pub mod args { "The validator's address whose commission rate to query.", )) .arg(EPOCH.def().help( - "The epoch at which to query (corresponding to the last \ - committed block, if not specified).", + "The epoch at which to query (last committed, if not \ + specified).", )) } } diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 6f2be4a9c5..2b0b71650e 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -1,7 +1,7 @@ //! Client RPC queries use std::cmp::Ordering; -use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use std::collections::{HashMap, HashSet}; use std::fs::File; use std::io::{self, Write}; use std::iter::Iterator; @@ -36,7 +36,7 @@ use namada::ledger::rpc::{ }; use namada::ledger::storage::ConversionState; use namada::ledger::wallet::{AddressVpType, Wallet}; -use namada::proof_of_stake::types::{ValidatorState, WeightedValidator}; +use namada::proof_of_stake::types::WeightedValidator; use namada::types::address::{masp, Address}; use namada::types::control_flow::ProceedOrElse; use namada::types::governance::{ @@ -1567,14 +1567,14 @@ pub async fn query_bonded_stake( } None => { let consensus = - unwrap_client_response::>( + unwrap_client_response::>( RPC.vp() .pos() .consensus_validator_set(client, &Some(epoch)) .await, ); let below_capacity = - unwrap_client_response::>( + unwrap_client_response::>( RPC.vp() .pos() .below_capacity_validator_set(client, &Some(epoch)) @@ -1586,7 +1586,7 @@ pub async fn query_bonded_stake( let mut w = stdout.lock(); writeln!(w, "Consensus validators:").unwrap(); - for val in consensus.into_iter().rev() { + for val in consensus { writeln!( w, " {}: {}", @@ -1597,7 +1597,7 @@ pub async fn query_bonded_stake( } if !below_capacity.is_empty() { writeln!(w, "Below capacity validators:").unwrap(); - for val in below_capacity.into_iter().rev() { + for val in &below_capacity { writeln!( w, " {}: {}", @@ -1634,60 +1634,6 @@ pub async fn query_commission_rate< ) } -/// Query and return validator's state -pub async fn query_validator_state< - C: namada::ledger::queries::Client + Sync, ->( - client: &C, - validator: &Address, - epoch: Option, -) -> Option { - unwrap_client_response::>( - RPC.vp() - .pos() - .validator_state(client, validator, &epoch) - .await, - ) -} - -/// Query a validator's state information -pub async fn query_and_print_validator_state< - C: namada::ledger::queries::Client + Sync, ->( - client: &C, - _wallet: &mut Wallet, - args: args::QueryValidatorState, -) { - let validator = args.validator; - let state: Option = - query_validator_state(client, &validator, args.epoch).await; - - match state { - Some(state) => match state { - ValidatorState::Consensus => { - println!("Validator {validator} is in the consensus set") - } - ValidatorState::BelowCapacity => { - println!("Validator {validator} is in the below-capacity set") - } - ValidatorState::BelowThreshold => { - println!("Validator {validator} is in the below-threshold set") - } - ValidatorState::Inactive => { - println!("Validator {validator} is inactive") - } - ValidatorState::Jailed => { - println!("Validator {validator} is jailed") - } - }, - None => println!( - "Validator {validator} is either not a validator, or an epoch \ - before the current epoch has been queried (and the validator \ - state information is no longer stored)" - ), - } -} - /// Query PoS validator's commission rate information pub async fn query_and_print_commission_rate< C: namada::ledger::queries::Client + Sync, @@ -1728,6 +1674,11 @@ pub async fn query_slashes( _wallet: &mut Wallet, args: args::QuerySlashes, ) { + let params_key = pos::params_key(); + let params = query_storage_value::(client, ¶ms_key) + .await + .expect("Parameter should be defined."); + match args.validator { Some(validator) => { let validator = validator; @@ -1736,54 +1687,18 @@ pub async fn query_slashes( RPC.vp().pos().validator_slashes(client, &validator).await, ); if !slashes.is_empty() { - println!("Processed slashes:"); let stdout = io::stdout(); let mut w = stdout.lock(); for slash in slashes { writeln!( w, - "Infraction epoch {}, block height {}, type {}, rate \ - {}", - slash.epoch, - slash.block_height, - slash.r#type, - slash.rate + "Slash epoch {}, type {}, rate {}", + slash.epoch, slash.r#type, slash.rate ) .unwrap(); } } else { - println!( - "No processed slashes found for {}", - validator.encode() - ) - } - // Find enqueued slashes to be processed in the future for the given - // validator - let enqueued_slashes: HashMap< - Address, - BTreeMap>, - > = unwrap_client_response::< - C, - HashMap>>, - >(RPC.vp().pos().enqueued_slashes(client).await); - let enqueued_slashes = enqueued_slashes.get(&validator).cloned(); - if let Some(enqueued) = enqueued_slashes { - println!("\nEnqueued slashes for future processing"); - for (epoch, slashes) in enqueued { - println!("To be processed in epoch {}", epoch); - for slash in slashes { - let stdout = io::stdout(); - let mut w = stdout.lock(); - writeln!( - w, - "Infraction epoch {}, block height {}, type {}", - slash.epoch, slash.block_height, slash.r#type, - ) - .unwrap(); - } - } - } else { - println!("No enqueued slashes found for {}", validator.encode()) + println!("No slashes found for {}", validator.encode()) } } None => { @@ -1795,16 +1710,15 @@ pub async fn query_slashes( if !all_slashes.is_empty() { let stdout = io::stdout(); let mut w = stdout.lock(); - println!("Processed slashes:"); for (validator, slashes) in all_slashes.into_iter() { for slash in slashes { writeln!( w, - "Infraction epoch {}, block height {}, rate {}, \ - type {}, validator {}", + "Slash epoch {}, block height {}, rate {}, type \ + {}, validator {}", slash.epoch, slash.block_height, - slash.rate, + slash.r#type.get_slash_rate(¶ms), slash.r#type, validator, ) @@ -1812,41 +1726,7 @@ pub async fn query_slashes( } } } else { - println!("No processed slashes found") - } - - // Find enqueued slashes to be processed in the future for the given - // validator - let enqueued_slashes: HashMap< - Address, - BTreeMap>, - > = unwrap_client_response::< - C, - HashMap>>, - >(RPC.vp().pos().enqueued_slashes(client).await); - if !enqueued_slashes.is_empty() { - println!("\nEnqueued slashes for future processing"); - for (validator, slashes_by_epoch) in enqueued_slashes { - for (epoch, slashes) in slashes_by_epoch { - println!("\nTo be processed in epoch {}", epoch); - for slash in slashes { - let stdout = io::stdout(); - let mut w = stdout.lock(); - writeln!( - w, - "Infraction epoch {}, block height {}, type \ - {}, validator {}", - slash.epoch, - slash.block_height, - slash.r#type, - validator - ) - .unwrap(); - } - } - } - } else { - println!("\nNo enqueued slashes found for future processing") + println!("No slashes found") } } } diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index 538eb105f2..805d79443c 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -756,7 +756,7 @@ where pub fn read_consensus_validator_set_addresses_with_stake( storage: &S, epoch: namada_core::types::storage::Epoch, -) -> storage_api::Result> +) -> storage_api::Result> where S: StorageRead, { @@ -800,7 +800,7 @@ where pub fn read_below_capacity_validator_set_addresses_with_stake( storage: &S, epoch: namada_core::types::storage::Epoch, -) -> storage_api::Result> +) -> storage_api::Result> where S: StorageRead, { @@ -2827,42 +2827,6 @@ where } } -/// Collect the details of all of the enqueued slashes to be processed in future -/// epochs into a nested map -pub fn find_all_enqueued_slashes( - storage: &S, - epoch: Epoch, -) -> storage_api::Result>>> -where - S: StorageRead, -{ - let mut enqueued = HashMap::>>::new(); - for res in enqueued_slashes_handle().get_data_handler().iter(storage)? { - let ( - NestedSubKey::Data { - key: processing_epoch, - nested_sub_key: - NestedSubKey::Data { - key: address, - nested_sub_key: _, - }, - }, - slash, - ) = res?; - if processing_epoch <= epoch { - continue; - } - - let slashes = enqueued - .entry(address) - .or_default() - .entry(processing_epoch) - .or_default(); - slashes.push(slash); - } - Ok(enqueued) -} - /// Find all slashes and the associated validators in the PoS system pub fn find_all_slashes( storage: &S, diff --git a/shared/src/ledger/args.rs b/shared/src/ledger/args.rs index 47117ba594..35081d3283 100644 --- a/shared/src/ledger/args.rs +++ b/shared/src/ledger/args.rs @@ -356,17 +356,6 @@ pub struct QueryBondedStake { pub epoch: Option, } -/// Query the state of a validator (its validator set or if it is jailed) -#[derive(Clone, Debug)] -pub struct QueryValidatorState { - /// Common query args - pub query: Query, - /// Address of a validator - pub validator: C::Address, - /// Epoch in which to find the validator state - pub epoch: Option, -} - #[derive(Clone, Debug)] /// Commission rate change args pub struct CommissionRateChange { diff --git a/shared/src/ledger/queries/vp/pos.rs b/shared/src/ledger/queries/vp/pos.rs index 177b31ed87..d872aa5002 100644 --- a/shared/src/ledger/queries/vp/pos.rs +++ b/shared/src/ledger/queries/vp/pos.rs @@ -1,23 +1,21 @@ //! Queries router and handlers for PoS validity predicate -use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use std::collections::{HashMap, HashSet}; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use namada_core::ledger::storage_api::collections::lazy_map; use namada_core::ledger::storage_api::OptionExt; use namada_proof_of_stake::types::{ BondId, BondsAndUnbondsDetail, BondsAndUnbondsDetails, CommissionPair, - Slash, ValidatorState, WeightedValidator, + Slash, WeightedValidator, }; use namada_proof_of_stake::{ - self, bond_amount, bond_handle, find_all_enqueued_slashes, - find_all_slashes, find_delegation_validators, find_delegations, - read_all_validator_addresses, - read_below_capacity_validator_set_addresses_with_stake, - read_consensus_validator_set_addresses_with_stake, read_pos_params, - read_total_stake, read_validator_max_commission_rate_change, - read_validator_stake, unbond_handle, validator_commission_rate_handle, - validator_slashes_handle, validator_state_handle, + self, below_capacity_validator_set_handle, bond_amount, bond_handle, + consensus_validator_set_handle, find_all_slashes, + find_delegation_validators, find_delegations, read_all_validator_addresses, + read_pos_params, read_total_stake, + read_validator_max_commission_rate_change, read_validator_stake, + unbond_handle, validator_commission_rate_handle, validator_slashes_handle, }; use crate::ledger::queries::types::RequestCtx; @@ -45,17 +43,14 @@ router! {POS, ( "commission" / [validator: Address] / [epoch: opt Epoch] ) -> Option = validator_commission, - - ( "state" / [validator: Address] / [epoch: opt Epoch] ) - -> Option = validator_state, }, ( "validator_set" ) = { ( "consensus" / [epoch: opt Epoch] ) - -> BTreeSet = consensus_validator_set, + -> HashSet = consensus_validator_set, ( "below_capacity" / [epoch: opt Epoch] ) - -> BTreeSet = below_capacity_validator_set, + -> HashSet = below_capacity_validator_set, // TODO: add "below_threshold" }, @@ -87,9 +82,6 @@ router! {POS, ( "bonds_and_unbonds" / [source: opt Address] / [validator: opt Address] ) -> BondsAndUnbondsDetails = bonds_and_unbonds, - ( "enqueued_slashes" ) - -> HashMap>> = enqueued_slashes, - ( "all_slashes" ) -> HashMap> = slashes, ( "is_delegator" / [addr: Address ] / [epoch: opt Epoch] ) -> bool = is_delegator, @@ -211,26 +203,6 @@ where } } -/// Get the validator state -fn validator_state( - ctx: RequestCtx<'_, D, H>, - validator: Address, - epoch: Option, -) -> storage_api::Result> -where - D: 'static + DB + for<'iter> DBIter<'iter> + Sync, - H: 'static + StorageHasher + Sync, -{ - let epoch = epoch.unwrap_or(ctx.wl_storage.storage.last_epoch); - let params = read_pos_params(ctx.wl_storage)?; - let state = validator_state_handle(&validator).get( - ctx.wl_storage, - epoch, - ¶ms, - )?; - Ok(state) -} - /// Get the total stake of a validator at the given epoch or current when /// `None`. The total stake is a sum of validator's self-bonds and delegations /// to their address. @@ -254,29 +226,64 @@ where fn consensus_validator_set( ctx: RequestCtx<'_, D, H>, epoch: Option, -) -> storage_api::Result> +) -> storage_api::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { let epoch = epoch.unwrap_or(ctx.wl_storage.storage.last_epoch); - read_consensus_validator_set_addresses_with_stake(ctx.wl_storage, epoch) + consensus_validator_set_handle() + .at(&epoch) + .iter(ctx.wl_storage)? + .map(|next_result| { + next_result.map( + |( + lazy_map::NestedSubKey::Data { + key: bonded_stake, + nested_sub_key: _position, + }, + address, + )| { + WeightedValidator { + bonded_stake, + address, + } + }, + ) + }) + .collect() } /// Get all the validator in the below-capacity set with their bonded stake. fn below_capacity_validator_set( ctx: RequestCtx<'_, D, H>, epoch: Option, -) -> storage_api::Result> +) -> storage_api::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { let epoch = epoch.unwrap_or(ctx.wl_storage.storage.last_epoch); - read_below_capacity_validator_set_addresses_with_stake( - ctx.wl_storage, - epoch, - ) + below_capacity_validator_set_handle() + .at(&epoch) + .iter(ctx.wl_storage)? + .map(|next_result| { + next_result.map( + |( + lazy_map::NestedSubKey::Data { + key: bonded_stake, + nested_sub_key: _position, + }, + address, + )| { + WeightedValidator { + bonded_stake: bonded_stake.into(), + address, + } + }, + ) + }) + .collect() } /// Get the total stake in PoS system at the given epoch or current when `None`. @@ -489,19 +496,7 @@ where find_all_slashes(ctx.wl_storage) } -/// Enqueued slashes -fn enqueued_slashes( - ctx: RequestCtx<'_, D, H>, -) -> storage_api::Result>>> -where - D: 'static + DB + for<'iter> DBIter<'iter> + Sync, - H: 'static + StorageHasher + Sync, -{ - let current_epoch = ctx.wl_storage.storage.last_epoch; - find_all_enqueued_slashes(ctx.wl_storage, current_epoch) -} - -/// Native validator address by looking up the Tendermint address +/// All slashes fn validator_by_tm_addr( ctx: RequestCtx<'_, D, H>, tm_addr: String, diff --git a/wasm/checksums.json b/wasm/checksums.json index c2d3d7e014..8397ffb0d6 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -19,4 +19,4 @@ "vp_token.wasm": "vp_token.ec4f3914d074168f7ecbd43d5587e014381d409b3df4db4f63eaf73d3a56cdd6.wasm", "vp_user.wasm": "vp_user.fd9810232a2ec79ff3f9f8fc70a6427ce9d07a9ef51c1468a785247a9b08b337.wasm", "vp_validator.wasm": "vp_validator.8ce4c52a53aa451459e37ec560aa56ac4083d8829b0c29168c448e1e9d764c22.wasm" -} +} \ No newline at end of file diff --git a/wasm/wasm_source/src/tx_bond.rs b/wasm/wasm_source/src/tx_bond.rs index 9340592bb0..df55270ceb 100644 --- a/wasm/wasm_source/src/tx_bond.rs +++ b/wasm/wasm_source/src/tx_bond.rs @@ -15,7 +15,7 @@ fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { #[cfg(test)] mod tests { - use std::collections::BTreeSet; + use std::collections::HashSet; use namada::ledger::pos::{GenesisValidator, PosParams, PosVP}; use namada::proof_of_stake::{ @@ -132,7 +132,7 @@ mod tests { // Read some data before the tx is executed let mut epoched_total_stake_pre: Vec = Vec::new(); let mut epoched_validator_stake_pre: Vec = Vec::new(); - let mut epoched_validator_set_pre: Vec> = + let mut epoched_validator_set_pre: Vec> = Vec::new(); for epoch in 0..=pos_params.unbonding_len { @@ -163,7 +163,7 @@ mod tests { // Read the data after the tx is executed. let mut epoched_total_stake_post: Vec = Vec::new(); let mut epoched_validator_stake_post: Vec = Vec::new(); - let mut epoched_validator_set_post: Vec> = + let mut epoched_validator_set_post: Vec> = Vec::new(); println!("\nFILLING POST STATE\n"); diff --git a/wasm/wasm_source/src/tx_unbond.rs b/wasm/wasm_source/src/tx_unbond.rs index fc69294b2b..7fb1de8f4f 100644 --- a/wasm/wasm_source/src/tx_unbond.rs +++ b/wasm/wasm_source/src/tx_unbond.rs @@ -15,7 +15,7 @@ fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { #[cfg(test)] mod tests { - use std::collections::BTreeSet; + use std::collections::HashSet; use namada::ledger::pos::{GenesisValidator, PosParams, PosVP}; use namada::proof_of_stake::types::WeightedValidator; @@ -164,7 +164,7 @@ mod tests { let mut epoched_total_stake_pre: Vec = Vec::new(); let mut epoched_validator_stake_pre: Vec = Vec::new(); let mut epoched_bonds_pre: Vec> = Vec::new(); - let mut epoched_validator_set_pre: Vec> = + let mut epoched_validator_set_pre: Vec> = Vec::new(); for epoch in 0..=pos_params.unbonding_len { From 85256e83bc26c7247331571eb86b2af5a0c9bc25 Mon Sep 17 00:00:00 2001 From: bengtlofgren Date: Tue, 4 Jul 2023 11:31:47 +0200 Subject: [PATCH 069/120] query bonded-stake and order --- apps/src/lib/client/rpc.rs | 17 ++++++++++++++--- wasm/checksums.json | 2 +- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 44955967d0..fb1dbd6c33 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -10,7 +10,7 @@ use std::str::FromStr; use borsh::{BorshDeserialize, BorshSerialize}; use data_encoding::HEXLOWER; -use itertools::Either; +use itertools::{Either, Itertools}; use masp_primitives::asset_type::AssetType; use masp_primitives::merkle_tree::MerklePath; use masp_primitives::sapling::{Node, ViewingKey}; @@ -1580,13 +1580,19 @@ pub async fn query_bonded_stake( .below_capacity_validator_set(client, &Some(epoch)) .await, ); + + let sorted_consensus = consensus.into_iter(). + sorted_by_key(|v| + -(i128::try_from(v.bonded_stake.change())) + .expect("Failed to sort consensus validators") + ); // Iterate all validators let stdout = io::stdout(); let mut w = stdout.lock(); writeln!(w, "Consensus validators:").unwrap(); - for val in consensus { + for val in sorted_consensus { writeln!( w, " {}: {}", @@ -1596,8 +1602,13 @@ pub async fn query_bonded_stake( .unwrap(); } if !below_capacity.is_empty() { + let sorted_below_capacity = below_capacity.into_iter(). + sorted_by_key(|v| + -(i128::try_from(v.bonded_stake.change())) + .expect("Failed to sort consensus validators") + ); writeln!(w, "Below capacity validators:").unwrap(); - for val in &below_capacity { + for val in sorted_below_capacity { writeln!( w, " {}: {}", diff --git a/wasm/checksums.json b/wasm/checksums.json index 8397ffb0d6..c2d3d7e014 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -19,4 +19,4 @@ "vp_token.wasm": "vp_token.ec4f3914d074168f7ecbd43d5587e014381d409b3df4db4f63eaf73d3a56cdd6.wasm", "vp_user.wasm": "vp_user.fd9810232a2ec79ff3f9f8fc70a6427ce9d07a9ef51c1468a785247a9b08b337.wasm", "vp_validator.wasm": "vp_validator.8ce4c52a53aa451459e37ec560aa56ac4083d8829b0c29168c448e1e9d764c22.wasm" -} \ No newline at end of file +} From 8ca6cebdbbded21a700ea2736d97cd05744cc762 Mon Sep 17 00:00:00 2001 From: brentstone Date: Tue, 4 Jul 2023 14:18:33 +0200 Subject: [PATCH 070/120] CLI query a validator's state --- apps/src/bin/namada-client/cli.rs | 16 ++++++ apps/src/lib/cli.rs | 72 +++++++++++++++++++++++++-- apps/src/lib/client/rpc.rs | 75 ++++++++++++++++++++++++----- shared/src/ledger/args.rs | 11 +++++ shared/src/ledger/queries/vp/pos.rs | 26 +++++++++- 5 files changed, 183 insertions(+), 17 deletions(-) diff --git a/apps/src/bin/namada-client/cli.rs b/apps/src/bin/namada-client/cli.rs index 609588045d..be77d2b982 100644 --- a/apps/src/bin/namada-client/cli.rs +++ b/apps/src/bin/namada-client/cli.rs @@ -325,6 +325,22 @@ pub async fn main() -> Result<()> { let args = args.to_sdk(&mut ctx); rpc::query_bonded_stake(&client, args).await; } + Sub::QueryValidatorState(QueryValidatorState(mut args)) => { + let client = HttpClient::new(utils::take_config_address( + &mut args.query.ledger_address, + )) + .unwrap(); + wait_until_node_is_synched(&client) + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_and_print_validator_state( + &client, + &mut ctx.wallet, + args, + ) + .await; + } Sub::QueryCommissionRate(QueryCommissionRate(mut args)) => { let client = HttpClient::new(utils::take_config_address( &mut args.query.ledger_address, diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 06d6f3f406..00a24c45b2 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -238,6 +238,7 @@ pub mod cmds { .subcommand(QueryProposal::def().display_order(4)) .subcommand(QueryProposalResult::def().display_order(4)) .subcommand(QueryProtocolParameters::def().display_order(4)) + .subcommand(QueryValidatorState::def().display_order(4)) // Utils .subcommand(Utils::def().display_order(5)) } @@ -282,6 +283,8 @@ pub mod cmds { Self::parse_with_ctx(matches, QueryProposalResult); let query_protocol_parameters = Self::parse_with_ctx(matches, QueryProtocolParameters); + let query_validator_state = + Self::parse_with_ctx(matches, QueryValidatorState); let add_to_eth_bridge_pool = Self::parse_with_ctx(matches, AddToEthBridgePool); let utils = SubCmd::parse(matches).map(Self::WithoutContext); @@ -314,6 +317,7 @@ pub mod cmds { .or(query_proposal) .or(query_proposal_result) .or(query_protocol_parameters) + .or(query_validator_state) .or(utils) } } @@ -381,6 +385,7 @@ pub mod cmds { QueryProposal(QueryProposal), QueryProposalResult(QueryProposalResult), QueryProtocolParameters(QueryProtocolParameters), + QueryValidatorState(QueryValidatorState), } #[allow(clippy::large_enum_variant)] @@ -1453,6 +1458,27 @@ pub mod cmds { } } + #[derive(Clone, Debug)] + pub struct QueryValidatorState( + pub args::QueryValidatorState, + ); + + impl SubCmd for QueryValidatorState { + const CMD: &'static str = "validator-state"; + + fn parse(matches: &ArgMatches) -> Option { + matches.subcommand_matches(Self::CMD).map(|matches| { + QueryValidatorState(args::QueryValidatorState::parse(matches)) + }) + } + + fn def() -> App { + App::new(Self::CMD) + .about("Query the state of a PoS validator.") + .add_args::>() + } + } + #[derive(Clone, Debug)] pub struct QueryTransfers(pub args::QueryTransfers); @@ -1488,7 +1514,7 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) - .about("Query commission rate.") + .about("Query a validator's commission rate.") .add_args::>() } } @@ -4016,8 +4042,44 @@ pub mod args { "The validator's address whose bonded stake to query.", )) .arg(EPOCH.def().help( - "The epoch at which to query (last committed, if not \ - specified).", + "The epoch at which to query (corresponding to the last \ + committed block, if not specified).", + )) + } + } + + impl CliToSdk> for QueryValidatorState { + fn to_sdk(self, ctx: &mut Context) -> QueryValidatorState { + QueryValidatorState:: { + query: self.query.to_sdk(ctx), + validator: ctx.get(&self.validator), + epoch: self.epoch, + } + } + } + + impl Args for QueryValidatorState { + fn parse(matches: &ArgMatches) -> Self { + let query = Query::parse(matches); + let validator = VALIDATOR.parse(matches); + let epoch = EPOCH.parse(matches); + Self { + query, + validator, + epoch, + } + } + + fn def(app: App) -> App { + app.add_args::>() + .arg( + VALIDATOR.def().help( + "The validator's address whose state is queried.", + ), + ) + .arg(EPOCH.def().help( + "The epoch at which to query (corresponding to the last \ + committed block, if not specified).", )) } } @@ -4127,8 +4189,8 @@ pub mod args { "The validator's address whose commission rate to query.", )) .arg(EPOCH.def().help( - "The epoch at which to query (last committed, if not \ - specified).", + "The epoch at which to query (corresponding to the last \ + committed block, if not specified).", )) } } diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index fb1dbd6c33..81d9d71009 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -36,7 +36,7 @@ use namada::ledger::rpc::{ }; use namada::ledger::storage::ConversionState; use namada::ledger::wallet::{AddressVpType, Wallet}; -use namada::proof_of_stake::types::WeightedValidator; +use namada::proof_of_stake::types::{ValidatorState, WeightedValidator}; use namada::types::address::{masp, Address}; use namada::types::control_flow::ProceedOrElse; use namada::types::governance::{ @@ -1580,12 +1580,11 @@ pub async fn query_bonded_stake( .below_capacity_validator_set(client, &Some(epoch)) .await, ); - - let sorted_consensus = consensus.into_iter(). - sorted_by_key(|v| + + let sorted_consensus = consensus.into_iter().sorted_by_key(|v| { -(i128::try_from(v.bonded_stake.change())) - .expect("Failed to sort consensus validators") - ); + .expect("Failed to sort consensus validators") + }); // Iterate all validators let stdout = io::stdout(); @@ -1602,11 +1601,11 @@ pub async fn query_bonded_stake( .unwrap(); } if !below_capacity.is_empty() { - let sorted_below_capacity = below_capacity.into_iter(). - sorted_by_key(|v| - -(i128::try_from(v.bonded_stake.change())) - .expect("Failed to sort consensus validators") - ); + let sorted_below_capacity = + below_capacity.into_iter().sorted_by_key(|v| { + -(i128::try_from(v.bonded_stake.change())) + .expect("Failed to sort consensus validators") + }); writeln!(w, "Below capacity validators:").unwrap(); for val in sorted_below_capacity { writeln!( @@ -1645,6 +1644,60 @@ pub async fn query_commission_rate< ) } +/// Query and return validator's state +pub async fn query_validator_state< + C: namada::ledger::queries::Client + Sync, +>( + client: &C, + validator: &Address, + epoch: Option, +) -> Option { + unwrap_client_response::>( + RPC.vp() + .pos() + .validator_state(client, validator, &epoch) + .await, + ) +} + +/// Query a validator's state information +pub async fn query_and_print_validator_state< + C: namada::ledger::queries::Client + Sync, +>( + client: &C, + _wallet: &mut Wallet, + args: args::QueryValidatorState, +) { + let validator = args.validator; + let state: Option = + query_validator_state(client, &validator, args.epoch).await; + + match state { + Some(state) => match state { + ValidatorState::Consensus => { + println!("Validator {validator} is in the consensus set") + } + ValidatorState::BelowCapacity => { + println!("Validator {validator} is in the below-capacity set") + } + ValidatorState::BelowThreshold => { + println!("Validator {validator} is in the below-threshold set") + } + ValidatorState::Inactive => { + println!("Validator {validator} is inactive") + } + ValidatorState::Jailed => { + println!("Validator {validator} is jailed") + } + }, + None => println!( + "Validator {validator} is either not a validator, or an epoch \ + before the current epoch has been queried (and the validator \ + state information is no longer stored)" + ), + } +} + /// Query PoS validator's commission rate information pub async fn query_and_print_commission_rate< C: namada::ledger::queries::Client + Sync, diff --git a/shared/src/ledger/args.rs b/shared/src/ledger/args.rs index 35081d3283..47117ba594 100644 --- a/shared/src/ledger/args.rs +++ b/shared/src/ledger/args.rs @@ -356,6 +356,17 @@ pub struct QueryBondedStake { pub epoch: Option, } +/// Query the state of a validator (its validator set or if it is jailed) +#[derive(Clone, Debug)] +pub struct QueryValidatorState { + /// Common query args + pub query: Query, + /// Address of a validator + pub validator: C::Address, + /// Epoch in which to find the validator state + pub epoch: Option, +} + #[derive(Clone, Debug)] /// Commission rate change args pub struct CommissionRateChange { diff --git a/shared/src/ledger/queries/vp/pos.rs b/shared/src/ledger/queries/vp/pos.rs index d872aa5002..9934a71271 100644 --- a/shared/src/ledger/queries/vp/pos.rs +++ b/shared/src/ledger/queries/vp/pos.rs @@ -7,7 +7,7 @@ use namada_core::ledger::storage_api::collections::lazy_map; use namada_core::ledger::storage_api::OptionExt; use namada_proof_of_stake::types::{ BondId, BondsAndUnbondsDetail, BondsAndUnbondsDetails, CommissionPair, - Slash, WeightedValidator, + Slash, ValidatorState, WeightedValidator, }; use namada_proof_of_stake::{ self, below_capacity_validator_set_handle, bond_amount, bond_handle, @@ -16,6 +16,7 @@ use namada_proof_of_stake::{ read_pos_params, read_total_stake, read_validator_max_commission_rate_change, read_validator_stake, unbond_handle, validator_commission_rate_handle, validator_slashes_handle, + validator_state_handle, }; use crate::ledger::queries::types::RequestCtx; @@ -43,6 +44,9 @@ router! {POS, ( "commission" / [validator: Address] / [epoch: opt Epoch] ) -> Option = validator_commission, + + ( "state" / [validator: Address] / [epoch: opt Epoch] ) + -> Option = validator_state, }, ( "validator_set" ) = { @@ -203,6 +207,26 @@ where } } +/// Get the validator state +fn validator_state( + ctx: RequestCtx<'_, D, H>, + validator: Address, + epoch: Option, +) -> storage_api::Result> +where + D: 'static + DB + for<'iter> DBIter<'iter> + Sync, + H: 'static + StorageHasher + Sync, +{ + let epoch = epoch.unwrap_or(ctx.wl_storage.storage.last_epoch); + let params = read_pos_params(ctx.wl_storage)?; + let state = validator_state_handle(&validator).get( + ctx.wl_storage, + epoch, + ¶ms, + )?; + Ok(state) +} + /// Get the total stake of a validator at the given epoch or current when /// `None`. The total stake is a sum of validator's self-bonds and delegations /// to their address. From aae3f5416c31d39c200b10d60b85e05a9c21fa2d Mon Sep 17 00:00:00 2001 From: brentstone Date: Thu, 6 Jul 2023 11:59:19 +0200 Subject: [PATCH 071/120] Expanding and fixing slashes query --- apps/src/lib/client/rpc.rs | 92 +++++++++++++++++++++++++---- proof_of_stake/src/lib.rs | 36 +++++++++++ shared/src/ledger/queries/vp/pos.rs | 25 ++++++-- 3 files changed, 135 insertions(+), 18 deletions(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 81d9d71009..626c946f3d 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -1,7 +1,7 @@ //! Client RPC queries use std::cmp::Ordering; -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeMap, HashMap, HashSet}; use std::fs::File; use std::io::{self, Write}; use std::iter::Iterator; @@ -1738,11 +1738,6 @@ pub async fn query_slashes( _wallet: &mut Wallet, args: args::QuerySlashes, ) { - let params_key = pos::params_key(); - let params = query_storage_value::(client, ¶ms_key) - .await - .expect("Parameter should be defined."); - match args.validator { Some(validator) => { let validator = validator; @@ -1751,18 +1746,54 @@ pub async fn query_slashes( RPC.vp().pos().validator_slashes(client, &validator).await, ); if !slashes.is_empty() { + println!("Processed slashes:"); let stdout = io::stdout(); let mut w = stdout.lock(); for slash in slashes { writeln!( w, - "Slash epoch {}, type {}, rate {}", - slash.epoch, slash.r#type, slash.rate + "Infraction epoch {}, block height {}, type {}, rate \ + {}", + slash.epoch, + slash.block_height, + slash.r#type, + slash.rate ) .unwrap(); } } else { - println!("No slashes found for {}", validator.encode()) + println!( + "No processed slashes found for {}", + validator.encode() + ) + } + // Find enqueued slashes to be processed in the future for the given + // validator + let enqueued_slashes: HashMap< + Address, + BTreeMap>, + > = unwrap_client_response::< + C, + HashMap>>, + >(RPC.vp().pos().enqueued_slashes(client).await); + let enqueued_slashes = enqueued_slashes.get(&validator).cloned(); + if let Some(enqueued) = enqueued_slashes { + println!("\nEnqueued slashes for future processing"); + for (epoch, slashes) in enqueued { + println!("To be processed in epoch {}", epoch); + for slash in slashes { + let stdout = io::stdout(); + let mut w = stdout.lock(); + writeln!( + w, + "Infraction epoch {}, block height {}, type {}", + slash.epoch, slash.block_height, slash.r#type, + ) + .unwrap(); + } + } + } else { + println!("No enqueued slashes found for {}", validator.encode()) } } None => { @@ -1774,15 +1805,16 @@ pub async fn query_slashes( if !all_slashes.is_empty() { let stdout = io::stdout(); let mut w = stdout.lock(); + println!("Processed slashes:"); for (validator, slashes) in all_slashes.into_iter() { for slash in slashes { writeln!( w, - "Slash epoch {}, block height {}, rate {}, type \ - {}, validator {}", + "Infraction epoch {}, block height {}, rate {}, \ + type {}, validator {}", slash.epoch, slash.block_height, - slash.r#type.get_slash_rate(¶ms), + slash.rate, slash.r#type, validator, ) @@ -1790,7 +1822,41 @@ pub async fn query_slashes( } } } else { - println!("No slashes found") + println!("No processed slashes found") + } + + // Find enqueued slashes to be processed in the future for the given + // validator + let enqueued_slashes: HashMap< + Address, + BTreeMap>, + > = unwrap_client_response::< + C, + HashMap>>, + >(RPC.vp().pos().enqueued_slashes(client).await); + if !enqueued_slashes.is_empty() { + println!("\nEnqueued slashes for future processing"); + for (validator, slashes_by_epoch) in enqueued_slashes { + for (epoch, slashes) in slashes_by_epoch { + println!("\nTo be processed in epoch {}", epoch); + for slash in slashes { + let stdout = io::stdout(); + let mut w = stdout.lock(); + writeln!( + w, + "Infraction epoch {}, block height {}, type \ + {}, validator {}", + slash.epoch, + slash.block_height, + slash.r#type, + validator + ) + .unwrap(); + } + } + } + } else { + println!("\nNo enqueued slashes found for future processing") } } } diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index 7f7265be91..050334debc 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -2753,6 +2753,42 @@ where } } +/// Collect the details of all of the enqueued slashes to be processed in future +/// epochs into a nested map +pub fn find_all_enqueued_slashes( + storage: &S, + epoch: Epoch, +) -> storage_api::Result>>> +where + S: StorageRead, +{ + let mut enqueued = HashMap::>>::new(); + for res in enqueued_slashes_handle().get_data_handler().iter(storage)? { + let ( + NestedSubKey::Data { + key: processing_epoch, + nested_sub_key: + NestedSubKey::Data { + key: address, + nested_sub_key: _, + }, + }, + slash, + ) = res?; + if processing_epoch <= epoch { + continue; + } + + let slashes = enqueued + .entry(address) + .or_default() + .entry(processing_epoch) + .or_default(); + slashes.push(slash); + } + Ok(enqueued) +} + /// Find all slashes and the associated validators in the PoS system pub fn find_all_slashes( storage: &S, diff --git a/shared/src/ledger/queries/vp/pos.rs b/shared/src/ledger/queries/vp/pos.rs index 9934a71271..94c93f6a54 100644 --- a/shared/src/ledger/queries/vp/pos.rs +++ b/shared/src/ledger/queries/vp/pos.rs @@ -1,6 +1,6 @@ //! Queries router and handlers for PoS validity predicate -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeMap, HashMap, HashSet}; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use namada_core::ledger::storage_api::collections::lazy_map; @@ -11,9 +11,9 @@ use namada_proof_of_stake::types::{ }; use namada_proof_of_stake::{ self, below_capacity_validator_set_handle, bond_amount, bond_handle, - consensus_validator_set_handle, find_all_slashes, - find_delegation_validators, find_delegations, read_all_validator_addresses, - read_pos_params, read_total_stake, + consensus_validator_set_handle, find_all_enqueued_slashes, + find_all_slashes, find_delegation_validators, find_delegations, + read_all_validator_addresses, read_pos_params, read_total_stake, read_validator_max_commission_rate_change, read_validator_stake, unbond_handle, validator_commission_rate_handle, validator_slashes_handle, validator_state_handle, @@ -86,6 +86,9 @@ router! {POS, ( "bonds_and_unbonds" / [source: opt Address] / [validator: opt Address] ) -> BondsAndUnbondsDetails = bonds_and_unbonds, + ( "enqueued_slashes" ) + -> HashMap>> = enqueued_slashes, + ( "all_slashes" ) -> HashMap> = slashes, ( "is_delegator" / [addr: Address ] / [epoch: opt Epoch] ) -> bool = is_delegator, @@ -520,7 +523,19 @@ where find_all_slashes(ctx.wl_storage) } -/// All slashes +/// Enqueued slashes +fn enqueued_slashes( + ctx: RequestCtx<'_, D, H>, +) -> storage_api::Result>>> +where + D: 'static + DB + for<'iter> DBIter<'iter> + Sync, + H: 'static + StorageHasher + Sync, +{ + let current_epoch = ctx.wl_storage.storage.last_epoch; + find_all_enqueued_slashes(ctx.wl_storage, current_epoch) +} + +/// Native validator address by looking up the Tendermint address fn validator_by_tm_addr( ctx: RequestCtx<'_, D, H>, tm_addr: String, From b39d3efa26b584461afa9513a50b8e73aea1084a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 11 Jul 2023 09:25:19 +0100 Subject: [PATCH 072/120] pos: return sorted validator sets and code re-use for queries --- apps/src/lib/client/rpc.rs | 22 +++------- proof_of_stake/src/lib.rs | 4 +- shared/src/ledger/queries/vp/pos.rs | 68 ++++++++--------------------- wasm/wasm_source/src/tx_bond.rs | 6 +-- wasm/wasm_source/src/tx_unbond.rs | 4 +- 5 files changed, 30 insertions(+), 74 deletions(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 626c946f3d..2895238071 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -1,7 +1,7 @@ //! Client RPC queries use std::cmp::Ordering; -use std::collections::{BTreeMap, HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::fs::File; use std::io::{self, Write}; use std::iter::Iterator; @@ -10,7 +10,7 @@ use std::str::FromStr; use borsh::{BorshDeserialize, BorshSerialize}; use data_encoding::HEXLOWER; -use itertools::{Either, Itertools}; +use itertools::Either; use masp_primitives::asset_type::AssetType; use masp_primitives::merkle_tree::MerklePath; use masp_primitives::sapling::{Node, ViewingKey}; @@ -1567,31 +1567,26 @@ pub async fn query_bonded_stake( } None => { let consensus = - unwrap_client_response::>( + unwrap_client_response::>( RPC.vp() .pos() .consensus_validator_set(client, &Some(epoch)) .await, ); let below_capacity = - unwrap_client_response::>( + unwrap_client_response::>( RPC.vp() .pos() .below_capacity_validator_set(client, &Some(epoch)) .await, ); - let sorted_consensus = consensus.into_iter().sorted_by_key(|v| { - -(i128::try_from(v.bonded_stake.change())) - .expect("Failed to sort consensus validators") - }); - // Iterate all validators let stdout = io::stdout(); let mut w = stdout.lock(); writeln!(w, "Consensus validators:").unwrap(); - for val in sorted_consensus { + for val in consensus.into_iter().rev() { writeln!( w, " {}: {}", @@ -1601,13 +1596,8 @@ pub async fn query_bonded_stake( .unwrap(); } if !below_capacity.is_empty() { - let sorted_below_capacity = - below_capacity.into_iter().sorted_by_key(|v| { - -(i128::try_from(v.bonded_stake.change())) - .expect("Failed to sort consensus validators") - }); writeln!(w, "Below capacity validators:").unwrap(); - for val in sorted_below_capacity { + for val in below_capacity.into_iter().rev() { writeln!( w, " {}: {}", diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index 050334debc..4022d49ffc 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -748,7 +748,7 @@ where pub fn read_consensus_validator_set_addresses_with_stake( storage: &S, epoch: namada_core::types::storage::Epoch, -) -> storage_api::Result> +) -> storage_api::Result> where S: StorageRead, { @@ -792,7 +792,7 @@ where pub fn read_below_capacity_validator_set_addresses_with_stake( storage: &S, epoch: namada_core::types::storage::Epoch, -) -> storage_api::Result> +) -> storage_api::Result> where S: StorageRead, { diff --git a/shared/src/ledger/queries/vp/pos.rs b/shared/src/ledger/queries/vp/pos.rs index 94c93f6a54..177b31ed87 100644 --- a/shared/src/ledger/queries/vp/pos.rs +++ b/shared/src/ledger/queries/vp/pos.rs @@ -1,6 +1,6 @@ //! Queries router and handlers for PoS validity predicate -use std::collections::{BTreeMap, HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use namada_core::ledger::storage_api::collections::lazy_map; @@ -10,13 +10,14 @@ use namada_proof_of_stake::types::{ Slash, ValidatorState, WeightedValidator, }; use namada_proof_of_stake::{ - self, below_capacity_validator_set_handle, bond_amount, bond_handle, - consensus_validator_set_handle, find_all_enqueued_slashes, + self, bond_amount, bond_handle, find_all_enqueued_slashes, find_all_slashes, find_delegation_validators, find_delegations, - read_all_validator_addresses, read_pos_params, read_total_stake, - read_validator_max_commission_rate_change, read_validator_stake, - unbond_handle, validator_commission_rate_handle, validator_slashes_handle, - validator_state_handle, + read_all_validator_addresses, + read_below_capacity_validator_set_addresses_with_stake, + read_consensus_validator_set_addresses_with_stake, read_pos_params, + read_total_stake, read_validator_max_commission_rate_change, + read_validator_stake, unbond_handle, validator_commission_rate_handle, + validator_slashes_handle, validator_state_handle, }; use crate::ledger::queries::types::RequestCtx; @@ -51,10 +52,10 @@ router! {POS, ( "validator_set" ) = { ( "consensus" / [epoch: opt Epoch] ) - -> HashSet = consensus_validator_set, + -> BTreeSet = consensus_validator_set, ( "below_capacity" / [epoch: opt Epoch] ) - -> HashSet = below_capacity_validator_set, + -> BTreeSet = below_capacity_validator_set, // TODO: add "below_threshold" }, @@ -253,64 +254,29 @@ where fn consensus_validator_set( ctx: RequestCtx<'_, D, H>, epoch: Option, -) -> storage_api::Result> +) -> storage_api::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { let epoch = epoch.unwrap_or(ctx.wl_storage.storage.last_epoch); - consensus_validator_set_handle() - .at(&epoch) - .iter(ctx.wl_storage)? - .map(|next_result| { - next_result.map( - |( - lazy_map::NestedSubKey::Data { - key: bonded_stake, - nested_sub_key: _position, - }, - address, - )| { - WeightedValidator { - bonded_stake, - address, - } - }, - ) - }) - .collect() + read_consensus_validator_set_addresses_with_stake(ctx.wl_storage, epoch) } /// Get all the validator in the below-capacity set with their bonded stake. fn below_capacity_validator_set( ctx: RequestCtx<'_, D, H>, epoch: Option, -) -> storage_api::Result> +) -> storage_api::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { let epoch = epoch.unwrap_or(ctx.wl_storage.storage.last_epoch); - below_capacity_validator_set_handle() - .at(&epoch) - .iter(ctx.wl_storage)? - .map(|next_result| { - next_result.map( - |( - lazy_map::NestedSubKey::Data { - key: bonded_stake, - nested_sub_key: _position, - }, - address, - )| { - WeightedValidator { - bonded_stake: bonded_stake.into(), - address, - } - }, - ) - }) - .collect() + read_below_capacity_validator_set_addresses_with_stake( + ctx.wl_storage, + epoch, + ) } /// Get the total stake in PoS system at the given epoch or current when `None`. diff --git a/wasm/wasm_source/src/tx_bond.rs b/wasm/wasm_source/src/tx_bond.rs index df55270ceb..9340592bb0 100644 --- a/wasm/wasm_source/src/tx_bond.rs +++ b/wasm/wasm_source/src/tx_bond.rs @@ -15,7 +15,7 @@ fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { #[cfg(test)] mod tests { - use std::collections::HashSet; + use std::collections::BTreeSet; use namada::ledger::pos::{GenesisValidator, PosParams, PosVP}; use namada::proof_of_stake::{ @@ -132,7 +132,7 @@ mod tests { // Read some data before the tx is executed let mut epoched_total_stake_pre: Vec = Vec::new(); let mut epoched_validator_stake_pre: Vec = Vec::new(); - let mut epoched_validator_set_pre: Vec> = + let mut epoched_validator_set_pre: Vec> = Vec::new(); for epoch in 0..=pos_params.unbonding_len { @@ -163,7 +163,7 @@ mod tests { // Read the data after the tx is executed. let mut epoched_total_stake_post: Vec = Vec::new(); let mut epoched_validator_stake_post: Vec = Vec::new(); - let mut epoched_validator_set_post: Vec> = + let mut epoched_validator_set_post: Vec> = Vec::new(); println!("\nFILLING POST STATE\n"); diff --git a/wasm/wasm_source/src/tx_unbond.rs b/wasm/wasm_source/src/tx_unbond.rs index 7fb1de8f4f..fc69294b2b 100644 --- a/wasm/wasm_source/src/tx_unbond.rs +++ b/wasm/wasm_source/src/tx_unbond.rs @@ -15,7 +15,7 @@ fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { #[cfg(test)] mod tests { - use std::collections::HashSet; + use std::collections::BTreeSet; use namada::ledger::pos::{GenesisValidator, PosParams, PosVP}; use namada::proof_of_stake::types::WeightedValidator; @@ -164,7 +164,7 @@ mod tests { let mut epoched_total_stake_pre: Vec = Vec::new(); let mut epoched_validator_stake_pre: Vec = Vec::new(); let mut epoched_bonds_pre: Vec> = Vec::new(); - let mut epoched_validator_set_pre: Vec> = + let mut epoched_validator_set_pre: Vec> = Vec::new(); for epoch in 0..=pos_params.unbonding_len { From 86638ec9b126ce8de3719a05f32f256c771f677e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 11 Jul 2023 12:16:11 +0100 Subject: [PATCH 073/120] changelog: add #1656 --- .changelog/unreleased/improvements/1656-pos-cli-queries.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/improvements/1656-pos-cli-queries.md diff --git a/.changelog/unreleased/improvements/1656-pos-cli-queries.md b/.changelog/unreleased/improvements/1656-pos-cli-queries.md new file mode 100644 index 0000000000..a114c6f2f6 --- /dev/null +++ b/.changelog/unreleased/improvements/1656-pos-cli-queries.md @@ -0,0 +1,2 @@ +- Added a client query for `validator-state` and improved the slashes query to + show more info. ([\#1656](https://github.com/anoma/namada/pull/1656)) \ No newline at end of file From b34387d07727c5b9790a4e4ff6e602df3ef4ecb7 Mon Sep 17 00:00:00 2001 From: bengtlofgren Date: Wed, 5 Jul 2023 16:09:35 +0200 Subject: [PATCH 074/120] converts faucet_withdrawal_limit to be correct --- apps/src/lib/config/genesis.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index fcd57be745..9cd8cf11ec 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -44,7 +44,8 @@ pub mod genesis_config { use namada::types::key::dkg_session_keys::DkgPublicKey; use namada::types::key::*; use namada::types::time::Rfc3339String; - use namada::types::token::Denomination; + use namada::types::token::{Denomination, NATIVE_MAX_DECIMAL_PLACES}; + use namada::types::uint::Uint; use namada::types::{storage, token}; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -557,7 +558,9 @@ pub mod genesis_config { .expect("Missing native token address"), ) .expect("Invalid address"); - + // If this line does not exist, it reads the faucet withdrawal limit as NAMNAM instead of NAM + #[cfg(not(feature = "mainnet"))] + let faucet_withdrawal_limit = faucet_withdrawal_limit.map(|a| a * Uint::exp10(NATIVE_MAX_DECIMAL_PLACES as usize)); let validators: HashMap = validator .iter() .map(|(name, cfg)| (name.clone(), load_validator(cfg, &wasm))) From 340b4adfbaf721bdd31c7fe45a2558cf91dc4d39 Mon Sep 17 00:00:00 2001 From: satan Date: Thu, 6 Jul 2023 18:11:58 +0200 Subject: [PATCH 075/120] [fix]: Fixed the faucet to use uint instead of amount. This makes it portable across different assets --- apps/src/lib/config/genesis.rs | 10 ++-- core/src/ledger/testnet_pow.rs | 8 +-- core/src/types/uint.rs | 70 ++++++++++++++++++++++- genesis/e2e-tests-single-node.toml | 2 +- tests/src/storage_api/testnet_pow.rs | 2 +- wasm/wasm_source/src/vp_testnet_faucet.rs | 34 +++++++++-- 6 files changed, 105 insertions(+), 21 deletions(-) diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index 9cd8cf11ec..b556a64864 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -22,6 +22,7 @@ use namada::types::key::dkg_session_keys::DkgPublicKey; use namada::types::key::*; use namada::types::time::{DateTimeUtc, DurationSecs}; use namada::types::token::Denomination; +use namada::types::uint::Uint; use namada::types::{storage, token}; /// Genesis configuration file format @@ -44,7 +45,7 @@ pub mod genesis_config { use namada::types::key::dkg_session_keys::DkgPublicKey; use namada::types::key::*; use namada::types::time::Rfc3339String; - use namada::types::token::{Denomination, NATIVE_MAX_DECIMAL_PLACES}; + use namada::types::token::Denomination; use namada::types::uint::Uint; use namada::types::{storage, token}; use serde::{Deserialize, Serialize}; @@ -124,7 +125,7 @@ pub mod genesis_config { pub faucet_pow_difficulty: Option, #[cfg(not(feature = "mainnet"))] /// Testnet faucet withdrawal limit - defaults to 1000 NAM when not set - pub faucet_withdrawal_limit: Option, + pub faucet_withdrawal_limit: Option, // Initial validator set pub validator: HashMap, // Token accounts present at genesis @@ -558,9 +559,6 @@ pub mod genesis_config { .expect("Missing native token address"), ) .expect("Invalid address"); - // If this line does not exist, it reads the faucet withdrawal limit as NAMNAM instead of NAM - #[cfg(not(feature = "mainnet"))] - let faucet_withdrawal_limit = faucet_withdrawal_limit.map(|a| a * Uint::exp10(NATIVE_MAX_DECIMAL_PLACES as usize)); let validators: HashMap = validator .iter() .map(|(name, cfg)| (name.clone(), load_validator(cfg, &wasm))) @@ -735,7 +733,7 @@ pub struct Genesis { #[cfg(not(feature = "mainnet"))] pub faucet_pow_difficulty: Option, #[cfg(not(feature = "mainnet"))] - pub faucet_withdrawal_limit: Option, + pub faucet_withdrawal_limit: Option, pub validators: Vec, pub token_accounts: Vec, pub established_accounts: Vec, diff --git a/core/src/ledger/testnet_pow.rs b/core/src/ledger/testnet_pow.rs index aa1257a886..dcb7c9feb2 100644 --- a/core/src/ledger/testnet_pow.rs +++ b/core/src/ledger/testnet_pow.rs @@ -12,7 +12,7 @@ use crate::ledger::storage_api::collections::LazyMap; use crate::types::address::Address; use crate::types::hash::Hash; use crate::types::storage::{self, DbKeySeg, Key}; -use crate::types::token; +use crate::types::uint::Uint; /// Initialize faucet's storage. This must be called at genesis if faucet /// account is being used. @@ -20,7 +20,7 @@ pub fn init_faucet_storage( storage: &mut S, address: &Address, difficulty: Difficulty, - withdrawal_limit: token::Amount, + withdrawal_limit: Uint, ) -> storage_api::Result<()> where S: StorageWrite, @@ -457,7 +457,7 @@ where pub fn read_withdrawal_limit( storage: &S, address: &Address, -) -> storage_api::Result +) -> storage_api::Result where S: StorageRead, { @@ -471,7 +471,7 @@ where pub fn write_withdrawal_limit( storage: &mut S, address: &Address, - withdrawal_limit: token::Amount, + withdrawal_limit: Uint, ) -> Result<(), storage_api::Error> where S: StorageWrite, diff --git a/core/src/types/uint.rs b/core/src/types/uint.rs index 637694a29c..8cd83efdd2 100644 --- a/core/src/types/uint.rs +++ b/core/src/types/uint.rs @@ -8,7 +8,6 @@ use std::ops::{Add, AddAssign, BitAnd, Div, Mul, Neg, Rem, Sub, SubAssign}; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use impl_num_traits::impl_uint_num_traits; use num_integer::Integer; -use serde::{Deserialize, Serialize}; use uint::construct_uint; use crate::types::token; @@ -31,8 +30,6 @@ construct_uint! { /// Namada native type to replace for unsigned 256 bit /// integers. #[derive( - Serialize, - Deserialize, BorshSerialize, BorshDeserialize, BorshSchema, @@ -41,6 +38,62 @@ construct_uint! { pub struct Uint(4); } +impl serde::Serialize for Uint { + fn serialize( + &self, + serializer: S, + ) -> std::result::Result + where + S: serde::Serializer, + { + let amount_string = self.to_string(); + serde::Serialize::serialize(&amount_string, serializer) + } +} + +impl<'de> serde::Deserialize<'de> for Uint { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + use serde::de::Error as serdeError; + let amount_string: String = + serde::Deserialize::deserialize(deserializer)?; + + let digits = amount_string + .chars() + .filter_map(|c| { + if c.is_numeric() { + c.to_digit(10).map(Uint::from) + } else { + None + } + }) + .rev() + .collect::>(); + if digits.len() != amount_string.len() { + return Err(D::Error::custom(AmountParseError::FromString)); + } + if digits.len() > 77 { + return Err(D::Error::custom(AmountParseError::ScaleTooLarge( + digits.len() as u32, + 77, + ))); + } + let mut value = Uint::default(); + let ten = Uint::from(10); + for (pow, digit) in digits.into_iter().enumerate() { + value = ten + .checked_pow(Uint::from(pow)) + .and_then(|scaling| scaling.checked_mul(digit)) + .and_then(|scaled| value.checked_add(scaled)) + .ok_or(AmountParseError::PrecisionOverflow) + .map_err(D::Error::custom)?; + } + Ok(value) + } +} + impl_uint_num_traits!(Uint, 4); impl Integer for Uint { @@ -624,4 +677,15 @@ mod test_uint { assert!(-that <= -this); assert!(-that <= this); } + + #[test] + fn test_serialization_roundtrip() { + let amount: Uint = serde_json::from_str(r#""1000000000""#).unwrap(); + assert_eq!(amount, Uint::from(1000000000)); + let serialized = serde_json::to_string(&amount).unwrap(); + assert_eq!(serialized, r#""1000000000""#); + + let amount: Result = serde_json::from_str(r#""1000000000.2""#); + assert!(amount.is_err()); + } } diff --git a/genesis/e2e-tests-single-node.toml b/genesis/e2e-tests-single-node.toml index beab8edc2f..c61c2c2bcd 100644 --- a/genesis/e2e-tests-single-node.toml +++ b/genesis/e2e-tests-single-node.toml @@ -5,7 +5,7 @@ genesis_time = "2021-09-30T10:00:00Z" native_token = "NAM" faucet_pow_difficulty = 1 -faucet_withdrawal_limit = "1000000000" +faucet_withdrawal_limit = "1000" [validator.validator-0] # Validator's staked NAM at genesis. diff --git a/tests/src/storage_api/testnet_pow.rs b/tests/src/storage_api/testnet_pow.rs index 5e54188c1b..ab45bc99c8 100644 --- a/tests/src/storage_api/testnet_pow.rs +++ b/tests/src/storage_api/testnet_pow.rs @@ -11,7 +11,7 @@ use crate::vp; fn test_challenge_and_solution() -> storage_api::Result<()> { let faucet_address = address::testing::established_address_1(); let difficulty = Difficulty::try_new(1).unwrap(); - let withdrawal_limit = token::Amount::native_whole(1_000); + let withdrawal_limit = token::Amount::native_whole(1_000).into(); let mut tx_env = TestTxEnv::default(); diff --git a/wasm/wasm_source/src/vp_testnet_faucet.rs b/wasm/wasm_source/src/vp_testnet_faucet.rs index 8e002663ee..c93f7fd140 100644 --- a/wasm/wasm_source/src/vp_testnet_faucet.rs +++ b/wasm/wasm_source/src/vp_testnet_faucet.rs @@ -9,6 +9,25 @@ use namada_vp_prelude::*; use once_cell::unsync::Lazy; +fn read_denom( + ctx: &Ctx, + token: &Address, + sub_prefix: Option<&storage::Key>, +) -> EnvResult { + if let Some(sub_prefix) = sub_prefix { + if sub_prefix + .segments + .contains(&storage::DbKeySeg::StringSeg("ibc".to_string())) + { + return Ok(token::NATIVE_MAX_DECIMAL_PLACES.into()); + } + } + let key = token::denom_key(token, sub_prefix); + ctx.read_pre(&key).map(|opt_denom| { + opt_denom.unwrap_or_else(|| token::NATIVE_MAX_DECIMAL_PLACES.into()) + }) +} + #[validity_predicate] fn validate_tx( ctx: &Ctx, @@ -43,7 +62,7 @@ fn validate_tx( } for key in keys_changed.iter() { - let is_valid = if let Some([_, owner]) = + let is_valid = if let Some([token, owner]) = token::is_any_token_balance_key(key) { if owner == &addr { @@ -51,7 +70,7 @@ fn validate_tx( let post: token::Amount = ctx.read_post(key)?.unwrap_or_default(); let change = post.change() - pre.change(); - + let denom = read_denom(ctx, token, None)?; if !change.non_negative() { // Allow to withdraw without a sig if there's a valid PoW if ctx.has_valid_pow() { @@ -60,7 +79,10 @@ fn validate_tx( &ctx.pre(), &addr, )?; - change >= -max_free_debit.change() + + token::Amount::from_uint(change.abs(), 0).unwrap() + <= token::Amount::from_uint(max_free_debit, denom) + .unwrap() } else { debug_log!("No PoW solution, a signature is required"); // Debit without a solution has to signed @@ -304,7 +326,7 @@ mod tests { let vp_owner = address::testing::established_address_1(); let difficulty = testnet_pow::Difficulty::try_new(0).unwrap(); let withdrawal_limit = token::Amount::from_uint(MAX_FREE_DEBIT as u64, 0).unwrap(); - testnet_pow::init_faucet_storage(&mut tx_env.wl_storage, &vp_owner, difficulty, withdrawal_limit).unwrap(); + testnet_pow::init_faucet_storage(&mut tx_env.wl_storage, &vp_owner, difficulty, withdrawal_limit.into()).unwrap(); let target = address::testing::established_address_2(); let token = address::nam(); @@ -349,7 +371,7 @@ mod tests { let vp_owner = address::testing::established_address_1(); let difficulty = testnet_pow::Difficulty::try_new(0).unwrap(); let withdrawal_limit = token::Amount::from_uint(MAX_FREE_DEBIT as u64, 0).unwrap(); - testnet_pow::init_faucet_storage(&mut tx_env.wl_storage, &vp_owner, difficulty, withdrawal_limit).unwrap(); + testnet_pow::init_faucet_storage(&mut tx_env.wl_storage, &vp_owner, difficulty, withdrawal_limit.into()).unwrap(); let target = address::testing::established_address_2(); let target_key = key::testing::keypair_1(); @@ -414,7 +436,7 @@ mod tests { // Init the VP let difficulty = testnet_pow::Difficulty::try_new(0).unwrap(); let withdrawal_limit = token::Amount::from_uint(MAX_FREE_DEBIT as u64, 0).unwrap(); - testnet_pow::init_faucet_storage(&mut tx_env.wl_storage, &vp_owner, difficulty, withdrawal_limit).unwrap(); + testnet_pow::init_faucet_storage(&mut tx_env.wl_storage, &vp_owner, difficulty, withdrawal_limit.into()).unwrap(); let keypair = key::testing::keypair_1(); let public_key = &keypair.ref_to(); From 52b882aa4a04833f888249d222422a9da901cf7b Mon Sep 17 00:00:00 2001 From: Jacob Turner Date: Mon, 10 Jul 2023 10:12:00 +0200 Subject: [PATCH 076/120] Update apps/src/lib/config/genesis.rs Co-authored-by: Tomas Zemanovic --- apps/src/lib/config/genesis.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index b556a64864..de66df8cd1 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -124,7 +124,7 @@ pub mod genesis_config { /// Testnet faucet PoW difficulty - defaults to `0` when not set pub faucet_pow_difficulty: Option, #[cfg(not(feature = "mainnet"))] - /// Testnet faucet withdrawal limit - defaults to 1000 NAM when not set + /// Testnet faucet withdrawal limit - defaults to 1000 tokens when not set pub faucet_withdrawal_limit: Option, // Initial validator set pub validator: HashMap, From 42b5335340c3bd7604437440a12d163750b84066 Mon Sep 17 00:00:00 2001 From: Jacob Turner Date: Mon, 10 Jul 2023 10:12:16 +0200 Subject: [PATCH 077/120] Update core/src/types/uint.rs Co-authored-by: Tomas Zemanovic --- core/src/types/uint.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/types/uint.rs b/core/src/types/uint.rs index 8cd83efdd2..132a09064a 100644 --- a/core/src/types/uint.rs +++ b/core/src/types/uint.rs @@ -63,7 +63,7 @@ impl<'de> serde::Deserialize<'de> for Uint { let digits = amount_string .chars() .filter_map(|c| { - if c.is_numeric() { + if c.is_digit(10) { c.to_digit(10).map(Uint::from) } else { None From 7fd0f6ec6b8bdd976c54e66f1d5be7b6dfc4dc96 Mon Sep 17 00:00:00 2001 From: satan Date: Mon, 10 Jul 2023 12:50:44 +0200 Subject: [PATCH 078/120] [chore]: Incorporating in review comments --- apps/src/lib/config/genesis.rs | 3 ++- core/src/types/uint.rs | 2 +- wasm/wasm_source/src/vp_testnet_faucet.rs | 31 ++++++++--------------- 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index de66df8cd1..1c659a056c 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -124,7 +124,8 @@ pub mod genesis_config { /// Testnet faucet PoW difficulty - defaults to `0` when not set pub faucet_pow_difficulty: Option, #[cfg(not(feature = "mainnet"))] - /// Testnet faucet withdrawal limit - defaults to 1000 tokens when not set + /// Testnet faucet withdrawal limit - defaults to 1000 tokens when not + /// set pub faucet_withdrawal_limit: Option, // Initial validator set pub validator: HashMap, diff --git a/core/src/types/uint.rs b/core/src/types/uint.rs index 132a09064a..ea935c1cc1 100644 --- a/core/src/types/uint.rs +++ b/core/src/types/uint.rs @@ -63,7 +63,7 @@ impl<'de> serde::Deserialize<'de> for Uint { let digits = amount_string .chars() .filter_map(|c| { - if c.is_digit(10) { + if c.is_ascii_digit() { c.to_digit(10).map(Uint::from) } else { None diff --git a/wasm/wasm_source/src/vp_testnet_faucet.rs b/wasm/wasm_source/src/vp_testnet_faucet.rs index c93f7fd140..3278f7a111 100644 --- a/wasm/wasm_source/src/vp_testnet_faucet.rs +++ b/wasm/wasm_source/src/vp_testnet_faucet.rs @@ -9,25 +9,6 @@ use namada_vp_prelude::*; use once_cell::unsync::Lazy; -fn read_denom( - ctx: &Ctx, - token: &Address, - sub_prefix: Option<&storage::Key>, -) -> EnvResult { - if let Some(sub_prefix) = sub_prefix { - if sub_prefix - .segments - .contains(&storage::DbKeySeg::StringSeg("ibc".to_string())) - { - return Ok(token::NATIVE_MAX_DECIMAL_PLACES.into()); - } - } - let key = token::denom_key(token, sub_prefix); - ctx.read_pre(&key).map(|opt_denom| { - opt_denom.unwrap_or_else(|| token::NATIVE_MAX_DECIMAL_PLACES.into()) - }) -} - #[validity_predicate] fn validate_tx( ctx: &Ctx, @@ -70,7 +51,17 @@ fn validate_tx( let post: token::Amount = ctx.read_post(key)?.unwrap_or_default(); let change = post.change() - pre.change(); - let denom = read_denom(ctx, token, None)?; + let maybe_denom = + storage_api::token::read_denom(&ctx.pre(), token, None)?; + if maybe_denom.is_none() { + debug_log!( + "A denomination for token address {} does not exist \ + in storage", + token, + ); + return reject(); + } + let denom = maybe_denom.unwrap(); if !change.non_negative() { // Allow to withdraw without a sig if there's a valid PoW if ctx.has_valid_pow() { From 5de784e77fca0c806a2f3c6e59f2126a0b40c26d Mon Sep 17 00:00:00 2001 From: satan Date: Mon, 10 Jul 2023 13:16:39 +0200 Subject: [PATCH 079/120] [fix]: Fixing errors introduced by merging in main --- apps/src/lib/node/ledger/shell/init_chain.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index 91f48e3e05..7ce3e963f8 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -260,7 +260,7 @@ where fn initialize_established_accounts( &mut self, faucet_pow_difficulty: Option, - faucet_withdrawal_limit: Option, + faucet_withdrawal_limit: Option, accounts: Vec, implicit_vp_code_path: &str, ) -> Result<()> { @@ -311,8 +311,11 @@ where if vp_code_path == "vp_testnet_faucet.wasm" { let difficulty = faucet_pow_difficulty.unwrap_or_default(); // withdrawal limit defaults to 1000 NAM when not set - let withdrawal_limit = faucet_withdrawal_limit - .unwrap_or_else(|| token::Amount::native_whole(1_000)); + let withdrawal_limit = + faucet_withdrawal_limit.unwrap_or_else(|| { + token::Amount::native_whole(1_000).into() + }); + testnet_pow::init_faucet_storage( &mut self.wl_storage, &address, From 5ca0eab99b44a33fefa439c42fc7a33a27f4a060 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 12 Jul 2023 08:15:25 +0100 Subject: [PATCH 080/120] changelog: add #1667 --- .changelog/unreleased/bug-fixes/1667-faucet-limit-fix.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/bug-fixes/1667-faucet-limit-fix.md diff --git a/.changelog/unreleased/bug-fixes/1667-faucet-limit-fix.md b/.changelog/unreleased/bug-fixes/1667-faucet-limit-fix.md new file mode 100644 index 0000000000..49c3647b55 --- /dev/null +++ b/.changelog/unreleased/bug-fixes/1667-faucet-limit-fix.md @@ -0,0 +1,2 @@ +- Fix genesis `faucet_withdrawal_limit` parser to respect tokens' denomination. + ([\#1667](https://github.com/anoma/namada/pull/1667)) \ No newline at end of file From a22eacb5d884a6089e6b8e9fb6155611c51cd931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 12 Jul 2023 08:19:43 +0100 Subject: [PATCH 081/120] changelog: add #1670 --- .../unreleased/improvements/1670-remove-unused-assoc-ty.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .changelog/unreleased/improvements/1670-remove-unused-assoc-ty.md diff --git a/.changelog/unreleased/improvements/1670-remove-unused-assoc-ty.md b/.changelog/unreleased/improvements/1670-remove-unused-assoc-ty.md new file mode 100644 index 0000000000..b4db773566 --- /dev/null +++ b/.changelog/unreleased/improvements/1670-remove-unused-assoc-ty.md @@ -0,0 +1,4 @@ +- Removed associated type on `masp::ShieldedUtils`. This type was an + attempt to reduce the number of generic parameters needed when interacting + with MASP but resulted in making code re-use extremely difficult. + ([\#1670](https://github.com/anoma/namada/pull/1670)) \ No newline at end of file From c0cbacbcfeb927a4baa4027961db0be200709d02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 12 Jul 2023 08:30:06 +0100 Subject: [PATCH 082/120] changelog: add #1692 --- .../improvements/1692-rm-from-u64-on-ethbridge-stake.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/improvements/1692-rm-from-u64-on-ethbridge-stake.md diff --git a/.changelog/unreleased/improvements/1692-rm-from-u64-on-ethbridge-stake.md b/.changelog/unreleased/improvements/1692-rm-from-u64-on-ethbridge-stake.md new file mode 100644 index 0000000000..4c6813670c --- /dev/null +++ b/.changelog/unreleased/improvements/1692-rm-from-u64-on-ethbridge-stake.md @@ -0,0 +1,2 @@ +- Removed `impl From for EthBridgeVotingPower` and replaced it with a + `TryFrom`. ([\#1692](https://github.com/anoma/namada/pull/1692)) \ No newline at end of file From 9ed7177c95fe7f5d3a47e49ced3c71dacf65c881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 22 Feb 2023 09:53:42 +0000 Subject: [PATCH 083/120] shared/ledger/queries: add token balance client-only method --- shared/src/ledger/queries/vp/token.rs | 39 +++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/shared/src/ledger/queries/vp/token.rs b/shared/src/ledger/queries/vp/token.rs index cbad27005f..1a2ab3a1bd 100644 --- a/shared/src/ledger/queries/vp/token.rs +++ b/shared/src/ledger/queries/vp/token.rs @@ -1,3 +1,5 @@ +//! 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; @@ -41,3 +43,40 @@ where { read_denom(ctx.wl_storage, &addr, None) } + +#[cfg(any(test, feature = "async-client"))] +pub mod client_only_methods { + use borsh::BorshDeserialize; + + use super::Token; + use crate::ledger::queries::{Client, RPC}; + use crate::types::address::Address; + use crate::types::token; + + impl Token { + /// Get the balance of the given `token` belonging to the given `owner`. + pub async fn balance( + &self, + client: &CLIENT, + token: &Address, + owner: &Address, + ) -> Result::Error> + where + CLIENT: Client + Sync, + { + let balance_key = token::balance_key(token, owner); + let response = RPC + .shell() + .storage_value(client, None, None, false, &balance_key) + .await?; + + let balance = if response.data.is_empty() { + token::Amount::default() + } else { + token::Amount::try_from_slice(&response.data) + .unwrap_or_default() + }; + Ok(balance) + } + } +} From 223753f65f852ae67f48c0eb1de5bd509ab4420c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 22 Feb 2023 09:56:08 +0000 Subject: [PATCH 084/120] client/rpc: use the new token balance method --- apps/src/lib/client/rpc.rs | 2 +- apps/src/lib/client/tx.rs | 3 +-- shared/src/ledger/rpc.rs | 8 ++++---- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 44955967d0..38f79b83c1 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -1098,7 +1098,7 @@ pub async fn get_token_balance( client: &C, token: &Address, owner: &Address, -) -> Option { +) -> token::Amount { namada::ledger::rpc::get_token_balance(client, token, owner).await } diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index d70f8c7e6f..0b228e6a2b 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -702,8 +702,7 @@ where let balance = rpc::get_token_balance(client, &ctx.native_token, &proposal.author) - .await - .unwrap_or_default(); + .await; if balance < token::Amount::from_uint( governance_parameters.min_proposal_fund, diff --git a/shared/src/ledger/rpc.rs b/shared/src/ledger/rpc.rs index 90d05ebd13..5979229721 100644 --- a/shared/src/ledger/rpc.rs +++ b/shared/src/ledger/rpc.rs @@ -36,7 +36,6 @@ use crate::types::governance::{ProposalVote, VotePower}; use crate::types::hash::Hash; use crate::types::key::*; use crate::types::storage::{BlockHeight, BlockResults, Epoch, PrefixValue}; -use crate::types::token::balance_key; use crate::types::{storage, token}; /// Query the status of a given transaction. @@ -137,9 +136,10 @@ pub async fn get_token_balance( client: &C, token: &Address, owner: &Address, -) -> Option { - let balance_key = balance_key(token, owner); - query_storage_value(client, &balance_key).await +) -> token::Amount { + unwrap_client_response::( + RPC.vp().token().balance(client, token, owner).await, + ) } /// Get account's public key stored in its storage sub-space From d03df95e8774d314e42d1ac571c0f3b109efb35a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 12 Jul 2023 08:54:50 +0100 Subject: [PATCH 085/120] changelog: add #1173 --- .changelog/unreleased/improvements/1173-token-balance-query.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/improvements/1173-token-balance-query.md diff --git a/.changelog/unreleased/improvements/1173-token-balance-query.md b/.changelog/unreleased/improvements/1173-token-balance-query.md new file mode 100644 index 0000000000..e5a9ea2322 --- /dev/null +++ b/.changelog/unreleased/improvements/1173-token-balance-query.md @@ -0,0 +1,2 @@ +- Added a reusable token balance query method. + ([\#1173](https://github.com/anoma/namada/pull/1173)) \ No newline at end of file From e65ffebac5741ba2fafa8e6c09ced807f5e9d61e Mon Sep 17 00:00:00 2001 From: yito88 Date: Wed, 12 Jul 2023 18:41:46 +0200 Subject: [PATCH 086/120] remove vp_token --- apps/src/lib/config/genesis.rs | 22 -- apps/src/lib/node/ledger/shell/init_chain.rs | 34 +-- core/src/ledger/storage/write_log.rs | 45 ++-- genesis/dev.toml | 12 - genesis/e2e-tests-single-node.toml | 11 - shared/src/ledger/native_vp/multitoken.rs | 192 ++++++++------ shared/src/ledger/tx.rs | 24 +- shared/src/vm/host_env.rs | 14 + tests/src/vm_host_env/ibc.rs | 22 +- wasm/wasm_source/Makefile | 1 - wasm/wasm_source/src/lib.rs | 2 - wasm/wasm_source/src/vp_token.rs | 263 ------------------- 12 files changed, 179 insertions(+), 463 deletions(-) delete mode 100644 wasm/wasm_source/src/vp_token.rs diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index fcd57be745..0fd5a319ec 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -392,27 +392,13 @@ pub mod genesis_config { fn load_token( config: &TokenAccountConfig, - wasm: &HashMap, validators: &HashMap, established_accounts: &HashMap, implicit_accounts: &HashMap, ) -> TokenAccount { - let token_vp_name = config.vp.as_ref().unwrap(); - let token_vp_config = wasm.get(token_vp_name).unwrap(); - TokenAccount { address: Address::decode(config.address.as_ref().unwrap()).unwrap(), denom: config.denom, - vp_code_path: token_vp_config.filename.to_owned(), - vp_sha256: token_vp_config - .sha256 - .clone() - .unwrap_or_else(|| { - eprintln!("Unknown token VP WASM sha256"); - cli::safe_exit(1); - }) - .to_sha256_bytes() - .unwrap(), balances: config .balances .as_ref() @@ -579,7 +565,6 @@ pub mod genesis_config { .map(|(_name, cfg)| { load_token( cfg, - &wasm, &validators, &established_accounts, &implicit_accounts, @@ -814,10 +799,6 @@ pub struct TokenAccount { pub address: Address, /// The number of decimal places amounts of this token has pub denom: Denomination, - /// Validity predicate code WASM - pub vp_code_path: String, - /// Expected SHA-256 hash of the validity predicate wasm - pub vp_sha256: [u8; 32], /// Accounts' balances of this token #[derivative(PartialOrd = "ignore", Ord = "ignore")] pub balances: HashMap, @@ -903,7 +884,6 @@ pub fn genesis(num_validators: u64) -> Genesis { use crate::wallet; let vp_implicit_path = "vp_implicit.wasm"; - let vp_token_path = "vp_token.wasm"; let vp_user_path = "vp_user.wasm"; // NOTE When the validator's key changes, tendermint must be reset with @@ -1081,8 +1061,6 @@ pub fn genesis(num_validators: u64) -> Genesis { .map(|(address, (_, denom))| TokenAccount { address, denom, - vp_code_path: vp_token_path.into(), - vp_sha256: Default::default(), balances: balances.clone(), }) .collect(); diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index 4fa8889753..cf029b5a84 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -236,10 +236,7 @@ where self.initialize_implicit_accounts(genesis.implicit_accounts); // Initialize genesis token accounts - self.initialize_token_accounts( - genesis.token_accounts, - &implicit_vp_code_path, - ); + self.initialize_token_accounts(genesis.token_accounts); // Initialize genesis validator accounts let staking_token = staking_token_address(&self.wl_storage); @@ -342,45 +339,16 @@ where fn initialize_token_accounts( &mut self, accounts: Vec, - implicit_vp_code_path: &str, ) { // Initialize genesis token accounts for genesis::TokenAccount { address, denom, - vp_code_path, - vp_sha256, balances, } in accounts { // associate a token with its denomination. write_denom(&mut self.wl_storage, &address, denom).unwrap(); - let vp_code_hash = - read_wasm_hash(&self.wl_storage, vp_code_path.clone()) - .unwrap() - .ok_or(Error::LoadingWasm(format!( - "Unknown vp code path: {}", - implicit_vp_code_path - ))) - .expect("Reading wasms should succeed"); - - // In dev, we don't check the hash - #[cfg(feature = "dev")] - let _ = vp_sha256; - #[cfg(not(feature = "dev"))] - { - assert_eq!( - vp_code_hash.0.as_slice(), - &vp_sha256, - "Invalid token account's VP sha256 hash for {}", - vp_code_path - ); - } - - self.wl_storage - .write_bytes(&Key::validity_predicate(&address), vp_code_hash) - .unwrap(); - for (owner, amount) in balances { credit_tokens(&mut self.wl_storage, &address, &owner, amount) .unwrap(); diff --git a/core/src/ledger/storage/write_log.rs b/core/src/ledger/storage/write_log.rs index 4b4c055c50..641fa7fc19 100644 --- a/core/src/ledger/storage/write_log.rs +++ b/core/src/ledger/storage/write_log.rs @@ -9,10 +9,13 @@ use thiserror::Error; use crate::ledger; use crate::ledger::storage::traits::StorageHasher; use crate::ledger::storage::Storage; -use crate::types::address::{Address, EstablishedAddressGen}; +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::{ + is_any_minted_balance_key, is_any_minter_key, is_any_token_balance_key, +}; #[allow(missing_docs)] #[derive(Error, Debug)] @@ -470,21 +473,33 @@ impl WriteLog { // get changed keys grouped by the address for key in changed_keys.iter() { - for addr in &key.find_addresses() { - if verifiers_from_tx.contains(addr) - || initialized_accounts.contains(addr) - { - // We can skip this when the address has been added from the - // Tx above. - // Also skip if it's an address of a newly initialized - // account, because anything can be written into an - // account's storage in the same tx in which it's - // initialized (there is no VP in the state prior to tx - // execution). - continue; + // for token keys, trigger Multitoken VP and the owner's VP + if let Some([_, owner]) = is_any_token_balance_key(key) { + verifiers + .insert(Address::Internal(InternalAddress::Multitoken)); + verifiers.insert(owner.clone()); + } else if is_any_minted_balance_key(key).is_some() + || is_any_minter_key(key).is_some() + { + verifiers + .insert(Address::Internal(InternalAddress::Multitoken)); + } else { + for addr in &key.find_addresses() { + if verifiers_from_tx.contains(addr) + || initialized_accounts.contains(addr) + { + // We can skip this when the address has been added from + // the Tx above. + // Also skip if it's an address of a newly initialized + // account, because anything can be written into an + // account's storage in the same tx in which it's + // initialized (there is no VP in the state prior to tx + // execution). + continue; + } + // Add the address as a verifier + verifiers.insert(addr.clone()); } - // Add the address as a verifier - verifiers.insert(addr.clone()); } } (verifiers, changed_keys) diff --git a/genesis/dev.toml b/genesis/dev.toml index b6eb070b42..19985a3b9e 100644 --- a/genesis/dev.toml +++ b/genesis/dev.toml @@ -28,7 +28,6 @@ net_address = "127.0.0.1:26656" [token.NAM] address = "atest1v4ehgw36x3prswzxggunzv6pxqmnvdj9xvcyzvpsggeyvs3cg9qnywf589qnwvfsg5erg3fkl09rg5" denom = 8 -vp = "vp_token" [token.NAM.balances] # In token balances, we can use: # 1. An address any account @@ -45,7 +44,6 @@ Bertha = "1000000" [token.BTC] address = "atest1v4ehgw36xdzryve5gsc52veeg5cnsv2yx5eygvp38qcrvd29xy6rys6p8yc5xvp4xfpy2v694wgwcp" denom = 8 -vp = "vp_token" [token.BTC.balances] atest1v4ehgw368ycryv2z8qcnxv3cxgmrgvjpxs6yg333gym5vv2zxepnj334g4rryvj9xucrgve4x3xvr4 = 1000000 atest1v4ehgw36x3qng3jzggu5yvpsxgcngv2xgguy2dpkgvu5x33kx3pr2w2zgep5xwfkxscrxs2pj8075p = 1000000 @@ -55,7 +53,6 @@ a1qyqzsqqqqqcyvvf5xcu5vd6rg4z5233hg9pn23pjgdryzdjy8pz52wzxxscnvvjxx3rryvzz8y5p6m [token.ETH] address = "atest1v4ehgw36xqmr2d3nx3ryvd2xxgmrq33j8qcns33sxezrgv6zxdzrydjrxveygd2yxumrsdpsf9jc2p" denom = 18 -vp = "vp_token" [token.ETH.balances] atest1v4ehgw368ycryv2z8qcnxv3cxgmrgvjpxs6yg333gym5vv2zxepnj334g4rryvj9xucrgve4x3xvr4 = 1000000 atest1v4ehgw36x3qng3jzggu5yvpsxgcngv2xgguy2dpkgvu5x33kx3pr2w2zgep5xwfkxscrxs2pj8075p = 1000000 @@ -65,7 +62,6 @@ a1qyqzsqqqqqcyvvf5xcu5vd6rg4z5233hg9pn23pjgdryzdjy8pz52wzxxscnvvjxx3rryvzz8y5p6m [token.DOT] address = "atest1v4ehgw36gg6nvs2zgfpyxsfjgc65yv6pxy6nwwfsxgungdzrggeyzv35gveyxsjyxymyz335hur2jn" denom = 10 -vp = "vp_token" [token.DOT.balances] atest1v4ehgw368ycryv2z8qcnxv3cxgmrgvjpxs6yg333gym5vv2zxepnj334g4rryvj9xucrgve4x3xvr4 = 1000000 atest1v4ehgw36x3qng3jzggu5yvpsxgcngv2xgguy2dpkgvu5x33kx3pr2w2zgep5xwfkxscrxs2pj8075p = 1000000 @@ -75,7 +71,6 @@ a1qyqzsqqqqqcyvvf5xcu5vd6rg4z5233hg9pn23pjgdryzdjy8pz52wzxxscnvvjxx3rryvzz8y5p6m [token.schnitzel] address = "atest1v4ehgw36xue5xvf5xvuyzvpjx5un2v3k8qeyvd3cxdqns32p89rrxd6xx9zngvpegccnzs699rdnnt" denom = 6 -vp = "vp_token" [token.schnitzel.balances] atest1v4ehgw368ycryv2z8qcnxv3cxgmrgvjpxs6yg333gym5vv2zxepnj334g4rryvj9xucrgve4x3xvr4 = 1000000 atest1v4ehgw36x3qng3jzggu5yvpsxgcngv2xgguy2dpkgvu5x33kx3pr2w2zgep5xwfkxscrxs2pj8075p = 1000000 @@ -85,7 +80,6 @@ a1qyqzsqqqqqcyvvf5xcu5vd6rg4z5233hg9pn23pjgdryzdjy8pz52wzxxscnvvjxx3rryvzz8y5p6m [token.apfel] address = "atest1v4ehgw36gfryydj9g3p5zv3kg9znyd358ycnzsfcggc5gvecgc6ygs2rxv6ry3zpg4zrwdfeumqcz9" denom = 6 -vp = "vp_token" [token.apfel.balances] atest1v4ehgw368ycryv2z8qcnxv3cxgmrgvjpxs6yg333gym5vv2zxepnj334g4rryvj9xucrgve4x3xvr4 = 1000000 atest1v4ehgw36x3qng3jzggu5yvpsxgcngv2xgguy2dpkgvu5x33kx3pr2w2zgep5xwfkxscrxs2pj8075p = 1000000 @@ -96,7 +90,6 @@ a1qyqzsqqqqqcyvvf5xcu5vd6rg4z5233hg9pn23pjgdryzdjy8pz52wzxxscnvvjxx3rryvzz8y5p6m address = "atest1v4ehgw36gep5ysecxq6nyv3jg3zygv3e89qn2vp48pryxsf4xpznvve5gvmy23fs89pryvf5a6ht90" denom = 6 public_key = "" -vp = "vp_token" [token.kartoffel.balances] atest1v4ehgw368ycryv2z8qcnxv3cxgmrgvjpxs6yg333gym5vv2zxepnj334g4rryvj9xucrgve4x3xvr4 = 1000000 atest1v4ehgw36x3qng3jzggu5yvpsxgcngv2xgguy2dpkgvu5x33kx3pr2w2zgep5xwfkxscrxs2pj8075p = 1000000 @@ -140,11 +133,6 @@ sha256 = "dc7b97f0448f2369bd2401c3c1d8898f53cac8c464a8c1b1f7f81415a658625d" # filename (relative to wasm path used by the node) filename = "vp_validator.wasm" -# Token VP -[wasm.vp_token] -filename = "vp_token.wasm" -sha256 = "e428a11f570d21dd3c871f5d35de6fe18098eb8ee0456b3e11a72ccdd8685cd0" - # General protocol parameters. [parameters] # Minimum number of blocks in an epoch. diff --git a/genesis/e2e-tests-single-node.toml b/genesis/e2e-tests-single-node.toml index beab8edc2f..e3d309f6d1 100644 --- a/genesis/e2e-tests-single-node.toml +++ b/genesis/e2e-tests-single-node.toml @@ -28,7 +28,6 @@ net_address = "127.0.0.1:27656" [token.NAM] address = "atest1v4ehgw36x3prswzxggunzv6pxqmnvdj9xvcyzvpsggeyvs3cg9qnywf589qnwvfsg5erg3fkl09rg5" denom = 6 -vp = "vp_token" [token.NAM.balances] Albert = "1000000" "Albert.public_key" = "100" @@ -45,7 +44,6 @@ faucet = "9223372036854" [token.BTC] address = "atest1v4ehgw36xdzryve5gsc52veeg5cnsv2yx5eygvp38qcrvd29xy6rys6p8yc5xvp4xfpy2v694wgwcp" denom = 8 -vp = "vp_token" [token.BTC.balances] Albert = "1000000" Bertha = "1000000" @@ -57,7 +55,6 @@ faucet = "9223372036854" [token.ETH] address = "atest1v4ehgw36xqmr2d3nx3ryvd2xxgmrq33j8qcns33sxezrgv6zxdzrydjrxveygd2yxumrsdpsf9jc2p" denom = 18 -vp = "vp_token" [token.ETH.balances] Albert = "1000000" Bertha = "1000000" @@ -69,7 +66,6 @@ faucet = "9223372036854" [token.DOT] address = "atest1v4ehgw36gg6nvs2zgfpyxsfjgc65yv6pxy6nwwfsxgungdzrggeyzv35gveyxsjyxymyz335hur2jn" denom = 10 -vp = "vp_token" [token.DOT.balances] Albert = "1000000" Bertha = "1000000" @@ -81,7 +77,6 @@ faucet = "9223372036854" [token.Schnitzel] address = "atest1v4ehgw36xue5xvf5xvuyzvpjx5un2v3k8qeyvd3cxdqns32p89rrxd6xx9zngvpegccnzs699rdnnt" denom = 6 -vp = "vp_token" [token.Schnitzel.balances] Albert = "1000000" Bertha = "1000000" @@ -93,7 +88,6 @@ faucet = "9223372036854" [token.Apfel] address = "atest1v4ehgw36gfryydj9g3p5zv3kg9znyd358ycnzsfcggc5gvecgc6ygs2rxv6ry3zpg4zrwdfeumqcz9" denom = 6 -vp = "vp_token" [token.Apfel.balances] Albert = "1000000" Bertha = "1000000" @@ -106,7 +100,6 @@ faucet = "9223372036854" address = "atest1v4ehgw36gep5ysecxq6nyv3jg3zygv3e89qn2vp48pryxsf4xpznvve5gvmy23fs89pryvf5a6ht90" public_key = "" denom = 6 -vp = "vp_token" [token.Kartoffel.balances] Albert = "1000000" Bertha = "1000000" @@ -151,10 +144,6 @@ filename = "vp_user.wasm" # filename (relative to wasm path used by the node) filename = "vp_validator.wasm" -# Token VP -[wasm.vp_token] -filename = "vp_token.wasm" - # Faucet VP [wasm.vp_testnet_faucet] filename = "vp_testnet_faucet.wasm" diff --git a/shared/src/ledger/native_vp/multitoken.rs b/shared/src/ledger/native_vp/multitoken.rs index b3d93e5170..038e40349e 100644 --- a/shared/src/ledger/native_vp/multitoken.rs +++ b/shared/src/ledger/native_vp/multitoken.rs @@ -8,7 +8,7 @@ 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::address::{self, 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, @@ -56,10 +56,15 @@ where let mut changes = HashMap::new(); let mut mints = HashMap::new(); for key in keys_changed { - if let Some([token, _]) = is_any_token_balance_key(key) { + if let Some([token, owner]) = is_any_token_balance_key(key) { let pre: Amount = self.ctx.read_pre(key)?.unwrap_or_default(); let post: Amount = self.ctx.read_post(key)?.unwrap_or_default(); let diff = post.change() - pre.change(); + if diff.is_negative() + && !(verifiers.contains(owner) || *owner == address::masp()) + { + return Ok(false); + } match changes.get_mut(token) { Some(change) => *change += diff, None => _ = changes.insert(token, diff), @@ -74,14 +79,12 @@ where } // Check if the minter is set - match self.check_minter(token)? { - Some(minter) if verifiers.contains(&minter) => {} - _ => return Ok(false), + if !self.is_valid_minter(token, verifiers)? { + return Ok(false); } } else if let Some(token) = is_any_minter_key(key) { - match self.check_minter(token)? { - Some(_) => {} - None => return Ok(false), + if !self.is_valid_minter(token, verifiers)? { + return Ok(false); } } else if key.segments.get(0) == Some( @@ -111,32 +114,31 @@ where CA: 'static + WasmCacheAccess, { /// Return the minter if the minter is valid and the minter VP exists - pub fn check_minter(&self, token: &Address) -> Result> { - // Check if the minter is set - let minter_key = minter_key(token); - let minter = match self.ctx.read_post(&minter_key)? { - Some(m) => m, - None => return Ok(None), - }; + pub fn is_valid_minter( + &self, + token: &Address, + verifiers: &BTreeSet
, + ) -> Result { match token { - Address::Internal(InternalAddress::Erc20(_)) => { - // ERC20 token should not be minted by a wasm transaction - return Ok(None); - } Address::Internal(InternalAddress::IbcToken(_)) => { - if minter == Address::Internal(InternalAddress::Ibc) { - return Ok(Some(minter)); + // Check if the minter is set + let minter_key = minter_key(token); + match self.ctx.read_post::
(&minter_key)? { + Some(minter) + if minter + == Address::Internal(InternalAddress::Ibc) => + { + Ok(verifiers.contains(&minter)) + } + _ => Ok(false), } } _ => { - // Check the minter VP exists - let vp_key = Key::validity_predicate(&minter); - if self.ctx.has_key_post(&vp_key)? { - return Ok(Some(minter)); - } + // ERC20 and other tokens should not be minted by a wasm + // transaction + Ok(false) } } - Ok(None) } } @@ -152,12 +154,10 @@ mod tests { use crate::core::types::address::testing::{ established_address_1, established_address_2, }; - use crate::eth_bridge::storage::wrapped_erc20s; use crate::ledger::gas::VpGasMeter; use crate::ledger::ibc::storage::ibc_token; use crate::proto::{Code, Data, Section, Signature, Tx}; use crate::types::address::{Address, InternalAddress}; - use crate::types::ethereum_events::testing::arbitrary_eth_address; use crate::types::key::testing::keypair_1; use crate::types::storage::TxIndex; use crate::types::token::{ @@ -215,7 +215,8 @@ mod tests { let tx = dummy_tx(&wl_storage); let gas_meter = VpGasMeter::new(0); let (vp_wasm_cache, _vp_cache_dir) = wasm_cache(); - let verifiers = BTreeSet::new(); + let mut verifiers = BTreeSet::new(); + verifiers.insert(sender); let ctx = Ctx::new( &ADDRESS, &wl_storage.storage, @@ -289,6 +290,60 @@ mod tests { ); } + #[test] + fn test_invalid_sender() { + let mut wl_storage = TestWlStorage::default(); + let mut keys_changed = BTreeSet::new(); + + let sender = established_address_1(); + let sender_key = balance_key(&nam(), &sender); + let amount = Amount::native_whole(100); + wl_storage + .storage + .write(&sender_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + + // transfer 10 + let amount = Amount::native_whole(90); + wl_storage + .write_log + .write(&sender_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(sender_key); + let receiver = established_address_2(); + let receiver_key = balance_key(&nam(), &receiver); + let amount = Amount::native_whole(10); + wl_storage + .write_log + .write(&receiver_key, amount.try_to_vec().unwrap()) + .expect("write failed"); + keys_changed.insert(receiver_key); + + let tx_index = TxIndex::default(); + let tx = dummy_tx(&wl_storage); + let gas_meter = VpGasMeter::new(0); + let (vp_wasm_cache, _vp_cache_dir) = wasm_cache(); + let verifiers = BTreeSet::new(); + // The sender is not set + let ctx = Ctx::new( + &ADDRESS, + &wl_storage.storage, + &wl_storage.write_log, + &tx, + &tx_index, + gas_meter, + &keys_changed, + &verifiers, + vp_wasm_cache, + ); + + let vp = MultitokenVp { ctx }; + assert!( + !vp.validate_tx(&tx, &keys_changed, &verifiers) + .expect("validation failed") + ); + } + #[test] fn test_valid_mint() { let mut wl_storage = TestWlStorage::default(); @@ -354,13 +409,6 @@ mod tests { let mut wl_storage = TestWlStorage::default(); let mut keys_changed = BTreeSet::new(); - // set the dummy nam vp - let vp_key = Key::validity_predicate(&nam()); - wl_storage - .storage - .write(&vp_key, vec![]) - .expect("write failed"); - // mint 100 let target = established_address_1(); let target_key = balance_key(&nam(), &target); @@ -419,16 +467,19 @@ mod tests { let mut wl_storage = TestWlStorage::default(); let mut keys_changed = BTreeSet::new(); + // IBC token + let token = ibc_token("/port-42/channel-42/denom"); + // mint 100 let target = established_address_1(); - let target_key = balance_key(&nam(), &target); + let target_key = balance_key(&token, &target); let amount = Amount::native_whole(100); wl_storage .write_log .write(&target_key, amount.try_to_vec().unwrap()) .expect("write failed"); keys_changed.insert(target_key); - let minted_key = minted_balance_key(&nam()); + let minted_key = minted_balance_key(&token); let amount = Amount::native_whole(100); wl_storage .write_log @@ -463,20 +514,23 @@ mod tests { } #[test] - fn test_no_minter_vp() { + fn test_invalid_minter() { let mut wl_storage = TestWlStorage::default(); let mut keys_changed = BTreeSet::new(); + // IBC token + let token = ibc_token("/port-42/channel-42/denom"); + // mint 100 let target = established_address_1(); - let target_key = balance_key(&nam(), &target); + let target_key = balance_key(&token, &target); let amount = Amount::native_whole(100); wl_storage .write_log .write(&target_key, amount.try_to_vec().unwrap()) .expect("write failed"); keys_changed.insert(target_key); - let minted_key = minted_balance_key(&nam()); + let minted_key = minted_balance_key(&token); let amount = Amount::native_whole(100); wl_storage .write_log @@ -484,9 +538,9 @@ mod tests { .expect("write failed"); keys_changed.insert(minted_key); - // minter - let minter = Address::Internal(InternalAddress::Ibc); - let minter_key = minter_key(&nam()); + // invalid minter + let minter = established_address_1(); + let minter_key = minter_key(&token); wl_storage .write_log .write(&minter_key, minter.try_to_vec().unwrap()) @@ -497,8 +551,9 @@ mod tests { let tx = dummy_tx(&wl_storage); let gas_meter = VpGasMeter::new(0); let (vp_wasm_cache, _vp_cache_dir) = wasm_cache(); - let verifiers = BTreeSet::new(); - // the minter isn't included in the verifiers + let mut verifiers = BTreeSet::new(); + // for the minter + verifiers.insert(minter); let ctx = Ctx::new( &ADDRESS, &wl_storage.storage, @@ -519,37 +574,17 @@ mod tests { } #[test] - fn test_invalid_minter() { + fn test_invalid_minter_update() { let mut wl_storage = TestWlStorage::default(); let mut keys_changed = BTreeSet::new(); - // ERC20 token - let token = wrapped_erc20s::token(&arbitrary_eth_address()); - - // mint 100 - let target = established_address_1(); - let target_key = balance_key(&token, &target); - let amount = Amount::native_whole(100); - wl_storage - .write_log - .write(&target_key, amount.try_to_vec().unwrap()) - .expect("write failed"); - keys_changed.insert(target_key); - let minted_key = minted_balance_key(&token); - let amount = Amount::native_whole(100); - wl_storage - .write_log - .write(&minted_key, amount.try_to_vec().unwrap()) - .expect("write failed"); - keys_changed.insert(minted_key); - - // invalid minter + let minter_key = minter_key(&nam()); let minter = established_address_1(); - let minter_key = minter_key(&token); wl_storage .write_log .write(&minter_key, minter.try_to_vec().unwrap()) .expect("write failed"); + keys_changed.insert(minter_key); let tx_index = TxIndex::default(); @@ -579,26 +614,27 @@ mod tests { } #[test] - fn test_invalid_minter_update() { + fn test_invalid_key_update() { let mut wl_storage = TestWlStorage::default(); let mut keys_changed = BTreeSet::new(); - let minter_key = minter_key(&nam()); - let minter = established_address_1(); + let key = Key::from( + Address::Internal(InternalAddress::Multitoken).to_db_key(), + ) + .push(&"invalid_segment".to_string()) + .unwrap(); wl_storage .write_log - .write(&minter_key, minter.try_to_vec().unwrap()) + .write(&key, 0.try_to_vec().unwrap()) .expect("write failed"); - keys_changed.insert(minter_key); + keys_changed.insert(key); let tx_index = TxIndex::default(); let tx = dummy_tx(&wl_storage); let gas_meter = VpGasMeter::new(0); let (vp_wasm_cache, _vp_cache_dir) = wasm_cache(); - let mut verifiers = BTreeSet::new(); - // for the minter - verifiers.insert(minter); + let verifiers = BTreeSet::new(); let ctx = Ctx::new( &ADDRESS, &wl_storage.storage, diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index 0a02f8dcf0..f90c7d4795 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -1140,7 +1140,7 @@ pub async fn build_ibc_transfer< .await?; // We cannot check the receiver - let token = token_exists_or_err(args.token, args.tx.force, client).await?; + let token = args.token; // Check source balance let balance_key = token::balance_key(&token, &source); @@ -1317,8 +1317,6 @@ pub async fn build_transfer< source_exists_or_err(source.clone(), args.tx.force, client).await?; // Check that the target address exists on chain target_exists_or_err(target.clone(), args.tx.force, client).await?; - // Check that the token address exists on chain - token_exists_or_err(token.clone(), args.tx.force, client).await?; // Check source balance let balance_key = token::balance_key(&token, &source); @@ -1704,26 +1702,6 @@ where } } -/// Returns the given token if the given address exists on chain -/// otherwise returns an error, force forces the address through even -/// if it isn't on chain -pub async fn token_exists_or_err( - token: Address, - force: bool, - client: &C, -) -> Result { - let message = - format!("The token address {} doesn't exist on chain.", token); - address_exists_or_err( - token, - force, - client, - message, - Error::TokenDoesNotExist, - ) - .await -} - /// Returns the given source address if the given address exists on chain /// otherwise returns an error, force forces the address through even /// if it isn't on chain diff --git a/shared/src/vm/host_env.rs b/shared/src/vm/host_env.rs index 56cbc7d680..f290e484ff 100644 --- a/shared/src/vm/host_env.rs +++ b/shared/src/vm/host_env.rs @@ -23,6 +23,9 @@ use crate::types::hash::Hash; use crate::types::ibc::IbcEvent; use crate::types::internal::HostEnvResult; use crate::types::storage::{BlockHeight, 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}; @@ -902,9 +905,20 @@ where H: StorageHasher, CA: WasmCacheAccess, { + // Get the token if the key is a balance or minter key + let token = if let Some([token, _]) = is_any_token_balance_key(key) { + Some(token) + } else { + is_any_minted_balance_key(key).or_else(|| is_any_minter_key(key)) + }; + let write_log = unsafe { env.ctx.write_log.get() }; let storage = unsafe { env.ctx.storage.get() }; for addr in key.find_addresses() { + // skip if the address is a token address + if Some(&addr) == token { + continue; + } // skip the check for implicit and internal addresses if let Address::Implicit(_) | Address::Internal(_) = &addr { continue; diff --git a/tests/src/vm_host_env/ibc.rs b/tests/src/vm_host_env/ibc.rs index 9baafd6fc2..5858abe7d3 100644 --- a/tests/src/vm_host_env/ibc.rs +++ b/tests/src/vm_host_env/ibc.rs @@ -93,7 +93,7 @@ use namada::vm::{wasm, WasmCacheRwAccess}; use namada_test_utils::TestWasms; use namada_tx_prelude::BorshSerialize; -use crate::tx::{self, *}; +use crate::tx::*; const ADDRESS: Address = Address::Internal(InternalAddress::Ibc); @@ -235,10 +235,10 @@ pub fn init_storage() -> (Address, Address) { }); // initialize a token - let token = tx::ctx().init_account(code_hash).unwrap(); + let token = tx_host_env::ctx().init_account(code_hash).unwrap(); // initialize an account - let account = tx::ctx().init_account(code_hash).unwrap(); + let account = tx_host_env::ctx().init_account(code_hash).unwrap(); let key = token::balance_key(&token, &account); let init_bal = Amount::native_whole(100); let bytes = init_bal.try_to_vec().expect("encoding failed"); @@ -265,6 +265,22 @@ pub fn init_storage() -> (Address, Address) { env.wl_storage.storage.write(&key, &bytes).unwrap(); }); + // commit the initialized token and account + tx_host_env::with(|env| { + env.wl_storage.commit_tx(); + env.wl_storage.commit_block().unwrap(); + + // block header to check timeout timestamp + env.wl_storage + .storage + .set_header(tm_dummy_header()) + .unwrap(); + env.wl_storage + .storage + .begin_block(BlockHash::default(), BlockHeight(2)) + .unwrap(); + }); + (token, account) } diff --git a/wasm/wasm_source/Makefile b/wasm/wasm_source/Makefile index a2b5ab284d..f394179dc7 100644 --- a/wasm/wasm_source/Makefile +++ b/wasm/wasm_source/Makefile @@ -22,7 +22,6 @@ wasms += tx_withdraw wasms += vp_implicit wasms += vp_masp wasms += vp_testnet_faucet -wasms += vp_token wasms += vp_user wasms += vp_validator diff --git a/wasm/wasm_source/src/lib.rs b/wasm/wasm_source/src/lib.rs index 3d5be30b56..2fc69f65c9 100644 --- a/wasm/wasm_source/src/lib.rs +++ b/wasm/wasm_source/src/lib.rs @@ -33,8 +33,6 @@ pub mod vp_implicit; pub mod vp_masp; #[cfg(feature = "vp_testnet_faucet")] pub mod vp_testnet_faucet; -#[cfg(feature = "vp_token")] -pub mod vp_token; #[cfg(feature = "vp_user")] pub mod vp_user; #[cfg(feature = "vp_validator")] diff --git a/wasm/wasm_source/src/vp_token.rs b/wasm/wasm_source/src/vp_token.rs deleted file mode 100644 index 002553dd75..0000000000 --- a/wasm/wasm_source/src/vp_token.rs +++ /dev/null @@ -1,263 +0,0 @@ -//! A VP for a fungible token. Enforces that the total supply is unchanged in a -//! transaction that moves balance(s). - -use std::collections::BTreeSet; - -use namada_vp_prelude::address::{self, Address}; -use namada_vp_prelude::storage::KeySeg; -use namada_vp_prelude::{storage, token, *}; - -#[validity_predicate] -fn validate_tx( - ctx: &Ctx, - tx_data: Tx, - addr: Address, - keys_changed: BTreeSet, - verifiers: BTreeSet
, -) -> VpResult { - debug_log!( - "validate_tx called with token addr: {}, key_changed: {:?}, \ - verifiers: {:?}", - addr, - keys_changed, - verifiers - ); - - if !is_valid_tx(ctx, &tx_data)? { - return reject(); - } - - for key in keys_changed.iter() { - if key.is_validity_predicate().is_some() { - let vp_hash: Vec = ctx.read_bytes_post(key)?.unwrap(); - if !is_vp_whitelisted(ctx, &vp_hash)? { - return reject(); - } - } - } - - token_checks(ctx, &addr, &keys_changed, &verifiers) -} - -/// A token validity predicate checks that the total supply is preserved. -/// This implies that: -/// -/// - The value associated with the `total_supply` storage key may not change. -/// - For any balance changes, the total of outputs must be equal to the total -/// of inputs. -fn token_checks( - ctx: &Ctx, - token: &Address, - keys_touched: &BTreeSet, - verifiers: &BTreeSet
, -) -> VpResult { - for key in keys_touched.iter() { - let owner: Option<&Address> = token::is_balance_key(token, key); - - match owner { - None => { - if let Some(t) = token::is_any_minted_balance_key(key) { - if t == token { - // check if total supply is changed, which it should - // never be from a tx - let total_pre: token::Amount = - ctx.read_pre(key)?.unwrap(); - let total_post: token::Amount = - ctx.read_post(key)?.unwrap(); - if total_pre != total_post { - return reject(); - } - } - } else if key.segments.get(0) == Some(&token.to_db_key()) { - // Unknown changes to this address space are disallowed, but - // unknown changes anywhere else are permitted - return reject(); - } - } - Some(owner) => { - let pre: token::Amount = ctx.read_pre(key)?.unwrap_or_default(); - let post: token::Amount = - ctx.read_post(key)?.unwrap_or_default(); - // make sure that the spender approved the transaction - if post < pre - && !(verifiers.contains(owner) || *owner == address::masp()) - { - return reject(); - } - } - } - } - // The total change should be validated by multitoken VP - Ok(true) -} - -#[cfg(test)] -mod tests { - // Use this as `#[test]` annotation to enable logging - use namada::core::ledger::storage_api::token; - use namada::proto::Data; - use namada::types::transaction::TxType; - use namada_tests::log::test; - use namada_tests::tx::{self, TestTxEnv}; - use namada_tests::vp::*; - use namada_vp_prelude::storage_api::StorageWrite; - - use super::*; - - #[test] - fn test_transfer_inputs_eq_outputs_is_accepted() { - // Initialize a tx environment - let mut tx_env = TestTxEnv::default(); - let token = address::nam(); - let src = address::testing::established_address_1(); - let dest = address::testing::established_address_2(); - let total_supply = - token::Amount::from_uint(10_098_123, 0).expect("Test failed"); - - // Spawn the accounts to be able to modify their storage - tx_env.spawn_accounts([&token, &src, &dest]); - token::credit_tokens( - &mut tx_env.wl_storage, - &token, - &src, - total_supply, - ) - .unwrap(); - // Commit the initial state - tx_env.commit_tx_and_block(); - - // Initialize VP environment from a transaction - vp_host_env::init_from_tx(token.clone(), tx_env, |_address| { - // Apply a transfer - - let amount = token::Amount::from_uint(100, 0).expect("Test failed"); - token::transfer(tx::ctx(), &token, &src, &dest, amount).unwrap(); - }); - - let vp_env = vp_host_env::take(); - let mut tx_data = Tx::new(TxType::Raw); - tx_data.set_data(Data::new(vec![])); - let keys_changed: BTreeSet = - vp_env.all_touched_storage_keys(); - let verifiers = vp_env.get_verifiers(); - vp_host_env::set(vp_env); - - assert!( - validate_tx(&CTX, tx_data, token, keys_changed, verifiers).unwrap(), - "A transfer where inputs == outputs should be accepted" - ); - } - - #[test] - fn test_transfer_inputs_neq_outputs_is_rejected() { - // Initialize a tx environment - let mut tx_env = TestTxEnv::default(); - let token = address::nam(); - let src = address::testing::established_address_1(); - let dest = address::testing::established_address_2(); - let total_supply = - token::Amount::from_uint(10_098_123, 0).expect("Test failed"); - - // Spawn the accounts to be able to modify their storage - tx_env.spawn_accounts([&token, &src, &dest]); - token::credit_tokens( - &mut tx_env.wl_storage, - &token, - &src, - total_supply, - ) - .unwrap(); - // Commit the initial state - tx_env.commit_tx_and_block(); - - // Initialize VP environment from a transaction - vp_host_env::init_from_tx(token.clone(), tx_env, |_address| { - // Apply a transfer - - let amount_in = - token::Amount::from_uint(100, 0).expect("Test failed"); - let amount_out = - token::Amount::from_uint(900, 0).expect("Test failed"); - - let src_key = token::balance_key(&token, &src); - let src_balance = - token::read_balance(tx::ctx(), &token, &src).unwrap(); - let new_src_balance = src_balance + amount_out; - let dest_key = token::balance_key(&token, &dest); - let dest_balance = - token::read_balance(tx::ctx(), &token, &dest).unwrap(); - let new_dest_balance = dest_balance + amount_in; - tx::ctx().write(&src_key, new_src_balance).unwrap(); - tx::ctx().write(&dest_key, new_dest_balance).unwrap(); - }); - - let vp_env = vp_host_env::take(); - let mut tx_data = Tx::new(TxType::Raw); - tx_data.set_data(Data::new(vec![])); - let keys_changed: BTreeSet = - vp_env.all_touched_storage_keys(); - let verifiers = vp_env.get_verifiers(); - vp_host_env::set(vp_env); - - assert!( - !validate_tx(&CTX, tx_data, token, keys_changed, verifiers) - .unwrap(), - "A transfer where inputs != outputs should be rejected" - ); - } - - #[test] - fn test_total_supply_change_is_rejected() { - // Initialize a tx environment - let mut tx_env = TestTxEnv::default(); - let token = address::nam(); - let owner = address::testing::established_address_1(); - let total_supply = - token::Amount::from_uint(10_098_123, 0).expect("Test failed"); - - // Spawn the accounts to be able to modify their storage - tx_env.spawn_accounts([&token, &owner]); - token::credit_tokens( - &mut tx_env.wl_storage, - &token, - &owner, - total_supply, - ) - .unwrap(); - // Commit the initial state - tx_env.commit_tx_and_block(); - - let total_supply_key = token::minted_balance_key(&token); - - // Initialize VP environment from a transaction - vp_host_env::init_from_tx(token.clone(), tx_env, |_address| { - // Try to change total supply from a tx - - let current_supply = tx::ctx() - .read::(&total_supply_key) - .unwrap() - .unwrap_or_default(); - tx::ctx() - .write( - &total_supply_key, - current_supply - + token::Amount::from_uint(1, 0).expect("Test failed"), - ) - .unwrap(); - }); - - let vp_env = vp_host_env::take(); - let mut tx_data = Tx::new(TxType::Raw); - tx_data.set_data(Data::new(vec![])); - let keys_changed: BTreeSet = - vp_env.all_touched_storage_keys(); - let verifiers = vp_env.get_verifiers(); - vp_host_env::set(vp_env); - - assert!( - !validate_tx(&CTX, tx_data, token, keys_changed, verifiers) - .unwrap(), - "Change of a `total_supply` value should be rejected" - ); - } -} From 59a7fcc24c61a63d2d532c4e7ece9c7bc1cbcf2e Mon Sep 17 00:00:00 2001 From: yito88 Date: Wed, 12 Jul 2023 23:07:34 +0200 Subject: [PATCH 087/120] fix changes in finalize_block --- .../lib/node/ledger/shell/finalize_block.rs | 37 +++++++++++-------- core/src/ledger/storage/wl_storage.rs | 7 +++- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 3af8ab9e3f..4485ebaccb 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -201,7 +201,6 @@ where let tx_hash_key = replay_protection::get_tx_hash_key(&tx_hash); self.wl_storage - .storage .delete(&tx_hash_key) .expect("Error while deleting tx hash from storage"); } @@ -221,16 +220,14 @@ where processed_tx.header_hash().0, )); self.wl_storage - .storage - .write(&wrapper_tx_hash_key, vec![]) + .write_bytes(&wrapper_tx_hash_key, vec![]) .expect("Error while writing tx hash to storage"); let inner_tx_hash_key = replay_protection::get_tx_hash_key( &tx.clone().update_header(TxType::Raw).header_hash(), ); self.wl_storage - .storage - .write(&inner_tx_hash_key, vec![]) + .write_bytes(&inner_tx_hash_key, vec![]) .expect("Error while writing tx hash to storage"); #[cfg(not(feature = "mainnet"))] @@ -256,11 +253,7 @@ where match balance.checked_sub(wrapper_fees) { Some(amount) => { self.wl_storage - .storage - .write( - &balance_key, - amount.try_to_vec().unwrap(), - ) + .write(&balance_key, amount) .unwrap(); } None => { @@ -271,12 +264,9 @@ where if reject { // Burn remaining funds self.wl_storage - .storage .write( &balance_key, - Amount::native_whole(0) - .try_to_vec() - .unwrap(), + Amount::native_whole(0), ) .unwrap(); tx_event["info"] = @@ -481,6 +471,7 @@ where ); stats.increment_errored_txs(); + self.wl_storage.drop_tx(); // If transaction type is Decrypted and failed because of // out of gas, remove its hash from storage to allow // rewrapping it @@ -491,15 +482,16 @@ where let tx_hash_key = replay_protection::get_tx_hash_key(&hash); self.wl_storage - .storage .delete(&tx_hash_key) .expect( "Error while deleting tx hash key from storage", ); + // Apply only to remove its hash, + // since all other changes have already been dropped + self.wl_storage.commit_tx(); } } - self.wl_storage.drop_tx(); tx_event["gas_used"] = self .gas_meter .get_current_transaction_gas() @@ -1834,7 +1826,14 @@ mod test_finalize_block { votes: votes.clone(), ..Default::default() }; + // merkle tree root before finalize_block + let root_pre = shell.shell.wl_storage.storage.block.tree.root(); + let _events = shell.finalize_block(req).unwrap(); + + // the merkle tree root should not change after finalize_block + let root_post = shell.shell.wl_storage.storage.block.tree.root(); + assert_eq!(root_pre.0, root_post.0); let new_state = store_block_state(&shell); // The new state must be unchanged itertools::assert_equal( @@ -2226,6 +2225,8 @@ mod test_finalize_block { }, }; shell.enqueue_tx(wrapper_tx); + // merkle tree root before finalize_block + let root_pre = shell.shell.wl_storage.storage.block.tree.root(); let _event = &shell .finalize_block(FinalizeBlock { @@ -2234,6 +2235,10 @@ mod test_finalize_block { }) .expect("Test failed")[0]; + // the merkle tree root should not change after finalize_block + let root_post = shell.shell.wl_storage.storage.block.tree.root(); + assert_eq!(root_pre.0, root_post.0); + // FIXME: uncomment when proper gas metering is in place // // Check inner tx hash has been removed from storage // assert_eq!(event.event_type.to_string(), String::from("applied")); diff --git a/core/src/ledger/storage/wl_storage.rs b/core/src/ledger/storage/wl_storage.rs index 1cb7e56a27..4fb2490ab9 100644 --- a/core/src/ledger/storage/wl_storage.rs +++ b/core/src/ledger/storage/wl_storage.rs @@ -143,6 +143,12 @@ 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<()> { + if self.storage.last_epoch != self.storage.block.epoch { + self.storage + .update_epoch_in_merkle_tree() + .into_storage_result()?; + } + let mut batch = D::batch(); self.write_log .commit_block(&mut self.storage, &mut batch) @@ -205,7 +211,6 @@ where .new_epoch(height, evidence_max_age_num_blocks); tracing::info!("Began a new epoch {}", self.storage.block.epoch); } - self.storage.update_epoch_in_merkle_tree()?; Ok(new_epoch) } } From f1669e7cb7724544edec36ffd30756cb289aaf16 Mon Sep 17 00:00:00 2001 From: yito88 Date: Wed, 12 Jul 2023 23:12:38 +0200 Subject: [PATCH 088/120] add changelog --- .../unreleased/bug-fixes/1709-fix_changes_before_commit.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/bug-fixes/1709-fix_changes_before_commit.md diff --git a/.changelog/unreleased/bug-fixes/1709-fix_changes_before_commit.md b/.changelog/unreleased/bug-fixes/1709-fix_changes_before_commit.md new file mode 100644 index 0000000000..33eb543193 --- /dev/null +++ b/.changelog/unreleased/bug-fixes/1709-fix_changes_before_commit.md @@ -0,0 +1,2 @@ +- Fix inconsistency state before commit + ([\#1709](https://github.com/anoma/namada/issues/1709)) \ No newline at end of file From 49fd38d4b1a7ad2bd23cd5f9d50969cb0e10f686 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 13 Jul 2023 14:43:42 +0200 Subject: [PATCH 089/120] refactor: remove duplicated code --- apps/src/lib/node/ledger/storage/rocksdb.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/apps/src/lib/node/ledger/storage/rocksdb.rs b/apps/src/lib/node/ledger/storage/rocksdb.rs index 9b6077f50a..f1ab743ce3 100644 --- a/apps/src/lib/node/ledger/storage/rocksdb.rs +++ b/apps/src/lib/node/ledger/storage/rocksdb.rs @@ -176,14 +176,6 @@ impl RocksDB { .ok_or(Error::DBError("No {cf_name} column family".to_string())) } - fn flush(&self, wait: bool) -> Result<()> { - let mut flush_opts = FlushOptions::default(); - flush_opts.set_wait(wait); - self.0 - .flush_opt(&flush_opts) - .map_err(|e| Error::DBError(e.into_string())) - } - /// Persist the diff of an account subspace key-val under the height where /// it was changed. fn write_subspace_diff( From c4fcad62db368fd9ceb5b05850848fb2ef1ec71c Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 13 Jul 2023 14:44:52 +0200 Subject: [PATCH 090/120] refactor: use immutable reference --- apps/src/lib/node/ledger/storage/rocksdb.rs | 2 +- core/src/ledger/storage/mockdb.rs | 2 +- core/src/ledger/storage/mod.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/src/lib/node/ledger/storage/rocksdb.rs b/apps/src/lib/node/ledger/storage/rocksdb.rs index f1ab743ce3..681f6db4ed 100644 --- a/apps/src/lib/node/ledger/storage/rocksdb.rs +++ b/apps/src/lib/node/ledger/storage/rocksdb.rs @@ -504,7 +504,7 @@ impl DB for RocksDB { .map_err(|e| Error::DBError(e.into_string())) } - fn read_last_block(&mut self) -> Result> { + fn read_last_block(&self) -> Result> { // Block height let state_cf = self.get_column_family(STATE_CF)?; let height: BlockHeight = match self diff --git a/core/src/ledger/storage/mockdb.rs b/core/src/ledger/storage/mockdb.rs index 24ffd0e59b..5056d64cac 100644 --- a/core/src/ledger/storage/mockdb.rs +++ b/core/src/ledger/storage/mockdb.rs @@ -53,7 +53,7 @@ impl DB for MockDB { Ok(()) } - fn read_last_block(&mut self) -> Result> { + fn read_last_block(&self) -> Result> { // Block height let height: BlockHeight = match self.0.borrow().get("height") { Some(bytes) => types::decode(bytes).map_err(Error::CodingError)?, diff --git a/core/src/ledger/storage/mod.rs b/core/src/ledger/storage/mod.rs index 5aa3d0cf80..73a70944ff 100644 --- a/core/src/ledger/storage/mod.rs +++ b/core/src/ledger/storage/mod.rs @@ -256,7 +256,7 @@ pub trait DB: std::fmt::Debug { fn flush(&self, wait: bool) -> Result<()>; /// Read the last committed block's metadata - fn read_last_block(&mut self) -> Result>; + 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). From 32a10a42cd3a9c79aa0d993146100f9ee6d7ce39 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 13 Jul 2023 14:46:48 +0200 Subject: [PATCH 091/120] refactor: use immutable reference --- apps/src/lib/node/ledger/storage/rocksdb.rs | 10 +++++----- core/src/ledger/storage/mockdb.rs | 2 +- core/src/ledger/storage/mod.rs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/src/lib/node/ledger/storage/rocksdb.rs b/apps/src/lib/node/ledger/storage/rocksdb.rs index 681f6db4ed..7ada28c9ad 100644 --- a/apps/src/lib/node/ledger/storage/rocksdb.rs +++ b/apps/src/lib/node/ledger/storage/rocksdb.rs @@ -723,7 +723,7 @@ impl DB for RocksDB { } fn write_block( - &mut self, + &self, state: BlockStateWrite, batch: &mut Self::WriteBatch, is_full_commit: bool, @@ -1535,7 +1535,7 @@ mod test { ) .unwrap(); - write_block(&mut db, &mut batch, BlockHeight::default()).unwrap(); + write_block(&db, &mut batch, BlockHeight::default()).unwrap(); db.exec_batch(batch.0).unwrap(); let _state = db @@ -1726,7 +1726,7 @@ mod test { ) .unwrap(); - write_block(&mut db, &mut batch, height_0).unwrap(); + write_block(&db, &mut batch, height_0).unwrap(); db.exec_batch(batch.0).unwrap(); // Write second block @@ -1746,7 +1746,7 @@ mod test { db.batch_delete_subspace_val(&mut batch, height_1, &delete_key) .unwrap(); - write_block(&mut db, &mut batch, height_1).unwrap(); + write_block(&db, &mut batch, height_1).unwrap(); db.exec_batch(batch.0).unwrap(); // Check that the values are as expected from second block @@ -1771,7 +1771,7 @@ mod test { /// A test helper to write a block fn write_block( - db: &mut RocksDB, + db: &RocksDB, batch: &mut RocksDBWriteBatch, height: BlockHeight, ) -> Result<()> { diff --git a/core/src/ledger/storage/mockdb.rs b/core/src/ledger/storage/mockdb.rs index 5056d64cac..f5cfef09aa 100644 --- a/core/src/ledger/storage/mockdb.rs +++ b/core/src/ledger/storage/mockdb.rs @@ -212,7 +212,7 @@ impl DB for MockDB { } fn write_block( - &mut self, + &self, state: BlockStateWrite, _batch: &mut Self::WriteBatch, _is_full_commit: bool, diff --git a/core/src/ledger/storage/mod.rs b/core/src/ledger/storage/mod.rs index 73a70944ff..5480eec51d 100644 --- a/core/src/ledger/storage/mod.rs +++ b/core/src/ledger/storage/mod.rs @@ -261,7 +261,7 @@ pub trait DB: std::fmt::Debug { /// 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 write_block( - &mut self, + &self, state: BlockStateWrite, batch: &mut Self::WriteBatch, is_full_commit: bool, From 749a8ec5134ea3efcb98893cd17bd92c0cfabf9b Mon Sep 17 00:00:00 2001 From: yito88 Date: Fri, 14 Jul 2023 11:42:05 +0200 Subject: [PATCH 092/120] remove negative check --- shared/src/ledger/native_vp/multitoken.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/shared/src/ledger/native_vp/multitoken.rs b/shared/src/ledger/native_vp/multitoken.rs index 038e40349e..e4d3785ef2 100644 --- a/shared/src/ledger/native_vp/multitoken.rs +++ b/shared/src/ledger/native_vp/multitoken.rs @@ -8,7 +8,7 @@ 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::{self, Address, InternalAddress}; +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, @@ -56,15 +56,10 @@ where let mut changes = HashMap::new(); let mut mints = HashMap::new(); for key in keys_changed { - if let Some([token, owner]) = is_any_token_balance_key(key) { + if let Some([token, _]) = is_any_token_balance_key(key) { let pre: Amount = self.ctx.read_pre(key)?.unwrap_or_default(); let post: Amount = self.ctx.read_post(key)?.unwrap_or_default(); let diff = post.change() - pre.change(); - if diff.is_negative() - && !(verifiers.contains(owner) || *owner == address::masp()) - { - return Ok(false); - } match changes.get_mut(token) { Some(change) => *change += diff, None => _ = changes.insert(token, diff), From cd95787afd8dcf4ef4ec3e6b29fa832e30334860 Mon Sep 17 00:00:00 2001 From: yito88 Date: Fri, 14 Jul 2023 12:16:50 +0200 Subject: [PATCH 093/120] remove test_invalid_sender --- shared/src/ledger/native_vp/multitoken.rs | 54 ----------------------- 1 file changed, 54 deletions(-) diff --git a/shared/src/ledger/native_vp/multitoken.rs b/shared/src/ledger/native_vp/multitoken.rs index e4d3785ef2..a932714240 100644 --- a/shared/src/ledger/native_vp/multitoken.rs +++ b/shared/src/ledger/native_vp/multitoken.rs @@ -285,60 +285,6 @@ mod tests { ); } - #[test] - fn test_invalid_sender() { - let mut wl_storage = TestWlStorage::default(); - let mut keys_changed = BTreeSet::new(); - - let sender = established_address_1(); - let sender_key = balance_key(&nam(), &sender); - let amount = Amount::native_whole(100); - wl_storage - .storage - .write(&sender_key, amount.try_to_vec().unwrap()) - .expect("write failed"); - - // transfer 10 - let amount = Amount::native_whole(90); - wl_storage - .write_log - .write(&sender_key, amount.try_to_vec().unwrap()) - .expect("write failed"); - keys_changed.insert(sender_key); - let receiver = established_address_2(); - let receiver_key = balance_key(&nam(), &receiver); - let amount = Amount::native_whole(10); - wl_storage - .write_log - .write(&receiver_key, amount.try_to_vec().unwrap()) - .expect("write failed"); - keys_changed.insert(receiver_key); - - let tx_index = TxIndex::default(); - let tx = dummy_tx(&wl_storage); - let gas_meter = VpGasMeter::new(0); - let (vp_wasm_cache, _vp_cache_dir) = wasm_cache(); - let verifiers = BTreeSet::new(); - // The sender is not set - let ctx = Ctx::new( - &ADDRESS, - &wl_storage.storage, - &wl_storage.write_log, - &tx, - &tx_index, - gas_meter, - &keys_changed, - &verifiers, - vp_wasm_cache, - ); - - let vp = MultitokenVp { ctx }; - assert!( - !vp.validate_tx(&tx, &keys_changed, &verifiers) - .expect("validation failed") - ); - } - #[test] fn test_valid_mint() { let mut wl_storage = TestWlStorage::default(); From 20a110f2fab761868c08b1e21ca074a7da59c0ac Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 14 Jul 2023 12:47:30 +0200 Subject: [PATCH 094/120] refactor: rename method --- apps/src/lib/node/ledger/storage/rocksdb.rs | 4 ++-- core/src/ledger/storage/mockdb.rs | 2 +- core/src/ledger/storage/mod.rs | 5 +++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/src/lib/node/ledger/storage/rocksdb.rs b/apps/src/lib/node/ledger/storage/rocksdb.rs index 7ada28c9ad..6929603c77 100644 --- a/apps/src/lib/node/ledger/storage/rocksdb.rs +++ b/apps/src/lib/node/ledger/storage/rocksdb.rs @@ -722,7 +722,7 @@ impl DB for RocksDB { } } - fn write_block( + fn add_block_to_batch( &self, state: BlockStateWrite, batch: &mut Self::WriteBatch, @@ -1806,6 +1806,6 @@ mod test { eth_events_queue: ð_events_queue, }; - db.write_block(block, batch, true) + db.add_block_to_batch(block, batch, true) } } diff --git a/core/src/ledger/storage/mockdb.rs b/core/src/ledger/storage/mockdb.rs index f5cfef09aa..971584e742 100644 --- a/core/src/ledger/storage/mockdb.rs +++ b/core/src/ledger/storage/mockdb.rs @@ -211,7 +211,7 @@ impl DB for MockDB { } } - fn write_block( + fn add_block_to_batch( &self, state: BlockStateWrite, _batch: &mut Self::WriteBatch, diff --git a/core/src/ledger/storage/mod.rs b/core/src/ledger/storage/mod.rs index 5480eec51d..6509c02d34 100644 --- a/core/src/ledger/storage/mod.rs +++ b/core/src/ledger/storage/mod.rs @@ -260,7 +260,7 @@ pub trait DB: std::fmt::Debug { /// 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 write_block( + fn add_block_to_batch( &self, state: BlockStateWrite, batch: &mut Self::WriteBatch, @@ -532,7 +532,8 @@ where ethereum_height: self.ethereum_height.as_ref(), eth_events_queue: &self.eth_events_queue, }; - self.db.write_block(state, &mut batch, is_full_commit)?; + self.db + .add_block_to_batch(state, &mut batch, is_full_commit)?; let header = self .header .take() From 5483cdc83e0e50b4f7c1aa877c6007e8bdaae51b Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 14 Jul 2023 12:52:23 +0200 Subject: [PATCH 095/120] refactor: rename function --- apps/src/lib/node/ledger/storage/rocksdb.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/src/lib/node/ledger/storage/rocksdb.rs b/apps/src/lib/node/ledger/storage/rocksdb.rs index 6929603c77..f679ca9232 100644 --- a/apps/src/lib/node/ledger/storage/rocksdb.rs +++ b/apps/src/lib/node/ledger/storage/rocksdb.rs @@ -1535,7 +1535,7 @@ mod test { ) .unwrap(); - write_block(&db, &mut batch, BlockHeight::default()).unwrap(); + add_block_to_batch(&db, &mut batch, BlockHeight::default()).unwrap(); db.exec_batch(batch.0).unwrap(); let _state = db @@ -1726,7 +1726,7 @@ mod test { ) .unwrap(); - write_block(&db, &mut batch, height_0).unwrap(); + add_block_to_batch(&db, &mut batch, height_0).unwrap(); db.exec_batch(batch.0).unwrap(); // Write second block @@ -1746,7 +1746,7 @@ mod test { db.batch_delete_subspace_val(&mut batch, height_1, &delete_key) .unwrap(); - write_block(&db, &mut batch, height_1).unwrap(); + add_block_to_batch(&db, &mut batch, height_1).unwrap(); db.exec_batch(batch.0).unwrap(); // Check that the values are as expected from second block @@ -1770,7 +1770,7 @@ mod test { } /// A test helper to write a block - fn write_block( + fn add_block_to_batch( db: &RocksDB, batch: &mut RocksDBWriteBatch, height: BlockHeight, From d87df2a56a8f93d8a25667b7d2c443ad32cd247a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Thu, 13 Jul 2023 09:01:30 +0100 Subject: [PATCH 096/120] test/shell/finalize_block: add some txs to DB commit test --- .../lib/node/ledger/shell/finalize_block.rs | 213 ++++++++++-------- 1 file changed, 117 insertions(+), 96 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 4485ebaccb..e32b4db993 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -1018,6 +1018,85 @@ mod test_finalize_block { FinalizeBlock, ProcessedTx, }; + /// Make a wrapper tx and a processed tx from the wrapped tx that can be + /// added to `FinalizeBlock` request. + fn mk_wrapper_tx( + shell: &TestShell, + keypair: &common::SecretKey, + ) -> (Tx, ProcessedTx) { + let mut wrapper_tx = + Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( + Fee { + amount: MIN_FEE_AMOUNT, + token: shell.wl_storage.storage.native_token.clone(), + }, + keypair.ref_to(), + Epoch(0), + Default::default(), + #[cfg(not(feature = "mainnet"))] + None, + )))); + wrapper_tx.header.chain_id = shell.chain_id.clone(); + wrapper_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper_tx.set_data(Data::new( + "Encrypted transaction data".as_bytes().to_owned(), + )); + wrapper_tx.add_section(Section::Signature(Signature::new( + wrapper_tx.sechashes(), + keypair, + ))); + let tx = wrapper_tx.to_bytes(); + ( + wrapper_tx, + ProcessedTx { + tx, + result: TxResult { + code: ErrorCodes::Ok.into(), + info: "".into(), + }, + }, + ) + } + + /// Make a wrapper tx and a processed tx from the wrapped tx that can be + /// added to `FinalizeBlock` request. + fn mk_decrypted_tx( + shell: &mut TestShell, + keypair: &common::SecretKey, + ) -> ProcessedTx { + let tx_code = TestWasms::TxNoOp.read_bytes(); + let mut outer_tx = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( + Fee { + amount: MIN_FEE_AMOUNT, + token: shell.wl_storage.storage.native_token.clone(), + }, + keypair.ref_to(), + Epoch(0), + Default::default(), + #[cfg(not(feature = "mainnet"))] + None, + )))); + outer_tx.header.chain_id = shell.chain_id.clone(); + outer_tx.set_code(Code::new(tx_code)); + outer_tx.set_data(Data::new( + "Decrypted transaction data".as_bytes().to_owned(), + )); + shell.enqueue_tx(outer_tx.clone()); + outer_tx.update_header(TxType::Decrypted(DecryptedTx::Decrypted { + #[cfg(not(feature = "mainnet"))] + has_valid_pow: false, + })); + outer_tx.decrypt(::G2Affine::prime_subgroup_generator()) + .expect("Test failed"); + ProcessedTx { + tx: outer_tx.to_bytes(), + result: TxResult { + code: ErrorCodes::Ok.into(), + info: "".into(), + }, + } + } + /// Check that if a wrapper tx was rejected by [`process_proposal`], /// check that the correct event is returned. Check that it does /// not appear in the queue of txs to be decrypted @@ -1044,36 +1123,11 @@ mod test_finalize_block { // create some wrapper txs for i in 1u64..5 { - let mut wrapper = - Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( - Fee { - amount: MIN_FEE_AMOUNT, - token: shell.wl_storage.storage.native_token.clone(), - }, - keypair.ref_to(), - Epoch(0), - Default::default(), - #[cfg(not(feature = "mainnet"))] - None, - )))); - wrapper.header.chain_id = shell.chain_id.clone(); - wrapper.set_data(Data::new("wasm_code".as_bytes().to_owned())); - wrapper.set_code(Code::new( - format!("transaction data: {}", i).as_bytes().to_owned(), - )); - wrapper.add_section(Section::Signature(Signature::new( - wrapper.sechashes(), - &keypair, - ))); + let (wrapper, mut processed_tx) = mk_wrapper_tx(&shell, &keypair); if i > 1 { - processed_txs.push(ProcessedTx { - tx: wrapper.to_bytes(), - result: TxResult { - code: u32::try_from(i.rem_euclid(2)) - .expect("Test failed"), - info: "".into(), - }, - }); + processed_tx.result.code = + u32::try_from(i.rem_euclid(2)).unwrap(); + processed_txs.push(processed_tx); } else { shell.enqueue_tx(wrapper.clone()); } @@ -1239,75 +1293,14 @@ mod test_finalize_block { .unwrap(); // create two decrypted txs - let tx_code = TestWasms::TxNoOp.read_bytes(); - for i in 0..2 { - let mut outer_tx = - Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( - Fee { - amount: MIN_FEE_AMOUNT, - token: shell.wl_storage.storage.native_token.clone(), - }, - keypair.ref_to(), - Epoch(0), - Default::default(), - #[cfg(not(feature = "mainnet"))] - None, - )))); - outer_tx.header.chain_id = shell.chain_id.clone(); - outer_tx.set_code(Code::new(tx_code.clone())); - outer_tx.set_data(Data::new( - format!("Decrypted transaction data: {}", i) - .as_bytes() - .to_owned(), - )); - shell.enqueue_tx(outer_tx.clone()); - outer_tx.update_header(TxType::Decrypted(DecryptedTx::Decrypted { - #[cfg(not(feature = "mainnet"))] - has_valid_pow: false, - })); - outer_tx.decrypt(::G2Affine::prime_subgroup_generator()) - .expect("Test failed"); - processed_txs.push(ProcessedTx { - tx: outer_tx.to_bytes(), - result: TxResult { - code: ErrorCodes::Ok.into(), - info: "".into(), - }, - }); + for _ in 0..2 { + processed_txs.push(mk_decrypted_tx(&mut shell, &keypair)); } // create two wrapper txs - for i in 0..2 { - let mut wrapper_tx = - Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( - Fee { - amount: MIN_FEE_AMOUNT, - token: shell.wl_storage.storage.native_token.clone(), - }, - keypair.ref_to(), - Epoch(0), - Default::default(), - #[cfg(not(feature = "mainnet"))] - None, - )))); - wrapper_tx.header.chain_id = shell.chain_id.clone(); - wrapper_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); - wrapper_tx.set_data(Data::new( - format!("Encrypted transaction data: {}", i) - .as_bytes() - .to_owned(), - )); - wrapper_tx.add_section(Section::Signature(Signature::new( - wrapper_tx.sechashes(), - &keypair, - ))); - valid_txs.push(wrapper_tx.clone()); - processed_txs.push(ProcessedTx { - tx: wrapper_tx.to_bytes(), - result: TxResult { - code: ErrorCodes::Ok.into(), - info: "".into(), - }, - }); + for _ in 0..2 { + let (tx, processed_tx) = mk_wrapper_tx(&shell, &keypair); + valid_txs.push(tx.clone()); + processed_txs.push(processed_tx); } // Put the wrapper txs in front of the decrypted txs processed_txs.rotate_left(2); @@ -1726,6 +1719,21 @@ mod test_finalize_block { shell.wl_storage.storage.next_epoch_min_start_height = BlockHeight(5); shell.wl_storage.storage.next_epoch_min_start_time = DateTimeUtc::now(); + let txs_key = gen_keypair(); + // Add unshielded balance for fee payment + let balance_key = token::balance_key( + &shell.wl_storage.storage.native_token, + &Address::from(&txs_key.ref_to()), + ); + shell + .wl_storage + .storage + .write( + &balance_key, + Amount::native_whole(1000).try_to_vec().unwrap(), + ) + .unwrap(); + // Add a proposal to be executed on next epoch change. let mut add_proposal = |proposal_id, vote| { let validator = shell.mode.get_validator_address().unwrap().clone(); @@ -1821,7 +1829,20 @@ mod test_finalize_block { // Need to supply a proposer address and votes to flow through the // inflation code for _ in 0..20 { + // Add some txs + let mut txs = vec![]; + // create two decrypted txs + for _ in 0..2 { + txs.push(mk_decrypted_tx(&mut shell, &txs_key)); + } + // create two wrapper txs + for _ in 0..2 { + let (_tx, processed_tx) = mk_wrapper_tx(&shell, &txs_key); + txs.push(processed_tx); + } + let req = FinalizeBlock { + txs, proposer_address: proposer_address.clone(), votes: votes.clone(), ..Default::default() From 37bbbdcf30cfa2e4af163bda5c8ea98f72ba7f08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 14 Jul 2023 12:44:20 +0100 Subject: [PATCH 097/120] changelog: add #1717 --- .changelog/unreleased/improvements/1717-storage-refactor.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/improvements/1717-storage-refactor.md diff --git a/.changelog/unreleased/improvements/1717-storage-refactor.md b/.changelog/unreleased/improvements/1717-storage-refactor.md new file mode 100644 index 0000000000..ae44ad180a --- /dev/null +++ b/.changelog/unreleased/improvements/1717-storage-refactor.md @@ -0,0 +1,2 @@ +- Refactored storage code to only use an immutable reference when reading and + writing to a batch. ([\#1717](https://github.com/anoma/namada/pull/1717)) \ No newline at end of file From 2dcd6d6783ea2c0029e7e88ce7cab0f4a654afc3 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Sat, 15 Jul 2023 09:13:25 +0100 Subject: [PATCH 098/120] Remove `ADDR` associated type from native VPs --- shared/src/ledger/ibc/vp/mod.rs | 6 ++---- shared/src/ledger/ibc/vp/token.rs | 2 -- .../src/ledger/native_vp/ethereum_bridge/bridge_pool_vp.rs | 2 -- shared/src/ledger/native_vp/ethereum_bridge/vp.rs | 4 +--- shared/src/ledger/native_vp/governance/mod.rs | 4 +--- shared/src/ledger/native_vp/mod.rs | 5 +---- shared/src/ledger/native_vp/parameters.rs | 4 +--- shared/src/ledger/native_vp/replay_protection.rs | 4 +--- shared/src/ledger/native_vp/slash_fund.rs | 4 +--- shared/src/ledger/pos/vp.rs | 4 +--- 10 files changed, 9 insertions(+), 30 deletions(-) diff --git a/shared/src/ledger/ibc/vp/mod.rs b/shared/src/ledger/ibc/vp/mod.rs index 5986a01771..af83e570f0 100644 --- a/shared/src/ledger/ibc/vp/mod.rs +++ b/shared/src/ledger/ibc/vp/mod.rs @@ -17,7 +17,7 @@ use namada_core::ledger::ibc::{ 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, InternalAddress}; +use namada_core::types::address::Address; use namada_core::types::storage::Key; use namada_proof_of_stake::read_pos_params; use thiserror::Error; @@ -68,8 +68,6 @@ where { type Error = Error; - const ADDR: InternalAddress = InternalAddress::Ibc; - fn validate_tx( &self, tx_data: &Tx, @@ -283,8 +281,8 @@ mod tests { }; use super::{get_dummy_header, *}; use crate::core::ledger::storage::testing::TestWlStorage; - use crate::core::types::address::nam; use crate::core::types::address::testing::established_address_1; + use crate::core::types::address::{nam, InternalAddress}; use crate::core::types::storage::Epoch; use crate::ibc::applications::transfer::acknowledgement::TokenTransferAcknowledgement; use crate::ibc::applications::transfer::coin::PrefixedCoin; diff --git a/shared/src/ledger/ibc/vp/token.rs b/shared/src/ledger/ibc/vp/token.rs index 18234abd35..1c9cc5bc1d 100644 --- a/shared/src/ledger/ibc/vp/token.rs +++ b/shared/src/ledger/ibc/vp/token.rs @@ -80,8 +80,6 @@ where { type Error = Error; - const ADDR: InternalAddress = InternalAddress::IbcBurn; - fn validate_tx( &self, tx_data: &Tx, 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 283cf52c58..e662c06667 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 @@ -269,8 +269,6 @@ where { type Error = Error; - const ADDR: InternalAddress = InternalAddress::EthBridgePool; - fn validate_tx( &self, tx: &Tx, diff --git a/shared/src/ledger/native_vp/ethereum_bridge/vp.rs b/shared/src/ledger/native_vp/ethereum_bridge/vp.rs index 47eed29f3c..c382fed66f 100644 --- a/shared/src/ledger/native_vp/ethereum_bridge/vp.rs +++ b/shared/src/ledger/native_vp/ethereum_bridge/vp.rs @@ -9,7 +9,7 @@ use namada_core::ledger::eth_bridge::storage::{ }; use namada_core::ledger::storage::traits::StorageHasher; use namada_core::ledger::{eth_bridge, storage as ledger_storage}; -use namada_core::types::address::{Address, InternalAddress}; +use namada_core::types::address::Address; use namada_core::types::storage::Key; use namada_core::types::token::{balance_key, Amount, Change}; @@ -110,8 +110,6 @@ where { type Error = Error; - const ADDR: InternalAddress = eth_bridge::INTERNAL_ADDRESS; - /// Validate that a wasm transaction is permitted to change keys under this /// account. /// diff --git a/shared/src/ledger/native_vp/governance/mod.rs b/shared/src/ledger/native_vp/governance/mod.rs index 7d9323f256..d41242a477 100644 --- a/shared/src/ledger/native_vp/governance/mod.rs +++ b/shared/src/ledger/native_vp/governance/mod.rs @@ -16,7 +16,7 @@ use crate::ledger::native_vp::{Ctx, NativeVp}; use crate::ledger::storage_api::StorageRead; use crate::ledger::{native_vp, pos}; use crate::proto::Tx; -use crate::types::address::{Address, InternalAddress}; +use crate::types::address::Address; use crate::types::storage::{Epoch, Key}; use crate::types::token; use crate::vm::WasmCacheAccess; @@ -50,8 +50,6 @@ where { type Error = Error; - const ADDR: InternalAddress = InternalAddress::Governance; - fn validate_tx( &self, tx_data: &Tx, diff --git a/shared/src/ledger/native_vp/mod.rs b/shared/src/ledger/native_vp/mod.rs index dcc0432ea7..98d3093134 100644 --- a/shared/src/ledger/native_vp/mod.rs +++ b/shared/src/ledger/native_vp/mod.rs @@ -21,7 +21,7 @@ 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, InternalAddress}; +use crate::types::address::Address; use crate::types::hash::Hash; use crate::types::storage::{ BlockHash, BlockHeight, Epoch, Header, Key, TxIndex, @@ -36,9 +36,6 @@ pub type Error = storage_api::Error; /// A native VP module should implement its validation logic using this trait. pub trait NativeVp { - /// The address of this VP - const ADDR: InternalAddress; - /// Error type for the methods' results. type Error: std::error::Error; diff --git a/shared/src/ledger/native_vp/parameters.rs b/shared/src/ledger/native_vp/parameters.rs index d367c16698..bb1db0ab30 100644 --- a/shared/src/ledger/native_vp/parameters.rs +++ b/shared/src/ledger/native_vp/parameters.rs @@ -4,7 +4,7 @@ use std::collections::BTreeSet; use namada_core::ledger::storage; use namada_core::proto::Tx; -use namada_core::types::address::{Address, InternalAddress}; +use namada_core::types::address::Address; use namada_core::types::storage::Key; use thiserror::Error; @@ -41,8 +41,6 @@ where { type Error = Error; - const ADDR: InternalAddress = InternalAddress::Parameters; - fn validate_tx( &self, tx_data: &Tx, diff --git a/shared/src/ledger/native_vp/replay_protection.rs b/shared/src/ledger/native_vp/replay_protection.rs index 9b300e376b..a2a2a66f36 100644 --- a/shared/src/ledger/native_vp/replay_protection.rs +++ b/shared/src/ledger/native_vp/replay_protection.rs @@ -3,7 +3,7 @@ use std::collections::BTreeSet; use namada_core::ledger::storage; -use namada_core::types::address::{Address, InternalAddress}; +use namada_core::types::address::Address; use namada_core::types::storage::Key; use thiserror::Error; @@ -40,8 +40,6 @@ where { type Error = Error; - const ADDR: InternalAddress = InternalAddress::ReplayProtection; - fn validate_tx( &self, _tx_data: &Tx, diff --git a/shared/src/ledger/native_vp/slash_fund.rs b/shared/src/ledger/native_vp/slash_fund.rs index bed71d3bd9..8f2ab54400 100644 --- a/shared/src/ledger/native_vp/slash_fund.rs +++ b/shared/src/ledger/native_vp/slash_fund.rs @@ -11,7 +11,7 @@ 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, InternalAddress}; +use crate::types::address::Address; use crate::types::storage::Key; use crate::types::token; use crate::vm::WasmCacheAccess; @@ -45,8 +45,6 @@ where { type Error = Error; - const ADDR: InternalAddress = InternalAddress::SlashFund; - fn validate_tx( &self, tx_data: &Tx, diff --git a/shared/src/ledger/pos/vp.rs b/shared/src/ledger/pos/vp.rs index dda3497027..18085c5c53 100644 --- a/shared/src/ledger/pos/vp.rs +++ b/shared/src/ledger/pos/vp.rs @@ -89,8 +89,6 @@ where { type Error = Error; - const ADDR: InternalAddress = InternalAddress::PoS; - fn validate_tx( &self, tx_data: &Tx, @@ -101,7 +99,7 @@ where // use validation::DataUpdate::{self, *}; // use validation::ValidatorUpdate::*; - let addr = Address::Internal(Self::ADDR); + let addr = Address::Internal(InternalAddress::PoS); // let mut changes: Vec = vec![]; let _current_epoch = self.ctx.pre().get_block_epoch()?; From 5af1f94efa6ba76c7db03eff1cec8f73ffb74c85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Mon, 17 Jul 2023 12:29:06 +0100 Subject: [PATCH 099/120] pos: error out if validator is not in expected state for rewards --- proof_of_stake/src/lib.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index 7f7265be91..72e03f8278 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -96,6 +96,8 @@ pub enum GenesisError { pub enum InflationError { #[error("Error in calculating rewards: {0}")] Rewards(rewards::RewardsError), + #[error("Expected validator {0} to be in consensus set but got: {1:?}")] + ExpectedValidatorInConsensus(Address, Option), } #[allow(missing_docs)] @@ -3136,7 +3138,11 @@ where let state = validator_state_handle(&validator_address) .get(storage, epoch, ¶ms)?; if state != Some(ValidatorState::Consensus) { - continue; + return Err(InflationError::ExpectedValidatorInConsensus( + validator_address, + state, + )) + .into_storage_result(); } let stake_from_deltas = From ca02d265059f74df6b93060c7e67769098c642d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Mon, 17 Jul 2023 12:34:53 +0100 Subject: [PATCH 100/120] test/e2e/slashing: extend the test to discover rewards issues --- tests/src/e2e/ledger_tests.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 9b579bef92..e08fd174c2 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -4308,6 +4308,7 @@ fn test_genesis_validators() -> Result<()> { /// 4. Run it to get it to double vote and sign blocks /// 5. Submit a valid token transfer tx to validator 0 /// 6. Wait for double signing evidence +/// 7. Make sure the the first validator can proceed to the next epoch #[test] fn double_signing_gets_slashed() -> Result<()> { use std::net::SocketAddr; @@ -4319,7 +4320,13 @@ fn double_signing_gets_slashed() -> Result<()> { // Setup 2 genesis validator nodes let test = setup::network( - |genesis| setup::set_validators(2, genesis, default_port_offset), + |genesis| { + let mut genesis = + setup::set_validators(2, genesis, default_port_offset); + // Make faster epochs to be more likely to discover boundary issues + genesis.parameters.min_num_of_blocks = 2; + genesis + }, None, )?; @@ -4468,6 +4475,18 @@ fn double_signing_gets_slashed() -> Result<()> { let mut validator_1 = bg_validator_1.foreground(); validator_1.exp_string("Processing evidence")?; validator_1.exp_string("Slashing")?; + let bg_validator_1 = validator_1.background(); + + // 7. Make sure the the first validator can proceed to the next epoch + epoch_sleep(&test, &validator_one_rpc, 120)?; + + // Make sure there are no errors + let mut validator_1 = bg_validator_1.foreground(); + validator_1.interrupt()?; + // Wait for the node to stop running to finish writing the state and tx + // queue + validator_1.exp_string("Namada ledger node has shut down.")?; + validator_1.assert_success(); Ok(()) } From 2afb27c925cd9a7b27129602cbae36ce3e9d236f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Mon, 17 Jul 2023 12:44:46 +0100 Subject: [PATCH 101/120] app/ledger/finalize_block: log block rewards before recording slashes --- .../lib/node/ledger/shell/finalize_block.rs | 86 +++++++++++-------- 1 file changed, 50 insertions(+), 36 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 3af8ab9e3f..ca0dbfbb2f 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -110,6 +110,14 @@ where )?; } + // Invariant: Has to be applied before `record_slashes_from_evidence` + // because it potentially needs to be able to read validator state from + // previous epoch and jailing validator removes the historical state + self.log_block_rewards(&req.votes, height, current_epoch, new_epoch)?; + if new_epoch { + self.apply_inflation(current_epoch)?; + } + // Invariant: This has to be applied after // `copy_validator_sets_and_positions` and before `self.update_epoch`. self.record_slashes_from_evidence(); @@ -530,42 +538,6 @@ where self.update_eth_oracle(); } - // Read the block proposer of the previously committed block in storage - // (n-1 if we are in the process of finalizing n right now). - match read_last_block_proposer_address(&self.wl_storage)? { - Some(proposer_address) => { - tracing::debug!( - "Found last block proposer: {proposer_address}" - ); - let votes = pos_votes_from_abci(&self.wl_storage, &req.votes); - namada_proof_of_stake::log_block_rewards( - &mut self.wl_storage, - if new_epoch { - current_epoch.prev() - } else { - current_epoch - }, - &proposer_address, - votes, - )?; - } - None => { - if height > BlockHeight::default().next_height() { - tracing::error!( - "Can't find the last block proposer at height {height}" - ); - } else { - tracing::debug!( - "No last block proposer at height {height}" - ); - } - } - } - - if new_epoch { - self.apply_inflation(current_epoch)?; - } - if !req.proposer_address.is_empty() { let tm_raw_hash_string = tm_raw_hash_to_string(req.proposer_address); @@ -887,6 +859,48 @@ where Ok(()) } + + // Process the proposer and votes in the block to assign their PoS rewards. + fn log_block_rewards( + &mut self, + votes: &[VoteInfo], + height: BlockHeight, + current_epoch: Epoch, + new_epoch: bool, + ) -> Result<()> { + // Read the block proposer of the previously committed block in storage + // (n-1 if we are in the process of finalizing n right now). + match read_last_block_proposer_address(&self.wl_storage)? { + Some(proposer_address) => { + tracing::debug!( + "Found last block proposer: {proposer_address}" + ); + let votes = pos_votes_from_abci(&self.wl_storage, votes); + namada_proof_of_stake::log_block_rewards( + &mut self.wl_storage, + if new_epoch { + current_epoch.prev() + } else { + current_epoch + }, + &proposer_address, + votes, + )?; + } + None => { + if height > BlockHeight::default().next_height() { + tracing::error!( + "Can't find the last block proposer at height {height}" + ); + } else { + tracing::debug!( + "No last block proposer at height {height}" + ); + } + } + } + Ok(()) + } } /// Convert ABCI vote info to PoS vote info. Any info which fails the conversion From 6552a5a15c76bdb84a48daf7a7adcc6461785dfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 18 Jul 2023 09:35:22 +0100 Subject: [PATCH 102/120] changelog: add #1725 --- .../unreleased/improvements/1725-remove-native-vp-addr.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/improvements/1725-remove-native-vp-addr.md diff --git a/.changelog/unreleased/improvements/1725-remove-native-vp-addr.md b/.changelog/unreleased/improvements/1725-remove-native-vp-addr.md new file mode 100644 index 0000000000..5d9bb3c4f0 --- /dev/null +++ b/.changelog/unreleased/improvements/1725-remove-native-vp-addr.md @@ -0,0 +1,2 @@ +- Removed the associated type for an address from `trait NativeVp`. + ([\#1725](https://github.com/anoma/namada/pull/1725)) \ No newline at end of file From ed57e33ce6e8c9bd70736335ab61ef1372a8caec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 18 Jul 2023 09:39:39 +0100 Subject: [PATCH 103/120] pos/slash: fix the validator state update to be in sync with set changes --- proof_of_stake/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index 72e03f8278..1699f6dee4 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -3444,8 +3444,10 @@ where } } } + // Safe sub cause `validator_set_update_epoch > current_epoch` + let start_offset = validator_set_update_epoch.0 - current_epoch.0; // Set the validator state as `Jailed` thru the pipeline epoch - for offset in 1..=params.pipeline_len { + for offset in start_offset..=params.pipeline_len { validator_state_handle(validator).set( storage, ValidatorState::Jailed, From 0c16a199e529f5248e090b6265d794805c85126f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 18 Jul 2023 11:26:00 +0100 Subject: [PATCH 104/120] test/ledger/finalize_block: fix slashing tests --- apps/src/lib/node/ledger/shell/finalize_block.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index ca0dbfbb2f..137d4038d6 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -2420,8 +2420,11 @@ mod test_finalize_block { ); // Advance to the processing epoch - let votes = get_default_true_votes(&shell.wl_storage, Epoch::default()); loop { + let votes = get_default_true_votes( + &shell.wl_storage, + shell.wl_storage.storage.block.epoch, + ); next_block_for_inflation( &mut shell, pkh1.clone(), @@ -2960,10 +2963,14 @@ mod test_finalize_block { total_voting_power: Default::default(), }, ]; + let votes = get_default_true_votes( + &shell.wl_storage, + shell.wl_storage.storage.block.epoch, + ); next_block_for_inflation( &mut shell, pkh1.clone(), - votes.clone(), + votes, Some(misbehaviors), ); assert_eq!(current_epoch.0, 7_u64); From 4e5293625ae40c5d86a6f84de634e6b3b7d2e09d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 18 Jul 2023 11:34:02 +0100 Subject: [PATCH 105/120] changelog: add #1729 --- .../unreleased/bug-fixes/1729-pos-fix-rewards-boundary.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/unreleased/bug-fixes/1729-pos-fix-rewards-boundary.md diff --git a/.changelog/unreleased/bug-fixes/1729-pos-fix-rewards-boundary.md b/.changelog/unreleased/bug-fixes/1729-pos-fix-rewards-boundary.md new file mode 100644 index 0000000000..3b6fcaa903 --- /dev/null +++ b/.changelog/unreleased/bug-fixes/1729-pos-fix-rewards-boundary.md @@ -0,0 +1,3 @@ +- PoS: Fixed an epoch boundary issue in which a validator who's being slashed + on a start of a new epoch is disregarded during processing of block votes. + ([\#1729](https://github.com/anoma/namada/pull/1729)) \ No newline at end of file From 670f7e4a73aad63dda1a803d141ab80d91e59c14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 18 Jul 2023 11:39:19 +0100 Subject: [PATCH 106/120] pos/epoched: keep 2 past epochs of data by default --- proof_of_stake/src/epoched.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/proof_of_stake/src/epoched.rs b/proof_of_stake/src/epoched.rs index 4899ae1e1d..b671d7e2f2 100644 --- a/proof_of_stake/src/epoched.rs +++ b/proof_of_stake/src/epoched.rs @@ -24,11 +24,14 @@ pub const LAST_UPDATE_SUB_KEY: &str = "last_update"; /// Sub-key for an epoched data structure's oldest epoch with some data pub const OLDEST_EPOCH_SUB_KEY: &str = "oldest_epoch"; +/// Default number of past epochs to keep. +const DEFAULT_NUM_PAST_EPOCHS: u64 = 2; + /// Discrete epoched data handle pub struct Epoched< Data, FutureEpochs, - const NUM_PAST_EPOCHS: u64 = 0, + const NUM_PAST_EPOCHS: u64 = DEFAULT_NUM_PAST_EPOCHS, SON = collections::Simple, > { storage_prefix: storage::Key, @@ -38,8 +41,11 @@ pub struct Epoched< } /// Discrete epoched data handle with nested lazy structure -pub type NestedEpoched = - Epoched; +pub type NestedEpoched< + Data, + FutureEpochs, + const NUM_PAST_EPOCHS: u64 = DEFAULT_NUM_PAST_EPOCHS, +> = Epoched; /// Delta epoched data handle pub struct EpochedDelta { From 641f9e036426dde62e468937c0a6cb3694196c30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 18 Jul 2023 12:34:14 +0100 Subject: [PATCH 107/120] changelog: add #1733 --- .changelog/unreleased/miscellaneous/1733-pos-data-history.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/miscellaneous/1733-pos-data-history.md diff --git a/.changelog/unreleased/miscellaneous/1733-pos-data-history.md b/.changelog/unreleased/miscellaneous/1733-pos-data-history.md new file mode 100644 index 0000000000..aa3adccc66 --- /dev/null +++ b/.changelog/unreleased/miscellaneous/1733-pos-data-history.md @@ -0,0 +1,2 @@ +- PoS: Keep the data for last two epochs by default. + ([\#1733](https://github.com/anoma/namada/pull/1733)) \ No newline at end of file From ca154cd0543c6d3748e05a3136eb70bda5e57ff6 Mon Sep 17 00:00:00 2001 From: satan Date: Wed, 19 Jul 2023 13:52:34 +0200 Subject: [PATCH 108/120] [feat]: Moved cli commands common to testing, cli, and sdk out into apps --- apps/src/bin/namada-client/cli.rs | 461 --------------- apps/src/bin/namada-client/main.rs | 12 +- apps/src/bin/namada-relayer/cli.rs | 143 ----- apps/src/bin/namada-relayer/main.rs | 9 +- apps/src/bin/namada-wallet/main.rs | 7 +- apps/src/lib/cli.rs | 4 + apps/src/lib/cli/api.rs | 29 + apps/src/lib/cli/client.rs | 525 ++++++++++++++++++ apps/src/lib/cli/relayer.rs | 166 ++++++ .../cli.rs => lib/cli/wallet.rs} | 146 ++--- apps/src/lib/client/tx.rs | 6 +- 11 files changed, 819 insertions(+), 689 deletions(-) delete mode 100644 apps/src/bin/namada-client/cli.rs delete mode 100644 apps/src/bin/namada-relayer/cli.rs create mode 100644 apps/src/lib/cli/api.rs create mode 100644 apps/src/lib/cli/client.rs create mode 100644 apps/src/lib/cli/relayer.rs rename apps/src/{bin/namada-wallet/cli.rs => lib/cli/wallet.rs} (83%) diff --git a/apps/src/bin/namada-client/cli.rs b/apps/src/bin/namada-client/cli.rs deleted file mode 100644 index 609588045d..0000000000 --- a/apps/src/bin/namada-client/cli.rs +++ /dev/null @@ -1,461 +0,0 @@ -//! Namada client CLI. - -use color_eyre::eyre::{eyre, Report, Result}; -use namada::ledger::eth_bridge::bridge_pool; -use namada::ledger::rpc::wait_until_node_is_synched; -use namada::ledger::{signing, tx as sdk_tx}; -use namada::types::control_flow::ProceedOrElse; -use namada_apps::cli; -use namada_apps::cli::args::CliToSdk; -use namada_apps::cli::cmds::*; -use namada_apps::client::{rpc, tx, utils}; -use namada_apps::facade::tendermint_rpc::HttpClient; - -fn error() -> Report { - eyre!("Fatal error") -} - -pub async fn main() -> Result<()> { - match cli::namada_client_cli()? { - cli::NamadaClient::WithContext(cmd_box) => { - let (cmd, mut ctx) = *cmd_box; - use NamadaClientWithContext as Sub; - match cmd { - // Ledger cmds - Sub::TxCustom(TxCustom(mut args)) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.tx.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk(&mut ctx); - let dry_run = args.tx.dry_run; - tx::submit_custom::(&client, &mut ctx, args) - .await?; - if !dry_run { - namada_apps::wallet::save(&ctx.wallet) - .unwrap_or_else(|err| eprintln!("{}", err)); - } else { - println!( - "Transaction dry run. No addresses have been \ - saved." - ) - } - } - Sub::TxTransfer(TxTransfer(mut args)) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.tx.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk(&mut ctx); - tx::submit_transfer(&client, ctx, args).await?; - } - Sub::TxIbcTransfer(TxIbcTransfer(mut args)) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.tx.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk(&mut ctx); - tx::submit_ibc_transfer::(&client, ctx, args) - .await?; - } - Sub::TxUpdateVp(TxUpdateVp(mut args)) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.tx.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk(&mut ctx); - tx::submit_update_vp::(&client, &mut ctx, args) - .await?; - } - Sub::TxInitAccount(TxInitAccount(mut args)) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.tx.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk(&mut ctx); - let dry_run = args.tx.dry_run; - tx::submit_init_account::( - &client, &mut ctx, args, - ) - .await?; - if !dry_run { - namada_apps::wallet::save(&ctx.wallet) - .unwrap_or_else(|err| eprintln!("{}", err)); - } else { - println!( - "Transaction dry run. No addresses have been \ - saved." - ) - } - } - Sub::TxInitValidator(TxInitValidator(mut args)) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.tx.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk(&mut ctx); - tx::submit_init_validator::(&client, ctx, args) - .await?; - } - Sub::TxInitProposal(TxInitProposal(mut args)) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.tx.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk(&mut ctx); - tx::submit_init_proposal::(&client, ctx, args) - .await?; - } - Sub::TxVoteProposal(TxVoteProposal(mut args)) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.tx.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk(&mut ctx); - tx::submit_vote_proposal::(&client, ctx, args) - .await?; - } - Sub::TxRevealPk(TxRevealPk(mut args)) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.tx.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk(&mut ctx); - tx::submit_reveal_pk::(&client, &mut ctx, args) - .await?; - } - Sub::Bond(Bond(mut args)) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.tx.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk(&mut ctx); - tx::submit_bond::(&client, &mut ctx, args) - .await?; - } - Sub::Unbond(Unbond(mut args)) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.tx.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk(&mut ctx); - tx::submit_unbond::(&client, &mut ctx, args) - .await?; - } - Sub::Withdraw(Withdraw(mut args)) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.tx.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk(&mut ctx); - tx::submit_withdraw::(&client, ctx, args) - .await?; - } - Sub::TxCommissionRateChange(TxCommissionRateChange( - mut args, - )) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.tx.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client).await; - let args = args.to_sdk(&mut ctx); - tx::submit_validator_commission_change::( - &client, ctx, args, - ) - .await?; - } - // Eth bridge - Sub::AddToEthBridgePool(args) => { - let mut args = args.0; - let client = HttpClient::new(utils::take_config_address( - &mut args.tx.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk(&mut ctx); - let tx_args = args.tx.clone(); - let (mut tx, addr, pk) = bridge_pool::build_bridge_pool_tx( - &client, - &mut ctx.wallet, - args, - ) - .await - .unwrap(); - tx::submit_reveal_aux( - &client, - &mut ctx, - &tx_args, - addr, - pk.clone(), - &mut tx, - ) - .await?; - signing::sign_tx(&mut ctx.wallet, &mut tx, &tx_args, &pk) - .await?; - sdk_tx::process_tx(&client, &mut ctx.wallet, &tx_args, tx) - .await?; - } - // Ledger queries - Sub::QueryEpoch(QueryEpoch(mut args)) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - rpc::query_and_print_epoch(&client).await; - } - Sub::QueryTransfers(QueryTransfers(mut args)) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk(&mut ctx); - rpc::query_transfers( - &client, - &mut ctx.wallet, - &mut ctx.shielded, - args, - ) - .await; - } - Sub::QueryConversions(QueryConversions(mut args)) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk(&mut ctx); - rpc::query_conversions(&client, &mut ctx.wallet, args) - .await; - } - Sub::QueryBlock(QueryBlock(mut args)) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - rpc::query_block(&client).await; - } - Sub::QueryBalance(QueryBalance(mut args)) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk(&mut ctx); - rpc::query_balance( - &client, - &mut ctx.wallet, - &mut ctx.shielded, - args, - ) - .await; - } - Sub::QueryBonds(QueryBonds(mut args)) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk(&mut ctx); - rpc::query_bonds(&client, &mut ctx.wallet, args) - .await - .expect("expected successful query of bonds"); - } - Sub::QueryBondedStake(QueryBondedStake(mut args)) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk(&mut ctx); - rpc::query_bonded_stake(&client, args).await; - } - Sub::QueryCommissionRate(QueryCommissionRate(mut args)) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk(&mut ctx); - rpc::query_and_print_commission_rate( - &client, - &mut ctx.wallet, - args, - ) - .await; - } - Sub::QuerySlashes(QuerySlashes(mut args)) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk(&mut ctx); - rpc::query_slashes(&client, &mut ctx.wallet, args).await; - } - Sub::QueryDelegations(QueryDelegations(mut args)) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk(&mut ctx); - rpc::query_delegations(&client, &mut ctx.wallet, args) - .await; - } - Sub::QueryFindValidator(QueryFindValidator(mut args)) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk(&mut ctx); - rpc::query_find_validator(&client, args).await; - } - Sub::QueryResult(QueryResult(mut args)) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk(&mut ctx); - rpc::query_result(&client, args).await; - } - Sub::QueryRawBytes(QueryRawBytes(mut args)) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk(&mut ctx); - rpc::query_raw_bytes(&client, args).await; - } - - Sub::QueryProposal(QueryProposal(mut args)) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk(&mut ctx); - rpc::query_proposal(&client, args).await; - } - Sub::QueryProposalResult(QueryProposalResult(mut args)) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk(&mut ctx); - rpc::query_proposal_result(&client, args).await; - } - Sub::QueryProtocolParameters(QueryProtocolParameters( - mut args, - )) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk(&mut ctx); - rpc::query_protocol_parameters(&client, args).await; - } - } - } - cli::NamadaClient::WithoutContext(cmd, global_args) => match cmd { - // Utils cmds - Utils::JoinNetwork(JoinNetwork(args)) => { - utils::join_network(global_args, args).await - } - Utils::FetchWasms(FetchWasms(args)) => { - utils::fetch_wasms(global_args, args).await - } - Utils::InitNetwork(InitNetwork(args)) => { - utils::init_network(global_args, args) - } - Utils::InitGenesisValidator(InitGenesisValidator(args)) => { - utils::init_genesis_validator(global_args, args) - } - Utils::PkToTmAddress(PkToTmAddress(args)) => { - utils::pk_to_tm_address(global_args, args) - } - Utils::DefaultBaseDir(DefaultBaseDir(args)) => { - utils::default_base_dir(global_args, args) - } - }, - } - Ok(()) -} diff --git a/apps/src/bin/namada-client/main.rs b/apps/src/bin/namada-client/main.rs index ccdc0bb2eb..a9e1fb4948 100644 --- a/apps/src/bin/namada-client/main.rs +++ b/apps/src/bin/namada-client/main.rs @@ -1,7 +1,7 @@ -mod cli; - use color_eyre::eyre::Result; -use namada_apps::logging; +use namada_apps::cli::api::CliApi; +use namada_apps::facade::tendermint_rpc::HttpClient; +use namada_apps::{cli, logging}; use tracing_subscriber::filter::LevelFilter; #[tokio::main] @@ -13,5 +13,9 @@ async fn main() -> Result<()> { let _log_guard = logging::init_from_env_or(LevelFilter::INFO)?; // run the CLI - cli::main().await + CliApi::<()>::handle_client_command::( + None, + cli::namada_client_cli()?, + ) + .await } diff --git a/apps/src/bin/namada-relayer/cli.rs b/apps/src/bin/namada-relayer/cli.rs deleted file mode 100644 index fa816dbeae..0000000000 --- a/apps/src/bin/namada-relayer/cli.rs +++ /dev/null @@ -1,143 +0,0 @@ -//! Namada relayer CLI. - -use std::sync::Arc; - -use color_eyre::eyre::{eyre, Report, Result}; -use namada::eth_bridge::ethers::providers::{Http, Provider}; -use namada::ledger::eth_bridge::{bridge_pool, validator_set}; -use namada::ledger::rpc::wait_until_node_is_synched; -use namada::types::control_flow::ProceedOrElse; -use namada_apps::cli::args::CliToSdkCtxless; -use namada_apps::cli::{self, cmds}; -use namada_apps::client::utils; -use namada_apps::facade::tendermint_rpc::HttpClient; - -fn error() -> Report { - eyre!("Fatal error") -} - -pub async fn main() -> Result<()> { - let (cmd, _) = cli::namada_relayer_cli()?; - match cmd { - cmds::NamadaRelayer::EthBridgePool(sub) => match sub { - cmds::EthBridgePool::RecommendBatch(mut args) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk_ctxless(); - bridge_pool::recommend_batch(&client, args) - .await - .proceed_or_else(error)?; - } - cmds::EthBridgePool::ConstructProof(mut args) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk_ctxless(); - bridge_pool::construct_proof(&client, args) - .await - .proceed_or_else(error)?; - } - cmds::EthBridgePool::RelayProof(mut args) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let eth_client = Arc::new( - Provider::::try_from(&args.eth_rpc_endpoint).unwrap(), - ); - let args = args.to_sdk_ctxless(); - bridge_pool::relay_bridge_pool_proof(eth_client, &client, args) - .await - .proceed_or_else(error)?; - } - cmds::EthBridgePool::QueryPool(mut query) => { - let client = HttpClient::new(utils::take_config_address( - &mut query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - bridge_pool::query_bridge_pool(&client).await; - } - cmds::EthBridgePool::QuerySigned(mut query) => { - let client = HttpClient::new(utils::take_config_address( - &mut query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - bridge_pool::query_signed_bridge_pool(&client) - .await - .proceed_or_else(error)?; - } - cmds::EthBridgePool::QueryRelays(mut query) => { - let client = HttpClient::new(utils::take_config_address( - &mut query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - bridge_pool::query_relay_progress(&client).await; - } - }, - cmds::NamadaRelayer::ValidatorSet(sub) => match sub { - cmds::ValidatorSet::ConsensusValidatorSet(mut args) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk_ctxless(); - validator_set::query_validator_set_args(&client, args).await; - } - cmds::ValidatorSet::ValidatorSetProof(mut args) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let args = args.to_sdk_ctxless(); - validator_set::query_validator_set_update_proof(&client, args) - .await; - } - cmds::ValidatorSet::ValidatorSetUpdateRelay(mut args) => { - let client = HttpClient::new(utils::take_config_address( - &mut args.query.ledger_address, - )) - .unwrap(); - wait_until_node_is_synched(&client) - .await - .proceed_or_else(error)?; - let eth_client = Arc::new( - Provider::::try_from(&args.eth_rpc_endpoint).unwrap(), - ); - let args = args.to_sdk_ctxless(); - validator_set::relay_validator_set_update( - eth_client, &client, args, - ) - .await - .proceed_or_else(error)?; - } - }, - } - Ok(()) -} diff --git a/apps/src/bin/namada-relayer/main.rs b/apps/src/bin/namada-relayer/main.rs index 73876fe7d2..0b314cb9fa 100644 --- a/apps/src/bin/namada-relayer/main.rs +++ b/apps/src/bin/namada-relayer/main.rs @@ -1,7 +1,7 @@ -mod cli; - use color_eyre::eyre::Result; -use namada_apps::logging; +use namada::tendermint_rpc::HttpClient; +use namada_apps::cli::api::CliApi; +use namada_apps::{cli, logging}; use tracing_subscriber::filter::LevelFilter; #[tokio::main] @@ -12,6 +12,7 @@ async fn main() -> Result<()> { // init logging logging::init_from_env_or(LevelFilter::INFO)?; + let (cmd, _) = cli::namada_relayer_cli()?; // run the CLI - cli::main().await + CliApi::<()>::handle_relayer_command::(None, cmd).await } diff --git a/apps/src/bin/namada-wallet/main.rs b/apps/src/bin/namada-wallet/main.rs index 252ecb7b88..7459234c79 100644 --- a/apps/src/bin/namada-wallet/main.rs +++ b/apps/src/bin/namada-wallet/main.rs @@ -1,9 +1,10 @@ -mod cli; use color_eyre::eyre::Result; +use namada_apps::cli; +use namada_apps::cli::api::CliApi; pub fn main() -> Result<()> { color_eyre::install()?; - + let (cmd, ctx) = cli::namada_wallet_cli()?; // run the CLI - cli::main() + CliApi::<()>::handle_wallet_command(cmd, ctx) } diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 06d6f3f406..699a2b7f17 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -6,8 +6,12 @@ //! client can be dispatched via `namada node ...` or `namada client ...`, //! respectively. +pub mod api; +pub mod client; pub mod context; +pub mod relayer; mod utils; +pub mod wallet; use clap::{ArgGroup, ArgMatches, ColorChoice}; use color_eyre::eyre::Result; diff --git a/apps/src/lib/cli/api.rs b/apps/src/lib/cli/api.rs new file mode 100644 index 0000000000..c22fe39fd3 --- /dev/null +++ b/apps/src/lib/cli/api.rs @@ -0,0 +1,29 @@ +use std::marker::PhantomData; + +use namada::ledger::queries::Client; +use namada::ledger::rpc::wait_until_node_is_synched; +use namada::tendermint_rpc::HttpClient; +use namada::types::control_flow::Halt; +use tendermint_config::net::Address as TendermintAddress; + +use crate::client::utils; + +/// Trait for clients that can be used with the CLI. +#[async_trait::async_trait(?Send)] +pub trait CliClient: Client + Sync { + fn from_tendermint_address(address: &mut TendermintAddress) -> Self; + async fn wait_until_node_is_synced(&self) -> Halt<()>; +} + +#[async_trait::async_trait(?Send)] +impl CliClient for HttpClient { + fn from_tendermint_address(address: &mut TendermintAddress) -> Self { + HttpClient::new(utils::take_config_address(address)).unwrap() + } + + async fn wait_until_node_is_synced(&self) -> Halt<()> { + wait_until_node_is_synched(self).await + } +} + +pub struct CliApi(PhantomData); diff --git a/apps/src/lib/cli/client.rs b/apps/src/lib/cli/client.rs new file mode 100644 index 0000000000..fb7d01559b --- /dev/null +++ b/apps/src/lib/cli/client.rs @@ -0,0 +1,525 @@ +use color_eyre::eyre::{eyre, Report, Result}; +use namada::ledger::eth_bridge::bridge_pool; +use namada::ledger::{signing, tx as sdk_tx}; +use namada::types::control_flow::ProceedOrElse; + +use crate::cli; +use crate::cli::api::{CliApi, CliClient}; +use crate::cli::args::CliToSdk; +use crate::cli::cmds::*; +use crate::client::{rpc, tx, utils}; + +fn error() -> Report { + eyre!("Fatal error") +} + +impl CliApi { + pub async fn handle_client_command( + client: Option, + cmd: cli::NamadaClient, + ) -> Result<()> + where + C: CliClient, + { + match cmd { + cli::NamadaClient::WithContext(cmd_box) => { + let (cmd, mut ctx) = *cmd_box; + use NamadaClientWithContext as Sub; + match cmd { + // Ledger cmds + Sub::TxCustom(TxCustom(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + let dry_run = args.tx.dry_run; + tx::submit_custom(&client, &mut ctx, args).await?; + if !dry_run { + crate::wallet::save(&ctx.wallet) + .unwrap_or_else(|err| eprintln!("{}", err)); + } else { + println!( + "Transaction dry run. No addresses have been \ + saved." + ) + } + } + Sub::TxTransfer(TxTransfer(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + tx::submit_transfer(&client, ctx, args).await?; + } + Sub::TxIbcTransfer(TxIbcTransfer(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + tx::submit_ibc_transfer(&client, ctx, args).await?; + } + Sub::TxUpdateVp(TxUpdateVp(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + tx::submit_update_vp(&client, &mut ctx, args).await?; + } + Sub::TxInitAccount(TxInitAccount(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + let dry_run = args.tx.dry_run; + tx::submit_init_account(&client, &mut ctx, args) + .await?; + if !dry_run { + crate::wallet::save(&ctx.wallet) + .unwrap_or_else(|err| eprintln!("{}", err)); + } else { + println!( + "Transaction dry run. No addresses have been \ + saved." + ) + } + } + Sub::TxInitValidator(TxInitValidator(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + tx::submit_init_validator(&client, ctx, args).await?; + } + Sub::TxInitProposal(TxInitProposal(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + tx::submit_init_proposal(&client, ctx, args).await?; + } + Sub::TxVoteProposal(TxVoteProposal(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + tx::submit_vote_proposal(&client, ctx, args).await?; + } + Sub::TxRevealPk(TxRevealPk(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + tx::submit_reveal_pk(&client, &mut ctx, args).await?; + } + Sub::Bond(Bond(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + tx::submit_bond(&client, &mut ctx, args).await?; + } + Sub::Unbond(Unbond(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + tx::submit_unbond(&client, &mut ctx, args).await?; + } + Sub::Withdraw(Withdraw(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + tx::submit_withdraw(&client, ctx, args).await?; + } + Sub::TxCommissionRateChange(TxCommissionRateChange( + mut args, + )) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + tx::submit_validator_commission_change( + &client, ctx, args, + ) + .await?; + } + // Eth bridge + Sub::AddToEthBridgePool(args) => { + let mut args = args.0; + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + let tx_args = args.tx.clone(); + let (mut tx, addr, pk) = + bridge_pool::build_bridge_pool_tx( + &client, + &mut ctx.wallet, + args, + ) + .await + .unwrap(); + tx::submit_reveal_aux( + &client, + &mut ctx, + &tx_args, + addr, + pk.clone(), + &mut tx, + ) + .await?; + signing::sign_tx( + &mut ctx.wallet, + &mut tx, + &tx_args, + &pk, + ) + .await?; + sdk_tx::process_tx( + &client, + &mut ctx.wallet, + &tx_args, + tx, + ) + .await?; + } + // Ledger queries + Sub::QueryEpoch(QueryEpoch(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address(&mut args.ledger_address) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + rpc::query_and_print_epoch(&client).await; + } + Sub::QueryTransfers(QueryTransfers(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_transfers( + &client, + &mut ctx.wallet, + &mut ctx.shielded, + args, + ) + .await; + } + Sub::QueryConversions(QueryConversions(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_conversions(&client, &mut ctx.wallet, args) + .await; + } + Sub::QueryBlock(QueryBlock(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address(&mut args.ledger_address) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + rpc::query_block(&client).await; + } + Sub::QueryBalance(QueryBalance(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_balance( + &client, + &mut ctx.wallet, + &mut ctx.shielded, + args, + ) + .await; + } + Sub::QueryBonds(QueryBonds(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_bonds(&client, &mut ctx.wallet, args) + .await + .expect("expected successful query of bonds"); + } + Sub::QueryBondedStake(QueryBondedStake(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_bonded_stake(&client, args).await; + } + Sub::QueryCommissionRate(QueryCommissionRate(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_and_print_commission_rate( + &client, + &mut ctx.wallet, + args, + ) + .await; + } + Sub::QuerySlashes(QuerySlashes(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_slashes(&client, &mut ctx.wallet, args) + .await; + } + Sub::QueryDelegations(QueryDelegations(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_delegations(&client, &mut ctx.wallet, args) + .await; + } + Sub::QueryFindValidator(QueryFindValidator(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_find_validator(&client, args).await; + } + Sub::QueryResult(QueryResult(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_result(&client, args).await; + } + Sub::QueryRawBytes(QueryRawBytes(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_raw_bytes(&client, args).await; + } + + Sub::QueryProposal(QueryProposal(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_proposal(&client, args).await; + } + Sub::QueryProposalResult(QueryProposalResult(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_proposal_result(&client, args).await; + } + Sub::QueryProtocolParameters(QueryProtocolParameters( + mut args, + )) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_protocol_parameters(&client, args).await; + } + } + } + cli::NamadaClient::WithoutContext(cmd, global_args) => match cmd { + // Utils cmds + Utils::JoinNetwork(JoinNetwork(args)) => { + utils::join_network(global_args, args).await + } + Utils::FetchWasms(FetchWasms(args)) => { + utils::fetch_wasms(global_args, args).await + } + Utils::InitNetwork(InitNetwork(args)) => { + utils::init_network(global_args, args) + } + Utils::InitGenesisValidator(InitGenesisValidator(args)) => { + utils::init_genesis_validator(global_args, args) + } + Utils::PkToTmAddress(PkToTmAddress(args)) => { + utils::pk_to_tm_address(global_args, args) + } + Utils::DefaultBaseDir(DefaultBaseDir(args)) => { + utils::default_base_dir(global_args, args) + } + }, + } + Ok(()) + } +} diff --git a/apps/src/lib/cli/relayer.rs b/apps/src/lib/cli/relayer.rs new file mode 100644 index 0000000000..531051d27a --- /dev/null +++ b/apps/src/lib/cli/relayer.rs @@ -0,0 +1,166 @@ +use std::sync::Arc; + +use color_eyre::eyre::{eyre, Report, Result}; +use namada::eth_bridge::ethers::providers::{Http, Provider}; +use namada::ledger::eth_bridge::{bridge_pool, validator_set}; +use namada::types::control_flow::ProceedOrElse; + +use crate::cli::api::{CliApi, CliClient}; +use crate::cli::args::CliToSdkCtxless; +use crate::cli::cmds; + +fn error() -> Report { + eyre!("Fatal error") +} + +impl CliApi { + pub async fn handle_relayer_command( + client: Option, + cmd: cmds::NamadaRelayer, + ) -> Result<()> + where + C: CliClient, + { + match cmd { + cmds::NamadaRelayer::EthBridgePool(sub) => match sub { + cmds::EthBridgePool::RecommendBatch(mut args) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk_ctxless(); + bridge_pool::recommend_batch(&client, args) + .await + .proceed_or_else(error)?; + } + cmds::EthBridgePool::ConstructProof(mut args) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk_ctxless(); + bridge_pool::construct_proof(&client, args) + .await + .proceed_or_else(error)?; + } + cmds::EthBridgePool::RelayProof(mut args) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let eth_client = Arc::new( + Provider::::try_from(&args.eth_rpc_endpoint) + .unwrap(), + ); + let args = args.to_sdk_ctxless(); + bridge_pool::relay_bridge_pool_proof( + eth_client, &client, args, + ) + .await + .proceed_or_else(error)?; + } + cmds::EthBridgePool::QueryPool(mut query) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address(&mut query.ledger_address) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + bridge_pool::query_bridge_pool(&client).await; + } + cmds::EthBridgePool::QuerySigned(mut query) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address(&mut query.ledger_address) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + bridge_pool::query_signed_bridge_pool(&client) + .await + .proceed_or_else(error)?; + } + cmds::EthBridgePool::QueryRelays(mut query) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address(&mut query.ledger_address) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + bridge_pool::query_relay_progress(&client).await; + } + }, + cmds::NamadaRelayer::ValidatorSet(sub) => match sub { + cmds::ValidatorSet::ConsensusValidatorSet(mut args) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk_ctxless(); + validator_set::query_validator_set_args(&client, args) + .await; + } + cmds::ValidatorSet::ValidatorSetProof(mut args) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk_ctxless(); + validator_set::query_validator_set_update_proof( + &client, args, + ) + .await; + } + cmds::ValidatorSet::ValidatorSetUpdateRelay(mut args) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let eth_client = Arc::new( + Provider::::try_from(&args.eth_rpc_endpoint) + .unwrap(), + ); + let args = args.to_sdk_ctxless(); + validator_set::relay_validator_set_update( + eth_client, &client, args, + ) + .await + .proceed_or_else(error)?; + } + }, + } + Ok(()) + } +} diff --git a/apps/src/bin/namada-wallet/cli.rs b/apps/src/lib/cli/wallet.rs similarity index 83% rename from apps/src/bin/namada-wallet/cli.rs rename to apps/src/lib/cli/wallet.rs index 685ed7f116..7505c59efe 100644 --- a/apps/src/bin/namada-wallet/cli.rs +++ b/apps/src/lib/cli/wallet.rs @@ -11,68 +11,78 @@ use namada::ledger::masp::find_valid_diversifier; use namada::ledger::wallet::{DecryptionError, FindKeyError}; use namada::types::key::*; use namada::types::masp::{MaspValue, PaymentAddress}; -use namada_apps::cli; -use namada_apps::cli::args::CliToSdk; -use namada_apps::cli::{args, cmds, Context}; -use namada_apps::wallet::{ - read_and_confirm_encryption_password, CliWalletUtils, -}; use rand_core::OsRng; -pub fn main() -> Result<()> { - let (cmd, mut ctx) = cli::namada_wallet_cli()?; - match cmd { - cmds::NamadaWallet::Key(sub) => match sub { - cmds::WalletKey::Restore(cmds::KeyRestore(args)) => { - key_and_address_restore(ctx, args) - } - cmds::WalletKey::Gen(cmds::KeyGen(args)) => { - key_and_address_gen(ctx, args) - } - cmds::WalletKey::Find(cmds::KeyFind(args)) => key_find(ctx, args), - cmds::WalletKey::List(cmds::KeyList(args)) => key_list(ctx, args), - cmds::WalletKey::Export(cmds::Export(args)) => { - key_export(ctx, args) - } - }, - cmds::NamadaWallet::Address(sub) => match sub { - cmds::WalletAddress::Gen(cmds::AddressGen(args)) => { - key_and_address_gen(ctx, args) - } - cmds::WalletAddress::Restore(cmds::AddressRestore(args)) => { - key_and_address_restore(ctx, args) - } - cmds::WalletAddress::Find(cmds::AddressOrAliasFind(args)) => { - address_or_alias_find(ctx, args) - } - cmds::WalletAddress::List(cmds::AddressList) => address_list(ctx), - cmds::WalletAddress::Add(cmds::AddressAdd(args)) => { - address_add(ctx, args) - } - }, - cmds::NamadaWallet::Masp(sub) => match sub { - cmds::WalletMasp::GenSpendKey(cmds::MaspGenSpendKey(args)) => { - spending_key_gen(ctx, args) - } - cmds::WalletMasp::GenPayAddr(cmds::MaspGenPayAddr(args)) => { - let args = args.to_sdk(&mut ctx); - payment_address_gen(ctx, args) - } - cmds::WalletMasp::AddAddrKey(cmds::MaspAddAddrKey(args)) => { - address_key_add(ctx, args) - } - cmds::WalletMasp::ListPayAddrs(cmds::MaspListPayAddrs) => { - payment_addresses_list(ctx) - } - cmds::WalletMasp::ListKeys(cmds::MaspListKeys(args)) => { - spending_keys_list(ctx, args) - } - cmds::WalletMasp::FindAddrKey(cmds::MaspFindAddrKey(args)) => { - address_key_find(ctx, args) - } - }, +use crate::cli; +use crate::cli::api::CliApi; +use crate::cli::args::CliToSdk; +use crate::cli::{args, cmds, Context}; +use crate::wallet::{read_and_confirm_encryption_password, CliWalletUtils}; + +impl CliApi { + pub fn handle_wallet_command( + cmd: cmds::NamadaWallet, + mut ctx: Context, + ) -> Result<()> { + match cmd { + cmds::NamadaWallet::Key(sub) => match sub { + cmds::WalletKey::Restore(cmds::KeyRestore(args)) => { + key_and_address_restore(ctx, args) + } + cmds::WalletKey::Gen(cmds::KeyGen(args)) => { + key_and_address_gen(ctx, args) + } + cmds::WalletKey::Find(cmds::KeyFind(args)) => { + key_find(ctx, args) + } + cmds::WalletKey::List(cmds::KeyList(args)) => { + key_list(ctx, args) + } + cmds::WalletKey::Export(cmds::Export(args)) => { + key_export(ctx, args) + } + }, + cmds::NamadaWallet::Address(sub) => match sub { + cmds::WalletAddress::Gen(cmds::AddressGen(args)) => { + key_and_address_gen(ctx, args) + } + cmds::WalletAddress::Restore(cmds::AddressRestore(args)) => { + key_and_address_restore(ctx, args) + } + cmds::WalletAddress::Find(cmds::AddressOrAliasFind(args)) => { + address_or_alias_find(ctx, args) + } + cmds::WalletAddress::List(cmds::AddressList) => { + address_list(ctx) + } + cmds::WalletAddress::Add(cmds::AddressAdd(args)) => { + address_add(ctx, args) + } + }, + cmds::NamadaWallet::Masp(sub) => match sub { + cmds::WalletMasp::GenSpendKey(cmds::MaspGenSpendKey(args)) => { + spending_key_gen(ctx, args) + } + cmds::WalletMasp::GenPayAddr(cmds::MaspGenPayAddr(args)) => { + let args = args.to_sdk(&mut ctx); + payment_address_gen(ctx, args) + } + cmds::WalletMasp::AddAddrKey(cmds::MaspAddAddrKey(args)) => { + address_key_add(ctx, args) + } + cmds::WalletMasp::ListPayAddrs(cmds::MaspListPayAddrs) => { + payment_addresses_list(ctx) + } + cmds::WalletMasp::ListKeys(cmds::MaspListKeys(args)) => { + spending_keys_list(ctx, args) + } + cmds::WalletMasp::FindAddrKey(cmds::MaspFindAddrKey(args)) => { + address_key_find(ctx, args) + } + }, + } + Ok(()) } - Ok(()) } /// Find shielded address or key @@ -213,8 +223,7 @@ fn spending_key_gen( let alias = alias.to_lowercase(); let password = read_and_confirm_encryption_password(unsafe_dont_encrypt); let (alias, _key) = wallet.gen_spending_key(alias, password, alias_force); - namada_apps::wallet::save(&wallet) - .unwrap_or_else(|err| eprintln!("{}", err)); + crate::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); println!( "Successfully added a spending key with alias: \"{}\"", alias @@ -248,8 +257,7 @@ fn payment_address_gen( eprintln!("Payment address not added"); cli::safe_exit(1); }); - namada_apps::wallet::save(&wallet) - .unwrap_or_else(|err| eprintln!("{}", err)); + crate::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); println!( "Successfully generated a payment address with the following alias: {}", alias, @@ -306,8 +314,7 @@ fn address_key_add( (alias, "payment address") } }; - namada_apps::wallet::save(&ctx.wallet) - .unwrap_or_else(|err| eprintln!("{}", err)); + crate::wallet::save(&ctx.wallet).unwrap_or_else(|err| eprintln!("{}", err)); println!( "Successfully added a {} with the following alias to wallet: {}", typ, alias, @@ -345,8 +352,7 @@ fn key_and_address_restore( println!("No changes are persisted. Exiting."); cli::safe_exit(0); }); - namada_apps::wallet::save(&wallet) - .unwrap_or_else(|err| eprintln!("{}", err)); + crate::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); println!( "Successfully added a key and an address with alias: \"{}\"", alias @@ -387,8 +393,7 @@ fn key_and_address_gen( println!("No changes are persisted. Exiting."); cli::safe_exit(0); }); - namada_apps::wallet::save(&wallet) - .unwrap_or_else(|err| eprintln!("{}", err)); + crate::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); println!( "Successfully added a key and an address with alias: \"{}\"", alias @@ -573,8 +578,7 @@ fn address_add(ctx: Context, args: args::AddressAdd) { eprintln!("Address not added"); cli::safe_exit(1); } - namada_apps::wallet::save(&wallet) - .unwrap_or_else(|err| eprintln!("{}", err)); + crate::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); println!( "Successfully added a key and an address with alias: \"{}\"", args.alias.to_lowercase() diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index a68738c479..e2683babba 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -10,6 +10,7 @@ use borsh::{BorshDeserialize, BorshSerialize}; use data_encoding::HEXLOWER_PERMISSIVE; use masp_proofs::prover::LocalTxProver; use namada::ledger::governance::storage as gov_storage; +use namada::ledger::queries::Client; use namada::ledger::rpc::{TxBroadcastData, TxResponse}; use namada::ledger::signing::TxSigningKey; use namada::ledger::wallet::{Wallet, WalletUtils}; @@ -35,7 +36,6 @@ use crate::client::signing::find_pk; use crate::client::tx::tx::ProcessTxResponse; use crate::config::TendermintMode; use crate::facade::tendermint_rpc::endpoint::broadcast::tx_sync::Response; -use crate::facade::tendermint_rpc::HttpClient; use crate::node::ledger::tendermint_node; use crate::wallet::{gen_validator_keys, read_and_confirm_encryption_password}; @@ -523,8 +523,8 @@ impl masp::ShieldedUtils for CLIShieldedUtils { } } -pub async fn submit_transfer( - client: &HttpClient, +pub async fn submit_transfer( + client: &C, mut ctx: Context, args: args::TxTransfer, ) -> Result<(), tx::Error> { From 432ea9fdee4e43ed4a5db0527ae405a82efea5e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 19 Jul 2023 15:07:15 +0100 Subject: [PATCH 109/120] fixup! Merge branch 'bat/feature/refactor-cli' (#1738) --- apps/src/lib/cli/client.rs | 44 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/apps/src/lib/cli/client.rs b/apps/src/lib/cli/client.rs index fb7d01559b..6b68316850 100644 --- a/apps/src/lib/cli/client.rs +++ b/apps/src/lib/cli/client.rs @@ -268,6 +268,19 @@ impl CliApi { ) .await?; } + Sub::TxUnjailValidator(TxUnjailValidator(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.tx.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + tx::submit_unjail_validator(&client, ctx, args).await?; + } // Ledger queries Sub::QueryEpoch(QueryEpoch(mut args)) => { let client = client.unwrap_or_else(|| { @@ -279,6 +292,24 @@ impl CliApi { .proceed_or_else(error)?; rpc::query_and_print_epoch(&client).await; } + Sub::QueryValidatorState(QueryValidatorState(mut args)) => { + let client = client.unwrap_or_else(|| { + C::from_tendermint_address( + &mut args.query.ledger_address, + ) + }); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_and_print_validator_state( + &client, + &mut ctx.wallet, + args, + ) + .await; + } Sub::QueryTransfers(QueryTransfers(mut args)) => { let client = client.unwrap_or_else(|| { C::from_tendermint_address( @@ -518,6 +549,19 @@ impl CliApi { Utils::DefaultBaseDir(DefaultBaseDir(args)) => { utils::default_base_dir(global_args, args) } + Utils::EpochSleep(EpochSleep(args)) => { + let mut ctx = cli::Context::new(global_args) + .expect("expected to construct a context"); + let mut ledger_address = args.ledger_address.clone(); + let client = + C::from_tendermint_address(&mut ledger_address); + client + .wait_until_node_is_synced() + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::epoch_sleep(&client, args).await; + } }, } Ok(()) From d828cd4806a3bd618117be30ae239eeb9c301b1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 19 Jul 2023 15:52:00 +0100 Subject: [PATCH 110/120] wasm: update checksums --- wasm/checksums.json | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/wasm/checksums.json b/wasm/checksums.json index c2d3d7e014..d234911742 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,22 +1,22 @@ { - "tx_bond.wasm": "tx_bond.e45489077b1f944d15197ecb432290be523189d8cf1f35b3c37211688ad582b2.wasm", + "tx_bond.wasm": "tx_bond.31a145f2c40142d3c1eb7befb4d9e4962689fe920e32595199a206629f119180.wasm", "tx_bridge_pool.wasm": "tx_bridge_pool.8c942b7e6a49562ff20770ac6e04df85188b49fabf4ca7f82fa3a5986a66a363.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.37a8ec36194c2da2d9625a71a37dda7f5ef2530de562dacc29d70d9e1bd6d475.wasm", - "tx_ibc.wasm": "tx_ibc.a719260d45a15a3eeed5442abeda18be739face4ab509abcb00a6a10151ffc5c.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.0608a4e73e0323518e6b1e58c99d0eb6785420faa7cb51c674c264dd361b757d.wasm", + "tx_ibc.wasm": "tx_ibc.75a38e6a94a34046ef1ba2e224a3db2014bc0a3f0993bd0ec4674f287fb37b13.wasm", "tx_init_account.wasm": "tx_init_account.f979613d2b8b540ad471c663ec1aa3d9fad085ba7b1b059e2564c7a1eb5fa139.wasm", "tx_init_proposal.wasm": "tx_init_proposal.9a6c2aa5771fd08f26fb4d56ceb361c723d4b617da0ca2ab1a44c2b07f3b58b0.wasm", - "tx_init_validator.wasm": "tx_init_validator.01e521286a61e0a55e606319cadb330077824deb2d0d8462e180b27ec6a7567e.wasm", + "tx_init_validator.wasm": "tx_init_validator.40167452486f583c6869e5c6e3a67cf2900dd6e4a4c9d022d6bcc8b0d2187277.wasm", "tx_reveal_pk.wasm": "tx_reveal_pk.01442c5045ff10d7da05b1843803db99d23a6992594b2c3eb83955f6af9a26fb.wasm", "tx_transfer.wasm": "tx_transfer.f50a99b865d57c95ccfaec95963e87ba61f3a2d9f9fe7c0ab3cd9b09e0095d9c.wasm", - "tx_unbond.wasm": "tx_unbond.b081405d6a7bacf1aabf8f650014fda97b8cca9ae9d3a9d6746f601600f0d563.wasm", - "tx_unjail_validator.wasm": "tx_unjail_validator.87f90fb263cb9eee693bea861ed5a3b797b075c0164b7ac1f9a1611d661bffb4.wasm", + "tx_unbond.wasm": "tx_unbond.bcb85b7bc0cb9b0b562142073f24476358ada48044d6d812473ec68d9a19548b.wasm", + "tx_unjail_validator.wasm": "tx_unjail_validator.c935555c22240363f1ada75ba12ed878e2acad8df18f0a237d9c3a2c657591d1.wasm", "tx_update_vp.wasm": "tx_update_vp.78e3064cd6f24b376ce7aa85611e9b9f77cee6d6629b4849d6a60cb12017e1f4.wasm", "tx_vote_proposal.wasm": "tx_vote_proposal.36047c640ca8a10a62811cc15ba6e054e639313adf69c70e80e428e152c972cb.wasm", - "tx_withdraw.wasm": "tx_withdraw.9ad086dbcee5bdbfc915730c58f5c9b897cdfdd7192ffc839c3c8fd3adbbbe88.wasm", - "vp_implicit.wasm": "vp_implicit.16bb18c3b7973747a6f9581b769fff007f9189ecde87d2aeeb2317fb0abd1acb.wasm", + "tx_withdraw.wasm": "tx_withdraw.bfc1b7c657a9b9c96c9bfcf38a07755ef0101816af2e5d6d7b0bc32a2d3559d2.wasm", + "vp_implicit.wasm": "vp_implicit.2c4d317f6aeb7f6d26d8b414b6b7d4c15bb656b342d7fdf224cbc28b47000395.wasm", "vp_masp.wasm": "vp_masp.70bcfc40b3d9e9f792f298ba2e0c5e60fb44b4d1e4152635b2236b4a59faf235.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.7b8ea312ee9820c6129a861067e881cbd9212275d48a9ffb9bffe2dcf6576b31.wasm", - "vp_token.wasm": "vp_token.ec4f3914d074168f7ecbd43d5587e014381d409b3df4db4f63eaf73d3a56cdd6.wasm", - "vp_user.wasm": "vp_user.fd9810232a2ec79ff3f9f8fc70a6427ce9d07a9ef51c1468a785247a9b08b337.wasm", - "vp_validator.wasm": "vp_validator.8ce4c52a53aa451459e37ec560aa56ac4083d8829b0c29168c448e1e9d764c22.wasm" -} + "vp_testnet_faucet.wasm": "vp_testnet_faucet.276f8f7c508ad8e8eb99f2dc307f22da2a238e2f238f55815cecfca3de383b69.wasm", + "vp_token.wasm": "vp_token.f6fd7224d538a2f5739512ddbf1e39e09fc1822bcba21e7983362157fae3568a.wasm", + "vp_user.wasm": "vp_user.4b6fcf1ec7ee50c48ea3c09b07223b0663e826a34bfe3daa2980427b598ab713.wasm", + "vp_validator.wasm": "vp_validator.f18538bd8992188cb61ef2f33303fb07ff24935b8fce39edd831d4f9ea58a85c.wasm" +} \ No newline at end of file From 23b4af456c6c2902fd2305ef834fb0bb9890e777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 19 Jul 2023 16:04:50 +0100 Subject: [PATCH 111/120] fixup! Merge branch 'origin/yuji/ibc-multitoken' (#1693) --- shared/src/ledger/native_vp/multitoken.rs | 2 -- wasm/wasm_source/src/vp_testnet_faucet.rs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/shared/src/ledger/native_vp/multitoken.rs b/shared/src/ledger/native_vp/multitoken.rs index a932714240..5b88c0f152 100644 --- a/shared/src/ledger/native_vp/multitoken.rs +++ b/shared/src/ledger/native_vp/multitoken.rs @@ -45,8 +45,6 @@ where { type Error = Error; - const ADDR: InternalAddress = InternalAddress::Multitoken; - fn validate_tx( &self, _tx: &Tx, diff --git a/wasm/wasm_source/src/vp_testnet_faucet.rs b/wasm/wasm_source/src/vp_testnet_faucet.rs index f7c902af1c..5905c3cc4c 100644 --- a/wasm/wasm_source/src/vp_testnet_faucet.rs +++ b/wasm/wasm_source/src/vp_testnet_faucet.rs @@ -52,7 +52,7 @@ fn validate_tx( ctx.read_post(key)?.unwrap_or_default(); let change = post.change() - pre.change(); let maybe_denom = - storage_api::token::read_denom(&ctx.pre(), token, None)?; + storage_api::token::read_denom(&ctx.pre(), token)?; if maybe_denom.is_none() { debug_log!( "A denomination for token address {} does not exist \ From f166f0c222ff36b415deb6d4068cf5bf42b4f461 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 19 Jul 2023 16:22:05 +0100 Subject: [PATCH 112/120] wasm: update checksums --- wasm/checksums.json | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/wasm/checksums.json b/wasm/checksums.json index d234911742..e31501f0a0 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,22 +1,21 @@ { - "tx_bond.wasm": "tx_bond.31a145f2c40142d3c1eb7befb4d9e4962689fe920e32595199a206629f119180.wasm", - "tx_bridge_pool.wasm": "tx_bridge_pool.8c942b7e6a49562ff20770ac6e04df85188b49fabf4ca7f82fa3a5986a66a363.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.0608a4e73e0323518e6b1e58c99d0eb6785420faa7cb51c674c264dd361b757d.wasm", - "tx_ibc.wasm": "tx_ibc.75a38e6a94a34046ef1ba2e224a3db2014bc0a3f0993bd0ec4674f287fb37b13.wasm", - "tx_init_account.wasm": "tx_init_account.f979613d2b8b540ad471c663ec1aa3d9fad085ba7b1b059e2564c7a1eb5fa139.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.9a6c2aa5771fd08f26fb4d56ceb361c723d4b617da0ca2ab1a44c2b07f3b58b0.wasm", - "tx_init_validator.wasm": "tx_init_validator.40167452486f583c6869e5c6e3a67cf2900dd6e4a4c9d022d6bcc8b0d2187277.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.01442c5045ff10d7da05b1843803db99d23a6992594b2c3eb83955f6af9a26fb.wasm", - "tx_transfer.wasm": "tx_transfer.f50a99b865d57c95ccfaec95963e87ba61f3a2d9f9fe7c0ab3cd9b09e0095d9c.wasm", - "tx_unbond.wasm": "tx_unbond.bcb85b7bc0cb9b0b562142073f24476358ada48044d6d812473ec68d9a19548b.wasm", - "tx_unjail_validator.wasm": "tx_unjail_validator.c935555c22240363f1ada75ba12ed878e2acad8df18f0a237d9c3a2c657591d1.wasm", - "tx_update_vp.wasm": "tx_update_vp.78e3064cd6f24b376ce7aa85611e9b9f77cee6d6629b4849d6a60cb12017e1f4.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.36047c640ca8a10a62811cc15ba6e054e639313adf69c70e80e428e152c972cb.wasm", - "tx_withdraw.wasm": "tx_withdraw.bfc1b7c657a9b9c96c9bfcf38a07755ef0101816af2e5d6d7b0bc32a2d3559d2.wasm", - "vp_implicit.wasm": "vp_implicit.2c4d317f6aeb7f6d26d8b414b6b7d4c15bb656b342d7fdf224cbc28b47000395.wasm", - "vp_masp.wasm": "vp_masp.70bcfc40b3d9e9f792f298ba2e0c5e60fb44b4d1e4152635b2236b4a59faf235.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.276f8f7c508ad8e8eb99f2dc307f22da2a238e2f238f55815cecfca3de383b69.wasm", - "vp_token.wasm": "vp_token.f6fd7224d538a2f5739512ddbf1e39e09fc1822bcba21e7983362157fae3568a.wasm", - "vp_user.wasm": "vp_user.4b6fcf1ec7ee50c48ea3c09b07223b0663e826a34bfe3daa2980427b598ab713.wasm", - "vp_validator.wasm": "vp_validator.f18538bd8992188cb61ef2f33303fb07ff24935b8fce39edd831d4f9ea58a85c.wasm" + "tx_bond.wasm": "tx_bond.80194e8ff6602b6c00569e1200a25aab2af74dbf42b3b60db260e1d61d781294.wasm", + "tx_bridge_pool.wasm": "tx_bridge_pool.fba7fdec6a314696fa5b2b1ce6618c4d5d693c23d9712c77bae63538b01b6a6e.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.ea48bc666b0543abbb572a4f4d9006395364ef5cbc616bb07e1e3e88c11f76d6.wasm", + "tx_ibc.wasm": "tx_ibc.3e8c53cee14023125589b1f847ba153a7e2dff4afb4f0cd31a0841527be1816d.wasm", + "tx_init_account.wasm": "tx_init_account.41c10ff88a435c160c54f1301cea8972dc51551f37afeaaba6691bffab9a85d8.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.316b4c5009892b6edc587e912ebda6c5bea050ef2af6d101a5a006b2d1011872.wasm", + "tx_init_validator.wasm": "tx_init_validator.4ea9662e1793d49bd66547d397438fb015cbf9f546fb115b0d2abd3c41dc08fe.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.a903df5bb89910d6b98bf8c6bd14a560c4134e668b5fcb4cc0dfc088ac7e3568.wasm", + "tx_transfer.wasm": "tx_transfer.71fee59101ce7883b28cdc3f5c1c8151e878bd0b3363f26e2c1122bb20071c74.wasm", + "tx_unbond.wasm": "tx_unbond.6a1bbf108746f0ebd83df98399cab88f52de8d6312b63b6f3c5ad78a304d5239.wasm", + "tx_unjail_validator.wasm": "tx_unjail_validator.a8ba7519b642531a9fbcd6d637e6e24d35329559177d39e7559127d985f3fd46.wasm", + "tx_update_vp.wasm": "tx_update_vp.9ccfa2aff2b98d78b8a176ff26de417904fec1a08dc1198b5805fd8c7320336a.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.aa458ba0411b81fcd2e27e0aff128f2765de799ae9e734928b68cda559715755.wasm", + "tx_withdraw.wasm": "tx_withdraw.e781e1d387bce20a854d90f9dc192ba031e2f8a3d1af4aee3d9aad48d36c9e85.wasm", + "vp_implicit.wasm": "vp_implicit.6332320fbbf3a90ae25e18c1c0a04798030c4d2f4a1fb3f0091b6cb84bc6782c.wasm", + "vp_masp.wasm": "vp_masp.a73e26a4791783f326b35b23a8c4ba57011bf585a69139288f280166ede8f308.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.757f0e7b8be70141bfb142773e8a8946c96e68063ecb61d080c63392b64aacdc.wasm", + "vp_user.wasm": "vp_user.f150756b841643b2b4c4d26c790430eaf3aee343e584760fcdc8e687f6ed80c6.wasm", + "vp_validator.wasm": "vp_validator.cf7e9f48685b444e43cdd23d3f1d89c1a73fc9b8247f10032c199c802f8b9d93.wasm" } \ No newline at end of file From bc39e053e84461ed8a2375ba67e2d2cbac2e971b Mon Sep 17 00:00:00 2001 From: Gianmarco Fraccaroli Date: Thu, 20 Jul 2023 11:22:18 +0200 Subject: [PATCH 113/120] core: added multisignature tx format, refacctor account storage api, added max_signature_per_transaction genesis parameter --- core/src/ledger/parameters/mod.rs | 45 ++++ core/src/ledger/parameters/storage.rs | 21 ++ core/src/ledger/storage/mod.rs | 1 + core/src/ledger/storage_api/account.rs | 101 +++++++++ core/src/ledger/storage_api/key.rs | 24 +- core/src/ledger/storage_api/mod.rs | 1 + core/src/proto/mod.rs | 4 +- core/src/proto/types.rs | 292 +++++++++++++++++++++++-- core/src/types/account.rs | 94 ++++++++ core/src/types/key/mod.rs | 75 +++++-- core/src/types/mod.rs | 1 + core/src/types/transaction/account.rs | 52 +++++ core/src/types/transaction/mod.rs | 82 +------ core/src/types/transaction/pos.rs | 40 ++++ 14 files changed, 702 insertions(+), 131 deletions(-) create mode 100644 core/src/ledger/storage_api/account.rs create mode 100644 core/src/types/account.rs create mode 100644 core/src/types/transaction/account.rs diff --git a/core/src/ledger/parameters/mod.rs b/core/src/ledger/parameters/mod.rs index c507e55d49..03d27fa2da 100644 --- a/core/src/ledger/parameters/mod.rs +++ b/core/src/ledger/parameters/mod.rs @@ -46,6 +46,8 @@ pub struct Parameters { pub implicit_vp_code_hash: Hash, /// 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 gain p (read only) pub pos_gain_p: Dec, /// PoS gain d (read only) @@ -117,6 +119,7 @@ impl Parameters { tx_whitelist, implicit_vp_code_hash, epochs_per_year, + max_signatures_per_transaction, pos_gain_p, pos_gain_d, staked_ratio, @@ -168,6 +171,13 @@ impl Parameters { 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 pos_gain_p_key = storage::get_pos_gain_p_key(); storage.write(&pos_gain_p_key, pos_gain_p)?; @@ -197,6 +207,17 @@ impl Parameters { } } +/// Get the max signatures per transactio parameter +pub fn max_signatures_per_transaction( + storage: &S, +) -> storage_api::Result> +where + S: StorageRead, +{ + let key = storage::get_max_signatures_per_transaction_key(); + storage.read(&key) +} + /// Update the max_expected_time_per_block parameter in storage. Returns the /// parameters and gas cost. pub fn update_max_expected_time_per_block_parameter( @@ -340,6 +361,20 @@ where storage.write_bytes(&key, implicit_vp) } +/// Update the max signatures per transaction storage parameter +pub fn update_max_signature_per_tx( + storage: &mut S, + value: u8, +) -> storage_api::Result<()> +where + S: StorageRead + StorageWrite, +{ + let key = storage::get_max_signatures_per_transaction_key(); + // Using `fn write_bytes` here, because implicit_vp doesn't need to be + // encoded, it's bytes already. + storage.write(&key, value) +} + /// Read the the epoch duration parameter from store pub fn read_epoch_duration_parameter( storage: &S, @@ -434,6 +469,15 @@ where .ok_or(ReadError::ParametersMissing) .into_storage_result()?; + // read the maximum signatures per transaction + let max_signatures_per_transaction_key = + storage::get_max_signatures_per_transaction_key(); + let value: Option = + storage.read(&max_signatures_per_transaction_key)?; + let max_signatures_per_transaction: u8 = value + .ok_or(ReadError::ParametersMissing) + .into_storage_result()?; + // read PoS gain P let pos_gain_p_key = storage::get_pos_gain_p_key(); let value = storage.read(&pos_gain_p_key)?; @@ -478,6 +522,7 @@ where tx_whitelist, implicit_vp_code_hash, epochs_per_year, + max_signatures_per_transaction, pos_gain_p, pos_gain_d, staked_ratio, diff --git a/core/src/ledger/parameters/storage.rs b/core/src/ledger/parameters/storage.rs index 94498e3578..cdb484dd32 100644 --- a/core/src/ledger/parameters/storage.rs +++ b/core/src/ledger/parameters/storage.rs @@ -44,6 +44,7 @@ struct Keys { max_proposal_bytes: &'static str, faucet_account: &'static str, wrapper_tx_fees: &'static str, + max_signatures_per_transaction: &'static str, } /// Returns if the key is a parameter key. @@ -119,6 +120,14 @@ pub fn is_max_proposal_bytes_key(key: &Key) -> bool { is_max_proposal_bytes_key_at_addr(key, &ADDRESS) } +/// Returns if the key is the max signature per transacton key +pub fn is_max_signatures_per_transaction_key(key: &Key) -> bool { + matches!(&key.segments[..], [ + DbKeySeg::AddressSeg(addr), + DbKeySeg::StringSeg(max_signatures_per_transaction), + ] if addr == &ADDRESS && max_signatures_per_transaction == Keys::VALUES.max_signatures_per_transaction) +} + /// Storage key used for epoch parameter. pub fn get_epoch_duration_storage_key() -> Key { get_epoch_duration_key_at_addr(ADDRESS) @@ -183,3 +192,15 @@ pub fn get_faucet_account_key() -> Key { pub fn get_wrapper_tx_fees_key() -> Key { get_wrapper_tx_fees_key_at_addr(ADDRESS) } + +/// Storage key used for the max signatures per transaction key +pub fn get_max_signatures_per_transaction_key() -> Key { + Key { + segments: vec![ + DbKeySeg::AddressSeg(ADDRESS), + DbKeySeg::StringSeg( + Keys::VALUES.max_signatures_per_transaction.to_string(), + ), + ], + } +} diff --git a/core/src/ledger/storage/mod.rs b/core/src/ledger/storage/mod.rs index 5aa3d0cf80..5d022eadb1 100644 --- a/core/src/ledger/storage/mod.rs +++ b/core/src/ledger/storage/mod.rs @@ -1224,6 +1224,7 @@ mod tests { tx_whitelist: vec![], implicit_vp_code_hash: Hash::zero(), epochs_per_year: 100, + max_signatures_per_transaction: 15, pos_gain_p: Dec::new(1,1).expect("Cannot fail"), pos_gain_d: Dec::new(1,1).expect("Cannot fail"), staked_ratio: Dec::new(1,1).expect("Cannot fail"), diff --git a/core/src/ledger/storage_api/account.rs b/core/src/ledger/storage_api/account.rs new file mode 100644 index 0000000000..69d348710d --- /dev/null +++ b/core/src/ledger/storage_api/account.rs @@ -0,0 +1,101 @@ +//! Cryptographic signature keys storage API + +use super::*; +use crate::types::account::AccountPublicKeysMap; +use crate::types::address::Address; +use crate::types::key::*; +use crate::types::storage::Key; + +/// Init the subspace of a new account +pub fn init_account_storage( + storage: &mut S, + owner: &Address, + public_keys: &[common::PublicKey], + threshold: u8, +) -> Result<()> +where + S: StorageWrite + StorageRead, +{ + for (index, public_key) in public_keys.iter().enumerate() { + let index = index as u8; + pks_handle(owner).insert(storage, index, public_key.clone())?; + } + let threshold_key = threshold_key(owner); + storage.write(&threshold_key, threshold) +} + +/// Get the threshold associated with an account +pub fn threshold(storage: &S, owner: &Address) -> Result> +where + S: StorageRead, +{ + let threshold_key = threshold_key(owner); + storage.read(&threshold_key) +} + +/// Get the public keys index map associated with an account +pub fn public_keys( + storage: &S, + owner: &Address, +) -> Result> +where + S: StorageRead, +{ + let public_keys = pks_handle(owner) + .iter(storage)? + .filter_map(|data| match data { + Ok((_index, public_key)) => Some(public_key), + Err(_) => None, + }) + .collect::>(); + + Ok(public_keys) +} + +/// Get the public key index map associated with an account +pub fn public_keys_index_map( + storage: &S, + owner: &Address, +) -> Result +where + S: StorageRead, +{ + let public_keys = public_keys(storage, owner)?; + + Ok(AccountPublicKeysMap::from_iter(public_keys)) +} + +/// Check if an account exists in storage +pub fn exists(storage: &S, owner: &Address) -> Result +where + S: StorageRead, +{ + let vp_key = Key::validity_predicate(owner); + storage.has_key(&vp_key) +} + +/// Set public key at specific index +pub fn set_public_key_at( + storage: &mut S, + owner: &Address, + public_key: &common::PublicKey, + index: u8, +) -> Result<()> +where + S: StorageWrite + StorageRead, +{ + pks_handle(owner).insert(storage, index, public_key.clone())?; + Ok(()) +} + +/// Clear the public keys account subtorage space +pub fn clear_public_keys(storage: &mut S, owner: &Address) -> Result<()> +where + S: StorageWrite + StorageRead, +{ + let total_pks = pks_handle(owner).len(storage)?; + for index in 0..total_pks as u8 { + pks_handle(owner).remove(storage, &index)?; + } + Ok(()) +} diff --git a/core/src/ledger/storage_api/key.rs b/core/src/ledger/storage_api/key.rs index 6e3eba64aa..69bdd50720 100644 --- a/core/src/ledger/storage_api/key.rs +++ b/core/src/ledger/storage_api/key.rs @@ -4,23 +4,17 @@ use super::*; use crate::types::address::Address; use crate::types::key::*; -/// Get the public key associated with the given address. Returns `Ok(None)` if -/// not found. -pub fn get(storage: &S, owner: &Address) -> Result> -where - S: StorageRead, -{ - let key = pk_key(owner); - storage.read(&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, pk: &common::PublicKey) -> Result<()> +pub fn reveal_pk( + storage: &mut S, + public_key: &common::PublicKey, +) -> Result<()> where - S: StorageWrite, + S: StorageWrite + StorageRead, { - let addr: Address = pk.into(); - let key = pk_key(&addr); - storage.write(&key, pk) + let owner: Address = public_key.into(); + pks_handle(&owner).insert(storage, 0, public_key.clone())?; + + Ok(()) } diff --git a/core/src/ledger/storage_api/mod.rs b/core/src/ledger/storage_api/mod.rs index 451996d0e5..f97478e0d6 100644 --- a/core/src/ledger/storage_api/mod.rs +++ b/core/src/ledger/storage_api/mod.rs @@ -1,6 +1,7 @@ //! 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 governance; diff --git a/core/src/proto/mod.rs b/core/src/proto/mod.rs index 27ae37ff69..6eca982087 100644 --- a/core/src/proto/mod.rs +++ b/core/src/proto/mod.rs @@ -4,8 +4,8 @@ pub mod generated; mod types; pub use types::{ - Code, Commitment, Data, Dkg, Error, Header, MaspBuilder, Section, Signable, - SignableEthMessage, Signature, Signed, Tx, TxError, + Code, Commitment, Data, Dkg, Error, Header, MaspBuilder, MultiSignature, + Section, Signable, SignableEthMessage, Signature, Signed, Tx, TxError, }; #[cfg(test)] diff --git a/core/src/proto/types.rs b/core/src/proto/types.rs index 69da19f58d..be20621b43 100644 --- a/core/src/proto/types.rs +++ b/core/src/proto/types.rs @@ -1,8 +1,12 @@ use std::borrow::Cow; -use std::collections::{HashMap, HashSet}; +use std::cmp::Ordering; +use std::collections::{BTreeSet, HashMap, HashSet}; use std::convert::TryFrom; +use std::env; +use std::fs::File; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; +use std::path::PathBuf; #[cfg(feature = "ferveo-tpke")] use ark_ec::AffineCurve; @@ -24,6 +28,7 @@ use super::generated::types; use crate::ledger::storage::{KeccakHasher, Sha256Hasher, StorageHasher}; #[cfg(any(feature = "tendermint", feature = "tendermint-abcipp"))] use crate::tendermint_proto::abci::ResponseDeliverTx; +use crate::types::account::AccountPublicKeysMap; use crate::types::address::Address; use crate::types::chain::ChainId; use crate::types::keccak::{keccak_hash, KeccakHash}; @@ -57,6 +62,12 @@ pub enum Error { 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, } pub type Result = std::result::Result; @@ -360,6 +371,146 @@ impl Code { } } +#[derive( + Clone, + Debug, + BorshSerialize, + BorshDeserialize, + BorshSchema, + Serialize, + Deserialize, + Eq, + PartialEq, +)] +pub struct SignatureIndex { + pub signature: common::Signature, + pub index: u8, +} + +impl SignatureIndex { + pub fn from_single_signature(signature: common::Signature) -> Self { + Self { + signature, + index: 0, + } + } + + pub fn to_vec(&self) -> Vec { + vec![self.clone()] + } + + pub fn verify( + &self, + public_key_index_map: &AccountPublicKeysMap, + data: &impl SignableBytes, + ) -> std::result::Result<(), VerifySigError> { + let public_key = + public_key_index_map.get_public_key_from_index(self.index); + if let Some(public_key) = public_key { + common::SigScheme::verify_signature( + &public_key, + data, + &self.signature, + ) + } else { + Err(VerifySigError::MissingData) + } + } +} + +impl Ord for SignatureIndex { + fn cmp(&self, other: &Self) -> Ordering { + self.index.cmp(&other.index) + } +} + +impl PartialOrd for SignatureIndex { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +/// A section representing a multisig over another section +#[derive( + Clone, + Debug, + BorshSerialize, + BorshDeserialize, + BorshSchema, + Serialize, + Deserialize, +)] +pub struct MultiSignature { + /// The hash of the section being signed + targets: Vec, + /// The signature over the above hash + pub signatures: BTreeSet, +} + +impl MultiSignature { + /// Sign the given section hash with the given key and return a section + pub fn new( + targets: Vec, + secret_keys: &[common::SecretKey], + public_keys_index_map: &AccountPublicKeysMap, + ) -> Self { + let target = Self { + targets: targets.clone(), + signatures: BTreeSet::new(), + } + .get_hash(); + + let signatures_public_keys_map = + secret_keys.iter().map(|secret_key: &common::SecretKey| { + let signature = common::SigScheme::sign(secret_key, target); + let public_key = secret_key.ref_to(); + (public_key, signature) + }); + + let signatures = signatures_public_keys_map + .filter_map(|(public_key, signature)| { + let public_key_index = public_keys_index_map + .get_index_from_public_key(&public_key); + public_key_index + .map(|index| SignatureIndex { signature, index }) + }) + .collect::>(); + + Self { + targets, + signatures, + } + } + + pub fn total_signatures(&self) -> u8 { + self.signatures.len() as u8 + } + + /// Hash this signature section + pub fn hash<'a>(&self, hasher: &'a mut Sha256) -> &'a mut Sha256 { + hasher.update( + self.try_to_vec() + .expect("unable to serialize multisignature section"), + ); + hasher + } + + /// Get the hash of this section + pub fn get_hash(&self) -> crate::types::hash::Hash { + crate::types::hash::Hash( + self.hash(&mut Sha256::new()).finalize_reset().into(), + ) + } + + pub fn get_raw_hash(&self) -> crate::types::hash::Hash { + Self { + signatures: BTreeSet::new(), + ..self.clone() + } + .get_hash() + } +} + /// A section representing the signature over another section #[derive( Clone, @@ -371,26 +522,19 @@ impl Code { Deserialize, )] pub struct Signature { - /// Additional random data - salt: [u8; 8], /// The hash of the section being signed targets: Vec, - /// The public key to verify the below signature - pub_key: common::PublicKey, /// The signature over the above hashes pub signature: Option, } impl Signature { - /// Sign the given section hash with the given key and return a section pub fn new( targets: Vec, sec_key: &common::SecretKey, ) -> Self { let mut sec = Self { - salt: DateTimeUtc::now().0.timestamp_millis().to_le_bytes(), targets, - pub_key: sec_key.ref_to(), signature: None, }; sec.signature = Some(common::SigScheme::sign(sec_key, sec.get_hash())); @@ -414,11 +558,14 @@ impl Signature { } /// Verify that the signature contained in this section is valid - pub fn verify_signature(&self) -> std::result::Result<(), VerifySigError> { + pub fn verify_signature( + &self, + public_key: &common::PublicKey, + ) -> std::result::Result<(), VerifySigError> { let signature = self.signature.as_ref().ok_or(VerifySigError::MissingData)?; common::SigScheme::verify_signature( - &self.pub_key, + public_key, &Self { signature: None, ..self.clone() @@ -722,6 +869,7 @@ impl borsh::BorshSchema for MaspBuilder { Serialize, Deserialize, )] +#[allow(clippy::large_enum_variant)] pub enum Section { /// Transaction data that needs to be sent to hardware wallets Data(Data), @@ -730,6 +878,8 @@ pub enum Section { /// Transaction code. Sending to hardware wallets optional Code(Code), /// A transaction signature. Often produced by hardware wallets + SectionSignature(MultiSignature), + /// A transaction header/protocol signature Signature(Signature), /// Ciphertext obtained by encrypting arbitrary transaction sections Ciphertext(Ciphertext), @@ -759,7 +909,8 @@ impl Section { Self::Data(data) => data.hash(hasher), Self::ExtraData(extra) => extra.hash(hasher), Self::Code(code) => code.hash(hasher), - Self::Signature(sig) => sig.hash(hasher), + Self::Signature(signature) => signature.hash(hasher), + Self::SectionSignature(signatures) => signatures.hash(hasher), Self::Ciphertext(ct) => ct.hash(hasher), Self::MaspBuilder(mb) => mb.hash(hasher), Self::MaspTx(tx) => { @@ -831,6 +982,15 @@ impl Section { } } + /// Extract the section signature from this section if possible + pub fn section_signature(&self) -> Option { + if let Self::SectionSignature(data) = self { + Some(data.clone()) + } else { + None + } + } + /// Extract the ciphertext from this section if possible pub fn ciphertext(&self) -> Option { if let Self::Ciphertext(data) = self { @@ -987,6 +1147,24 @@ impl Tx { } } + /// Dump to file + // TODO: refactor + pub fn dump(&self, path: Option) -> Result { + let path = path + .unwrap_or(env::current_dir().expect( + "Should be able to get the current working directory.", + )); + let tx_filename = format!("{}.tx", self.header_hash()); + let tx_filename_clone_error = tx_filename.clone(); + let tx_filename_clone_ok = tx_filename.clone(); + let out = File::create(path.join(tx_filename)).unwrap(); + serde_json::to_writer_pretty(out, &self) + .map_err(|_| { + Error::InvalidJSONDeserialization(tx_filename_clone_error) + }) + .map(|_| path.join(tx_filename_clone_ok)) + } + /// Get the transaction header pub fn header(&self) -> Header { self.header.clone() @@ -1105,36 +1283,102 @@ impl Tx { bytes } + /// Verify that the section with the given hash has been signed by the given + /// public key + pub fn verify_section_signatures( + &self, + hashes: &[crate::types::hash::Hash], + public_keys_index_map: AccountPublicKeysMap, + threshold: u8, + max_signatures: Option, + ) -> std::result::Result<(), Error> { + let max_signatures = max_signatures.unwrap_or(u8::MAX); + let mut valid_signatures = 0; + + for section in &self.sections { + if let Section::SectionSignature(signatures) = section { + if !hashes.iter().all(|x| { + signatures.targets.contains(x) || section.get_hash() == *x + }) { + return Err(Error::InvalidSectionSignature( + "missing target hash.".to_string(), + )); + } + + for target in &signatures.targets { + if self.get_section(target).is_none() { + return Err(Error::InvalidSectionSignature( + "Missing target section.".to_string(), + )); + } + } + + if signatures.total_signatures() > max_signatures { + return Err(Error::InvalidSectionSignature( + "too many signatures.".to_string(), + )); + } + + if signatures.total_signatures() < threshold { + return Err(Error::InvalidSectionSignature( + "too few signatures.".to_string(), + )); + } + + for signature_index in &signatures.signatures { + let is_valid_signature = signature_index + .verify( + &public_keys_index_map, + &signatures.get_raw_hash(), + ) + .is_ok(); + if is_valid_signature { + valid_signatures += 1; + } + if valid_signatures >= threshold { + return Ok(()); + } + } + } + } + Err(Error::InvalidSectionSignature( + "invalid signatures.".to_string(), + )) + } + /// Verify that the sections with the given hashes have been signed together /// by the given public key. I.e. this function looks for one signature that /// covers over the given slice of hashes. pub fn verify_signature( &self, - pk: &common::PublicKey, + public_key: &common::PublicKey, hashes: &[crate::types::hash::Hash], - ) -> std::result::Result<&Signature, VerifySigError> { + ) -> Result<&Signature> { for section in &self.sections { - if let Section::Signature(sig_sec) = section { - // Check that the signer is matched and that the hashes being + if let Section::Signature(signature) = section { + // Check that the hashes being // checked are a subset of those in this section - if sig_sec.pub_key == *pk - && hashes.iter().all(|x| { - sig_sec.targets.contains(x) || section.get_hash() == *x - }) - { + if hashes.iter().all(|x| { + signature.targets.contains(x) || section.get_hash() == *x + }) { // Ensure that all the sections the signature signs over are // present - for target in &sig_sec.targets { + for target in &signature.targets { if self.get_section(target).is_none() { - return Err(VerifySigError::MissingData); + return Err(Error::InvalidSectionSignature( + "Target section is missing.".to_string(), + )); } } // Finally verify that the signature itself is valid - return sig_sec.verify_signature().map(|_| sig_sec); + return signature + .verify_signature(public_key) + .map(|_| signature) + .map_err(|_| Error::InvalidWrapperSignature); } } } - Err(VerifySigError::MissingData) + Err(Error::InvalidWrapperSignature) } /// Validate any and all ciphertexts stored in this transaction diff --git a/core/src/types/account.rs b/core/src/types/account.rs new file mode 100644 index 0000000000..95b8b6e2d3 --- /dev/null +++ b/core/src/types/account.rs @@ -0,0 +1,94 @@ +//! Helper structures to manage accounts + +use std::collections::HashMap; + +use borsh::{BorshDeserialize, BorshSerialize}; +use serde::{Deserialize, Serialize}; + +use super::address::Address; +use super::key::common; + +#[derive( + Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, +)] +/// Account data +pub struct Account { + /// The map between indexes and public keys for an account + pub public_keys_map: AccountPublicKeysMap, + /// The account signature threshold + pub threshold: u8, + /// The address corresponding to the account owner + pub address: Address, +} + +impl Account { + /// Retrive a public key from the index + pub fn get_public_key_from_index( + &self, + index: u8, + ) -> Option { + self.public_keys_map.get_public_key_from_index(index) + } + + /// Retrive the index of a public key + pub fn get_index_from_public_key( + &self, + public_key: &common::PublicKey, + ) -> Option { + self.public_keys_map.get_index_from_public_key(public_key) + } +} + +#[derive( + Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, +)] +/// 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() { + 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() + } + + /// Return an empty AccountPublicKeysMap + pub fn empty() -> Self { + AccountPublicKeysMap { + pk_to_idx: HashMap::new(), + idx_to_pk: HashMap::new(), + } + } +} diff --git a/core/src/types/key/mod.rs b/core/src/types/key/mod.rs index 8c59262f72..685e3464f7 100644 --- a/core/src/types/key/mod.rs +++ b/core/src/types/key/mod.rs @@ -12,6 +12,8 @@ use std::str::FromStr; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use data_encoding::HEXUPPER; +use lazy_map::LazyMap; +use namada_macros::StorageKeys; #[cfg(feature = "rand")] use rand::{CryptoRng, RngCore}; use serde::Serialize; @@ -19,25 +21,45 @@ use sha2::{Digest, Sha256}; use thiserror::Error; use super::address::Address; -use super::storage::{self, DbKeySeg, Key, KeySeg}; +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; -const PK_STORAGE_KEY: &str = "public_key"; -const PROTOCOL_PK_STORAGE_KEY: &str = "protocol_public_key"; +/// 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 pk_key(owner: &Address) -> storage::Key { - Key::from(owner.to_db_key()) - .push(&PK_STORAGE_KEY.to_owned()) - .expect("Cannot obtain a storage 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()), + ], + } +} + +/// Object that 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_pk_key(key: &Key) -> Option<&Address> { +pub fn is_pks_key(key: &Key) -> Option<&Address> { match &key.segments[..] { - [DbKeySeg::AddressSeg(owner), DbKeySeg::StringSeg(key)] - if key == PK_STORAGE_KEY => + [ + 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) } @@ -45,18 +67,43 @@ pub fn is_pk_key(key: &Key) -> Option<&Address> { } } +/// Check if the given storage key is a threshol 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::from(owner.to_db_key()) - .push(&PROTOCOL_PK_STORAGE_KEY.to_owned()) - .expect("Cannot obtain a 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 == PROTOCOL_PK_STORAGE_KEY => + if key.as_str() == Keys::VALUES.protocol_public_keys => { Some(owner) } diff --git a/core/src/types/mod.rs b/core/src/types/mod.rs index c35b314296..8303e75742 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/core/src/types/transaction/account.rs b/core/src/types/transaction/account.rs new file mode 100644 index 0000000000..f2eaafe7ef --- /dev/null +++ b/core/src/types/transaction/account.rs @@ -0,0 +1,52 @@ +use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; +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, + Clone, + PartialEq, + BorshSerialize, + BorshDeserialize, + BorshSchema, + Serialize, + Deserialize, +)] +pub struct InitAccount { + /// Public keys to be written into the account's storage. This can be used + /// for signature verification of transactions for the newly created + /// account. + pub public_keys: Vec, + /// The VP code hash + pub vp_code_hash: Hash, + /// The account signature threshold + pub threshold: u8, +} + +/// A tx data type to update an account's validity predicate +#[derive( + Debug, + Clone, + PartialEq, + BorshSerialize, + BorshDeserialize, + BorshSchema, + Serialize, + Deserialize, +)] +pub struct UpdateAccount { + /// An address of the account + pub addr: Address, + /// The new VP code hash + pub vp_code_hash: Option, + /// Public keys to be written into the account's storage. This can be used + /// for signature verification of transactions for the newly created + /// account. + pub public_keys: Vec, + /// The account signature threshold + pub threshold: Option, +} diff --git a/core/src/types/transaction/mod.rs b/core/src/types/transaction/mod.rs index c6cc23dffa..88ded1970d 100644 --- a/core/src/types/transaction/mod.rs +++ b/core/src/types/transaction/mod.rs @@ -1,5 +1,7 @@ //! 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; @@ -7,6 +9,7 @@ pub mod decrypted; pub mod encrypted; /// txs to manage governance pub mod governance; +/// txs to manage pos pub mod pos; /// transaction protocols made by validators pub mod protocol; @@ -27,10 +30,8 @@ pub use wrapper::*; use crate::ledger::gas::VpsGas; use crate::types::address::Address; -use crate::types::dec::Dec; use crate::types::hash::Hash; use crate::types::ibc::IbcEvent; -use crate::types::key::*; use crate::types::storage; #[cfg(feature = "ferveo-tpke")] use crate::types::transaction::protocol::ProtocolTx; @@ -131,80 +132,6 @@ fn iterable_to_string( } } -/// A tx data type to update an account's validity predicate -#[derive( - Debug, - Clone, - PartialEq, - BorshSerialize, - BorshDeserialize, - BorshSchema, - Serialize, - Deserialize, -)] -pub struct UpdateVp { - /// An address of the account - pub addr: Address, - /// The new VP code hash - pub vp_code_hash: Hash, -} - -/// A tx data type to initialize a new established account -#[derive( - Debug, - Clone, - PartialEq, - BorshSerialize, - BorshDeserialize, - BorshSchema, - Serialize, - Deserialize, -)] -pub struct InitAccount { - /// Public key to be written into the account's storage. This can be used - /// for signature verification of transactions for the newly created - /// account. - pub public_key: common::PublicKey, - /// The VP code hash - pub vp_code_hash: Hash, -} - -/// A tx data type to initialize a new validator account. -#[derive( - Debug, - Clone, - PartialEq, - BorshSerialize, - BorshDeserialize, - BorshSchema, - Serialize, - Deserialize, -)] -pub struct InitValidator { - /// Public key to be written into the account's storage. This can be used - /// for signature verification of transactions for the newly created - /// account. - pub account_key: common::PublicKey, - /// A key to be used for signing blocks and votes on blocks. - pub consensus_key: common::PublicKey, - /// An Eth bridge governance public key - pub eth_cold_key: secp256k1::PublicKey, - /// An Eth bridge hot signing public key used for validator set updates and - /// cross-chain transactions - pub eth_hot_key: secp256k1::PublicKey, - /// Public key used to sign protocol transactions - pub protocol_key: common::PublicKey, - /// Serialization of the public session key used in the DKG - pub dkg_key: crate::types::key::dkg_session_keys::DkgPublicKey, - /// The initial commission rate charged for delegation rewards - pub commission_rate: Dec, - /// The maximum change allowed per epoch to the commission rate. This is - /// immutable once set here. - pub max_commission_rate_change: Dec, - /// The VP code for validator account - pub validator_vp_code_hash: Hash, -} - /// Struct that classifies that kind of Tx /// based on the contents of its data. #[derive( @@ -241,6 +168,7 @@ mod test_process_tx { 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; @@ -411,6 +339,8 @@ fn test_process_tx_decrypted_unsigned() { #[test] fn test_process_tx_decrypted_signed() { use crate::proto::{Code, Data, Section, Signature, Tx}; + use crate::types::key::*; + fn gen_keypair() -> common::SecretKey { use rand::prelude::ThreadRng; use rand::thread_rng; diff --git a/core/src/types/transaction/pos.rs b/core/src/types/transaction/pos.rs index d334047ffe..fa0e3d0891 100644 --- a/core/src/types/transaction/pos.rs +++ b/core/src/types/transaction/pos.rs @@ -5,8 +5,48 @@ use serde::{Deserialize, Serialize}; use crate::types::address::Address; use crate::types::dec::Dec; +use crate::types::hash::Hash; +use crate::types::key::{common, secp256k1}; use crate::types::token; +/// A tx data type to initialize a new validator account. +#[derive( + Debug, + Clone, + PartialEq, + BorshSerialize, + BorshDeserialize, + BorshSchema, + Serialize, + Deserialize, +)] +pub struct InitValidator { + /// Public key to be written into the account's storage. This can be used + /// for signature verification of transactions for the newly created + /// account. + pub account_keys: Vec, + /// The minimum number of signatures needed + pub threshold: u8, + /// A key to be used for signing blocks and votes on blocks. + pub consensus_key: common::PublicKey, + /// An Eth bridge governance public key + pub eth_cold_key: secp256k1::PublicKey, + /// An Eth bridge hot signing public key used for validator set updates and + /// cross-chain transactions + pub eth_hot_key: secp256k1::PublicKey, + /// Public key used to sign protocol transactions + pub protocol_key: common::PublicKey, + /// Serialization of the public session key used in the DKG + pub dkg_key: crate::types::key::dkg_session_keys::DkgPublicKey, + /// The initial commission rate charged for delegation rewards + pub commission_rate: Dec, + /// The maximum change allowed per epoch to the commission rate. This is + /// immutable once set here. + pub max_commission_rate_change: Dec, + /// The VP code for validator account + pub validator_vp_code_hash: Hash, +} + /// A bond is a validator's self-bond or a delegation from non-validator to a /// validator. #[derive( From 9f498bfde6aa11eccac1cc9af889f845df9f1ad8 Mon Sep 17 00:00:00 2001 From: Gianmarco Fraccaroli Date: Thu, 20 Jul 2023 11:25:08 +0200 Subject: [PATCH 114/120] apps,shared: added cli tx and query methods --- apps/src/bin/namada-client/cli.rs | 55 ++- apps/src/bin/namada/cli.rs | 2 +- apps/src/lib/cli.rs | 268 ++++++---- apps/src/lib/cli/utils.rs | 21 + apps/src/lib/client/mod.rs | 1 - apps/src/lib/client/rpc.rs | 41 +- apps/src/lib/client/tx.rs | 457 +++++++++++++----- apps/src/lib/config/genesis.rs | 9 + apps/src/lib/config/mod.rs | 2 +- apps/src/lib/node/ledger/shell/init_chain.rs | 34 +- apps/src/lib/node/ledger/shell/mod.rs | 32 -- .../lib/node/ledger/shell/process_proposal.rs | 14 +- shared/src/ledger/args.rs | 43 +- shared/src/ledger/eth_bridge/bridge_pool.rs | 5 +- shared/src/ledger/queries/shell.rs | 47 ++ shared/src/ledger/rpc.rs | 49 +- shared/src/ledger/signing.rs | 347 +++++-------- shared/src/ledger/tx.rs | 207 +++++--- 18 files changed, 1065 insertions(+), 569 deletions(-) diff --git a/apps/src/bin/namada-client/cli.rs b/apps/src/bin/namada-client/cli.rs index 609588045d..b07eab3691 100644 --- a/apps/src/bin/namada-client/cli.rs +++ b/apps/src/bin/namada-client/cli.rs @@ -67,7 +67,7 @@ pub async fn main() -> Result<()> { tx::submit_ibc_transfer::(&client, ctx, args) .await?; } - Sub::TxUpdateVp(TxUpdateVp(mut args)) => { + Sub::TxUpdateAccount(TxUpdateAccount(mut args)) => { let client = HttpClient::new(utils::take_config_address( &mut args.tx.ledger_address, )) @@ -76,8 +76,10 @@ pub async fn main() -> Result<()> { .await .proceed_or_else(error)?; let args = args.to_sdk(&mut ctx); - tx::submit_update_vp::(&client, &mut ctx, args) - .await?; + tx::submit_update_account::( + &client, &mut ctx, args, + ) + .await?; } Sub::TxInitAccount(TxInitAccount(mut args)) => { let client = HttpClient::new(utils::take_config_address( @@ -213,24 +215,39 @@ pub async fn main() -> Result<()> { .proceed_or_else(error)?; let args = args.to_sdk(&mut ctx); let tx_args = args.tx.clone(); - let (mut tx, addr, pk) = bridge_pool::build_bridge_pool_tx( - &client, - &mut ctx.wallet, - args, - ) - .await - .unwrap(); + let (mut tx, addr, public_keys) = + bridge_pool::build_bridge_pool_tx( + &client, + &mut ctx.wallet, + args, + ) + .await + .unwrap(); tx::submit_reveal_aux( &client, &mut ctx, &tx_args, - addr, - pk.clone(), + addr.clone(), + &public_keys, &mut tx, ) .await?; - signing::sign_tx(&mut ctx.wallet, &mut tx, &tx_args, &pk) - .await?; + let (account_public_keys_map, threshold) = + signing::aux_signing_data( + &client, + addr, + public_keys.clone(), + ) + .await; + signing::sign_tx( + &mut ctx.wallet, + &mut tx, + &tx_args, + &account_public_keys_map, + &public_keys, + threshold, + ) + .await?; sdk_tx::process_tx(&client, &mut ctx.wallet, &tx_args, tx) .await?; } @@ -433,6 +450,16 @@ pub async fn main() -> Result<()> { let args = args.to_sdk(&mut ctx); rpc::query_protocol_parameters(&client, args).await; } + Sub::QueryAccount(QueryAccount(args)) => { + let client = + HttpClient::new(args.query.ledger_address.clone()) + .unwrap(); + wait_until_node_is_synched(&client) + .await + .proceed_or_else(error)?; + let args = args.to_sdk(&mut ctx); + rpc::query_account(&client, args).await; + } } } cli::NamadaClient::WithoutContext(cmd, global_args) => match cmd { diff --git a/apps/src/bin/namada/cli.rs b/apps/src/bin/namada/cli.rs index 8c7a1e0b49..0259ba525a 100644 --- a/apps/src/bin/namada/cli.rs +++ b/apps/src/bin/namada/cli.rs @@ -47,7 +47,7 @@ fn handle_command(cmd: cli::cmds::Namada, raw_sub_cmd: String) -> Result<()> { | cli::cmds::Namada::TxCustom(_) | cli::cmds::Namada::TxTransfer(_) | cli::cmds::Namada::TxIbcTransfer(_) - | cli::cmds::Namada::TxUpdateVp(_) + | cli::cmds::Namada::TxUpdateAccount(_) | cli::cmds::Namada::TxRevealPk(_) | cli::cmds::Namada::TxInitProposal(_) | cli::cmds::Namada::TxVoteProposal(_) => { diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 06d6f3f406..87e91e3057 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -52,7 +52,7 @@ pub mod cmds { TxCustom(TxCustom), TxTransfer(TxTransfer), TxIbcTransfer(TxIbcTransfer), - TxUpdateVp(TxUpdateVp), + TxUpdateAccount(TxUpdateAccount), TxInitProposal(TxInitProposal), TxVoteProposal(TxVoteProposal), TxRevealPk(TxRevealPk), @@ -69,7 +69,7 @@ pub mod cmds { .subcommand(TxCustom::def()) .subcommand(TxTransfer::def()) .subcommand(TxIbcTransfer::def()) - .subcommand(TxUpdateVp::def()) + .subcommand(TxUpdateAccount::def()) .subcommand(TxInitProposal::def()) .subcommand(TxVoteProposal::def()) .subcommand(TxRevealPk::def()) @@ -87,7 +87,8 @@ pub mod cmds { let tx_transfer = SubCmd::parse(matches).map(Self::TxTransfer); let tx_ibc_transfer = SubCmd::parse(matches).map(Self::TxIbcTransfer); - let tx_update_vp = SubCmd::parse(matches).map(Self::TxUpdateVp); + let tx_update_account = + SubCmd::parse(matches).map(Self::TxUpdateAccount); let tx_init_proposal = SubCmd::parse(matches).map(Self::TxInitProposal); let tx_vote_proposal = @@ -101,7 +102,7 @@ pub mod cmds { .or(tx_custom) .or(tx_transfer) .or(tx_ibc_transfer) - .or(tx_update_vp) + .or(tx_update_account) .or(tx_init_proposal) .or(tx_vote_proposal) .or(tx_reveal_pk) @@ -208,7 +209,7 @@ pub mod cmds { .subcommand(TxCustom::def().display_order(1)) .subcommand(TxTransfer::def().display_order(1)) .subcommand(TxIbcTransfer::def().display_order(1)) - .subcommand(TxUpdateVp::def().display_order(1)) + .subcommand(TxUpdateAccount::def().display_order(1)) .subcommand(TxInitAccount::def().display_order(1)) .subcommand(TxRevealPk::def().display_order(1)) // Proposal transactions @@ -224,6 +225,7 @@ pub mod cmds { .subcommand(AddToEthBridgePool::def().display_order(3)) // Queries .subcommand(QueryEpoch::def().display_order(4)) + .subcommand(QueryAccount::def().display_order(4)) .subcommand(QueryTransfers::def().display_order(4)) .subcommand(QueryConversions::def().display_order(4)) .subcommand(QueryBlock::def().display_order(4)) @@ -247,7 +249,8 @@ pub mod cmds { let tx_custom = Self::parse_with_ctx(matches, TxCustom); let tx_transfer = Self::parse_with_ctx(matches, TxTransfer); let tx_ibc_transfer = Self::parse_with_ctx(matches, TxIbcTransfer); - let tx_update_vp = Self::parse_with_ctx(matches, TxUpdateVp); + let tx_update_account = + Self::parse_with_ctx(matches, TxUpdateAccount); let tx_init_account = Self::parse_with_ctx(matches, TxInitAccount); let tx_init_validator = Self::parse_with_ctx(matches, TxInitValidator); @@ -262,6 +265,7 @@ pub mod cmds { let unbond = Self::parse_with_ctx(matches, Unbond); let withdraw = Self::parse_with_ctx(matches, Withdraw); let query_epoch = Self::parse_with_ctx(matches, QueryEpoch); + let query_account = Self::parse_with_ctx(matches, QueryAccount); let query_transfers = Self::parse_with_ctx(matches, QueryTransfers); let query_conversions = Self::parse_with_ctx(matches, QueryConversions); @@ -288,7 +292,7 @@ pub mod cmds { tx_custom .or(tx_transfer) .or(tx_ibc_transfer) - .or(tx_update_vp) + .or(tx_update_account) .or(tx_init_account) .or(tx_reveal_pk) .or(tx_init_proposal) @@ -314,6 +318,7 @@ pub mod cmds { .or(query_proposal) .or(query_proposal_result) .or(query_protocol_parameters) + .or(query_account) .or(utils) } } @@ -355,7 +360,7 @@ pub mod cmds { TxTransfer(TxTransfer), TxIbcTransfer(TxIbcTransfer), QueryResult(QueryResult), - TxUpdateVp(TxUpdateVp), + TxUpdateAccount(TxUpdateAccount), TxInitAccount(TxInitAccount), TxInitValidator(TxInitValidator), TxCommissionRateChange(TxCommissionRateChange), @@ -367,6 +372,7 @@ pub mod cmds { Withdraw(Withdraw), AddToEthBridgePool(AddToEthBridgePool), QueryEpoch(QueryEpoch), + QueryAccount(QueryAccount), QueryTransfers(QueryTransfers), QueryConversions(QueryConversions), QueryBlock(QueryBlock), @@ -1217,15 +1223,15 @@ pub mod cmds { } #[derive(Clone, Debug)] - pub struct TxUpdateVp(pub args::TxUpdateVp); + pub struct TxUpdateAccount(pub args::TxUpdateAccount); - impl SubCmd for TxUpdateVp { - const CMD: &'static str = "update"; + impl SubCmd for TxUpdateAccount { + const CMD: &'static str = "update-account"; fn parse(matches: &ArgMatches) -> Option { - matches - .subcommand_matches(Self::CMD) - .map(|matches| TxUpdateVp(args::TxUpdateVp::parse(matches))) + matches.subcommand_matches(Self::CMD).map(|matches| { + TxUpdateAccount(args::TxUpdateAccount::parse(matches)) + }) } fn def() -> App { @@ -1234,7 +1240,7 @@ pub mod cmds { "Send a signed transaction to update account's validity \ predicate.", ) - .add_args::>() + .add_args::>() } } @@ -1358,6 +1364,28 @@ pub mod cmds { } } + #[derive(Clone, Debug)] + pub struct QueryAccount(pub args::QueryAccount); + + impl SubCmd for QueryAccount { + const CMD: &'static str = "query-account"; + + fn parse(matches: &ArgMatches) -> Option { + matches + .subcommand_matches(Self::CMD) + .map(|matches| QueryAccount(args::QueryAccount::parse(matches))) + } + + fn def() -> App { + App::new(Self::CMD) + .about( + "Query the substorage space of a specific enstablished \ + address.", + ) + .add_args::>() + } + } + #[derive(Clone, Debug)] pub struct QueryConversions(pub args::QueryConversions); @@ -2233,6 +2261,7 @@ pub mod args { pub const TX_INIT_PROPOSAL: &str = "tx_init_proposal.wasm"; pub const TX_INIT_VALIDATOR_WASM: &str = "tx_init_validator.wasm"; pub const TX_REVEAL_PK: &str = "tx_reveal_pk.wasm"; + pub const TX_UPDATE_ACCOUNT_WASM: &str = "tx_update_account.wasm"; pub const TX_TRANSFER_WASM: &str = "tx_transfer.wasm"; pub const TX_UNBOND_WASM: &str = "tx_unbond.wasm"; pub const TX_UNJAIL_VALIDATOR_WASM: &str = "tx_unjail_validator.wasm"; @@ -2304,6 +2333,7 @@ pub mod args { ); pub const FEE_PAYER: Arg = arg("fee-payer"); pub const FORCE: ArgFlag = flag("force"); + pub const GAS_PAYER: ArgOpt = arg("fee-payer").opt(); pub const GAS_AMOUNT: ArgDefault = arg_default( "gas-amount", DefaultFn(|| token::DenominatedAmount { @@ -2350,6 +2380,8 @@ pub mod args { pub const NAMADA_START_TIME: ArgOpt = arg_opt("time"); pub const NO_CONVERSIONS: ArgFlag = flag("no-conversions"); pub const OUT_FILE_PATH_OPT: ArgOpt = arg_opt("out-file-path"); + pub const OUTPUT_FOLDER_PATH: ArgOpt = + arg_opt("output-folder-path"); pub const OWNER: Arg = arg("owner"); pub const OWNER_OPT: ArgOpt = OWNER.opt(); pub const PIN: ArgFlag = flag("pin"); @@ -2361,6 +2393,7 @@ pub mod args { pub const PROTOCOL_KEY: ArgOpt = arg_opt("protocol-key"); pub const PRE_GENESIS_PATH: ArgOpt = arg_opt("pre-genesis-path"); pub const PUBLIC_KEY: Arg = arg("public-key"); + pub const PUBLIC_KEYS: ArgMulti = arg_multi("public-keys"); pub const PROPOSAL_ID: Arg = arg("proposal-id"); pub const PROPOSAL_ID_OPT: ArgOpt = arg_opt("proposal-id"); pub const PROPOSAL_VOTE_PGF_OPT: ArgOpt = arg_opt("pgf"); @@ -2376,9 +2409,9 @@ pub mod args { pub const SAFE_MODE: ArgFlag = flag("safe-mode"); pub const SCHEME: ArgDefault = arg_default("scheme", DefaultFn(|| SchemeType::Ed25519)); - pub const SIGNER: ArgOpt = arg_opt("signer"); - pub const SIGNING_KEY_OPT: ArgOpt = SIGNING_KEY.opt(); - pub const SIGNING_KEY: Arg = arg("signing-key"); + pub const SIGNING_KEYS: ArgMulti = arg_multi("signing-keys"); + pub const SIGNATURES: ArgMulti = + arg_multi("signatures-paths"); pub const SOURCE: Arg = arg("source"); pub const SOURCE_OPT: ArgOpt = SOURCE.opt(); pub const STORAGE_KEY: Arg = arg("storage-key"); @@ -2392,16 +2425,19 @@ pub mod args { pub const TRANSFER_SOURCE: Arg = arg("source"); pub const TRANSFER_TARGET: Arg = arg("target"); pub const TX_HASH: Arg = arg("tx-hash"); + pub const THRESOLD: ArgOpt = arg_opt("threshold"); pub const UNSAFE_DONT_ENCRYPT: ArgFlag = flag("unsafe-dont-encrypt"); pub const UNSAFE_SHOW_SECRET: ArgFlag = flag("unsafe-show-secret"); pub const VALIDATOR: Arg = arg("validator"); pub const VALIDATOR_OPT: ArgOpt = VALIDATOR.opt(); pub const VALIDATOR_ACCOUNT_KEY: ArgOpt = arg_opt("account-key"); - pub const VALIDATOR_CODE_PATH: ArgOpt = - arg_opt("validator-code-path"); + pub const VALIDATOR_ACCOUNT_KEYS: ArgMulti = + arg_multi("account-keys"); pub const VALIDATOR_CONSENSUS_KEY: ArgOpt = arg_opt("consensus-key"); + pub const VALIDATOR_CODE_PATH: ArgOpt = + arg_opt("validator-code-path"); pub const VALIDATOR_ETH_COLD_KEY: ArgOpt = arg_opt("eth-cold-key"); pub const VALIDATOR_ETH_HOT_KEY: ArgOpt = @@ -3032,6 +3068,7 @@ pub mod args { std::fs::read(data_path) .expect("Expected a file at given data path") }), + owner: ctx.get(&self.owner), } } } @@ -3041,10 +3078,12 @@ pub mod args { let tx = Tx::parse(matches); let code_path = CODE_PATH.parse(matches); let data_path = DATA_PATH_OPT.parse(matches); + let owner = OWNER.parse(matches); Self { tx, code_path, data_path, + owner, } } @@ -3060,6 +3099,10 @@ pub mod args { will be passed to the transaction code when it's \ executed.", )) + .arg(OWNER.def().help( + "The address corresponding to the signatures or signing \ + keys.", + )) } } @@ -3188,10 +3231,14 @@ pub mod args { fn to_sdk(self, ctx: &mut Context) -> TxInitAccount { TxInitAccount:: { tx: self.tx.to_sdk(ctx), - source: ctx.get(&self.source), - vp_code_path: self.vp_code_path.to_path_buf(), - tx_code_path: self.tx_code_path.to_path_buf(), - public_key: ctx.get_cached(&self.public_key), + vp_code_path: self.vp_code_path, + tx_code_path: self.tx_code_path, + public_keys: self + .public_keys + .iter() + .map(|pk| ctx.get_cached(pk)) + .collect(), + threshold: self.threshold, } } } @@ -3199,34 +3246,36 @@ pub mod args { impl Args for TxInitAccount { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); - let source = SOURCE.parse(matches); let vp_code_path = CODE_PATH_OPT .parse(matches) .unwrap_or_else(|| PathBuf::from(VP_USER_WASM)); let tx_code_path = PathBuf::from(TX_INIT_ACCOUNT_WASM); - let public_key = PUBLIC_KEY.parse(matches); + let public_keys = PUBLIC_KEYS.parse(matches); + let threshold = THRESOLD.parse(matches); Self { tx, - source, vp_code_path, - public_key, + public_keys, + threshold, tx_code_path, } } fn def(app: App) -> App { app.add_args::>() - .arg(SOURCE.def().help( - "The source account's address that signs the transaction.", - )) .arg(CODE_PATH_OPT.def().help( "The path to the validity predicate WASM code to be used \ for the new account. Uses the default user VP if none \ specified.", )) - .arg(PUBLIC_KEY.def().help( - "A public key to be used for the new account in \ - hexadecimal encoding.", + .arg(PUBLIC_KEYS.def().help( + "A list public keys to be associated with the new account \ + in hexadecimal encoding.", + )) + .arg(THRESOLD.def().help( + "The minimum number of signature to be provided for \ + authorization. Must be less then the maximum number of \ + public keys provided.", )) } } @@ -3235,9 +3284,13 @@ pub mod args { fn to_sdk(self, ctx: &mut Context) -> TxInitValidator { TxInitValidator:: { tx: self.tx.to_sdk(ctx), - source: ctx.get(&self.source), scheme: self.scheme, - account_key: self.account_key.map(|x| ctx.get_cached(&x)), + account_keys: self + .account_keys + .iter() + .map(|x| ctx.get_cached(x)) + .collect(), + threshold: self.threshold, consensus_key: self.consensus_key.map(|x| ctx.get_cached(&x)), eth_cold_key: self.eth_cold_key.map(|x| ctx.get_cached(&x)), eth_hot_key: self.eth_hot_key.map(|x| ctx.get_cached(&x)), @@ -3256,9 +3309,8 @@ pub mod args { impl Args for TxInitValidator { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); - let source = SOURCE.parse(matches); let scheme = SCHEME.parse(matches); - let account_key = VALIDATOR_ACCOUNT_KEY.parse(matches); + let account_keys = VALIDATOR_ACCOUNT_KEYS.parse(matches); let consensus_key = VALIDATOR_CONSENSUS_KEY.parse(matches); let eth_cold_key = VALIDATOR_ETH_COLD_KEY.parse(matches); let eth_hot_key = VALIDATOR_ETH_HOT_KEY.parse(matches); @@ -3271,11 +3323,12 @@ pub mod args { .unwrap_or_else(|| PathBuf::from(VP_USER_WASM)); let unsafe_dont_encrypt = UNSAFE_DONT_ENCRYPT.parse(matches); let tx_code_path = PathBuf::from(TX_INIT_VALIDATOR_WASM); + let threshold = THRESOLD.parse(matches); Self { tx, - source, scheme, - account_key, + account_keys, + threshold, consensus_key, eth_cold_key, eth_hot_key, @@ -3290,16 +3343,14 @@ pub mod args { fn def(app: App) -> App { app.add_args::>() - .arg(SOURCE.def().help( - "The source account's address that signs the transaction.", - )) .arg(SCHEME.def().help( "The key scheme/type used for the validator keys. \ Currently supports ed25519 and secp256k1.", )) - .arg(VALIDATOR_ACCOUNT_KEY.def().help( - "A public key for the validator account. A new one will \ - be generated if none given.", + .arg(VALIDATOR_ACCOUNT_KEYS.def().help( + "A list public keys to be associated with the new account \ + in hexadecimal encoding. A new one will be generated if \ + none given.", )) .arg(VALIDATOR_CONSENSUS_KEY.def().help( "A consensus key for the validator account. A new one \ @@ -3340,38 +3391,53 @@ pub mod args { "UNSAFE: Do not encrypt the generated keypairs. Do not \ use this for keys used in a live network.", )) + .arg(THRESOLD.def().help( + "The minimum number of signature to be provided for \ + authorization. Must be less then the maximum number of \ + public keys provided.", + )) } } - impl CliToSdk> for TxUpdateVp { - fn to_sdk(self, ctx: &mut Context) -> TxUpdateVp { - TxUpdateVp:: { + impl CliToSdk> for TxUpdateAccount { + fn to_sdk(self, ctx: &mut Context) -> TxUpdateAccount { + TxUpdateAccount:: { tx: self.tx.to_sdk(ctx), vp_code_path: self.vp_code_path, tx_code_path: self.tx_code_path, addr: ctx.get(&self.addr), + public_keys: self + .public_keys + .iter() + .map(|pk| ctx.get_cached(pk)) + .collect(), + threshold: self.threshold, } } } - impl Args for TxUpdateVp { + impl Args for TxUpdateAccount { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); - let vp_code_path = CODE_PATH.parse(matches); + let vp_code_path = CODE_PATH_OPT.parse(matches); let addr = ADDRESS.parse(matches); - let tx_code_path = PathBuf::from(TX_UPDATE_VP_WASM); + let tx_code_path = PathBuf::from(TX_UPDATE_ACCOUNT_WASM); + let public_keys = PUBLIC_KEYS.parse(matches); + let threshold = THRESOLD.parse(matches); Self { tx, vp_code_path, addr, tx_code_path, + public_keys, + threshold, } } fn def(app: App) -> App { app.add_args::>() .arg( - CODE_PATH.def().help( + CODE_PATH_OPT.def().help( "The path to the new validity predicate WASM code.", ), ) @@ -3379,6 +3445,15 @@ pub mod args { "The account's address. It's key is used to produce the \ signature.", )) + .arg(PUBLIC_KEYS.def().help( + "A list public keys to be associated with the new account \ + in hexadecimal encoding.", + )) + .arg(THRESOLD.def().help( + "The minimum number of signature to be provided for \ + authorization. Must be less then the maximum number of \ + public keys provided.", + )) } } @@ -3545,6 +3620,8 @@ pub mod args { pub proposal_id: Option, /// The vote pub vote: String, + /// The address of the voter + pub voter_address: C::Address, /// PGF proposal pub proposal_pgf: Option, /// ETH proposal @@ -3563,6 +3640,7 @@ pub mod args { tx: self.tx.to_sdk(ctx), proposal_id: self.proposal_id, vote: self.vote, + voter_address: ctx.get(&self.voter_address), offline: self.offline, proposal_data: self.proposal_data, tx_code_path: self.tx_code_path.to_path_buf(), @@ -3579,6 +3657,7 @@ pub mod args { let proposal_pgf = PROPOSAL_VOTE_PGF_OPT.parse(matches); let proposal_eth = PROPOSAL_VOTE_ETH_OPT.parse(matches); let vote = PROPOSAL_VOTE.parse(matches); + let voter_address = ADDRESS.parse(matches); let offline = PROPOSAL_OFFLINE.parse(matches); let proposal_data = DATA_PATH_OPT.parse(matches); let tx_code_path = PathBuf::from(TX_VOTE_PROPOSAL); @@ -3590,6 +3669,7 @@ pub mod args { proposal_pgf, proposal_eth, offline, + voter_address, proposal_data, tx_code_path, } @@ -3648,6 +3728,7 @@ pub mod args { ) .conflicts_with(PROPOSAL_ID.name), ) + .arg(ADDRESS.def().help("The address of the voter.")) } } @@ -3857,6 +3938,31 @@ pub mod args { } } + impl CliToSdk> for QueryAccount { + fn to_sdk(self, ctx: &mut Context) -> QueryAccount { + QueryAccount:: { + query: self.query.to_sdk(ctx), + owner: ctx.get(&self.owner), + } + } + } + + impl Args for QueryAccount { + fn parse(matches: &ArgMatches) -> Self { + let query = Query::parse(matches); + let owner = OWNER.parse(matches); + Self { query, owner } + } + + fn def(app: App) -> App { + app.add_args::>().arg( + BALANCE_OWNER + .def() + .help("The substorage space address to query."), + ) + } + } + impl CliToSdk> for QueryBalance { fn to_sdk(self, ctx: &mut Context) -> QueryBalance { QueryBalance:: { @@ -4253,19 +4359,24 @@ pub mod args { Tx:: { dry_run: self.dry_run, dump_tx: self.dump_tx, + output_folder: self.output_folder, force: self.force, broadcast_only: self.broadcast_only, ledger_address: (), initialized_account_alias: self.initialized_account_alias, wallet_alias_force: self.wallet_alias_force, + fee_payer: ctx.get_opt_cached(&self.fee_payer), fee_amount: self.fee_amount, fee_token: ctx.get(&self.fee_token), gas_limit: self.gas_limit, - signing_key: self.signing_key.map(|x| ctx.get_cached(&x)), + signing_keys: self + .signing_keys + .iter() + .map(|key| ctx.get_cached(key)) + .collect(), verification_key: self .verification_key - .map(|x| ctx.get_cached(&x)), - signer: self.signer.map(|x| ctx.get(&x)), + .map(|public_key| ctx.get_cached(&public_key)), tx_reveal_code_path: self.tx_reveal_code_path, password: self.password, expiration: self.expiration, @@ -4307,6 +4418,10 @@ pub mod args { .arg(WALLET_ALIAS_FORCE.def().help( "Override the alias without confirmation if it already exists.", )) + .arg(GAS_PAYER.def().help( + "The implicit address of the gas payer. It defaults to the \ + address associated to the first key passed to --signing-keys.", + )) .arg(GAS_AMOUNT.def().help( "The amount being paid for the inclusion of this transaction", )) @@ -4322,27 +4437,13 @@ pub mod args { equivalent:\n2012-12-12T12:12:12Z\n2012-12-12 \ 12:12:12Z\n2012- 12-12T12: 12:12Z", )) - .arg( - SIGNING_KEY_OPT - .def() - .help( - "Sign the transaction with the key for the given \ - public key, public key hash or alias from your \ - wallet.", - ) - .conflicts_with(SIGNER.name) - .conflicts_with(VERIFICATION_KEY.name), - ) - .arg( - SIGNER - .def() - .help( - "Sign the transaction with the keypair of the public \ - key of the given address.", - ) - .conflicts_with(SIGNING_KEY_OPT.name) - .conflicts_with(VERIFICATION_KEY.name), - ) + .arg(SIGNING_KEYS.def().help( + "Sign the transaction with the key for the given public key, \ + public key hash or alias from your wallet.", + )) + .arg(OUTPUT_FOLDER_PATH.def().help( + "The output folder path where the artifact will be stored.", + )) .arg( VERIFICATION_KEY .def() @@ -4351,8 +4452,7 @@ pub mod args { public key, public key hash or alias from your \ wallet.", ) - .conflicts_with(SIGNER.name) - .conflicts_with(SIGNING_KEY_OPT.name), + .conflicts_with(SIGNING_KEYS.name), ) .arg(CHAIN_ID_OPT.def().help("The chain ID.")) } @@ -4365,17 +4465,18 @@ pub mod args { let ledger_address = LEDGER_ADDRESS_DEFAULT.parse(matches); let initialized_account_alias = ALIAS_OPT.parse(matches); let wallet_alias_force = WALLET_ALIAS_FORCE.parse(matches); + let fee_payer = GAS_PAYER.parse(matches); let fee_amount = InputAmount::Unvalidated(GAS_AMOUNT.parse(matches)); let fee_token = GAS_TOKEN.parse(matches); let gas_limit = GAS_LIMIT.parse(matches).amount.into(); let expiration = EXPIRATION_OPT.parse(matches); - let signing_key = SIGNING_KEY_OPT.parse(matches); + let signing_keys = SIGNING_KEYS.parse(matches); let verification_key = VERIFICATION_KEY.parse(matches); - let signer = SIGNER.parse(matches); let tx_reveal_code_path = PathBuf::from(TX_REVEAL_PK); let chain_id = CHAIN_ID_OPT.parse(matches); let password = None; + let output_folder = OUTPUT_FOLDER_PATH.parse(matches); Self { dry_run, dump_tx, @@ -4384,16 +4485,17 @@ pub mod args { ledger_address, initialized_account_alias, wallet_alias_force, + fee_payer, fee_amount, fee_token, gas_limit, expiration, - signing_key, + signing_keys, verification_key, - signer, tx_reveal_code_path, password, chain_id, + output_folder, } } } diff --git a/apps/src/lib/cli/utils.rs b/apps/src/lib/cli/utils.rs index eac233ed28..a50f58f5de 100644 --- a/apps/src/lib/cli/utils.rs +++ b/apps/src/lib/cli/utils.rs @@ -222,6 +222,27 @@ where } } +impl ArgMulti> +where + T: FromStr, + ::Err: Debug, +{ + pub fn def(&self) -> ClapArg { + ClapArg::new(self.name) + .long(self.name) + .num_args(1..) + .value_delimiter(',') + } + + pub fn parse(&self, matches: &ArgMatches) -> Vec> { + matches + .get_many(self.name) + .unwrap_or_default() + .map(|raw: &String| FromContext::new(raw.to_string())) + .collect() + } +} + impl ArgDefaultFromCtx> where T: FromStr, diff --git a/apps/src/lib/client/mod.rs b/apps/src/lib/client/mod.rs index 57f3c5a043..8862c5a564 100644 --- a/apps/src/lib/client/mod.rs +++ b/apps/src/lib/client/mod.rs @@ -1,4 +1,3 @@ pub mod rpc; -pub mod signing; pub mod tx; pub mod utils; diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 44955967d0..94d2a66063 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -31,7 +31,7 @@ use namada::ledger::pos::{ }; use namada::ledger::queries::RPC; use namada::ledger::rpc::{ - enriched_bonds_and_unbonds, format_denominated_amount, query_epoch, + self, enriched_bonds_and_unbonds, format_denominated_amount, query_epoch, TxResponse, }; use namada::ledger::storage::ConversionState; @@ -1222,10 +1222,13 @@ pub async fn query_proposal_result< "JSON was not well-formatted for proposal.", ); - let public_key = - get_public_key(client, &proposal.address) - .await - .expect("Public key should exist."); + let public_key = rpc::get_public_key_at( + client, + &proposal.address, + 0, + ) + .await + .expect("Public key should exist."); if !proposal.check_signature(&public_key) { eprintln!("Bad proposal signature."); @@ -1276,6 +1279,23 @@ pub async fn query_proposal_result< } } +pub async fn query_account( + client: &C, + args: args::QueryAccount, +) { + let account = rpc::get_account_info(client, &args.owner).await; + if let Some(account) = account { + println!("Address: {}", account.address); + println!("Threshold: {}", account.threshold); + println!("Public keys:"); + for (public_key, _) in account.public_keys_map.pk_to_idx { + println!("- {}", public_key); + } + } else { + println!("No account exists for {}", args.owner); + } +} + pub async fn query_protocol_parameters< C: namada::ledger::queries::Client + Sync, >( @@ -1791,8 +1811,9 @@ where pub async fn get_public_key( client: &C, address: &Address, + index: u8, ) -> Option { - namada::ledger::rpc::get_public_key(client, address).await + namada::ledger::rpc::get_public_key_at(client, address, index).await } /// Check if the given address is a known validator. @@ -2061,10 +2082,10 @@ pub async fn get_proposal_offline_votes< let proposal_vote: OfflineVote = serde_json::from_reader(file) .expect("JSON was not well-formatted for offline vote."); - let key = pk_key(&proposal_vote.address); - let public_key = query_storage_value(client, &key) - .await - .expect("Public key should exist."); + let public_key = + rpc::get_public_key_at(client, &proposal_vote.address, 0) + .await + .expect("Public key should exist."); if !proposal_vote.proposal_hash.eq(&proposal_hash) || !proposal_vote.check_signature(&public_key) diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index d70f8c7e6f..2ff219e0b7 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -9,9 +9,10 @@ use async_trait::async_trait; use borsh::{BorshDeserialize, BorshSerialize}; use data_encoding::HEXLOWER_PERMISSIVE; use masp_proofs::prover::LocalTxProver; +use namada::core::types::account::AccountPublicKeysMap; use namada::ledger::governance::storage as gov_storage; use namada::ledger::rpc::{TxBroadcastData, TxResponse}; -use namada::ledger::signing::TxSigningKey; +use namada::ledger::signing::{find_pk, TxSigningKey}; use namada::ledger::wallet::{Wallet, WalletUtils}; use namada::ledger::{masp, pos, signing, tx}; use namada::proof_of_stake::parameters::PosParams; @@ -25,13 +26,13 @@ use namada::types::key::{self, *}; use namada::types::storage::{Epoch, Key}; use namada::types::token; use namada::types::transaction::governance::{ProposalType, VoteProposalData}; -use namada::types::transaction::{InitValidator, TxType}; +use namada::types::transaction::pos::InitValidator; +use namada::types::transaction::TxType; use super::rpc; use crate::cli::context::WalletAddress; use crate::cli::{args, safe_exit, Context}; use crate::client::rpc::query_wasm_code_hash; -use crate::client::signing::find_pk; use crate::client::tx::tx::ProcessTxResponse; use crate::config::TendermintMode; use crate::facade::tendermint_rpc::endpoint::broadcast::tx_sync::Response; @@ -45,27 +46,44 @@ pub async fn submit_reveal_aux( ctx: &mut Context, args: &args::Tx, addr: Option
, - pk: common::PublicKey, + public_keys: &[common::PublicKey], tx: &mut Tx, ) -> Result<(), tx::Error> { if let Some(Address::Implicit(_)) = addr { - let reveal_pk = tx::build_reveal_pk( - client, - &mut ctx.wallet, - args::RevealPk { - tx: args.clone(), - public_key: pk.clone(), - }, - ) - .await?; - if let Some((mut rtx, _, pk)) = reveal_pk { - // Sign the reveal public key transaction with the fee payer - signing::sign_tx(&mut ctx.wallet, &mut rtx, args, &pk).await?; - // Submit the reveal public key transaction first - tx::process_tx(client, &mut ctx.wallet, args, rtx).await?; - // Update the stateful PoW challenge of the outer transaction - #[cfg(not(feature = "mainnet"))] - signing::update_pow_challenge(client, args, tx, &pk, false).await; + for public_key in public_keys { + let reveal_pk = tx::build_reveal_pk( + client, + &mut ctx.wallet, + args::RevealPk { + tx: args.clone(), + public_key: public_key.clone(), + }, + ) + .await?; + if let Some((mut rtx, _, public_keys)) = reveal_pk { + // Sign the reveal public key transaction with the fee payer + signing::sign_tx( + &mut ctx.wallet, + &mut rtx, + args, + &AccountPublicKeysMap::from_iter(public_keys.clone()), + &public_keys, + 1, + ) + .await?; + // Submit the reveal public key transaction first + tx::process_tx(client, &mut ctx.wallet, args, rtx).await?; + // Update the stateful PoW challenge of the outer transaction + #[cfg(not(feature = "mainnet"))] + signing::update_pow_challenge( + client, + args, + tx, + public_keys.get(0).unwrap(), + false, + ) + .await; + } } } Ok(()) @@ -80,27 +98,63 @@ where C: namada::ledger::queries::Client + Sync, C::Error: std::fmt::Display, { - let (mut tx, addr, pk) = + let (mut tx, addr, public_keys) = tx::build_custom(client, &mut ctx.wallet, args.clone()).await?; - submit_reveal_aux(client, ctx, &args.tx, addr, pk.clone(), &mut tx).await?; - signing::sign_tx(&mut ctx.wallet, &mut tx, &args.tx, &pk).await?; + submit_reveal_aux( + client, + ctx, + &args.tx, + addr.clone(), + &public_keys, + &mut tx, + ) + .await?; + let (account_public_keys_map, threshold) = + signing::aux_signing_data(client, addr, public_keys.clone()).await; + signing::sign_tx( + &mut ctx.wallet, + &mut tx, + &args.tx, + &account_public_keys_map, + &public_keys, + threshold, + ) + .await?; tx::process_tx(client, &mut ctx.wallet, &args.tx, tx).await?; Ok(()) } -pub async fn submit_update_vp( +pub async fn submit_update_account( client: &C, ctx: &mut Context, - args: args::TxUpdateVp, + args: args::TxUpdateAccount, ) -> Result<(), tx::Error> where C: namada::ledger::queries::Client + Sync, C::Error: std::fmt::Display, { - let (mut tx, addr, pk) = - tx::build_update_vp(client, &mut ctx.wallet, args.clone()).await?; - submit_reveal_aux(client, ctx, &args.tx, addr, pk.clone(), &mut tx).await?; - signing::sign_tx(&mut ctx.wallet, &mut tx, &args.tx, &pk).await?; + let (mut tx, addr, public_keys) = + tx::build_update_account(client, &mut ctx.wallet, args.clone()).await?; + submit_reveal_aux( + client, + ctx, + &args.tx, + addr.clone(), + &public_keys, + &mut tx, + ) + .await?; + let (account_public_keys_map, threshold) = + signing::aux_signing_data(client, addr, public_keys.clone()).await; + signing::sign_tx( + &mut ctx.wallet, + &mut tx, + &args.tx, + &account_public_keys_map, + &public_keys, + threshold, + ) + .await?; tx::process_tx(client, &mut ctx.wallet, &args.tx, tx).await?; Ok(()) } @@ -114,10 +168,29 @@ where C: namada::ledger::queries::Client + Sync, C::Error: std::fmt::Display, { - let (mut tx, addr, pk) = + let (mut tx, addr, public_keys) = tx::build_init_account(client, &mut ctx.wallet, args.clone()).await?; - submit_reveal_aux(client, ctx, &args.tx, addr, pk.clone(), &mut tx).await?; - signing::sign_tx(&mut ctx.wallet, &mut tx, &args.tx, &pk).await?; + + submit_reveal_aux( + client, + ctx, + &args.tx, + addr.clone(), + &public_keys, + &mut tx, + ) + .await?; + let (account_public_keys_map, threshold) = + signing::aux_signing_data(client, addr, public_keys.clone()).await; + signing::sign_tx( + &mut ctx.wallet, + &mut tx, + &args.tx, + &account_public_keys_map, + &public_keys, + threshold, + ) + .await?; tx::process_tx(client, &mut ctx.wallet, &args.tx, tx).await?; Ok(()) } @@ -127,9 +200,9 @@ pub async fn submit_init_validator( mut ctx: Context, args::TxInitValidator { tx: tx_args, - source, scheme, - account_key, + account_keys, + threshold, consensus_key, eth_cold_key, eth_hot_key, @@ -160,25 +233,19 @@ where let validator_key_alias = format!("{}-key", alias); let consensus_key_alias = format!("{}-consensus-key", alias); + + let threshold = match threshold { + Some(threshold) => threshold, + None => { + if account_keys.len() == 1 { + 1u8 + } else { + safe_exit(1) + } + } + }; let eth_hot_key_alias = format!("{}-eth-hot-key", alias); let eth_cold_key_alias = format!("{}-eth-cold-key", alias); - let account_key = account_key.unwrap_or_else(|| { - println!("Generating validator account key..."); - let password = - read_and_confirm_encryption_password(unsafe_dont_encrypt); - ctx.wallet - .gen_key( - scheme, - Some(validator_key_alias.clone()), - tx_args.wallet_alias_force, - password, - None, - ) - .expect("Key generation should not fail.") - .expect("No existing alias expected.") - .1 - .ref_to() - }); let consensus_key = consensus_key .map(|key| match key { @@ -316,7 +383,8 @@ where validator_vp_code_hash, ))); let data = InitValidator { - account_key, + account_keys, + threshold, consensus_key: consensus_key.ref_to(), eth_cold_key: key::secp256k1::PublicKey::try_from_pk(ð_cold_pk) .unwrap(), @@ -334,19 +402,37 @@ where tx.set_data(Data::new(data)); tx.set_code(Code::from_hash(tx_code_hash)); - let (mut tx, addr, pk) = tx::prepare_tx( + let (mut tx, addr, public_keys) = tx::prepare_tx( client, &mut ctx.wallet, &tx_args, tx, - TxSigningKey::WalletAddress(source), + None, + TxSigningKey::None, #[cfg(not(feature = "mainnet"))] false, ) .await?; - submit_reveal_aux(client, &mut ctx, &tx_args, addr, pk.clone(), &mut tx) - .await?; - signing::sign_tx(&mut ctx.wallet, &mut tx, &tx_args, &pk).await?; + submit_reveal_aux( + client, + &mut ctx, + &tx_args, + addr.clone(), + &public_keys, + &mut tx, + ) + .await?; + let (account_public_keys_map, threshold) = + signing::aux_signing_data(client, addr, public_keys.clone()).await; + signing::sign_tx( + &mut ctx.wallet, + &mut tx, + &tx_args, + &account_public_keys_map, + &public_keys, + threshold, + ) + .await?; let result = tx::process_tx(client, &mut ctx.wallet, &tx_args, tx) .await? .initialized_accounts(); @@ -532,19 +618,29 @@ pub async fn submit_transfer( ) -> Result<(), tx::Error> { for _ in 0..2 { let arg = args.clone(); - let (mut tx, addr, pk, tx_epoch, _isf) = + let (mut tx, addr, public_keys, tx_epoch, _isf) = tx::build_transfer(client, &mut ctx.wallet, &mut ctx.shielded, arg) .await?; submit_reveal_aux( client, &mut ctx, &args.tx, - addr, - pk.clone(), + addr.clone(), + &public_keys, &mut tx, ) .await?; - signing::sign_tx(&mut ctx.wallet, &mut tx, &args.tx, &pk).await?; + let (account_public_keys_map, threshold) = + signing::aux_signing_data(client, addr, public_keys.clone()).await; + signing::sign_tx( + &mut ctx.wallet, + &mut tx, + &args.tx, + &account_public_keys_map, + &public_keys, + threshold, + ) + .await?; let result = tx::process_tx(client, &mut ctx.wallet, &args.tx, tx).await?; // Query the epoch in which the transaction was probably submitted @@ -583,11 +679,28 @@ where C: namada::ledger::queries::Client + Sync, C::Error: std::fmt::Display, { - let (mut tx, addr, pk) = + let (mut tx, addr, public_keys) = tx::build_ibc_transfer(client, &mut ctx.wallet, args.clone()).await?; - submit_reveal_aux(client, &mut ctx, &args.tx, addr, pk.clone(), &mut tx) - .await?; - signing::sign_tx(&mut ctx.wallet, &mut tx, &args.tx, &pk).await?; + submit_reveal_aux( + client, + &mut ctx, + &args.tx, + addr.clone(), + &public_keys, + &mut tx, + ) + .await?; + let (account_public_keys_map, threshold) = + signing::aux_signing_data(client, addr, public_keys.clone()).await; + signing::sign_tx( + &mut ctx.wallet, + &mut tx, + &args.tx, + &account_public_keys_map, + &public_keys, + threshold, + ) + .await?; tx::process_tx(client, &mut ctx.wallet, &args.tx, tx).await?; Ok(()) } @@ -665,7 +778,7 @@ where if args.offline { let signer = ctx.get(&signer); - let key = find_pk(client, &mut ctx.wallet, &signer).await?; + let key = find_pk(client, &mut ctx.wallet, &signer, None).await?; let signing_key = signing::find_key_by_pk(&mut ctx.wallet, &args.tx, &key)?; let offline_proposal = @@ -753,11 +866,12 @@ where tx.set_data(Data::new(data)); tx.set_code(Code::from_hash(tx_code_hash)); - let (mut tx, addr, pk) = tx::prepare_tx( + let (mut tx, addr, public_keys) = tx::prepare_tx( client, &mut ctx.wallet, &args.tx, tx, + Some(signer.clone()), TxSigningKey::WalletAddress(signer), #[cfg(not(feature = "mainnet"))] false, @@ -767,13 +881,24 @@ where client, &mut ctx, &args.tx, - addr, - pk.clone(), + addr.clone(), + &public_keys, + &mut tx, + ) + .await?; + let (account_public_keys_map, threshold) = + signing::aux_signing_data(client, addr, public_keys.clone()).await; + signing::sign_tx( + &mut ctx.wallet, &mut tx, + &args.tx, + &account_public_keys_map, + &public_keys, + threshold, ) .await?; - signing::sign_tx(&mut ctx.wallet, &mut tx, &args.tx, &pk).await?; tx::process_tx(client, &mut ctx.wallet, &args.tx, tx).await?; + Ok(()) } } @@ -787,13 +912,6 @@ where C: namada::ledger::queries::Client + Sync, C::Error: std::fmt::Display, { - let signer = if let Some(addr) = &args.tx.signer { - addr - } else { - eprintln!("Missing mandatory argument --signer."); - safe_exit(1) - }; - // Construct vote let proposal_vote = match args.vote.to_ascii_lowercase().as_str() { "yay" => { @@ -864,28 +982,33 @@ where let proposal: OfflineProposal = serde_json::from_reader(file).expect("JSON was not well-formatted"); - let public_key = rpc::get_public_key(client, &proposal.address) - .await - .expect("Public key should exist."); + let public_key = namada::ledger::rpc::get_public_key_at( + client, + &proposal.address, + 0, + ) + .await + .expect("Public key should exist."); if !proposal.check_signature(&public_key) { eprintln!("Proposal signature mismatch!"); safe_exit(1) } - let key = find_pk(client, &mut ctx.wallet, signer).await?; + let key = + find_pk(client, &mut ctx.wallet, &args.voter_address, None).await?; let signing_key = signing::find_key_by_pk(&mut ctx.wallet, &args.tx, &key)?; let offline_vote = OfflineVote::new( &proposal, proposal_vote, - signer.clone(), + args.voter_address.clone(), &signing_key, ); let proposal_vote_filename = proposal_file_path .parent() .expect("No parent found") - .join(format!("proposal-vote-{}", &signer.to_string())); + .join(format!("proposal-vote-{}", &args.voter_address.to_string())); let out = File::create(&proposal_vote_filename).unwrap(); match serde_json::to_writer_pretty(out, &offline_vote) { Ok(_) => { @@ -903,7 +1026,7 @@ where } else { let current_epoch = rpc::query_and_print_epoch(client).await; - let voter_address = signer.clone(); + let voter_address = args.voter_address.clone(); let proposal_id = args.proposal_id.unwrap(); let proposal_start_epoch_key = gov_storage::get_voting_start_epoch_key(proposal_id); @@ -1022,12 +1145,13 @@ where tx.set_data(Data::new(data)); tx.set_code(Code::from_hash(tx_code_hash)); - let (mut tx, addr, pk) = tx::prepare_tx( + let (mut tx, addr, public_keys) = tx::prepare_tx( client, &mut ctx.wallet, &args.tx, tx, - TxSigningKey::WalletAddress(signer.clone()), + Some(args.voter_address.clone()), + TxSigningKey::WalletAddress(args.voter_address.clone()), #[cfg(not(feature = "mainnet"))] false, ) @@ -1036,13 +1160,27 @@ where client, &mut ctx, &args.tx, - addr, - pk.clone(), + addr.clone(), + &public_keys, &mut tx, ) .await?; - signing::sign_tx(&mut ctx.wallet, &mut tx, &args.tx, &pk) - .await?; + let (account_public_keys_map, threshold) = + signing::aux_signing_data( + client, + addr, + public_keys.clone(), + ) + .await; + signing::sign_tx( + &mut ctx.wallet, + &mut tx, + &args.tx, + &account_public_keys_map, + &public_keys, + threshold, + ) + .await?; tx::process_tx(client, &mut ctx.wallet, &args.tx, tx).await?; Ok(()) } @@ -1068,8 +1206,18 @@ where { let reveal_tx = tx::build_reveal_pk(client, &mut ctx.wallet, args.clone()).await?; - if let Some((mut tx, _, pk)) = reveal_tx { - signing::sign_tx(&mut ctx.wallet, &mut tx, &args.tx, &pk).await?; + if let Some((mut tx, _, public_keys)) = reveal_tx { + let (account_pks_index_map, threshold) = + signing::aux_signing_data(client, None, public_keys.clone()).await; + signing::sign_tx( + &mut ctx.wallet, + &mut tx, + &args.tx, + &account_pks_index_map, + &public_keys, + threshold, + ) + .await?; tx::process_tx(client, &mut ctx.wallet, &args.tx, tx).await?; } Ok(()) @@ -1142,10 +1290,28 @@ where C: namada::ledger::queries::Client + Sync, C::Error: std::fmt::Display, { - let (mut tx, addr, pk) = + let (mut tx, addr, public_keys) = tx::build_bond::(client, &mut ctx.wallet, args.clone()).await?; - submit_reveal_aux(client, ctx, &args.tx, addr, pk.clone(), &mut tx).await?; - signing::sign_tx(&mut ctx.wallet, &mut tx, &args.tx, &pk).await?; + submit_reveal_aux( + client, + ctx, + &args.tx, + addr.clone(), + &public_keys, + &mut tx, + ) + .await?; + let (account_public_keys_map, threshold) = + signing::aux_signing_data(client, addr, public_keys.clone()).await; + signing::sign_tx( + &mut ctx.wallet, + &mut tx, + &args.tx, + &account_public_keys_map, + &public_keys, + threshold, + ) + .await?; tx::process_tx(client, &mut ctx.wallet, &args.tx, tx).await?; Ok(()) } @@ -1159,10 +1325,28 @@ where C: namada::ledger::queries::Client + Sync, C::Error: std::fmt::Display, { - let (mut tx, addr, pk, latest_withdrawal_pre) = + let (mut tx, addr, public_keys, latest_withdrawal_pre) = tx::build_unbond(client, &mut ctx.wallet, args.clone()).await?; - submit_reveal_aux(client, ctx, &args.tx, addr, pk.clone(), &mut tx).await?; - signing::sign_tx(&mut ctx.wallet, &mut tx, &args.tx, &pk).await?; + submit_reveal_aux( + client, + ctx, + &args.tx, + addr.clone(), + &public_keys, + &mut tx, + ) + .await?; + let (account_public_keys_map, threshold) = + signing::aux_signing_data(client, addr, public_keys.clone()).await; + signing::sign_tx( + &mut ctx.wallet, + &mut tx, + &args.tx, + &account_public_keys_map, + &public_keys, + threshold, + ) + .await?; tx::process_tx(client, &mut ctx.wallet, &args.tx, tx).await?; tx::query_unbonds(client, args.clone(), latest_withdrawal_pre).await?; Ok(()) @@ -1177,11 +1361,28 @@ where C: namada::ledger::queries::Client + Sync, C::Error: std::fmt::Display, { - let (mut tx, addr, pk) = + let (mut tx, addr, public_keys) = tx::build_withdraw(client, &mut ctx.wallet, args.clone()).await?; - submit_reveal_aux(client, &mut ctx, &args.tx, addr, pk.clone(), &mut tx) - .await?; - signing::sign_tx(&mut ctx.wallet, &mut tx, &args.tx, &pk).await?; + submit_reveal_aux( + client, + &mut ctx, + &args.tx, + addr.clone(), + &public_keys, + &mut tx, + ) + .await?; + let (account_public_keys_map, threshold) = + signing::aux_signing_data(client, addr, public_keys.clone()).await; + signing::sign_tx( + &mut ctx.wallet, + &mut tx, + &args.tx, + &account_public_keys_map, + &public_keys, + threshold, + ) + .await?; tx::process_tx(client, &mut ctx.wallet, &args.tx, tx).await?; Ok(()) } @@ -1196,12 +1397,29 @@ where C::Error: std::fmt::Display, { let arg = args.clone(); - let (mut tx, addr, pk) = + let (mut tx, addr, public_keys) = tx::build_validator_commission_change(client, &mut ctx.wallet, arg) .await?; - submit_reveal_aux(client, &mut ctx, &args.tx, addr, pk.clone(), &mut tx) - .await?; - signing::sign_tx(&mut ctx.wallet, &mut tx, &args.tx, &pk).await?; + submit_reveal_aux( + client, + &mut ctx, + &args.tx, + addr.clone(), + &public_keys, + &mut tx, + ) + .await?; + let (account_public_keys_map, threshold) = + signing::aux_signing_data(client, addr, public_keys.clone()).await; + signing::sign_tx( + &mut ctx.wallet, + &mut tx, + &args.tx, + &account_public_keys_map, + &public_keys, + threshold, + ) + .await?; tx::process_tx(client, &mut ctx.wallet, &args.tx, tx).await?; Ok(()) } @@ -1217,12 +1435,29 @@ where C: namada::ledger::queries::Client + Sync, C::Error: std::fmt::Display, { - let (mut tx, addr, pk) = + let (mut tx, addr, public_keys) = tx::build_unjail_validator(client, &mut ctx.wallet, args.clone()) .await?; - submit_reveal_aux(client, &mut ctx, &args.tx, addr, pk.clone(), &mut tx) - .await?; - signing::sign_tx(&mut ctx.wallet, &mut tx, &args.tx, &pk).await?; + submit_reveal_aux( + client, + &mut ctx, + &args.tx, + addr.clone(), + &public_keys, + &mut tx, + ) + .await?; + let (account_public_keys_map, threshold) = + signing::aux_signing_data(client, addr, public_keys.clone()).await; + signing::sign_tx( + &mut ctx.wallet, + &mut tx, + &args.tx, + &account_public_keys_map, + &public_keys, + threshold, + ) + .await?; tx::process_tx(client, &mut ctx.wallet, &args.tx, tx).await?; Ok(()) } diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index fcd57be745..d1ff8417a5 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -264,6 +264,8 @@ pub mod genesis_config { pub implicit_vp: String, /// Expected number of epochs per year pub epochs_per_year: u64, + /// Max signature per transaction + pub max_signatures_per_transaction: u8, /// PoS gain p pub pos_gain_p: Dec, /// PoS gain d @@ -620,10 +622,13 @@ pub mod genesis_config { implicit_vp_code_path, implicit_vp_sha256, epochs_per_year: parameters.epochs_per_year, + max_signatures_per_transaction: parameters + .max_signatures_per_transaction, pos_gain_p: parameters.pos_gain_p, pos_gain_d: parameters.pos_gain_d, staked_ratio: Dec::zero(), pos_inflation_amount: token::Amount::zero(), + #[cfg(not(feature = "mainnet"))] wrapper_tx_fees: parameters.wrapper_tx_fees, }; @@ -871,6 +876,8 @@ pub struct Parameters { pub implicit_vp_sha256: [u8; 32], /// Expected number of epochs per year (read only) pub epochs_per_year: u64, + /// Maximum amount of signatures per transaction + pub max_signatures_per_transaction: u8, /// PoS gain p (read only) pub pos_gain_p: Dec, /// PoS gain d (read only) @@ -991,12 +998,14 @@ pub fn genesis(num_validators: u64) -> Genesis { tx_whitelist: vec![], implicit_vp_code_path: vp_implicit_path.into(), implicit_vp_sha256: Default::default(), + max_signatures_per_transaction: 15, epochs_per_year: 525_600, /* seconds in yr (60*60*24*365) div seconds * per epoch (60 = min_duration) */ pos_gain_p: Dec::new(1, 1).expect("This can't fail"), pos_gain_d: Dec::new(1, 1).expect("This can't fail"), staked_ratio: Dec::zero(), pos_inflation_amount: token::Amount::zero(), + #[cfg(not(feature = "mainnet"))] wrapper_tx_fees: Some(token::Amount::native_whole(0)), }; let albert = EstablishedAccount { diff --git a/apps/src/lib/config/mod.rs b/apps/src/lib/config/mod.rs index 0bcb1b5f61..f6307c6bd1 100644 --- a/apps/src/lib/config/mod.rs +++ b/apps/src/lib/config/mod.rs @@ -283,7 +283,7 @@ impl Config { .and_then(|c| c.merge(config::File::with_name(file_name))) .and_then(|c| { c.merge( - config::Environment::with_prefix("namada").separator("__"), + config::Environment::with_prefix("NAMADA").separator("__"), ) }) .map_err(Error::ReadError)?; diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index 91f48e3e05..9e249ab705 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -94,6 +94,7 @@ where implicit_vp_code_path, implicit_vp_sha256, epochs_per_year, + max_signatures_per_transaction, pos_gain_p, pos_gain_d, staked_ratio, @@ -186,6 +187,7 @@ where tx_whitelist, implicit_vp_code_hash, epochs_per_year, + max_signatures_per_transaction, pos_gain_p, pos_gain_d, staked_ratio, @@ -296,10 +298,12 @@ where .unwrap(); if let Some(pk) = public_key { - let pk_storage_key = pk_key(&address); - self.wl_storage - .write_bytes(&pk_storage_key, pk.try_to_vec().unwrap()) - .unwrap(); + storage_api::account::set_public_key_at( + &mut self.wl_storage, + &address, + &pk, + 0, + )?; } for (key, value) in storage { @@ -332,9 +336,14 @@ where ) { // Initialize genesis implicit for genesis::ImplicitAccount { public_key } in accounts { - let address: Address = (&public_key).into(); - let pk_storage_key = pk_key(&address); - self.wl_storage.write(&pk_storage_key, public_key).unwrap(); + let address: address::Address = (&public_key).into(); + storage_api::account::set_public_key_at( + &mut self.wl_storage, + &address, + &public_key, + 0, + ) + .unwrap(); } } @@ -430,10 +439,13 @@ where .write_bytes(&Key::validity_predicate(addr), vp_code_hash) .expect("Unable to write user VP"); // Validator account key - let pk_key = pk_key(addr); - self.wl_storage - .write(&pk_key, &validator.account_key) - .expect("Unable to set genesis user public key"); + storage_api::account::set_public_key_at( + &mut self.wl_storage, + addr, + &validator.account_key, + 0, + ) + .unwrap(); // Balances // Account balance (tokens not staked in PoS) diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index e1b7f08883..428a6f14df 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -1283,38 +1283,6 @@ where response } - /// Lookup a validator's keypair for their established account from their - /// wallet. If the node is not validator, this function returns None - #[allow(dead_code)] - fn get_account_keypair(&self) -> Option { - let wallet_path = &self.base_dir.join(self.chain_id.as_str()); - let genesis_path = &self - .base_dir - .join(format!("{}.toml", self.chain_id.as_str())); - let mut wallet = crate::wallet::load_or_new_from_genesis( - wallet_path, - genesis::genesis_config::open_genesis_config(genesis_path).unwrap(), - ); - self.mode.get_validator_address().map(|addr| { - let sk: common::SecretKey = self - .wl_storage - .read(&pk_key(addr)) - .expect( - "A validator should have a public key associated with \ - it's established account", - ) - .expect( - "A validator should have a public key associated with \ - it's established account", - ); - let pk = sk.ref_to(); - wallet.find_key_by_pk(&pk, None).expect( - "A validator's established keypair should be stored in its \ - wallet", - ) - }) - } - #[cfg(not(feature = "mainnet"))] /// Check if the tx has a valid PoW solution. Unlike /// `apply_pow_solution_if_valid`, this won't invalidate the solution. diff --git a/apps/src/lib/node/ledger/shell/process_proposal.rs b/apps/src/lib/node/ledger/shell/process_proposal.rs index ff3bd60236..3a5b4935db 100644 --- a/apps/src/lib/node/ledger/shell/process_proposal.rs +++ b/apps/src/lib/node/ledger/shell/process_proposal.rs @@ -1537,12 +1537,13 @@ mod test_process_proposal { fn test_unsigned_wrapper_rejected() { let (mut shell, _recv, _, _) = test_utils::setup_at_height(3u64); let keypair = gen_keypair(); + let public_key = keypair.ref_to(); let mut outer_tx = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount: Default::default(), token: shell.wl_storage.storage.native_token.clone(), }, - keypair.ref_to(), + public_key, Epoch(0), Default::default(), #[cfg(not(feature = "mainnet"))] @@ -1551,6 +1552,7 @@ mod test_process_proposal { outer_tx.header.chain_id = shell.chain_id.clone(); outer_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); outer_tx.set_data(Data::new("transaction data".as_bytes().to_owned())); + let tx = outer_tx.to_bytes(); let response = { @@ -1568,12 +1570,14 @@ mod test_process_proposal { } }; + println!("{}", response.result.info); + assert_eq!(response.result.code, u32::from(ErrorCodes::InvalidSig)); assert_eq!( response.result.info, String::from( - "WrapperTx signature verification failed: Transaction doesn't \ - have any data with a signature." + "WrapperTx signature verification failed: The wrapper \ + signature is invalid." ) ); } @@ -1622,8 +1626,8 @@ mod test_process_proposal { panic!("Test failed") }; let expected_error = "WrapperTx signature verification \ - failed: Transaction doesn't have any \ - data with a signature."; + failed: The wrapper signature is \ + invalid."; assert_eq!( response.result.code, u32::from(ErrorCodes::InvalidSig) diff --git a/shared/src/ledger/args.rs b/shared/src/ledger/args.rs index 35081d3283..913cc12bb3 100644 --- a/shared/src/ledger/args.rs +++ b/shared/src/ledger/args.rs @@ -102,6 +102,8 @@ pub struct TxCustom { pub code_path: C::Data, /// Path to the data file pub data_path: Option, + /// The address that correspond to the signatures/signing-keys + pub owner: C::Address, } /// Transfer transaction arguments @@ -168,14 +170,14 @@ pub struct TxIbcTransfer { pub struct TxInitAccount { /// Common tx arguments pub tx: Tx, - /// Address of the source account - pub source: C::Address, /// Path to the VP WASM code file for the new account pub vp_code_path: PathBuf, /// Path to the TX WASM code file pub tx_code_path: PathBuf, /// Public key for the new account - pub public_key: C::PublicKey, + pub public_keys: Vec, + /// The account multisignature threshold + pub threshold: Option, } /// Transaction to initialize a new account @@ -183,12 +185,12 @@ pub struct TxInitAccount { pub struct TxInitValidator { /// Common tx arguments pub tx: Tx, - /// Source - pub source: C::Address, /// Signature scheme pub scheme: SchemeType, - /// Account key - pub account_key: Option, + /// Account keys + pub account_keys: Vec, + /// The account multisignature threshold + pub threshold: Option, /// Consensus key pub consensus_key: Option, /// Ethereum cold key @@ -211,15 +213,19 @@ pub struct TxInitValidator { /// Transaction to update a VP arguments #[derive(Clone, Debug)] -pub struct TxUpdateVp { +pub struct TxUpdateAccount { /// Common tx arguments pub tx: Tx, /// Path to the VP WASM code file - pub vp_code_path: PathBuf, + pub vp_code_path: Option, /// Path to the TX WASM code file pub tx_code_path: PathBuf, /// Address of the account whose VP is to be updated pub addr: C::Address, + /// Public keys + pub public_keys: Vec, + /// The account threshold + pub threshold: Option, } /// Bond arguments @@ -306,6 +312,15 @@ pub struct QueryConversions { pub epoch: Option, } +/// Query token balance(s) +#[derive(Clone, Debug)] +pub struct QueryAccount { + /// Common query args + pub query: Query, + /// Address of an owner + pub owner: C::Address, +} + /// Query token balance(s) #[derive(Clone, Debug)] pub struct QueryBalance { @@ -432,8 +447,10 @@ pub struct QueryRawBytes { pub struct Tx { /// Simulate applying the transaction pub dry_run: bool, - /// Dump the transaction bytes + /// Dump the transaction bytes to file pub dump_tx: bool, + /// The output directory path to where serialize the transaction + pub output_folder: Option, /// Submit the transaction even if it doesn't pass client checks pub force: bool, /// Do not wait for the transaction to be added to the blockchain @@ -446,6 +463,8 @@ pub struct Tx { /// Whether to force overwrite the above alias, if it is provided, in the /// wallet. pub wallet_alias_force: bool, + /// The fee payer signing key + pub fee_payer: Option, /// The amount being payed to include the transaction pub fee_amount: InputAmount, /// The token in which the fee is being paid @@ -457,9 +476,7 @@ pub struct Tx { /// The chain id for which the transaction is intended pub chain_id: Option, /// Sign the tx with the key for the given alias from your wallet - pub signing_key: Option, - /// Sign the tx with the keypair of the public key of the given address - pub signer: Option, + pub signing_keys: Vec, /// Path to the TX WASM code file to reveal PK pub tx_reveal_code_path: PathBuf, /// Sign the tx with the public key for the given alias from your wallet diff --git a/shared/src/ledger/eth_bridge/bridge_pool.rs b/shared/src/ledger/eth_bridge/bridge_pool.rs index a95d56d475..fbee9fdb11 100644 --- a/shared/src/ledger/eth_bridge/bridge_pool.rs +++ b/shared/src/ledger/eth_bridge/bridge_pool.rs @@ -46,7 +46,7 @@ pub async fn build_bridge_pool_tx< client: &C, wallet: &mut Wallet, args: args::EthereumBridgePool, -) -> Result<(Tx, Option
, common::PublicKey), Error> { +) -> Result<(Tx, Option
, Vec), Error> { let args::EthereumBridgePool { ref tx, asset, @@ -67,7 +67,7 @@ pub async fn build_bridge_pool_tx< transfer: TransferToEthereum { asset, recipient, - sender, + sender: sender.clone(), amount, }, gas_fee: GasFee { @@ -92,6 +92,7 @@ pub async fn build_bridge_pool_tx< wallet, tx, transfer_tx, + Some(sender), TxSigningKey::None, #[cfg(not(feature = "mainnet"))] false, diff --git a/shared/src/ledger/queries/shell.rs b/shared/src/ledger/queries/shell.rs index 7583f8bcf2..6cb42ce0c9 100644 --- a/shared/src/ledger/queries/shell.rs +++ b/shared/src/ledger/queries/shell.rs @@ -5,6 +5,7 @@ use masp_primitives::asset_type::AssetType; use masp_primitives::merkle_tree::MerklePath; use masp_primitives::sapling::Node; use namada_core::ledger::storage::LastBlock; +use namada_core::types::account::{Account, AccountPublicKeysMap}; use namada_core::types::address::Address; use namada_core::types::hash::Hash; use namada_core::types::storage::{BlockHeight, BlockResults, Key, KeySeg}; @@ -76,6 +77,12 @@ router! {SHELL, // was the transaction applied? ( "applied" / [tx_hash: Hash] ) -> Option = applied, + // Query account subspace + ( "account" / [owner: Address] ) -> Option = account, + + // Query public key revealad + ( "revealed" / [owner: Address] ) -> bool = revealed, + // IBC UpdateClient event ( "ibc_client_update" / [client_id: ClientId] / [consensus_height: BlockHeight] ) -> Option = ibc_client_update, @@ -440,6 +447,46 @@ where .cloned()) } +fn account( + ctx: RequestCtx<'_, D, H>, + owner: Address, +) -> storage_api::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)?; + + 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)?; + + Ok(Some(Account { + public_keys_map: AccountPublicKeysMap::from_iter(public_keys), + address: owner, + threshold: threshold.unwrap_or(1), + })) + } else { + Ok(None) + } +} + +fn revealed( + ctx: RequestCtx<'_, D, H>, + owner: Address, +) -> storage_api::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)?; + + Ok(!public_keys.is_empty()) +} + #[cfg(test)] mod test { diff --git a/shared/src/ledger/rpc.rs b/shared/src/ledger/rpc.rs index 90d05ebd13..5a809d23b8 100644 --- a/shared/src/ledger/rpc.rs +++ b/shared/src/ledger/rpc.rs @@ -9,7 +9,9 @@ use masp_primitives::asset_type::AssetType; use masp_primitives::merkle_tree::MerklePath; use masp_primitives::sapling::Node; use namada_core::ledger::storage::LastBlock; +#[cfg(not(feature = "mainnet"))] use namada_core::ledger::testnet_pow; +use namada_core::types::account::Account; use namada_core::types::address::Address; use namada_core::types::storage::Key; use namada_core::types::token::{ @@ -34,7 +36,7 @@ use crate::tendermint_rpc::Order; use crate::types::control_flow::{time, Halt, TryHalt}; use crate::types::governance::{ProposalVote, VotePower}; use crate::types::hash::Hash; -use crate::types::key::*; +use crate::types::key::common; use crate::types::storage::{BlockHeight, BlockResults, Epoch, PrefixValue}; use crate::types::token::balance_key; use crate::types::{storage, token}; @@ -142,15 +144,6 @@ pub async fn get_token_balance( query_storage_value(client, &balance_key).await } -/// Get account's public key stored in its storage sub-space -pub async fn get_public_key( - client: &C, - address: &Address, -) -> Option { - let key = pk_key(address); - query_storage_value(client, &key).await -} - /// Check if the given address is a known validator. pub async fn is_validator( client: &C, @@ -775,6 +768,42 @@ pub async fn query_bond( ) } +/// Query the accunt substorage space of an address +pub async fn get_account_info( + client: &C, + owner: &Address, +) -> Option { + unwrap_client_response::>( + RPC.shell().account(client, owner).await, + ) +} + +/// Query if the public_key is revealed +pub async fn is_public_key_revealed< + C: crate::ledger::queries::Client + Sync, +>( + client: &C, + owner: &Address, +) -> bool { + unwrap_client_response::(RPC.shell().revealed(client, owner).await) +} + +/// Query an account substorage at a specific index +pub async fn get_public_key_at( + client: &C, + owner: &Address, + index: u8, +) -> Option { + let account = unwrap_client_response::>( + RPC.shell().account(client, owner).await, + ); + if let Some(account) = account { + account.get_public_key_from_index(index) + } else { + None + } +} + /// Query a validator's unbonds for a given epoch pub async fn query_and_print_unbonds< C: crate::ledger::queries::Client + Sync, diff --git a/shared/src/ledger/signing.rs b/shared/src/ledger/signing.rs index b6b06f0dae..b8cdf01c3a 100644 --- a/shared/src/ledger/signing.rs +++ b/shared/src/ledger/signing.rs @@ -15,6 +15,7 @@ use masp_primitives::asset_type::AssetType; use masp_primitives::transaction::components::sapling::fees::{ InputView, OutputView, }; +use namada_core::types::account::AccountPublicKeysMap; use namada_core::types::address::{ masp, masp_tx_key, Address, ImplicitAddress, }; @@ -22,7 +23,7 @@ use namada_core::types::storage::Key; use namada_core::types::token::{ self, Amount, DenominatedAmount, MaspDenom, TokenAddress, }; -use namada_core::types::transaction::{pos, MIN_FEE}; +use namada_core::types::transaction::pos; use prost::Message; use serde::{Deserialize, Serialize}; use zeroize::Zeroizing; @@ -33,29 +34,27 @@ use crate::ibc::applications::transfer::msgs::transfer::{ use crate::ibc_proto::google::protobuf::Any; use crate::ledger::masp::make_asset_type; use crate::ledger::parameters::storage as parameter_storage; -use crate::ledger::rpc::{ - format_denominated_amount, query_wasm_code_hash, TxBroadcastData, -}; +use crate::ledger::rpc::{format_denominated_amount, query_wasm_code_hash}; use crate::ledger::tx::{ Error, TX_BOND_WASM, TX_CHANGE_COMMISSION_WASM, TX_IBC_WASM, TX_INIT_ACCOUNT_WASM, TX_INIT_PROPOSAL, TX_INIT_VALIDATOR_WASM, - TX_REVEAL_PK, TX_TRANSFER_WASM, TX_UNBOND_WASM, TX_UPDATE_VP_WASM, + TX_REVEAL_PK, TX_TRANSFER_WASM, TX_UNBOND_WASM, TX_UPDATE_ACCOUNT_WASM, TX_VOTE_PROPOSAL, TX_WITHDRAW_WASM, VP_USER_WASM, }; pub use crate::ledger::wallet::store::AddressVpType; use crate::ledger::wallet::{Wallet, WalletUtils}; use crate::ledger::{args, rpc}; -use crate::proto::{MaspBuilder, Section, Signature, Tx}; +use crate::proto::{MaspBuilder, MultiSignature, Section, Signature, Tx}; use crate::types::key::*; use crate::types::masp::{ExtendedViewingKey, PaymentAddress}; use crate::types::storage::Epoch; use crate::types::token::Transfer; +use crate::types::transaction::account::{InitAccount, UpdateAccount}; use crate::types::transaction::governance::{ InitProposalData, VoteProposalData, }; -use crate::types::transaction::{ - Fee, InitAccount, InitValidator, TxType, UpdateVp, WrapperTx, -}; +use crate::types::transaction::pos::InitValidator; +use crate::types::transaction::{Fee, TxType, WrapperTx}; #[cfg(feature = "std")] /// Env. var specifying where to store signing test vectors @@ -83,12 +82,12 @@ pub async fn find_pk< "Looking-up public key of {} from the ledger...", addr.encode() ); - rpc::get_public_key(client, addr).await.ok_or(Error::Other( - format!( + rpc::get_public_key_at(client, addr, 0) + .await + .ok_or(Error::Other(format!( "No public key found for the address {}", addr.encode() - ), - )) + ))) } Address::Implicit(ImplicitAddress(pkh)) => Ok(wallet .find_key_by_pkh(pkh, password) @@ -114,28 +113,20 @@ pub async fn find_pk< pub fn find_key_by_pk( wallet: &mut Wallet, args: &args::Tx, - keypair: &common::PublicKey, + public_key: &common::PublicKey, ) -> Result { - if *keypair == masp_tx_key().ref_to() { + if *public_key == masp_tx_key().ref_to() { // We already know the secret key corresponding to the MASP sentinal key Ok(masp_tx_key()) - } else if args - .signing_key - .as_ref() - .map(|x| x.ref_to() == *keypair) - .unwrap_or(false) - { - // We can lookup the secret key from the CLI arguments in this case - Ok(args.signing_key.clone().unwrap()) } else { // Otherwise we need to search the wallet for the secret key wallet - .find_key_by_pk(keypair, args.password.clone()) + .find_key_by_pk(public_key, args.password.clone()) .map_err(|err| { Error::Other(format!( "Unable to load the keypair from the wallet for public \ key {}. Failed with: {}", - keypair, err + public_key, err )) }) } @@ -163,32 +154,27 @@ pub async fn tx_signer< wallet: &mut Wallet, args: &args::Tx, default: TxSigningKey, -) -> Result<(Option
, common::PublicKey), Error> { - let signer = if args.dry_run { - // We cannot override the signer if we're doing a dry run - default - } else if let Some(signing_key) = &args.signing_key { - // Otherwise use the signing key override provided by user - return Ok((None, signing_key.ref_to())); +) -> Result, Error> { + let signer = if !&args.signing_keys.is_empty() { + let public_keys = + args.signing_keys.iter().map(|key| key.ref_to()).collect(); + return Ok(public_keys); } else if let Some(verification_key) = &args.verification_key { - return Ok((None, verification_key.clone())); - } else if let Some(signer) = &args.signer { - // Otherwise use the signer address provided by user - TxSigningKey::WalletAddress(signer.clone()) + return Ok(vec![verification_key.clone()]); } else { // Otherwise use the signer determined by the caller default }; + // Now actually fetch the signing key and apply it match signer { TxSigningKey::WalletAddress(signer) if signer == masp() => { - Ok((None, masp_tx_key().ref_to())) + Ok(vec![masp_tx_key().ref_to()]) } - TxSigningKey::WalletAddress(signer) => Ok(( - Some(signer.clone()), + TxSigningKey::WalletAddress(signer) => Ok(vec![ find_pk::(client, wallet, &signer, args.password.clone()) .await?, - )), + ]), TxSigningKey::None => other_err( "All transactions must be signed; please either specify the key \ or the address from which to look up the signing key." @@ -209,24 +195,83 @@ pub async fn sign_tx( wallet: &mut Wallet, tx: &mut Tx, args: &args::Tx, - keypair: &common::PublicKey, + public_keys_index_map: &AccountPublicKeysMap, + public_keys: &[common::PublicKey], + threshold: u8, ) -> Result<(), Error> { - let keypair = find_key_by_pk(wallet, args, keypair)?; + let keypairs = public_keys + .iter() + .filter_map(|public_key| { + match find_key_by_pk(wallet, args, public_key) { + Ok(secret_key) => Some(secret_key), + Err(_) => None, + } + }) + .collect::>(); + // Sign over the transacttion data - tx.add_section(Section::Signature(Signature::new( + let multisignature_section = MultiSignature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &keypair, - ))); + &keypairs, + public_keys_index_map, + ); + + if (multisignature_section.total_signatures() < threshold) && !args.force { + return Err(Error::MissingSigningKeys( + threshold, + multisignature_section.total_signatures(), + )); + } + + // Sign over the transaction targets + tx.add_section(Section::SectionSignature(multisignature_section)); + // Remove all the sensitive sections tx.protocol_filter(); - // Then sign over the bound wrapper + + let fee_payer = match &args.fee_payer { + Some(keypair) => keypair, + None => { + if let Some(public_key) = keypairs.get(0) { + public_key + } else { + return Err(Error::InvalidFeePayer( + "Either --signing-keys or --fee-payer must be available." + .to_string(), + )); + } + } + }; + tx.add_section(Section::Signature(Signature::new( tx.sechashes(), - &keypair, + fee_payer, ))); + Ok(()) } +/// Return the necessary data regarding an account to be able to generate a +/// multisignature section +pub async fn aux_signing_data( + client: &C, + owner: Option
, + public_keys: Vec, +) -> (AccountPublicKeysMap, u8) { + if let Some(owner) = owner { + let account = rpc::get_account_info::(client, &owner).await; + let (public_keys_index_map, threshold) = if let Some(account) = account + { + (account.public_keys_map, account.threshold) + } else { + (AccountPublicKeysMap::from_iter(public_keys), 1u8) + }; + (public_keys_index_map, threshold) + } else { + (AccountPublicKeysMap::from_iter(public_keys), 0u8) + } +} + #[cfg(not(feature = "mainnet"))] /// Solve the PoW challenge if balance is insufficient to pay transaction fees /// or if solution is explicitly requested. @@ -372,149 +417,6 @@ pub async fn wrap_tx< tx } -/// Create a wrapper tx from a normal tx. Get the hash of the -/// wrapper and its payload which is needed for monitoring its -/// progress on chain. -pub async fn sign_wrapper< - C: crate::ledger::queries::Client + Sync, - U: WalletUtils, ->( - client: &C, - #[allow(unused_variables)] wallet: &mut Wallet, - args: &args::Tx, - epoch: Epoch, - mut tx: Tx, - keypair: &common::SecretKey, - #[cfg(not(feature = "mainnet"))] requires_pow: bool, -) -> TxBroadcastData { - let fee_amount = if cfg!(feature = "mainnet") { - Amount::native_whole(MIN_FEE) - } else { - let wrapper_tx_fees_key = parameter_storage::get_wrapper_tx_fees_key(); - rpc::query_storage_value::( - client, - &wrapper_tx_fees_key, - ) - .await - .unwrap_or_default() - }; - let fee_token = &args.fee_token; - let source = Address::from(&keypair.ref_to()); - let balance_key = token::balance_key(fee_token, &source); - let balance = - rpc::query_storage_value::(client, &balance_key) - .await - .unwrap_or_default(); - let is_bal_sufficient = fee_amount <= balance; - if !is_bal_sufficient { - let token_addr = TokenAddress { - address: args.fee_token.clone(), - sub_prefix: None, - }; - let err_msg = format!( - "The wrapper transaction source doesn't have enough balance to \ - pay fee {}, got {}.", - format_denominated_amount(client, &token_addr, fee_amount).await, - format_denominated_amount(client, &token_addr, balance).await, - ); - eprintln!("{}", err_msg); - if !args.force && cfg!(feature = "mainnet") { - panic!("{}", err_msg); - } - } - - #[cfg(not(feature = "mainnet"))] - // A PoW solution can be used to allow zero-fee testnet transactions - let pow_solution: Option = { - // If the address derived from the keypair doesn't have enough balance - // to pay for the fee, allow to find a PoW solution instead. - if requires_pow || !is_bal_sufficient { - println!( - "The transaction requires the completion of a PoW challenge." - ); - // Obtain a PoW challenge for faucet withdrawal - let challenge = - rpc::get_testnet_pow_challenge(client, source).await; - - // Solve the solution, this blocks until a solution is found - let solution = challenge.solve(); - Some(solution) - } else { - None - } - }; - - // This object governs how the payload will be processed - tx.update_header(TxType::Wrapper(Box::new(WrapperTx::new( - Fee { - amount: fee_amount, - token: fee_token.clone(), - }, - keypair.ref_to(), - epoch, - args.gas_limit.clone(), - #[cfg(not(feature = "mainnet"))] - pow_solution, - )))); - tx.header.chain_id = args.chain_id.clone().unwrap(); - tx.header.expiration = args.expiration; - - #[cfg(feature = "std")] - // Attempt to decode the construction - if let Ok(path) = env::var(ENV_VAR_LEDGER_LOG_PATH) { - let mut tx = tx.clone(); - // Contract the large data blobs in the transaction - tx.wallet_filter(); - // Convert the transaction to Ledger format - let decoding = to_ledger_vector(client, wallet, &tx) - .await - .expect("unable to decode transaction"); - let output = serde_json::to_string(&decoding) - .expect("failed to serialize decoding"); - // Record the transaction at the identified path - let mut f = File::options() - .append(true) - .create(true) - .open(path) - .expect("failed to open test vector file"); - writeln!(f, "{},", output) - .expect("unable to write test vector to file"); - } - #[cfg(feature = "std")] - // Attempt to decode the construction - if let Ok(path) = env::var(ENV_VAR_TX_LOG_PATH) { - let mut tx = tx.clone(); - // Contract the large data blobs in the transaction - tx.wallet_filter(); - // Record the transaction at the identified path - let mut f = File::options() - .append(true) - .create(true) - .open(path) - .expect("failed to open test vector file"); - writeln!(f, "{:x?},", tx).expect("unable to write test vector to file"); - } - - // Remove all the sensitive sections - tx.protocol_filter(); - // Then sign over the bound wrapper committing to all other sections - tx.add_section(Section::Signature(Signature::new(tx.sechashes(), keypair))); - // We use this to determine when the wrapper tx makes it on-chain - let wrapper_hash = tx.header_hash().to_string(); - // We use this to determine when the decrypted inner tx makes it - // on-chain - let decrypted_hash = tx - .clone() - .update_header(TxType::Raw) - .header_hash() - .to_string(); - TxBroadcastData::Wrapper { - tx, - wrapper_hash, - decrypted_hash, - } -} - #[allow(clippy::result_large_err)] fn other_err(string: String) -> Result { Err(Error::Other(string)) @@ -766,9 +668,10 @@ pub async fn to_ledger_vector< .unwrap(); let reveal_pk_hash = query_wasm_code_hash(client, TX_REVEAL_PK).await.unwrap(); - let update_vp_hash = query_wasm_code_hash(client, TX_UPDATE_VP_WASM) - .await - .unwrap(); + let update_account_hash = + query_wasm_code_hash(client, TX_UPDATE_ACCOUNT_WASM) + .await + .unwrap(); let transfer_hash = query_wasm_code_hash(client, TX_TRANSFER_WASM) .await .unwrap(); @@ -839,12 +742,12 @@ pub async fn to_ledger_vector< tv.output.extend(vec![ format!("Type : Init Account"), - format!("Public key : {}", init_account.public_key), + format!("Public key : {:?}", init_account.public_keys), format!("VP type : {}", vp_code), ]); tv.output_expert.extend(vec![ - format!("Public key : {}", init_account.public_key), + format!("Public key : {:?}", init_account.public_keys), format!("VP type : {}", HEXLOWER.encode(&extra.0)), ]); } else if code_hash == init_validator_hash { @@ -869,7 +772,7 @@ pub async fn to_ledger_vector< tv.output.extend(vec![ format!("Type : Init Validator"), - format!("Account key : {}", init_validator.account_key), + format!("Account key : {:?}", init_validator.account_keys), format!("Consensus key : {}", init_validator.consensus_key), format!("Protocol key : {}", init_validator.protocol_key), format!("DKG key : {}", init_validator.dkg_key), @@ -882,7 +785,7 @@ pub async fn to_ledger_vector< ]); tv.output_expert.extend(vec![ - format!("Account key : {}", init_validator.account_key), + format!("Account key : {:?}", init_validator.account_keys), format!("Consensus key : {}", init_validator.consensus_key), format!("Protocol key : {}", init_validator.protocol_key), format!("DKG key : {}", init_validator.dkg_key), @@ -980,36 +883,40 @@ pub async fn to_ledger_vector< tv.output_expert .extend(vec![format!("Public key : {}", public_key)]); - } else if code_hash == update_vp_hash { + } else if code_hash == update_account_hash { let transfer = - UpdateVp::try_from_slice(&tx.data().ok_or_else(|| { + UpdateAccount::try_from_slice(&tx.data().ok_or_else(|| { std::io::Error::from(ErrorKind::InvalidData) })?)?; tv.name = "Update VP 0".to_string(); - let extra = tx - .get_section(&transfer.vp_code_hash) - .and_then(|x| Section::extra_data_sec(x.as_ref())) - .expect("unable to load vp code") - .code - .hash(); - let vp_code = if extra == user_hash { - "User".to_string() - } else { - HEXLOWER.encode(&extra.0) - }; - - tv.output.extend(vec![ - format!("Type : Update VP"), - format!("Address : {}", transfer.addr), - format!("VP type : {}", vp_code), - ]); + match &transfer.vp_code_hash { + Some(hash) => { + let extra = tx + .get_section(hash) + .and_then(|x| Section::extra_data_sec(x.as_ref())) + .expect("unable to load vp code") + .code + .hash(); + let vp_code = if extra == user_hash { + "User".to_string() + } else { + HEXLOWER.encode(&extra.0) + }; + tv.output.extend(vec![ + format!("Type : Update VP"), + format!("Address : {}", transfer.addr), + format!("VP type : {}", vp_code), + ]); - tv.output_expert.extend(vec![ - format!("Address : {}", transfer.addr), - format!("VP type : {}", HEXLOWER.encode(&extra.0)), - ]); + tv.output_expert.extend(vec![ + format!("Address : {}", transfer.addr), + format!("VP type : {}", HEXLOWER.encode(&extra.0)), + ]); + } + None => (), + }; } else if code_hash == transfer_hash { let transfer = Transfer::try_from_slice(&tx.data().ok_or_else(|| { diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index 36644a3e14..f2aba5974e 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -15,7 +15,7 @@ use masp_primitives::transaction::components::transparent::fees::{ InputView as TransparentInputView, OutputView as TransparentOutputView, }; use masp_primitives::transaction::components::Amount; -use namada_core::types::address::{masp, masp_tx_key, Address}; +use namada_core::types::address::{masp, Address}; use namada_core::types::dec::Dec; use namada_core::types::storage::Key; use namada_core::types::token::MaspDenom; @@ -46,7 +46,8 @@ use crate::types::key::*; use crate::types::masp::TransferTarget; use crate::types::storage::{Epoch, RESERVED_ADDRESS_PREFIX}; use crate::types::time::DateTimeUtc; -use crate::types::transaction::{pos, InitAccount, TxType, UpdateVp}; +use crate::types::transaction::account::{InitAccount, UpdateAccount}; +use crate::types::transaction::{pos, TxType}; use crate::types::{storage, token}; use crate::vm; use crate::vm::WasmValidationError; @@ -62,7 +63,7 @@ pub const TX_VOTE_PROPOSAL: &str = "tx_vote_proposal.wasm"; /// Reveal public key transaction WASM path pub const TX_REVEAL_PK: &str = "tx_reveal_pk.wasm"; /// Update validity predicate WASM path -pub const TX_UPDATE_VP_WASM: &str = "tx_update_vp.wasm"; +pub const TX_UPDATE_ACCOUNT_WASM: &str = "tx_update_account.wasm"; /// Transfer transaction WASM path pub const TX_TRANSFER_WASM: &str = "tx_transfer.wasm"; /// IBC transaction WASM path @@ -206,6 +207,15 @@ pub enum Error { /// Epoch not in storage #[error("Proposal end epoch is not in the storage.")] EpochNotInStorage, + /// Couldn't understand who the fee pair is + #[error("{0}")] + InvalidFeePayer(String), + /// Account threshold is not set + #[error("Account threshold must be set.")] + MissingAccountThreshold, + /// Not enough signature + #[error("Account threshold is {0} but the valid signatures are {1}.")] + MissingSigningKeys(u8, u8), /// Other Errors that may show up when using the interface #[error("{0}")] Other(String), @@ -219,6 +229,8 @@ pub enum ProcessTxResponse { Broadcast(Response), /// Result of dry running transaction DryRun, + /// Dump transaction to disk + Dump, } impl ProcessTxResponse { @@ -241,13 +253,29 @@ pub async fn prepare_tx< wallet: &mut Wallet, args: &args::Tx, tx: Tx, + owner: Option
, default_signer: TxSigningKey, #[cfg(not(feature = "mainnet"))] requires_pow: bool, -) -> Result<(Tx, Option
, common::PublicKey), Error> { - let (signer_addr, signer_pk) = +) -> Result<(Tx, Option
, Vec), Error> { + let signer_public_keys = tx_signer::(client, wallet, args, default_signer.clone()).await?; + + let fee_payer = match &args.fee_payer { + Some(keypair) => keypair.ref_to(), + None => { + if let Some(public_key) = signer_public_keys.get(0) { + public_key.clone() + } else { + return Err(Error::InvalidFeePayer( + "Either --signing-keys or --fee-payer must be available." + .to_string(), + )); + } + } + }; + if args.dry_run { - Ok((tx, signer_addr, signer_pk)) + Ok((tx, owner, signer_public_keys)) } else { let epoch = rpc::query_epoch(client).await; Ok(( @@ -257,13 +285,13 @@ pub async fn prepare_tx< args, epoch, tx.clone(), - &signer_pk, + &fee_payer, #[cfg(not(feature = "mainnet"))] requires_pow, ) .await, - signer_addr, - signer_pk, + owner, + signer_public_keys, )) } } @@ -339,7 +367,7 @@ pub async fn build_reveal_pk< client: &C, wallet: &mut Wallet, args: args::RevealPk, -) -> Result, common::PublicKey)>, Error> { +) -> Result, Vec)>, Error> { let args::RevealPk { tx: args, public_key, @@ -381,7 +409,7 @@ pub async fn has_revealed_pk( client: &C, addr: &Address, ) -> bool { - rpc::get_public_key(client, addr).await.is_some() + rpc::is_public_key_revealed(client, addr).await } /// Submit transaction to reveal the given public key @@ -393,7 +421,7 @@ pub async fn build_reveal_pk_aux< wallet: &mut Wallet, public_key: &common::PublicKey, args: &args::Tx, -) -> Result<(Tx, Option
, common::PublicKey), Error> { +) -> Result<(Tx, Option
, Vec), Error> { let addr: Address = public_key.into(); println!("Submitting a tx to reveal the public key for address {addr}..."); let tx_data = public_key.try_to_vec().map_err(Error::EncodeKeyFailure)?; @@ -411,11 +439,14 @@ pub async fn build_reveal_pk_aux< tx.set_data(Data::new(tx_data)); tx.set_code(Code::from_hash(tx_code_hash)); + let owner: Address = public_key.into(); + prepare_tx::( client, wallet, args, tx, + Some(owner), TxSigningKey::WalletAddress(addr), #[cfg(not(feature = "mainnet"))] false, @@ -623,7 +654,7 @@ pub async fn build_validator_commission_change< client: &C, wallet: &mut Wallet, args: args::CommissionRateChange, -) -> Result<(Tx, Option
, common::PublicKey), Error> { +) -> Result<(Tx, Option
, Vec), Error> { let epoch = rpc::query_epoch(client).await; let tx_code_hash = @@ -702,6 +733,7 @@ pub async fn build_validator_commission_change< wallet, &args.tx, tx, + Some(default_signer.clone()), TxSigningKey::WalletAddress(default_signer), #[cfg(not(feature = "mainnet"))] false, @@ -717,7 +749,7 @@ pub async fn build_unjail_validator< client: &C, wallet: &mut Wallet, args: args::TxUnjailValidator, -) -> Result<(Tx, Option
, common::PublicKey), Error> { +) -> Result<(Tx, Option
, Vec), Error> { if !rpc::is_validator(client, &args.validator).await { eprintln!("The given address {} is not a validator.", &args.validator); if !args.tx.force { @@ -747,6 +779,7 @@ pub async fn build_unjail_validator< wallet, &args.tx, tx, + Some(default_signer.clone()), TxSigningKey::WalletAddress(default_signer), #[cfg(not(feature = "mainnet"))] false, @@ -792,6 +825,7 @@ pub async fn submit_unjail_validator< wallet, &args.tx, tx, + Some(default_signer.clone()), TxSigningKey::WalletAddress(default_signer), #[cfg(not(feature = "mainnet"))] false, @@ -808,7 +842,7 @@ pub async fn build_withdraw< client: &C, wallet: &mut Wallet, args: args::Withdraw, -) -> Result<(Tx, Option
, common::PublicKey), Error> { +) -> Result<(Tx, Option
, Vec), Error> { let epoch = rpc::query_epoch(client).await; let validator = @@ -864,6 +898,7 @@ pub async fn build_withdraw< wallet, &args.tx, tx, + Some(default_signer.clone()), TxSigningKey::WalletAddress(default_signer), #[cfg(not(feature = "mainnet"))] false, @@ -883,7 +918,7 @@ pub async fn build_unbond< ( Tx, Option
, - common::PublicKey, + Vec, Option<(Epoch, token::Amount)>, ), Error, @@ -952,18 +987,19 @@ pub async fn build_unbond< tx.set_code(Code::from_hash(tx_code_hash)); let default_signer = args.source.unwrap_or_else(|| args.validator.clone()); - let (tx, signer_addr, default_signer) = prepare_tx::( + let (tx, signer_addr, public_keys) = prepare_tx::( client, wallet, &args.tx, tx, - TxSigningKey::WalletAddress(default_signer), + Some(default_signer.clone()), + TxSigningKey::WalletAddress(default_signer.clone()), #[cfg(not(feature = "mainnet"))] false, ) .await?; - Ok((tx, signer_addr, default_signer, latest_withdrawal_pre)) + Ok((tx, signer_addr, public_keys, latest_withdrawal_pre)) } /// Query the unbonds post-tx @@ -1036,7 +1072,7 @@ pub async fn build_bond< client: &C, wallet: &mut Wallet, args: args::Bond, -) -> Result<(Tx, Option
, common::PublicKey), Error> { +) -> Result<(Tx, Option
, Vec), Error> { let validator = known_validator_or_err(args.validator.clone(), args.tx.force, client) .await?; @@ -1089,6 +1125,7 @@ pub async fn build_bond< wallet, &args.tx, tx, + Some(default_signer.clone()), TxSigningKey::WalletAddress(default_signer), #[cfg(not(feature = "mainnet"))] false, @@ -1134,7 +1171,7 @@ pub async fn build_ibc_transfer< client: &C, wallet: &mut Wallet, args: args::TxIbcTransfer, -) -> Result<(Tx, Option
, common::PublicKey), Error> { +) -> Result<(Tx, Option
, Vec), Error> { // Check that the source address exists on chain let source = source_exists_or_err(args.source.clone(), args.tx.force, client) @@ -1230,6 +1267,7 @@ pub async fn build_ibc_transfer< wallet, &args.tx, tx, + Some(args.source.clone()), TxSigningKey::WalletAddress(args.source), #[cfg(not(feature = "mainnet"))] false, @@ -1320,8 +1358,16 @@ pub async fn build_transfer< wallet: &mut Wallet, shielded: &mut ShieldedContext, mut args: args::TxTransfer, -) -> Result<(Tx, Option
, common::PublicKey, Option, bool), Error> -{ +) -> Result< + ( + Tx, + Option
, + Vec, + Option, + bool, + ), + Error, +> { let source = args.source.effective_address(); let target = args.target.effective_address(); let token = args.token.clone(); @@ -1384,21 +1430,16 @@ pub async fn build_transfer< // signer. Also, if the transaction is shielded, redact the amount and token // types by setting the transparent value to 0 and token type to a constant. // This has no side-effect because transaction is to self. - let (_amount, token) = if source == masp_addr && target == masp_addr { - // TODO Refactor me, we shouldn't rely on any specific token here. - (token::Amount::default(), args.native_token.clone()) - } else { - (validated_amount.amount, token) - }; + let (_amount, token, shielded_gas) = + if source == masp_addr && target == masp_addr { + // TODO Refactor me, we shouldn't rely on any specific token here. + (token::Amount::default(), args.native_token.clone(), true) + } else { + (validated_amount.amount, token, false) + }; let default_signer = TxSigningKey::WalletAddress(args.source.effective_address()); - // If our chosen signer is the MASP sentinel key, then our shielded inputs - // will need to cover the gas fees. - let chosen_signer = - tx_signer::(client, wallet, &args.tx, default_signer.clone()) - .await? - .1; - let shielded_gas = masp_tx_key().ref_to() == chosen_signer; + // Determine whether to pin this transaction to a storage key let key = match &args.target { TransferTarget::PaymentAddress(pa) if pa.is_pinned() => Some(pa.hash()), @@ -1407,6 +1448,8 @@ pub async fn build_transfer< #[cfg(not(feature = "mainnet"))] let is_source_faucet = rpc::is_faucet_account(client, &source).await; + #[cfg(feature = "mainnet")] + let is_source_faucet = false; let tx_code_hash = query_wasm_code_hash(client, args.tx_code_path.to_str().unwrap()) @@ -1474,6 +1517,21 @@ pub async fn build_transfer< // Link the Transfer to the MASP Transaction by hash code shielded: masp_hash, }; + + #[cfg(not(feature = "mainnet"))] + let owner = if is_source_faucet || source == masp_addr { + None + } else { + Some(source.clone()) + }; + + #[cfg(feature = "mainnet")] + let owner = if source == masp_addr { + None + } else { + Some(source.clone()) + }; + tracing::debug!("Transfer data {:?}", transfer); // Encode the Transfer and store it beside the MASP transaction let data = transfer @@ -1484,20 +1542,22 @@ pub async fn build_transfer< tx.set_code(Code::from_hash(tx_code_hash)); // Dry-run/broadcast/submit the transaction - let (tx, signer_addr, def_key) = prepare_tx::( + let (tx, signer_addr, public_keys) = prepare_tx::( client, wallet, &args.tx, tx, + owner, default_signer.clone(), #[cfg(not(feature = "mainnet"))] is_source_faucet, ) .await?; + Ok(( tx, signer_addr, - def_key, + public_keys, shielded_tx_epoch, is_source_faucet, )) @@ -1511,8 +1571,8 @@ pub async fn build_init_account< client: &C, wallet: &mut Wallet, args: args::TxInitAccount, -) -> Result<(Tx, Option
, common::PublicKey), Error> { - let public_key = args.public_key; +) -> Result<(Tx, Option
, Vec), Error> { + let public_keys = args.public_keys; let vp_code_hash = query_wasm_code_hash(client, args.vp_code_path.to_str().unwrap()) @@ -1524,14 +1584,27 @@ pub async fn build_init_account< .await .unwrap(); + let threshold = match args.threshold { + Some(threshold) => threshold, + None => { + if public_keys.len() == 1 { + 1u8 + } else { + return Err(Error::MissingAccountThreshold); + } + } + }; + let mut tx = Tx::new(TxType::Raw); tx.header.chain_id = args.tx.chain_id.clone().unwrap(); tx.header.expiration = args.tx.expiration; let extra = tx.add_section(Section::ExtraData(Code::from_hash(vp_code_hash))); - let data = InitAccount { - public_key, + + let data: InitAccount = InitAccount { + public_keys, vp_code_hash: extra.get_hash(), + threshold, }; let data = data.try_to_vec().map_err(Error::EncodeTxFailure)?; tx.set_data(Data::new(data)); @@ -1542,7 +1615,8 @@ pub async fn build_init_account< wallet, &args.tx, tx, - TxSigningKey::WalletAddress(args.source), + None, + TxSigningKey::None, #[cfg(not(feature = "mainnet"))] false, ) @@ -1550,14 +1624,14 @@ pub async fn build_init_account< } /// Submit a transaction to update a VP -pub async fn build_update_vp< +pub async fn build_update_account< C: crate::ledger::queries::Client + Sync, U: WalletUtils, >( client: &C, wallet: &mut Wallet, - args: args::TxUpdateVp, -) -> Result<(Tx, Option
, common::PublicKey), Error> { + args: args::TxUpdateAccount, +) -> Result<(Tx, Option
, Vec), Error> { let addr = args.addr.clone(); // Check that the address is established and exists on chain @@ -1600,10 +1674,19 @@ pub async fn build_update_vp< } }?; - let vp_code_hash = - query_wasm_code_hash(client, args.vp_code_path.to_str().unwrap()) - .await - .unwrap(); + let public_keys = args.public_keys; + let threshold = args.threshold; + + let vp_code_hash = match args.vp_code_path { + Some(code_path) => { + let vp_hash = + query_wasm_code_hash(client, code_path.to_str().unwrap()) + .await + .unwrap(); + Some(vp_hash) + } + None => None, + }; let tx_code_hash = query_wasm_code_hash(client, args.tx_code_path.to_str().unwrap()) @@ -1613,12 +1696,23 @@ pub async fn build_update_vp< let mut tx = Tx::new(TxType::Raw); tx.header.chain_id = args.tx.chain_id.clone().unwrap(); tx.header.expiration = args.tx.expiration; - let extra = - tx.add_section(Section::ExtraData(Code::from_hash(vp_code_hash))); - let data = UpdateVp { + + let extra_hash = match vp_code_hash { + Some(vp_code_hash) => { + let tx_section = tx + .add_section(Section::ExtraData(Code::from_hash(vp_code_hash))); + Some(tx_section.get_hash()) + } + None => None, + }; + + let data = UpdateAccount { addr, - vp_code_hash: extra.get_hash(), + vp_code_hash: extra_hash, + public_keys, + threshold, }; + let data = data.try_to_vec().map_err(Error::EncodeTxFailure)?; tx.set_data(Data::new(data)); tx.set_code(Code::from_hash(tx_code_hash)); @@ -1628,6 +1722,7 @@ pub async fn build_update_vp< wallet, &args.tx, tx, + Some(args.addr.clone()), TxSigningKey::WalletAddress(args.addr), #[cfg(not(feature = "mainnet"))] false, @@ -1643,10 +1738,11 @@ pub async fn build_custom< client: &C, wallet: &mut Wallet, args: args::TxCustom, -) -> Result<(Tx, Option
, common::PublicKey), Error> { +) -> Result<(Tx, Option
, Vec), Error> { let mut tx = Tx::new(TxType::Raw); tx.header.chain_id = args.tx.chain_id.clone().unwrap(); tx.header.expiration = args.tx.expiration; + args.data_path.map(|data| tx.set_data(Data::new(data))); tx.set_code(Code::new(args.code_path)); @@ -1655,6 +1751,7 @@ pub async fn build_custom< wallet, &args.tx, tx, + Some(args.owner), TxSigningKey::None, #[cfg(not(feature = "mainnet"))] false, From 673d1617a81d40eacad767e867b53e45bc2a2585 Mon Sep 17 00:00:00 2001 From: Gianmarco Fraccaroli Date: Thu, 20 Jul 2023 11:25:52 +0200 Subject: [PATCH 115/120] docs: update tx definitions --- .../src/specs/ledger/default-transactions.md | 2 +- encoding_spec/src/main.rs | 21 +++++++++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/documentation/dev/src/specs/ledger/default-transactions.md b/documentation/dev/src/specs/ledger/default-transactions.md index fb254f0cd3..d1e36b923a 100644 --- a/documentation/dev/src/specs/ledger/default-transactions.md +++ b/documentation/dev/src/specs/ledger/default-transactions.md @@ -28,7 +28,7 @@ Transparently transfer `amount` of fungible `token` from the `source` to the `ta Attach [Transfer](../encoding.md#transfer) to the `data`. -### tx_update_vp +### tx_update_account Update a validity predicate of an established account. diff --git a/encoding_spec/src/main.rs b/encoding_spec/src/main.rs index b9e2b034ef..5889b03b8d 100644 --- a/encoding_spec/src/main.rs +++ b/encoding_spec/src/main.rs @@ -70,10 +70,13 @@ fn main() -> Result<(), Box> { let public_key_schema = PublicKey::schema_container(); // TODO update after let signature_schema = Signature::schema_container(); - let init_account_schema = transaction::InitAccount::schema_container(); - let init_validator_schema = transaction::InitValidator::schema_container(); + let init_account_schema = + transaction::account::InitAccount::schema_container(); + let init_validator_schema = + transaction::pos::InitValidator::schema_container(); let token_transfer_schema = token::Transfer::schema_container(); - let update_vp_schema = transaction::UpdateVp::schema_container(); + let update_account = + transaction::account::UpdateAccount::schema_container(); let pos_bond_schema = pos::Bond::schema_container(); let pos_withdraw_schema = pos::Withdraw::schema_container(); let wrapper_tx_schema = transaction::WrapperTx::schema_container(); @@ -98,7 +101,7 @@ fn main() -> Result<(), Box> { definitions.extend(init_account_schema.definitions); definitions.extend(init_validator_schema.definitions); definitions.extend(token_transfer_schema.definitions); - definitions.extend(update_vp_schema.definitions); + definitions.extend(update_account.definitions); definitions.extend(pos_bond_schema.definitions); definitions.extend(pos_withdraw_schema.definitions); definitions.extend(wrapper_tx_schema.definitions); @@ -179,11 +182,11 @@ fn main() -> Result<(), Box> { ).with_rust_doc_link("https://dev.namada.net/master/rustdoc/namada/types/token/struct.Transfer.html"); tables.push(token_transfer_table); - let update_vp_definition = - definitions.remove(&update_vp_schema.declaration).unwrap(); - let update_vp_table = - definition_to_table(update_vp_schema.declaration, update_vp_definition).with_rust_doc_link("https://dev.namada.net/master/rustdoc/namada/types/transaction/struct.UpdateVp.html"); - tables.push(update_vp_table); + let update_account_definition = + definitions.remove(&update_account.declaration).unwrap(); + let update_accoun_table = + definition_to_table(update_account.declaration, update_account_definition).with_rust_doc_link("https://dev.namada.net/master/rustdoc/namada/types/transaction/struct.UpdateVp.html"); + tables.push(update_accoun_table); let pos_bond_definition = definitions.remove(&pos_bond_schema.declaration).unwrap(); From a56ae26164b82adc0a6790ebbb8641fa2ed669ea Mon Sep 17 00:00:00 2001 From: Gianmarco Fraccaroli Date: Thu, 20 Jul 2023 11:27:26 +0200 Subject: [PATCH 116/120] wasm: added new account methods to tx_prelude, refactor signature verifiation, rebuild wasm_for_tests --- tx_prelude/src/account.rs | 18 ++++ tx_prelude/src/lib.rs | 1 + tx_prelude/src/proof_of_stake.rs | 13 ++- vp_prelude/src/lib.rs | 23 +++- wasm/checksums.json | 40 +++---- wasm/wasm_source/Cargo.toml | 2 +- wasm/wasm_source/Makefile | 2 +- wasm/wasm_source/src/lib.rs | 4 +- wasm/wasm_source/src/tx_init_account.rs | 15 ++- wasm/wasm_source/src/tx_init_validator.rs | 2 +- wasm/wasm_source/src/tx_update_account.rs | 45 ++++++++ wasm/wasm_source/src/vp_implicit.rs | 122 ++++++++++++--------- wasm/wasm_source/src/vp_testnet_faucet.rs | 38 +++---- wasm/wasm_source/src/vp_user.rs | 124 +++++++++++++--------- wasm/wasm_source/src/vp_validator.rs | 123 ++++++++++++--------- wasm_for_tests/tx_memory_limit.wasm | Bin 440249 -> 453090 bytes wasm_for_tests/tx_mint_tokens.wasm | Bin 574210 -> 196 bytes wasm_for_tests/tx_no_op.wasm | Bin 359578 -> 371892 bytes wasm_for_tests/tx_proposal_code.wasm | Bin 494389 -> 506391 bytes wasm_for_tests/tx_read_storage_key.wasm | Bin 443432 -> 456095 bytes wasm_for_tests/tx_write.wasm | Bin 446580 -> 459426 bytes wasm_for_tests/tx_write_storage_key.wasm | Bin 465585 -> 465585 bytes wasm_for_tests/vp_always_false.wasm | Bin 410553 -> 422148 bytes wasm_for_tests/vp_always_true.wasm | Bin 410553 -> 422148 bytes wasm_for_tests/vp_eval.wasm | Bin 488056 -> 502024 bytes wasm_for_tests/vp_memory_limit.wasm | Bin 462956 -> 475594 bytes wasm_for_tests/vp_read_storage_key.wasm | Bin 468486 -> 480951 bytes wasm_for_tests/wasm_source/src/lib.rs | 31 ------ 28 files changed, 369 insertions(+), 234 deletions(-) create mode 100644 tx_prelude/src/account.rs create mode 100644 wasm/wasm_source/src/tx_update_account.rs diff --git a/tx_prelude/src/account.rs b/tx_prelude/src/account.rs new file mode 100644 index 0000000000..da6c213601 --- /dev/null +++ b/tx_prelude/src/account.rs @@ -0,0 +1,18 @@ +use namada_core::types::transaction::account::InitAccount; + +use super::*; + +pub fn init_account( + ctx: &mut Ctx, + owner: &Address, + data: InitAccount, +) -> EnvResult
{ + storage_api::account::init_account_storage( + ctx, + owner, + &data.public_keys, + data.threshold, + )?; + + Ok(owner.to_owned()) +} diff --git a/tx_prelude/src/lib.rs b/tx_prelude/src/lib.rs index 6e5a0192c8..16103d6d34 100644 --- a/tx_prelude/src/lib.rs +++ b/tx_prelude/src/lib.rs @@ -6,6 +6,7 @@ #![deny(rustdoc::broken_intra_doc_links)] #![deny(rustdoc::private_intra_doc_links)] +pub mod account; pub mod ibc; pub mod key; pub mod proof_of_stake; diff --git a/tx_prelude/src/proof_of_stake.rs b/tx_prelude/src/proof_of_stake.rs index 48b0cb665f..cc8bcb7b63 100644 --- a/tx_prelude/src/proof_of_stake.rs +++ b/tx_prelude/src/proof_of_stake.rs @@ -2,7 +2,7 @@ use namada_core::types::dec::Dec; use namada_core::types::hash::Hash; -use namada_core::types::transaction::InitValidator; +use namada_core::types::transaction::pos::InitValidator; use namada_core::types::{key, token}; pub use namada_proof_of_stake::parameters::PosParams; use namada_proof_of_stake::{ @@ -74,7 +74,8 @@ impl Ctx { pub fn init_validator( &mut self, InitValidator { - account_key, + account_keys, + threshold, consensus_key, eth_cold_key, eth_hot_key, @@ -89,8 +90,12 @@ impl Ctx { let current_epoch = self.get_block_epoch()?; // Init validator account let validator_address = self.init_account(validator_vp_code_hash)?; - let pk_key = key::pk_key(&validator_address); - self.write(&pk_key, &account_key)?; + storage_api::account::init_account_storage( + self, + &validator_address, + &account_keys, + threshold, + )?; let protocol_pk_key = key::protocol_pk_key(&validator_address); self.write(&protocol_pk_key, &protocol_key)?; let dkg_pk_key = key::dkg_session_keys::dkg_pk_key(&validator_address); diff --git a/vp_prelude/src/lib.rs b/vp_prelude/src/lib.rs index 8ec55ef7cd..a7d19832e1 100644 --- a/vp_prelude/src/lib.rs +++ b/vp_prelude/src/lib.rs @@ -6,8 +6,6 @@ #![deny(rustdoc::broken_intra_doc_links)] #![deny(rustdoc::private_intra_doc_links)] -pub mod key; - // used in the VP input use core::convert::AsRef; use core::slice; @@ -79,6 +77,27 @@ pub fn is_proposal_accepted(ctx: &Ctx, proposal_id: u64) -> VpResult { ctx.has_key_pre(&proposal_execution_key) } +/// Verify section signatures +pub fn verify_signatures(ctx: &Ctx, tx: &Tx, owner: &Address) -> VpResult { + let max_signatures_per_transaction = + 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); + + let targets = &[*tx.data_sechash(), *tx.code_sechash()]; + tx.verify_section_signatures( + targets, + public_keys_index_map, + threshold, + max_signatures_per_transaction, + ) + .map_err(|_e| Error::SimpleMessage("Invalid signatures")) + .map(|_| true) +} + /// Checks whether a transaction is valid, which happens in two cases: /// - tx is whitelisted, or /// - tx is executed by an approved governance proposal (no need to be diff --git a/wasm/checksums.json b/wasm/checksums.json index 8397ffb0d6..a86e9c87a8 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,22 +1,22 @@ { - "tx_bond.wasm": "tx_bond.e45489077b1f944d15197ecb432290be523189d8cf1f35b3c37211688ad582b2.wasm", - "tx_bridge_pool.wasm": "tx_bridge_pool.8c942b7e6a49562ff20770ac6e04df85188b49fabf4ca7f82fa3a5986a66a363.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.37a8ec36194c2da2d9625a71a37dda7f5ef2530de562dacc29d70d9e1bd6d475.wasm", - "tx_ibc.wasm": "tx_ibc.a719260d45a15a3eeed5442abeda18be739face4ab509abcb00a6a10151ffc5c.wasm", - "tx_init_account.wasm": "tx_init_account.f979613d2b8b540ad471c663ec1aa3d9fad085ba7b1b059e2564c7a1eb5fa139.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.9a6c2aa5771fd08f26fb4d56ceb361c723d4b617da0ca2ab1a44c2b07f3b58b0.wasm", - "tx_init_validator.wasm": "tx_init_validator.01e521286a61e0a55e606319cadb330077824deb2d0d8462e180b27ec6a7567e.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.01442c5045ff10d7da05b1843803db99d23a6992594b2c3eb83955f6af9a26fb.wasm", - "tx_transfer.wasm": "tx_transfer.f50a99b865d57c95ccfaec95963e87ba61f3a2d9f9fe7c0ab3cd9b09e0095d9c.wasm", - "tx_unbond.wasm": "tx_unbond.b081405d6a7bacf1aabf8f650014fda97b8cca9ae9d3a9d6746f601600f0d563.wasm", - "tx_unjail_validator.wasm": "tx_unjail_validator.87f90fb263cb9eee693bea861ed5a3b797b075c0164b7ac1f9a1611d661bffb4.wasm", - "tx_update_vp.wasm": "tx_update_vp.78e3064cd6f24b376ce7aa85611e9b9f77cee6d6629b4849d6a60cb12017e1f4.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.36047c640ca8a10a62811cc15ba6e054e639313adf69c70e80e428e152c972cb.wasm", - "tx_withdraw.wasm": "tx_withdraw.9ad086dbcee5bdbfc915730c58f5c9b897cdfdd7192ffc839c3c8fd3adbbbe88.wasm", - "vp_implicit.wasm": "vp_implicit.16bb18c3b7973747a6f9581b769fff007f9189ecde87d2aeeb2317fb0abd1acb.wasm", - "vp_masp.wasm": "vp_masp.70bcfc40b3d9e9f792f298ba2e0c5e60fb44b4d1e4152635b2236b4a59faf235.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.7b8ea312ee9820c6129a861067e881cbd9212275d48a9ffb9bffe2dcf6576b31.wasm", - "vp_token.wasm": "vp_token.ec4f3914d074168f7ecbd43d5587e014381d409b3df4db4f63eaf73d3a56cdd6.wasm", - "vp_user.wasm": "vp_user.fd9810232a2ec79ff3f9f8fc70a6427ce9d07a9ef51c1468a785247a9b08b337.wasm", - "vp_validator.wasm": "vp_validator.8ce4c52a53aa451459e37ec560aa56ac4083d8829b0c29168c448e1e9d764c22.wasm" + "tx_bond.wasm": "tx_bond.5bad50cd6097fd117b0f49b27f001ba07c67f1e62bacac0ad7918592b239c77c.wasm", + "tx_bridge_pool.wasm": "tx_bridge_pool.15c3c6f7d1bc9c00ae9128032a6cad1933f213d1d88537fcd6229aae29dca416.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.6a3c2ff0c51ca550447cf790fc0936d591f65a9702fdda3886425284c941c31a.wasm", + "tx_ibc.wasm": "tx_ibc.3810b04b25f124baad7f47b1505bc95b878b399a3caa311607317af41739ae3a.wasm", + "tx_init_account.wasm": "tx_init_account.6dddabfa67d2177d444da5edfca8ef45cff6e9bef174bec49293608ba015a491.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.8b38410576dbe474d9880a831368dc229ecfae918d861a9efd8c578e1d05522b.wasm", + "tx_init_validator.wasm": "tx_init_validator.430d6ebc0c477ec7ce628818ff263ccbf4cec92df92904fa939d981461509f77.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.f8e15cc1b2792dd51398d5fe20e8fde9a6cbc8502c3bc6fbbae8cf172e3ed434.wasm", + "tx_transfer.wasm": "tx_transfer.72a1719b6dc9fc40c5de61f3bb9215deddf0b0e982cb2f3444c64b5db939d290.wasm", + "tx_unbond.wasm": "tx_unbond.9443fe926e6420a380d994c6640d0abc75fd9ce58f2484f83137e0cde2bc260b.wasm", + "tx_unjail_validator.wasm": "tx_unjail_validator.04dfa8eee97453a15a5864b3bd5f2295201cd8c40ff1d14400d1f44e91566bfe.wasm", + "tx_update_account.wasm": "tx_update_account.8d8ea8f38c001ff4b04f68907ed38251a28eadc78722f61ec2a946273d2a9430.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.d3064c07343ff6d46654887839331d1835dd65e42fef2ca5ac12ae37b30c5ec7.wasm", + "tx_withdraw.wasm": "tx_withdraw.91090e09c191690c8b0a169daaed2bf04bdb75c04c5668017ee2c134d51c895a.wasm", + "vp_implicit.wasm": "vp_implicit.2f55ccc942f156fb46d305af34104ce6d3be02da0d2081217b66a89d5fa19834.wasm", + "vp_masp.wasm": "vp_masp.1dc9def28798be5ad206f01eb80c37764955482c6b0a61da52f0ed5833c86294.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.f276f01df7081d275e3c1f08d1e5f100c41548f0529f2919ee9e2ed2db53c575.wasm", + "vp_token.wasm": "vp_token.eff25120192554edd4397ddba1d03aacbf7b6839dbf134b24fc804884cc9a4eb.wasm", + "vp_user.wasm": "vp_user.b37170b7ac708ed959214fd11576dc5fb0301f110f6e8c7c9e753c77f4c824c2.wasm", + "vp_validator.wasm": "vp_validator.9cdf33ce7ee86c03c3e2127df4d769d9b69feb69e049d64980f4786d34d08ce9.wasm" } \ No newline at end of file diff --git a/wasm/wasm_source/Cargo.toml b/wasm/wasm_source/Cargo.toml index 266392752a..45f6ce13f4 100644 --- a/wasm/wasm_source/Cargo.toml +++ b/wasm/wasm_source/Cargo.toml @@ -24,7 +24,7 @@ tx_reveal_pk = ["namada_tx_prelude"] tx_transfer = ["namada_tx_prelude"] tx_unbond = ["namada_tx_prelude"] tx_unjail_validator = ["namada_tx_prelude"] -tx_update_vp = ["namada_tx_prelude"] +tx_update_account = ["namada_tx_prelude"] tx_vote_proposal = ["namada_tx_prelude"] tx_withdraw = ["namada_tx_prelude"] vp_implicit = ["namada_vp_prelude", "once_cell"] diff --git a/wasm/wasm_source/Makefile b/wasm/wasm_source/Makefile index a2b5ab284d..9e43f77f9c 100644 --- a/wasm/wasm_source/Makefile +++ b/wasm/wasm_source/Makefile @@ -16,7 +16,7 @@ wasms += tx_reveal_pk wasms += tx_transfer wasms += tx_unbond wasms += tx_unjail_validator -wasms += tx_update_vp +wasms += tx_update_account wasms += tx_vote_proposal wasms += tx_withdraw wasms += vp_implicit diff --git a/wasm/wasm_source/src/lib.rs b/wasm/wasm_source/src/lib.rs index 3d5be30b56..b59b7adf23 100644 --- a/wasm/wasm_source/src/lib.rs +++ b/wasm/wasm_source/src/lib.rs @@ -20,8 +20,8 @@ pub mod tx_transfer; pub mod tx_unbond; #[cfg(feature = "tx_unjail_validator")] pub mod tx_unjail_validator; -#[cfg(feature = "tx_update_vp")] -pub mod tx_update_vp; +#[cfg(feature = "tx_update_account")] +pub mod tx_update_account; #[cfg(feature = "tx_vote_proposal")] pub mod tx_vote_proposal; #[cfg(feature = "tx_withdraw")] diff --git a/wasm/wasm_source/src/tx_init_account.rs b/wasm/wasm_source/src/tx_init_account.rs index 2e85b70ae9..346afb2bec 100644 --- a/wasm/wasm_source/src/tx_init_account.rs +++ b/wasm/wasm_source/src/tx_init_account.rs @@ -7,7 +7,7 @@ use namada_tx_prelude::*; fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { let signed = tx_data; let data = signed.data().ok_or_err_msg("Missing data")?; - let tx_data = transaction::InitAccount::try_from_slice(&data[..]) + let tx_data = transaction::account::InitAccount::try_from_slice(&data[..]) .wrap_err("failed to decode InitAccount")?; debug_log!("apply_tx called to init a new established account"); @@ -18,8 +18,17 @@ fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { .ok_or_err_msg("vp code section must be tagged as extra")? .code .hash(); + let address = ctx.init_account(vp_code)?; - let pk_key = key::pk_key(&address); - ctx.write(&pk_key, &tx_data.public_key)?; + + match account::init_account(ctx, &address, tx_data) { + Ok(address) => { + debug_log!("Created account {}", address.encode(),) + } + Err(err) => { + debug_log!("Account creation failed with: {}", err); + panic!() + } + } Ok(()) } diff --git a/wasm/wasm_source/src/tx_init_validator.rs b/wasm/wasm_source/src/tx_init_validator.rs index 0cd37da111..eb80ec444e 100644 --- a/wasm/wasm_source/src/tx_init_validator.rs +++ b/wasm/wasm_source/src/tx_init_validator.rs @@ -1,7 +1,7 @@ //! A tx to initialize a new validator account with a given public keys and a //! validity predicates. -use namada_tx_prelude::transaction::InitValidator; +use namada_tx_prelude::transaction::pos::InitValidator; use namada_tx_prelude::*; #[transaction] diff --git a/wasm/wasm_source/src/tx_update_account.rs b/wasm/wasm_source/src/tx_update_account.rs new file mode 100644 index 0000000000..c2553759e5 --- /dev/null +++ b/wasm/wasm_source/src/tx_update_account.rs @@ -0,0 +1,45 @@ +//! A tx for updating an account's validity predicate. +//! 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] +fn apply_tx(ctx: &mut Ctx, tx: Tx) -> TxResult { + let signed = tx; + let data = signed.data().ok_or_err_msg("Missing data")?; + let tx_data = + transaction::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); + + if let Some(hash) = tx_data.vp_code_hash { + let vp_code_hash = signed + .get_section(&hash) + .ok_or_err_msg("vp code section not found")? + .extra_data_sec() + .ok_or_err_msg("vp code section must be tagged as extra")? + .code + .hash(); + + ctx.update_validity_predicate(owner, vp_code_hash)?; + } + + if let Some(threshold) = tx_data.threshold { + let threshold_key = key::threshold_key(owner); + ctx.write(&threshold_key, threshold)?; + } + + if !tx_data.public_keys.is_empty() { + storage_api::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())?; + } + } + + Ok(()) +} diff --git a/wasm/wasm_source/src/vp_implicit.rs b/wasm/wasm_source/src/vp_implicit.rs index 729ddb6fbe..f4c8e959e2 100644 --- a/wasm/wasm_source/src/vp_implicit.rs +++ b/wasm/wasm_source/src/vp_implicit.rs @@ -28,7 +28,7 @@ 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_pk_key(key) { + if let Some(address) = key::is_pks_key(key) { Self::Pk(address) } else if let Some([_, owner]) = token::is_any_token_balance_key(key) { Self::Token { owner } @@ -66,18 +66,8 @@ fn validate_tx( verifiers ); - let valid_sig = Lazy::new(|| { - let pk = key::get(ctx, &addr); - match pk { - Ok(Some(pk)) => tx_data - .verify_signature( - &pk, - &[*tx_data.data_sechash(), *tx_data.code_sechash()], - ) - .is_ok(), - _ => false, - } - }); + let valid_sig = + Lazy::new(|| verify_signatures(ctx, &tx_data, &addr).is_ok()); if !is_valid_tx(ctx, &tx_data)? { return reject(); @@ -200,7 +190,7 @@ fn validate_tx( mod tests { // Use this as `#[test]` annotation to enable logging use namada::ledger::pos::{GenesisValidator, PosParams}; - use namada::proto::{Code, Data, Signature}; + use namada::proto::{Code, Data, MultiSignature}; use namada::types::dec::Dec; use namada::types::storage::Epoch; use namada::types::transaction::TxType; @@ -211,6 +201,7 @@ mod tests { use namada_tests::vp::vp_host_env::storage::Key; use namada_tests::vp::*; use namada_tx_prelude::{storage_api, StorageWrite, TxEnv}; + use namada_vp_prelude::account::AccountPublicKeysMap; use namada_vp_prelude::key::RefTo; use proptest::prelude::*; use storage::testing::arb_account_storage_key_no_vp; @@ -244,7 +235,8 @@ mod tests { let addr: Address = (&public_key).into(); // Initialize a tx environment - let tx_env = TestTxEnv::default(); + let mut tx_env = TestTxEnv::default(); + tx_env.init_parameters(None, None, None, None); // Initialize VP environment from a transaction vp_host_env::init_from_tx(addr.clone(), tx_env, |_address| { @@ -312,8 +304,12 @@ mod tests { // Initialize VP environment from a transaction vp_host_env::init_from_tx(addr.clone(), tx_env, |_address| { // Do the same as reveal_pk, but with the wrong key - let key = namada_tx_prelude::key::pk_key(&addr); - tx_host_env::ctx().write(&key, &mismatched_pk).unwrap(); + let _ = storage_api::account::set_public_key_at( + tx_host_env::ctx(), + &addr, + &mismatched_pk, + 0, + ); }); let vp_env = vp_host_env::take(); @@ -420,6 +416,8 @@ mod tests { // Initialize a tx environment let mut tx_env = tx_host_env::take(); + tx_env.init_parameters(None, Some(vec![]), Some(vec![]), None); + let secret_key = key::testing::keypair_1(); let public_key = secret_key.ref_to(); let vp_owner: Address = (&public_key).into(); @@ -431,6 +429,7 @@ mod tests { // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&target, &token]); + tx_env.init_account_storage(&vp_owner, vec![public_key], 1); // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it @@ -467,8 +466,8 @@ mod tests { ); } - /// Test that a PoS action that must be authorized is accepted with a valid - /// signature. + /// Test that a PoS action that must be authorized is accepted with a + /// valid signature. #[test] fn test_signed_pos_action_accepted() { // Init PoS genesis @@ -511,6 +510,7 @@ mod tests { // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&target, &token]); + tx_env.init_account_storage(&vp_owner, vec![public_key.clone()], 1); // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it @@ -524,8 +524,6 @@ mod tests { ) .unwrap(); - tx_env.write_public_key(&vp_owner, &public_key); - // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |_address| { // Bond the tokens, then unbond some of them @@ -537,14 +535,18 @@ mod tests { .unwrap(); }); + let pks_map = AccountPublicKeysMap::from_iter(vec![public_key]); + let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &secret_key, + &[secret_key], + &pks_map, ))); + let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = @@ -572,6 +574,7 @@ mod tests { // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&vp_owner, &target, &token]); + tx_env.init_account_storage(&vp_owner, vec![public_key], 1); // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it @@ -633,8 +636,11 @@ mod tests { let token = address::nam(); let amount = token::Amount::from_uint(10_098_123, 0).unwrap(); + tx_env.init_parameters(None, None, None, None); + // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&vp_owner, &target, &token]); + tx_env.init_account_storage(&vp_owner, vec![public_key.clone()], 1); // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it @@ -648,8 +654,6 @@ mod tests { ) .unwrap(); - tx_env.write_public_key(&vp_owner, &public_key); - let amount = token::DenominatedAmount { amount, denom: token::NATIVE_MAX_DECIMAL_PLACES.into(), @@ -671,20 +675,25 @@ mod tests { .unwrap(); }); + let pks_map = AccountPublicKeysMap::from_iter(vec![public_key]); + let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &secret_key, + &[secret_key], + &pks_map, ))); + let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); vp_host_env::set(vp_env); + assert!( validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers) .unwrap() @@ -707,6 +716,7 @@ mod tests { // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&vp_owner, &source, &target, &token]); + tx_env.init_account_storage(&vp_owner, vec![public_key], 1); // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it @@ -773,8 +783,8 @@ mod tests { } proptest! { - /// Test that an unsigned tx that performs arbitrary storage writes or - /// deletes to the account is rejected. + /// Test that an unsigned tx that performs arbitrary storage writes + /// or deletes to the account is rejected. #[test] fn test_unsigned_arb_storage_write_rejected( (_sk, vp_owner, storage_key) in arb_account_storage_subspace_key(), @@ -808,17 +818,12 @@ mod tests { vp_host_env::set(vp_env); assert!(!validate_tx(&CTX, tx_data, vp_owner, keys_changed, verifiers).unwrap()); } - } - proptest! { - /// Test that a signed tx that performs arbitrary storage writes or - /// deletes to the account is accepted. - #[test] - fn test_signed_arb_storage_write( - (secret_key, vp_owner, storage_key) in arb_account_storage_subspace_key(), - // Generate bytes to write. If `None`, delete from the key instead - storage_value in any::>>(), - ) { + fn test_signed_arb_storage_write( + (secret_key, vp_owner, storage_key) in arb_account_storage_subspace_key(), + // Generate bytes to write. If `None`, delete from the key instead + storage_value in any::>>(), + ) { // Initialize a tx environment let mut tx_env = TestTxEnv::default(); @@ -828,7 +833,7 @@ mod tests { tx_env.spawn_accounts(storage_key_addresses); let public_key = secret_key.ref_to(); - tx_env.write_public_key(&vp_owner, &public_key); + let _ = storage_api::account::set_public_key_at(tx_host_env::ctx(), &vp_owner, &public_key, 0); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |_address| { @@ -840,11 +845,17 @@ mod tests { } }); + let pks_map = AccountPublicKeysMap::from_iter(vec![public_key]); + let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::Signature(Signature::new(vec![*tx.data_sechash(), *tx.code_sechash()], &secret_key))); + tx.add_section(Section::SectionSignature(MultiSignature::new( + vec![*tx.data_sechash(), *tx.code_sechash()], + &[secret_key], + &pks_map, + ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = @@ -912,12 +923,12 @@ mod tests { None, Some(vec![vp_hash.to_string()]), Some(vec!["some_hash".to_string()]), + None, ); // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&vp_owner]); - - tx_env.write_public_key(&vp_owner, &public_key); + tx_env.init_account_storage(&vp_owner, vec![public_key.clone()], 1); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { @@ -927,13 +938,16 @@ mod tests { .unwrap(); }); + let pks_map = AccountPublicKeysMap::from_iter(vec![public_key]); + let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &secret_key, + &[secret_key], + &pks_map, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); @@ -960,13 +974,16 @@ mod tests { // for the update tx_env.store_wasm_code(vp_code); - let empty_sha256 = sha256(&[]).to_string(); - tx_env.init_parameters(None, None, Some(vec![empty_sha256])); + tx_env.init_parameters( + None, + Some(vec![vp_hash.to_string()]), + None, + None, + ); // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&vp_owner]); - - tx_env.write_public_key(&vp_owner, &public_key); + tx_env.init_account_storage(&vp_owner, vec![public_key.clone()], 1); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { @@ -976,13 +993,16 @@ mod tests { .unwrap(); }); + let pks_map = AccountPublicKeysMap::from_iter(vec![public_key]); + let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(vec![])); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &secret_key, + &[secret_key], + &pks_map, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); diff --git a/wasm/wasm_source/src/vp_testnet_faucet.rs b/wasm/wasm_source/src/vp_testnet_faucet.rs index 8e002663ee..0878298a55 100644 --- a/wasm/wasm_source/src/vp_testnet_faucet.rs +++ b/wasm/wasm_source/src/vp_testnet_faucet.rs @@ -25,18 +25,8 @@ fn validate_tx( verifiers ); - let valid_sig = Lazy::new(|| { - let pk = key::get(ctx, &addr); - match pk { - Ok(Some(pk)) => tx_data - .verify_signature( - &pk, - &[*tx_data.data_sechash(), *tx_data.code_sechash()], - ) - .is_ok(), - _ => false, - } - }); + let valid_sig = + Lazy::new(|| verify_signatures(ctx, &tx_data, &addr).is_ok()); if !is_valid_tx(ctx, &tx_data)? { return reject(); @@ -104,7 +94,7 @@ fn validate_tx( #[cfg(test)] mod tests { use address::testing::arb_non_internal_address; - use namada::proto::{Code, Data, Signature}; + use namada::proto::{Code, Data, MultiSignature, Signature}; use namada::types::transaction::TxType; use namada_test_utils::TestWasms; // Use this as `#[test]` annotation to enable logging @@ -113,6 +103,7 @@ mod tests { use namada_tests::vp::vp_host_env::storage::Key; use namada_tests::vp::*; use namada_tx_prelude::{StorageWrite, TxEnv}; + use namada_vp_prelude::account::AccountPublicKeysMap; use namada_vp_prelude::key::RefTo; use proptest::prelude::*; use storage::testing::arb_account_storage_key_no_vp; @@ -246,8 +237,7 @@ mod tests { // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&vp_owner]); - - tx_env.write_public_key(&vp_owner, public_key); + tx_env.init_account_storage(&vp_owner, vec![public_key.clone()], 1); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { @@ -257,13 +247,16 @@ mod tests { .unwrap(); }); + let pks_map = AccountPublicKeysMap::from_iter(vec![public_key.clone()]); + let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &keypair, + &[keypair], + &pks_map, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); @@ -353,6 +346,7 @@ mod tests { let target = address::testing::established_address_2(); let target_key = key::testing::keypair_1(); + let _public_key = target_key.ref_to(); let token = address::nam(); let amount = token::Amount::from_uint(amount, 0).unwrap(); @@ -424,7 +418,7 @@ mod tests { let storage_key_addresses = storage_key.find_addresses(); tx_env.spawn_accounts(storage_key_addresses); - tx_env.write_public_key(&vp_owner, public_key); + tx_env.init_account_storage(&vp_owner, vec![public_key.clone()], 1); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |_address| { @@ -436,11 +430,17 @@ mod tests { } }); + let pks_map = AccountPublicKeysMap::from_iter(vec![public_key.clone()]); + let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::Signature(Signature::new(vec![*tx.data_sechash(), *tx.code_sechash()], &keypair))); + tx.add_section(Section::SectionSignature(MultiSignature::new( + vec![*tx.data_sechash(), *tx.code_sechash()], + &[keypair], + &pks_map, + ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = diff --git a/wasm/wasm_source/src/vp_user.rs b/wasm/wasm_source/src/vp_user.rs index b32d801ef2..f7523c6ae2 100644 --- a/wasm/wasm_source/src/vp_user.rs +++ b/wasm/wasm_source/src/vp_user.rs @@ -64,18 +64,8 @@ fn validate_tx( verifiers ); - let valid_sig = Lazy::new(|| { - let pk = key::get(ctx, &addr); - match pk { - Ok(Some(pk)) => tx_data - .verify_signature( - &pk, - &[*tx_data.data_sechash(), *tx_data.code_sechash()], - ) - .is_ok(), - _ => false, - } - }); + let valid_sig = + Lazy::new(|| verify_signatures(ctx, &tx_data, &addr).is_ok()); if !is_valid_tx(ctx, &tx_data)? { return reject(); @@ -188,7 +178,7 @@ fn validate_tx( mod tests { use address::testing::arb_non_internal_address; use namada::ledger::pos::{GenesisValidator, PosParams}; - use namada::proto::{Code, Data, Signature}; + use namada::proto::{Code, Data, MultiSignature}; use namada::types::dec::Dec; use namada::types::storage::Epoch; use namada::types::transaction::TxType; @@ -200,6 +190,7 @@ mod tests { use namada_tests::vp::vp_host_env::storage::Key; use namada_tests::vp::*; use namada_tx_prelude::{StorageWrite, TxEnv}; + use namada_vp_prelude::account::AccountPublicKeysMap; use namada_vp_prelude::key::RefTo; use proptest::prelude::*; use storage::testing::arb_account_storage_key_no_vp; @@ -358,6 +349,7 @@ mod tests { // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&vp_owner, &target, &token]); + tx_env.init_account_storage(&vp_owner, vec![public_key.clone()], 1); // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it @@ -371,8 +363,6 @@ mod tests { ) .unwrap(); - tx_env.write_public_key(&vp_owner, &public_key); - let amount = token::DenominatedAmount { amount, denom: token::NATIVE_MAX_DECIMAL_PLACES.into(), @@ -395,13 +385,16 @@ mod tests { .unwrap(); }); + let pks_map = AccountPublicKeysMap::from_iter(vec![public_key]); + let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &keypair, + &[keypair], + &pks_map, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); @@ -445,7 +438,7 @@ mod tests { let mut tx_env = tx_host_env::take(); let secret_key = key::testing::keypair_1(); - let _public_key = secret_key.ref_to(); + let public_key = secret_key.ref_to(); let vp_owner: Address = address::testing::established_address_2(); let target = address::testing::established_address_3(); let token = address::nam(); @@ -455,6 +448,7 @@ mod tests { // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&target, &token]); + tx_env.init_account_storage(&vp_owner, vec![public_key], 1); // write the denomination of NAM into storage storage_api::token::write_denom( &mut tx_env.wl_storage, @@ -536,6 +530,8 @@ mod tests { // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&target, &token]); + tx_env.init_account_storage(&vp_owner, vec![public_key.clone()], 1); + // write the denomination of NAM into storage storage_api::token::write_denom( &mut tx_env.wl_storage, @@ -549,8 +545,6 @@ mod tests { // be able to transfer from it tx_env.credit_tokens(&vp_owner, &token, None, amount); - tx_env.write_public_key(&vp_owner, &public_key); - // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |_address| { // Bond the tokens, then unbond some of them @@ -562,13 +556,16 @@ mod tests { .unwrap(); }); + let pks_map = AccountPublicKeysMap::from_iter(vec![public_key]); + let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &secret_key, + &[secret_key], + &pks_map, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); @@ -710,8 +707,7 @@ mod tests { // their storage let storage_key_addresses = storage_key.find_addresses(); tx_env.spawn_accounts(storage_key_addresses); - - tx_env.write_public_key(&vp_owner, &public_key); + tx_env.init_account_storage(&vp_owner, vec![public_key.clone()], 1); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |_address| { @@ -723,11 +719,13 @@ mod tests { } }); + let pks_map = AccountPublicKeysMap::from_iter(vec![public_key]); + let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(vec![])); - tx.add_section(Section::Signature(Signature::new(vec![*tx.data_sechash(), *tx.code_sechash()], &keypair))); + tx.add_section(Section::SectionSignature(MultiSignature::new(vec![*tx.data_sechash(), *tx.code_sechash()], &[keypair], &pks_map))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = @@ -782,7 +780,7 @@ mod tests { fn test_signed_vp_update_accepted() { // Initialize a tx environment let mut tx_env = TestTxEnv::default(); - tx_env.init_parameters(None, None, None); + tx_env.init_parameters(None, None, None, None); let vp_owner = address::testing::established_address_1(); let keypair = key::testing::keypair_1(); @@ -794,8 +792,7 @@ mod tests { // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&vp_owner]); - - tx_env.write_public_key(&vp_owner, &public_key); + tx_env.init_account_storage(&vp_owner, vec![public_key.clone()], 1); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { @@ -805,13 +802,16 @@ mod tests { .unwrap(); }); + let pks_map = AccountPublicKeysMap::from_iter(vec![public_key]); + let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &keypair, + &[keypair], + &pks_map, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); @@ -830,7 +830,12 @@ mod tests { fn test_signed_vp_update_not_whitelisted_rejected() { // Initialize a tx environment let mut tx_env = TestTxEnv::default(); - tx_env.init_parameters(None, Some(vec!["some_hash".to_string()]), None); + tx_env.init_parameters( + None, + Some(vec!["some_hash".to_string()]), + None, + None, + ); let vp_owner = address::testing::established_address_1(); let keypair = key::testing::keypair_1(); @@ -842,8 +847,7 @@ mod tests { // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&vp_owner]); - - tx_env.write_public_key(&vp_owner, &public_key); + tx_env.init_account_storage(&vp_owner, vec![public_key.clone()], 1); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { @@ -853,13 +857,16 @@ mod tests { .unwrap(); }); + let pks_map = AccountPublicKeysMap::from_iter(vec![public_key]); + let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &keypair, + &[keypair], + &pks_map, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); @@ -887,12 +894,16 @@ mod tests { // for the update tx_env.store_wasm_code(vp_code); - tx_env.init_parameters(None, Some(vec![vp_hash.to_string()]), None); + tx_env.init_parameters( + None, + Some(vec![vp_hash.to_string()]), + None, + None, + ); // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&vp_owner]); - - tx_env.write_public_key(&vp_owner, &public_key); + tx_env.init_account_storage(&vp_owner, vec![public_key.clone()], 1); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { @@ -902,13 +913,16 @@ mod tests { .unwrap(); }); + let pks_map = AccountPublicKeysMap::from_iter(vec![public_key]); + let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &keypair, + &[keypair], + &pks_map, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); @@ -940,12 +954,12 @@ mod tests { None, Some(vec![vp_hash.to_string()]), Some(vec!["some_hash".to_string()]), + None, ); // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&vp_owner]); - - tx_env.write_public_key(&vp_owner, &public_key); + tx_env.init_account_storage(&vp_owner, vec![public_key.clone()], 1); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { @@ -955,13 +969,16 @@ mod tests { .unwrap(); }); + let pks_map = AccountPublicKeysMap::from_iter(vec![public_key]); + let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &keypair, + &[keypair], + &pks_map, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); @@ -988,13 +1005,17 @@ mod tests { // for the update tx_env.store_wasm_code(vp_code); - let empty_sha256 = sha256(&[]).to_string(); - tx_env.init_parameters(None, None, Some(vec![empty_sha256])); + // hardcoded hash of VP_ALWAYS_TRUE_WASM + tx_env.init_parameters( + None, + Some(vec![vp_hash.to_string()]), + None, + None, + ); // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&vp_owner]); - - tx_env.write_public_key(&vp_owner, &public_key); + tx_env.init_account_storage(&vp_owner, vec![public_key.clone()], 1); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { @@ -1004,13 +1025,16 @@ mod tests { .unwrap(); }); + let pks_map = AccountPublicKeysMap::from_iter(vec![public_key]); + let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(vec![])); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &keypair, + &[keypair], + &pks_map, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); diff --git a/wasm/wasm_source/src/vp_validator.rs b/wasm/wasm_source/src/vp_validator.rs index 9b10999d89..3b5647b291 100644 --- a/wasm/wasm_source/src/vp_validator.rs +++ b/wasm/wasm_source/src/vp_validator.rs @@ -64,18 +64,8 @@ fn validate_tx( verifiers ); - let valid_sig = Lazy::new(|| { - let pk = key::get(ctx, &addr); - match pk { - Ok(Some(pk)) => tx_data - .verify_signature( - &pk, - &[*tx_data.data_sechash(), *tx_data.code_sechash()], - ) - .is_ok(), - _ => false, - } - }); + let valid_sig = + Lazy::new(|| verify_signatures(ctx, &tx_data, &addr).is_ok()); if !is_valid_tx(ctx, &tx_data)? { return reject(); @@ -195,7 +185,7 @@ fn validate_tx( mod tests { use address::testing::arb_non_internal_address; use namada::ledger::pos::{GenesisValidator, PosParams}; - use namada::proto::{Code, Data, Signature}; + use namada::proto::{Code, Data, MultiSignature}; use namada::types::dec::Dec; use namada::types::storage::Epoch; use namada::types::transaction::TxType; @@ -207,6 +197,7 @@ mod tests { use namada_tests::vp::vp_host_env::storage::Key; use namada_tests::vp::*; use namada_tx_prelude::{StorageWrite, TxEnv}; + use namada_vp_prelude::account::AccountPublicKeysMap; use namada_vp_prelude::key::RefTo; use proptest::prelude::*; use storage::testing::arb_account_storage_key_no_vp; @@ -365,6 +356,7 @@ mod tests { // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&vp_owner, &target, &token]); + tx_env.init_account_storage(&vp_owner, vec![public_key.clone()], 1); // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it @@ -378,7 +370,6 @@ mod tests { ) .unwrap(); - tx_env.write_public_key(&vp_owner, &public_key); let amount = token::DenominatedAmount { amount, denom: token::NATIVE_MAX_DECIMAL_PLACES.into(), @@ -401,13 +392,16 @@ mod tests { .unwrap(); }); + let pks_map = AccountPublicKeysMap::from_iter(vec![public_key]); + let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &keypair, + &[keypair], + &pks_map, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); @@ -451,7 +445,7 @@ mod tests { let mut tx_env = tx_host_env::take(); let secret_key = key::testing::keypair_1(); - let _public_key = secret_key.ref_to(); + let public_key = secret_key.ref_to(); let vp_owner: Address = address::testing::established_address_2(); let target = address::testing::established_address_3(); let token = address::nam(); @@ -461,6 +455,7 @@ mod tests { // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&target, &token]); + tx_env.init_account_storage(&vp_owner, vec![public_key], 1); // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it @@ -547,7 +542,8 @@ mod tests { let unbond_amount = token::Amount::from_uint(3_098_123, 0).unwrap(); // Spawn the accounts to be able to modify their storage - tx_env.spawn_accounts([&target, &token]); + tx_env.spawn_accounts([&vp_owner, &target, &token]); + tx_env.init_account_storage(&vp_owner, vec![public_key.clone()], 1); // Credit the tokens to the VP owner before running the transaction to // be able to transfer from it @@ -561,8 +557,6 @@ mod tests { ) .unwrap(); - tx_env.write_public_key(&vp_owner, &public_key); - // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |_address| { // Bond the tokens, then unbond some of them @@ -580,13 +574,16 @@ mod tests { .unwrap(); }); + let pks_map = AccountPublicKeysMap::from_iter(vec![public_key]); + let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &secret_key, + &[secret_key], + &pks_map, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); @@ -728,7 +725,7 @@ mod tests { let storage_key_addresses = storage_key.find_addresses(); tx_env.spawn_accounts(storage_key_addresses); - tx_env.write_public_key(&vp_owner, &public_key); + tx_env.init_account_storage(&vp_owner, vec![public_key.clone()], 1); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |_address| { @@ -740,11 +737,13 @@ mod tests { } }); + let pks_map = AccountPublicKeysMap::from_iter(vec![public_key]); + let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::Signature(Signature::new(vec![*tx.data_sechash(), *tx.code_sechash()], &keypair))); + tx.add_section(Section::SectionSignature(MultiSignature::new(vec![*tx.data_sechash(), *tx.code_sechash()], &[keypair], &pks_map))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = @@ -798,7 +797,7 @@ mod tests { fn test_signed_vp_update_accepted() { // Initialize a tx environment let mut tx_env = TestTxEnv::default(); - tx_env.init_parameters(None, None, None); + tx_env.init_parameters(None, None, None, None); let vp_owner = address::testing::established_address_1(); let keypair = key::testing::keypair_1(); @@ -810,8 +809,7 @@ mod tests { // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&vp_owner]); - - tx_env.write_public_key(&vp_owner, &public_key); + tx_env.init_account_storage(&vp_owner, vec![public_key.clone()], 1); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { @@ -821,13 +819,16 @@ mod tests { .unwrap(); }); + let pks_map = AccountPublicKeysMap::from_iter(vec![public_key]); + let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &keypair, + &[keypair], + &pks_map, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); @@ -846,7 +847,12 @@ mod tests { fn test_signed_vp_update_not_whitelisted_rejected() { // Initialize a tx environment let mut tx_env = TestTxEnv::default(); - tx_env.init_parameters(None, Some(vec!["some_hash".to_string()]), None); + tx_env.init_parameters( + None, + Some(vec!["some_hash".to_string()]), + None, + None, + ); let vp_owner = address::testing::established_address_1(); let keypair = key::testing::keypair_1(); @@ -858,8 +864,7 @@ mod tests { // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&vp_owner]); - - tx_env.write_public_key(&vp_owner, &public_key); + tx_env.init_account_storage(&vp_owner, vec![public_key.clone()], 1); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { @@ -869,13 +874,16 @@ mod tests { .unwrap(); }); + let pks_map = AccountPublicKeysMap::from_iter(vec![public_key]); + let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &keypair, + &[keypair], + &pks_map, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); @@ -903,12 +911,16 @@ mod tests { // for the update tx_env.store_wasm_code(vp_code); - tx_env.init_parameters(None, Some(vec![vp_hash.to_string()]), None); + tx_env.init_parameters( + None, + Some(vec![vp_hash.to_string()]), + None, + None, + ); // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&vp_owner]); - - tx_env.write_public_key(&vp_owner, &public_key); + tx_env.init_account_storage(&vp_owner, vec![public_key.clone()], 1); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { @@ -918,13 +930,16 @@ mod tests { .unwrap(); }); + let pks_map = AccountPublicKeysMap::from_iter(vec![public_key]); + let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &keypair, + &[keypair], + &pks_map, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); @@ -956,12 +971,12 @@ mod tests { None, Some(vec![vp_hash.to_string()]), Some(vec!["some_hash".to_string()]), + None, ); // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&vp_owner]); - - tx_env.write_public_key(&vp_owner, &public_key); + tx_env.init_account_storage(&vp_owner, vec![public_key.clone()], 1); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { @@ -971,13 +986,16 @@ mod tests { .unwrap(); }); + let pks_map = AccountPublicKeysMap::from_iter(vec![public_key]); + let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_data(Data::new(vec![])); tx.set_code(Code::new(vec![])); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &keypair, + &[keypair], + &pks_map, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); @@ -1004,13 +1022,17 @@ mod tests { // for the update tx_env.store_wasm_code(vp_code); - let empty_sha256 = sha256(&[]).to_string(); - tx_env.init_parameters(None, None, Some(vec![empty_sha256])); + // hardcoded hash of VP_ALWAYS_TRUE_WASM + tx_env.init_parameters( + None, + Some(vec![vp_hash.to_string()]), + None, + None, + ); // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&vp_owner]); - - tx_env.write_public_key(&vp_owner, &public_key); + tx_env.init_account_storage(&vp_owner, vec![public_key.clone()], 1); // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { @@ -1020,13 +1042,16 @@ mod tests { .unwrap(); }); + let pks_map = AccountPublicKeysMap::from_iter(vec![public_key]); + let mut vp_env = vp_host_env::take(); let mut tx = vp_env.tx.clone(); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(vec![])); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.data_sechash(), *tx.code_sechash()], - &keypair, + &[keypair], + &pks_map, ))); let signed_tx = tx.clone(); vp_env.tx = signed_tx.clone(); diff --git a/wasm_for_tests/tx_memory_limit.wasm b/wasm_for_tests/tx_memory_limit.wasm index 2544e625489dff54ab2443abd6dfdaaf82153789..67bd369c09210a4cc95eb4f80eaf34d5c94e479d 100755 GIT binary patch delta 138551 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 PoqN>!oqIA*{qVm5OWS+h delta 127376 zcmd443!EI+Rp(!io}QlR?wOv4-mhCFKV}@svSV9PB*#`w@C)164vFJDSVDLNMoO@Z zF)SOq#<84C5<(`lv(gg7il;@eL3t1cFfd`wuEO706i7e`Uj7Q0FouLEth1|tgZKM8 zx2n5mEZKSR-_Jje=H9whee2$H&-#zT8-EV(s?cB-qU&613gJBScaoitQ!cf&P ztOj8v41&H!90%`x`ik}?;ng>;)D8c?*oru|-|^F0#IGvxsxa;kQti*x+Z=n5|@SMT9pZ!=+&x364XWRYJj{J>bkcIVc8(O_)<3EO{!rSJ;SKV~& zjjw*=4^Ah)75-It!{3Mh5N`fL_{H#N!>7WVCnhg=-G2#_%}<8EAG%);UwXxrw|v)k zzy5pP@V)s@g?sM}XTm4L{}DbB{(5-THGdmk{RiPfcryH4_}AgzgoD5T$U8|%Tvk?F}e*b%E`##&DHw49Fg%!!_sQ`V9n?`c}Jrei%VC#_|nr{%b{ zJTo#q5e3^LjhnH}kL-v~FiPst)6|bq&;F}DdEe;t)*#pwXjlu@GB?`u+k9Ja@96IE zG5_Ouc63KPPkn1JGn#yxCVF5rxk%*$qsetD4~-@_cENBUOyexRv9c#yn}w}7jjn2j zF3MIN$)|$>1xSkAx9d=!t&gT8~O5bp_b2n>bl~&w-=88?Do$khW z1vPq^`=|E)Ysd0nDn5{`Zp3vB=fTnBr7CB_v^o(4$?E|^oK^O2_iI_rD%0D!$%yZ9 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ⅅ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%0D^Ur{H`eo%HzKUR|u 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 z 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 diff --git a/wasm_for_tests/tx_no_op.wasm b/wasm_for_tests/tx_no_op.wasm index 283ed205f5d76591dee1ad304db291776945d300..a47143814488ec5cb89306cb46ab2e46e1436183 100755 GIT binary patch delta 115506 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
    1254_hGsZh2x)PgQy}b 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
    a)A!=!lf;rDfS+ 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 delta 103173 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

    %TYYL+js_r7ZBZWBzg++zzI9N1Nkn4 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 rf8ctN>p8AJ0F zzUO-+4`=pV_L`YBYt~w`*39f>e;j)EcZPO6V85XLaK*!&6O^))Qc-PNDx#{CO~m}O zBPvRsr7X=))V8aM6{fFK2aR2O%@!+`vb+rv791MM|MJ;i{_3J{YODB+C7)k-?iW74 z`0O(lf5Y0E{;L{w)+6d6HEH_chmJpD?z|Ih@4v2B*QuYYC)ATF?`>-Yl==dmbYWY7M^N&B8U=1TfE)rE}blQ`*dCO zwPjl@??A6-W0hSMQ@hgROU}~m>o;Cel2B^<#-Egy>a*7Y*NJ$EmP9Nv!&`Atx8)r| zd~A!?EzdjnNfcW#k{Vk^S+TqGqAE4;Z=~fV79{OamY(5QGb1w`2ULMN)B#>7Az%538gVr-!aZZ_aN`YDanY zVy~@DwIm~sb|S~sdf#b_NCk-{GpuC9OC1w89a&5r0F5}7V~^Fv21PvU6fd9O+T!U& zQ#4tQ!PMT>x>n4zEPUDYBxtKZ;x$jxjLWuIEuKYWkD6DTjPBJ9`ogH_up0(LsiZZE zH%t!YlacVHl%Ci9ry^1qSJ7YQ`nzM!+GUC5XsCKs?Zu8EA%!F&D%~lKq1>Xr%fv%SbtlNPr6n zYU4MC{88(Uo711IS`2kRUNsjYPuJDd6$p(dey{x25`EFe`)mHHnF?LCNq)E24jaA; z5^teu$11a=ej&B!5^uNF>g`G=h7Y6Ify0OLJ9GHARolib!;6$^^M8MGWOO`M3Yj~3 z^aG+vzc=C@wJm*j-AroVQ}@m4!@8GIZw1Y4@St13nuJO#mJmWv48~KpQsOz)pU`O% z(zi|d587Vb)HS00k`P9o*KK(w1^^mqdn+$V_ck3)xe-%qhGxq}a?Aa>q0ztM4(0lt zcc}85pPuVgVfxmokJ8c$4!dDU*{^(9oRqFS;nHEIL}QC% zH(Sl`b-lCaXKk-O|D@GyB>;a$b?{aqtxn*rjej}eyE?@Sw%4Is9Nl0?lLgISTS`l6 zXONmm=I5qv2vTd3(cIK-Qk6j9Z6}g}&_}8y05$|^2MIt75>h>33ebX-o&Zu}Af&di zSQgTT!H{|f1L_R`)eHpGF%S^l>>doMFBek!s*^{~GwA4r!syu`v>NnAz6w=8i2sz}?lJ+)`0D>|Eqdgfjiub>g8TXJV zX#s;HFhUB_UY}}7KmFO^^l0z7bkymeC4KL?^wQ~9{gb)$o6kMAUvue&Gse%*SA8#? z=$uQ9r+3~ktgjt*GTeLfyG(osh5|+q!rEK*z4Wl_*A82Ed$)yX(J_(CQ*;FZG9!U_ z-uT@0j#eG%*H({Bk6yi1y~xvL`l;K~udhBJedCQIVr%aRc!~Ggvh*W2&eG^2RX3fi zZ|X=dxT#&;m45T4Gx`1G8b{sbd#iP&x<9>Q&4hR_4N8opCALS=->f;i^eI1^a)s^P zk)FQxXtg_?T6@%yt5F?1`4K^?Jkg(~d)ZxA}xYriy|x}$DLMoH72!JhUyFZb2Os>bhbQ^WSo zZ3}YK@jLgC*>hR&>aiE?>(y0Z?&bT;?Yq>xn|^MefKBG4y_>ViWK{d7U##M@hlr4F zFdCJ&_HxlMtysAcU|xY&6)Y-NPoI(yzaa?-QLxzewlu^>q*Gs(M>J<9(H#Qah!MesWJi&-2JH$S&C4e zP(+446w}V@O&@;Gs}T2?d&d`sdhe>W>CfIfeMsjTX|!m{^+7LI-ITuh-naC+*V5N- z?jHFrO=_8FS<@}iAN2w!>a9vG6s={?9=p|3i@kT#%YJ^kdN=*YpC7<)(J#gdp-le8 z>pH(J5Z4V`n)HgFrq9}Pq}rLje#>&TmEXGb%wPQ-wZXe@f%&beW;R)90^Sa4OdJNO z2eI^X_pMi(H-7K_bF})w#y9@YW~DZ#Z{7MCwJBZnz>NXQ?n~eKz!bgW=Ja147)Nf+ zgQEmWz60}l>U`S-5_^zSyl`cQuO{QLA<4^KfYIp~qI_`Up*di7lT zXOGmVr_x&=IfdsDkJixIDUUkTcIu;k;<|enpo6@(CAeRC_oUx@^q@rBoy5j^Jrev_ zd#|UbJ=Rmw8_si-un5?%>G^tQz0sHUdyXmJYYJ@G_jm83|BWnS43){+H^NHDczfr_A%YO_E^@JKCLpdy;2mMl}ulu9id=WjdT4iK9God^1pybx$cfi}Sl zB+~dGQ7|e!3FV>fNndF}zs^NZM8;;p2!^F6Syc*z$_u6+DWQX&5N0TJQISZHc4reh zNsxE4wKDPoGlL6LV;`+|gU{{IB|9I4T1&j$1jx>` z_d?CaZGX5jziP82Ai_kPma-yX`Qxp6Fx~dD!$iCOWmi3%zVGFcWh=sBUK>-u8|v-c z__vq8pvJ6ws9QK~q{R!LSV68JEisMNMOfvkhc;fcV`L<6n;KR;K0Uv$I^DhVd#nJC z`^zo0q#-<=yah>+H!FUa2=o>w<`g8hr^{Zun^F(G_Vv8&yg4r2`DZWP{`!4zgt>h; zn)k<~Yu;E+{JJ-W65sSjP<~8$wsY3eA`)V{w-Q@_1yuckKUHr0Dap#A-wuT$;U-aT3w03-b^ zMXEae#NWT8w)>xtsWIuc-4_k+@x%a3k?Ha9f80gURz=Zw{&5np=DrsVVATh(&fAlL zb#Zg7roaJI4Q3 zo-&>HHxw$GMe-S_+Q75(?WU7+?tz?d8a_+C_C`hSlqP|)?YLNNku^M78X zZXlJuuNmC$(K-G0M;5Ct%1Ot46;~&j0k2BWf6w!O5LXWvY*rc!_!pL__5kDDB|K2FOJTC0KYjp^lX%%q%xeFrZ6^sN;C|Gei1oJFG$-XKE`mwLMs&9^|E~ zEBjwMqsqK=OI80%->p{1rGzHqG4G-Xri;uR@@kpsn?**j+#MKka&rbm#^&Y>h)l`N z84&5QCgkSzpxyV^+bR4e+lnCOUsR*2QmZnPcxeRj_ngF#=-$N=dpIXAzdw(X1^qF4 z8}ZgEW(rL)A*VWR-vxO{Gv8>yI0u-c`H1M%Sqa2j_MD z!&NnT`*`8VuFoH!ejgc>(>3=X^+%=F`4cCox7E6?F%#AOs=th=6zjTnHLAOGe`1kI z+sMWS{c+RPNqeiwD<26I^Y>A`AN~NMBXvyLi{(N2v#Nu$Dzgi(TlZU6;S|-RC!Md)$~K z{Q72PECCt=w8?M(%L6+$U4Hf7jr{iKH>(j;@s(yZbTU=2MyIuCG|e|Y`NNCW?OFeq zR}C1+5!FL-1_HRZS=AkC-W1%RKMiVtyeayG>tuBd7SFEtPEi-d)sC*Vv(%XpwYBSobJQ+1xjT4M-eaxAs<!Bs8IHKDh>w5NE z3LnodKYG6U3c=UUSK~^vQygm~tF5jL=c`V8@9EqAcrbmx&60fY)yhu);EU8yvtHjj zv&Ur0a((Z-J0$Orz0+3)0EJoXo!J%uWSPEq-iy~jGVHnG6;PrpJ98WN|Cwu4vO+3^OdK((iPt64l6a=8 z*jF)|4%S?&&O!nC_O+(F>#kK}i9LO-8l#@++I_9i#P+TOSE+;5$jwyeU7@_5r^9M` zdGN-0E1n7Bo1!)TmV_SSU-={T{R-()B+tU#x#J0- z9N=|?jdyfT4?8nGS}lbR@wO2R>ft4w%B_^jSY|lIbdR|afzKwkcWv%ef0LoS<0qzz z)i;QH`7Mvt?VXiGe{wvFP>I5O1)oCQFvu zOrIa;#(n=sx9CItZ{MU6YFF2ao78Af=ik0Y?HS&a@n7W2JTfuyJ>a?Vdi_V%s-_{? zG>8iuy%R}n_p5)Z9M#h`^{47if%MwV>geGNWwVMg>+xK8J^6Ti@SL!4v%IyJ`15X2 z6J@$zc#Ar$vhSsCTrjc<3ZG=U`w!ou4*VCobtD|2Tj^RqwNCwnwj|f9sdVx)>(%r2 zVWE+z1G}9Wn@*v=Mz4p!#IO1jamVW&6z}+R*P`20T6{qH8`bWK;>L7h7{xG_(!Jn0 za3SW4sSLufVlj-N#ueMqb?_Z3PpRK_HQlK;DnsLOEGPwL2FW~v0N%Sx9VrBG#3pqn z!F8L|;X&*;f6U#G;4^Cuzgy|>UFDRTcg^hE?$5uM^~v_GXYN%Cl?{FQJ2$H%{iQ!= zOrG&q|6F~7`SI${As&-?kz}6sOOyG{U!t8q)Aj3Ls=w*u*6sugGT8ZE%*#fGc*WkP zFelTaWuWN;@#h!Zr`8r`=XTeo`yko6-GAurr%}fEP2yP^-Ra-iir%g_yJr7d%~P6(PwxRwT~Gf;{Wl2p?|wj?sQUcagX+iXw_Ue9sQUEK5bJHd zVH^8kg`VqO^@M-NBWhG8dsWB@GF@8zNB<4{`tC|-af+S~IR>_@(|96bHi5{p&X@e( zKdSEfC|$|xL2soi{@lmaK@xuJaj0T@*MuinD-z!QB-2s1J?8g5#Tae(*-`K*g43Q> z2NEoMT1}+T9Z#!Aq)^w=->Ruvp#1zK`yTwCs*(?61 zf3I#*yRUg(eUsVzt>={%MsvX*FtvlKJO7}*GE!uR_I6l(&`^u7c0{ z$MmXs^1{`<%$4oFenG7ovR&a%*jgyBp$>X43PZc?1vQ-fM_*8FflTtycql-zpukyw zWU0HoYy5vJUk2^hFRB*?5V+Y4kVD|Mx6{A>WoUPoZ@;3hlo9h^QD3DkYoa?;M6Yz~r6j-WwT_NjM( zwc-u+x$*$j-R#9Egj@VrFuVH=brgs={IAff^lr$TXms2C8{Smk*@gB~NGdb?hjYA-tbK`gTC+?4 zvkU1XunZsWcaHzf_tf)LCmir?rFQ%CmHsNjf~>nxS}{ZG+a&xStrzYux;~eug)o0y zphx+Awq9AXYeyD!m~Z`SA}0Uoh@LS=hCb@m&tvD%FgEQJCDqJ0e98mC`>BKE*19E6 zuXH+I&EZ}QNIUT?VYa>6?Vp*aEB(vz^#MU+UH`ZF?A+My@56U9o=qdZ#HK@PA?{#&N|K&0r%hvvj z5&F#i1vR%Wr};P4>EY$O|3_wo7}^LNg1G-wojyIcmi(@h>-DM1|F2SZzwPobNaz}W zR6?H?JUgQYt<^$v3>s#eLQ7;-@X$}xD1XcKs?NXsM=H<1=0~c0?1k(=yO6)&*M#h` z>MdT7)T1mtL(dRbWF$GlzkZ#r*xzmnG1Tt(tC{Pe>H&+jrzEw_N#g zyy)UZ@=(v_Eb~y8v^`vpHH#Ms%-;N<@CYw|F*w#sOqt_!!lkWV6@?3?=oiDn z!%}F4l#T?YhkC_9=^9EO)#_DH8ufHXSh|`82!->5!c|^TP`J`7ryPQJXIQR6nmHuX z-ttU)%aVDS_C}}ZK2ta~-bzS=+1M69EzLBzgwkPyi>Bx|L(p+)Z(gRo#hLcTC>*vI zC3{y`xJcTYmuYWdroBUH|nawxKqN?ehE!*r|TidVJL0Xf239lEQVBW*7iyaIu!NnIDRW9bR4Y?)rz~it_~5K$LO@~- zmgiD=4w{`DBF{m?lU4E@G(A}%&tc<})nP++N%I{83cH*%-!ZUI_NDob0mj%7Ijqnz z;Iy}mSg{kr15ytg5dSHX<8k`i!<#ZD0ivtl2ho0OqjpR8HBtp}F!2A{>x@99Qzmrs z?->E`hA=J68G%kB*%4StBs&5fMEXadomlP&v=x5X5oqJh4}n7WkdHJ1|68yByGH=L zLC&BgI|q7*WJiFtP5%gV6RFP5flb8vN5Fq$vL3QeiZx`vfx;gG>Hl%)M?(7hzHYEq z^6#VD4T81yq8zNX5y@h0S1#8224HQ+K&)Ng^g&qbrEu9lpY0b-eSfTd{JIFV_vN!6 zqi!GC05ayIkj=kaO!vxU+mt)mHsnsWwM4Q)qLWBJi&{x6hed7iK4`LaP+0Vke*`dvJf&ATt6lVV;!;Df;$?A3 zThZGH0zPkn?tgndO8Xpc*qdxHw zaMbtyICA|5=IDwHj_Ak$9MP8nIHEfPa72gpjU#FVgSj~B&c)HDTpVqPwanpn72w~Ya_amuj|68vcu4gP>`(B6| zOH*jVGS~@CSjNm0ny|1n^_#G;Q}qbq*B0j>zLQ8%hS{wo63d{vwu4w<$n3m!BE?P! zuhefn`GXMOM&S>!BslSu_ecE4uYaJD^D*l7ts8$#HXoDC`{>3WPnms88LX{khy*N$ zS#Qq!8oxe9;64!e;f!DZTc`_%udm?$k>MLMoizlay(Z&S>>cH_B>IJMb$5AgzLST??mc)l|< z8}B8a=agjQJBXJ!H9o^M#6S0Qx=Ka;w$JJDskm3Q1aD*4G$t{jZ$K zH8ufqf*wbL4!h!xUJ)?|%_FAI#bE~91-y_W(t&JR-Z`cs%i@)lm|Oqpnqf-5SMQb@{33W+HotgI*~B&jG(FbS8OOSN3&SJkVs$8p3027s0@@*(=F5NXTRtEef_7)wfF9;P(K_c^h#6SNpU~ zamnu_-{39tdSQn9S4thsjS!#gOp=2ZU*qkNuSQy(f>znc#Qvr1UZz6-@8{_84Z;_9 z*ew~7kh2=tcGw_e3wI6X61%FCMgECj)I(IvKl_XN5Eb!%@I^g2m5_!#WeYoQ&~MuA z5DAw>+$srIMBEAquZp8Z5G!9~N4lqmf-VS2fYH6U|!2!H8d5Obl zPR#$=m-MGAo_kGp18zgsi^9|+VTnKdTzzmE8(#CND%clAUCRI5xq8$&neBtNT%N<` z!Pt7oUf=9ovW#unW`E)c&l#|5w%}jdHM`?ncA+Z2phefG`m2;}h$5nuGs1JTxNr?F z*bS%Ls8ilxMcjILY57D;PglQ?!;5#qN=AURq1>1g?zH6!Boa$VtYGd)qOn5pc5#!v zR$h9NeC~2ozd+ZPmo^{Lettk6 zD=C-nl=?p=W!0J25U9IP<_x``=~s0PO@ zLS#(Cs$`>{ocPK!d9sa-<2V9FnuKxy6tBqS$zC6F!uMnk3sAfYlP6Ag(R`%ADf&b- z0*4A->?79yB9?IT;-sKLVl*x6sn{cjQsIkMP$SVYhXm6aIn_GMDf8sTxHByC3THJ8 z|4NEr6zZUUBTxm8;FK$6LSbqoE2+Jt3RMQAWJD~f+epngc+6(P9=nO%kWv*_*PO!g zyT*yrkfDyl+DOK8s6i-O7fpJZQ=2%^WdzEyl*>G{tqUOEHVo8u9I_R1GqkSyONk2zXgQ^ZWJ(qFmlEZEU$CZ{xh-$(O~$aiGCFahTglZanR)mB zsn(}Hxb?%B2%CPxDX^32$S^vR&!|a)Gt|(ISjEkiFHYs6MD6K5$Z?;942URA^gK_wS@(Pyp4@3VT z$UIM$`j>%BH*nlCDuQoF%8-@u#}pWx2cuPQI$atTGuTZIm%bp?_zMdeOfhx_R#2!e z7+V>=3Nu7sf_gR-CkKKGb~hVPGVU_tEBg7y`2i@r&!8q?o&|x+NI9kvwqvS^WH2f> z@#g!_eN7+m>2hA!s{1ua)>-(a7@3?jIV@J0S;A~0Qj>{r2D;z&w3bN5_T(h|&o0K0 zBOKUG7@xaaYA69UQ&_msGVLDQ9ZqGkdcrzFvS1@-C`_ITv#MUdV?J1Tp=LDLoe?R- z?B?}Ij@fA?@xbhq3JP?RkmuBd@X=<8mphf2xWL*KNC)lxP=$6EL*JdmMr5?3277D7 zt&`F{5!aP)Tf|LDxFh0@l(0AAjz$+ki+U3BcJ!!;C~MQL$0dSFC90?t<1Dt7rVC?H zPTd0kmj6N_l=WWFTDibOR)QvGpt6U5HvQxR524JU)YKe3Snk>o0t|969AyQc7>@cS zCWfP|pbV9BxRuej{W(kYCnC`15W``L;Xa1Lp#2-JGQzGvKNH4OoDiYKWMBvbQ8;;c z$W@Z{&Czy-dtXw?9nwF)M#9BKMw{MT?Y?6q%`=iVzlvu5LLl(^g z#BXZ1V(Xs0R$8V07rv<*{T~e1v2rE7m*cd6rcEHsKeJ5NoRnGo1SiEE6*A};q+`x( z2|5zoM=kuix7b`iV8!k!?!U`CI8Bz~6j_gDE;IM%j^d=+#1Xn+f6tE%JaHH6Z<_WG zTCXSNqh>Ei7Wxm4(8E(qVY438GlD-ST4HNu(au6l8vTbV|E1;OvdpayoZ62d()7$Y zODAubpq-2QuJx{Yqa$s%rAyL?mJvn>^ zyK{sHwGg3gdt?36pI5`f6OXmC8M%m8F<@zr8u2e2i0yLqkppPwygRUd0ba(XGCMAn z!MJ!OFmDDaJnzpqjkPeB(p9FN6-gE}+4R@RlUqR)B7MA7;flDh64@be3f6!;%m#zo zFW0+-2YSct$E_mmqxbH>u+;xz+s*A}6IJnaOF?WHoht~ig}?%w{3gR{bjq+Ak(_V@ zzE8jqmxM@@yN>sRZblhmak69+}Wn7fgL>YlCB)46KlZASRHkl z9)wIhrl+GhtbcT|e#S3+QBP1m^{2k5Usr2D4UorV6w`o#MOZ8_6Su-gP|>GZvmH5dC)aPN#I63e0&o^dou<6}CO1M;}WuP{27|OcJ*$8E)-W$VU!9gM4KnB9-Kp zU@mBbD>(t1Tg-p`5nWqJX>FR6&7wtJ^^fWW+WtwyDE~o6kMKvl4ttUhW?bm^I!s`Z zGZ!LdI*|c3=bH_mQHc#8Bm0*UmgyLiw&6kzdK9hzr+p2UINLi&@R6)a9OV?PaSUer7mmF055{wgphH`?$R$5SIn=RzrAoGXLp%jF*K4k?NUW^xc5M6l|1k?CW3 zvZ&uN?{T?Cz=BaEysJ1yPOif@NVq=$UQdLYa;q?4VdDapK)~*}EerOaL5)WR1nWYtG9xd8l_8{H;EmPiCAIh>#)4-JiIyXp zAT@nqa3cLw{l6pD{o8KQ^`i#}@&OIYC`9mg|1aNWBdC$&xQ3=#GTaRKkP@A*EJ4#j ze6AxH=E+&fx%WZ;?C@kpk~bO4kN_YH;tXhx^hEa%Huxoxo`r^C$Zf#>b+@AjiI>J9TlKpDQqI zuMPx^w?>MfG5V_QO&0SZ?2;8GGf41r7zT<4xtA+Nl4DP_-05mKPk2d!Vgj9nfzZLC z_p;zO3trZD{h*&F7J%)C`UNxT4r(!g(QX9r2m{zH;M*&yh_Iw+_WA^{WG+yM51>}K zV@479@Or?qK}u*uZV16iFG?t;Vi}2u=ihGYp*ch#9t>P>=FWuzFP~xH;QkaPY3$l2 zvSj@c%8VW6SEEh`daAjKAs-em@8dv59ZFVDvr03p)u}e>wfJBLDcC4EpZd3SE^lz> z{B@)Cuw(lPyx%($N57W)1<T`=A$EJajBEY-*#Ch=oH^@g1-m?`~ zy@-}>2@x2h7!VC5<3A&XNA4~VzRd#zj7xy){W9DFBH=a#k?HMdZsO)OD?a=j0WXfF z25{rxOF}t|7{mKyYAZGgxW$2;Zr_a!)&v6@%c@|7NCDHbY;)Oagq$SZ!}xQ+-VKJ1 zrNNy#r@?Fetq154g9w{FQ2&&&;a>(L@Sh)L_zx_nJ0emUOMvmvu-cDgme&~RNR!s} z_NPgEdLm6JyBFS@u{8;z0eb`EH{R-wB#S+JmVA&4rgMJ;tpPoQH9>TZ8+^veyNTkI zybp4eamGNGV+_J}*Ka3s^h9e*WV$k&JgBdiRmWP8l47So#AUC8F>~Q{l$vltW_tJ& z#bxbMZN5~SFeGm}n{+&FK3Zj7aCsph6&cCR=BBGGjMYMpTd@~L4i$RA=pe>0x)-T+ zQ}|pAxduJ|5k(Cu;|y`O(-;%;XF3TlG7Cq!p0kKeQ!_BL!%UFjPH7e=V)EV3m%bP% zn`5Qd%<}iIrG{E$1%_eA+*Fo3yzG1WEC<0kppttsbt#|ijlAemOs=h60azD5^+}|I~bxe3<@77%ZKtDna8*FK$nH1 zzTh~pk-dxykHrVKe<}sIU2EWtpwfXs?ZJmdb*PaUDhXQ8J_p()P0T~`x?B}Y zOA8kyS!IDpAQYNLR!AeULov0CpGEz%M!s(w>jxrPsu+M||2BKCMj}jD&KWxmL?hCR z57ue9x|309uviQr;b?bRC<61mGSlh^h?D~6GG_T;qEik>^enn8!zN#`%kUK;&3Mdijl|`N30K-IEV&so^e9S-i-U^fKA_OVid-3XdQK{H#O-0& za6bdaIYJWr3Fyhnz+->|g*3PrX{qGsFcJ2gaKgw+B6!AwoHim*OSXB&EuSN1*nNbL zH0zKs_$ZLN+G(l)X-CXR>Mn+F7YejI_wwA!bIN<3*V+Hp-_wcoh8~eA!W^mzqzQg2 zYzIY2)DMy7NxXc{$Ys(*z=ItyB1$Xv^sxk^nrtV?5LZtlkNCu96V%9c*XE60GUlhC zIH)BN_Ri2zSvG>#CF6pLm@P*cEO5szihM<;pi?}}`VvsxCcO;4>o{;n3G18eZxFyl z7ZXIA^g;qn2kq(Bxzzk;D}{&-yUr&DjNP72hg~-O#P~iVvX7D#In(H-2Hd_=qe30~Sj>DREYG;UIBg zv?S#Eb|4blV_lbdMeI2XskCH@{t^K!=nDj}zB37GoAhY}kivX|pE6aLt9}8+MM&W{5jOy=GNM{R)1@(Oj%~jb_h==S-oJP`o8N{$6 zS$5+hmHVNsn78V!ZX9PMIeZicBaye^ZMJX}`)x~gh*Uee3w7|F{es%`4T~2auj>8YtN5hdFT38pO5c#Ddi-mz)#vl& zJ-_CMTE4_^_7C+Mz3h8_$tpd!Y}5C;jROx>T-bcncM{KbEm))En-`4f^m0b67s?l z?ceVjbG?2?^GQBuHE=Uu=JQWot&ivHYh7zr>ud9dUbLDq80}qkBMmv2@d>Y2UU8HE zA2;i-s1^PLH{)~2ewU3;cG+I$yMEpdD&^&l6~23mK7N0PF9-L-#wWZ{a;krJDi}k8IKxGJodYt*=mV|M|O7bynT(@4QEk&@0~b$KIpe{e}GZTXFBMxhLpd z*FEef-r;|KgRT>Jv3vDd(&?`fs(bw>?$s9^5po6NGWS-$nPm&i3@tpILp`CZ?@K{~ zm|_`|GYLxlj?MaWKLZWSR7^c@)>FW2M_B;N<8?mUgVhXERGdb+4An7SCoz6(0S~~Pu#ZrrtP=9*H!j3 zUYWtS#ZG-jAIxpBEzjt4qImo)`gp&;=UH8?R{MR==mjc_t(4dS{tthvXP)@VdpURM z(v{b9<6MUPx7;|czdgBeSn+po(ubl*d87GCnSby$J@LeQa*F}>SGjS(KA0N^>=U_h zz;4ft1NIO8!O!Y?zh|3HoOo$&9UQ7`rJ6MZx`4TPwf3iPEP>4 zBR3A%zT7xqcjd+b+cpU7a{snxnPCCW^|pWbZ#=^-#pAc@fAVRf0P>s?v1%|;NI+a{+{M;lZ&Ly!X5qs*xuYYV0Yxk z0o#`w2kfrgIAH(ruJ1mtzk@^gr_bxLYFpP6&+7-(NIc0H&}Oi~j^J=iZeBNP*;vcm zA|H`Yciiv)vX_gwf9k)|tB+8>_D8><-y*2}qwaPypIc9?#f%fM>}eKT`{j7pe7M&p zfA=3@iktlL|E;h2%&v>!vgVVD-fhdaSl)q@VDFRH(+At{euKG`K`a>C4G#4&J|j| z7*cqpKI>l>+7m={^t%2GE7Ew}clcjUBRGj}PwO+J^z^Ir ztT9;$H?_rkN_5~r@3mqj7Fs)BG=Kpoe&_Rj=24^H&#!O34**tKJ4HReIhJU4a4hd*z>A%66v-FY8aKU--*k)=LF3d9sHm0%C@|q7MWyN4=uAL_02K7$$h# zesqU^NlN7l`r?$@wZouq#H;#i6u#nB{c1M5nC#_$GTAr(NgspAd*e^sSpQr9^MBUA z4P+y`!HjGamDI?_c{`14yl^K6>e~D#cV>iWA}lD|228;h|6&SOzNRmz$h3||BDiC{ ze~(se>J1VaM!(s=P#Ud7sMUFaBU&X;B$2wAX z5Xx=B{C04?&kZWrG*vX-+7V4&qA_Y&!c4#se4&iKN*;nM)N8*$( za?laSW;;E$CGj)qm%W3%Thgz!g~_W_%G1ZNx0+~Ap}UlP+yjN~0tq)4x+glIXH%hz zk%NSTlK7(fcTK^11E=E4ibCwf5^LEz+M&% zcE9nRp~f9F$awuBhT4Db&0w8U`jB0Dplh>N&1M&69$E33)wl>m#$KGQ&3b?2!$Z}x#LdGjpCi!K-&f79}-WsPhr;}0v2M%zJKiRbmQt|k(d=vy5 z#5aZ>pHinWB;ht2Vwp;x_=AqMvS7>^JlzSNxc5T#CY5?v>=`?^Op>lL8)gH9G#Uhb zPBY+w6}K$rOf`<#Drf2%cQjygykm5)^k1T}=t*|N;nKSVy$?R35%#Gt+b6NrC(GEJ z#JMvsBA?4Bm6iP}8Q`t{Bk$;8s?^`{jy^bre8e+G!|fikIa~&YLL5V-iy)gl#b)#K z`(Yc}vgp{8F987)@HDrdA;vk(B~F$#%e*qM|o5UnGK%g#Kof#nr^ zh`U&(OtU3|rPN!6sol6iOTE=Rioc1}zzw?6|J^%!VhZ!08Kz`3!HH<6Fma0+EM}P* zp$;A61bku%9`s7#HHG_b$-Wxm#MirXoyzqLXyJM>^k&EbcX{cddL`&=uu~EUZ6ph8 z-7!v~kciNQTlt~zLZd!f+fq}VF>B~*riFjSTN86UC}5feuAI_|Ox@|K4;nyaV2{@^ zkrW;2;j+#eIHR*5>5P&gcVsI}ib*~^NgxQHE@-w;0wR6B{akAq=*6q~>3*i#X=LBeejZsLeh zz0)8rI&q@V{#x09TIfu|SwSzxwm4(Na9+skPNP&-C`U$c3dj@gh0(zidb!!13w=!l zf3h(e|8k2H3vpZMjcReqQX!h9Zi7O@eW7G7CY1%6Osz;PX&E}_k_p<@;vVL;UxWI| z$!+VTxb3YZ(yZ~6H7j?v*C8>yZ=4&+JT=Sqe8XOJa%yv;9kL8q3;b>>XMAFA1`Ay4 z;8r;$$@0hyTR2&bQzbPf>u9v6=YYIA@XS69d1A85NSoPL7i<*{=OAyX7Pkrjb>Kz{ z88<^6D3izt#tWg{;+A-YmK!%Uac{|pNcG4P@&&wdVh~Bq!WJrV*^??42n011`os6= zaj9Z+B@kcDy|5L0n@>Jc#H}z47_QTjtQSz{wmj^%({>1Sgj$xV2pW$<;9wFQm9-ic72OWyqy{ zPT1UN7>!b!0ID<%^R&EWxN=YhC$UxxiHv5kpd=?2e9;n5zFDMEsabnnw#8z%HuXyd zuwJ%T7(4|i2^Jm(D%Vd^Bmg32N5;JI?Ch_eS(J2F$tXzU#wMr)MV!q#W$WG9jwMHUow?1?OPbr>+f%S~SuA|qQ@Rw= zBXgNETW(}7bt%6M~{zf&?6o0CFsscWfw!3u79&wAhI6Vf+@RGCFgsM z3t-?@^U(IB-Mq(I+I-Q)%i23SyW7--M!%gSZ*J1=EK%0((C(=+avQYkNw^M=frMye zQzS&=87CnUsKFV}L$7ih!B%pVTOt#OZqJAPHnt`?ZAP!hIL*u9PO0UbA$vpHLsL^J zF?Bw89pj8?R+lVoZp$<$wRFC0Aa^crY=p+b*DPx(ZZ}X zp1s5o{@Uuzhy6?j!4RD^z|7w95G*>DP;$(f3@vl2;I}HyX7AL%6mzDg!I{it7M{`& zaISH@lMu3&v?)Z)F$ZUyz>uAu&V-pH28^p}rrC_u`MW1p<2x|jg1&l{%tzMk{l&_NzNMU!#&UB~HnSh?sg1D)Y(DVze z3IE=AmeC+3i~5d4R>H_? z#PgoPqwu3FDrp2<2Hw;fH^W%vOjsn)A%;=TsR0HpXO=UW!zU*RM*`6e)9I-*9#ORj zrVsh9)PdBPLRDrn;RG2wkFl5ldJsTp;(`3(B5?QTy0WHU0!={u4J8nZ%d5NXij)B% z3s`1c$UHq^o?eC{t~K>R9nv+!F@>$qcSgySG1EMELL_CTcu}{6NyFXjf}axMFeSVZ z-NzdTrKMmtJ6R!Q+UZ%d870%k ziUVDi{Pdg67Hhmc7L|);)F}8^&`L6B2~Li|e*B*l*yp9><}eU|j*3YD=Q~D)9pJhW z#t()JPd6)0%Q;B2qtl#&P{?enn1$70PNm2LF5#gxGzt}Xh6*Yj&*(d{1Pb&WqX!s$ zr!XM%Pz&*92TUU*E2yBw^`Jme21R@tg~w1i(=pV0&UCQ zO47i2&YFKgveC@*3c(}#(PYk@2ZNHG?V|H}fqrx-n2bAzW;LTyuam`9iNCYRt~py+ zpJBm)LKEDGwUSkqETxfx!EB@sY%+3FfMD1ZU6RfIfh0AWnf&!I+sYa(MDsHlKi;#+ly^fv9Q=_d`M_EN2CJ- znE*2Y6aFK`c5U-VFl-0jf~ep3CbvI`h0Pr5j(VBTs0a2uZ2OJIiXAqm5<3j@8p6u4 zF3Cifv5sQilZmUmsn|lQys4P%N;rKP*zDj5a(YMx3Cie%bY#uDNdX+lfh(^>?T z!KVvTK+0q&PIHqz5v)YL!{T5KjsZfW8>q%gE>lI(k} zCnNJQ8())Hp@xXN!ZMd zAOxAIhhN2sPqRKjV0I&e--&8NWV$tzf%*-bLQjPoiD;7f>oE7^iB*4d@Py7fD|o_A zV9+RQ1S|%o9CLS4@Whn%IFAXIgB~D0lFVR+k)`v3WEFfS=ywv1x5Pa;unA%#ly1w$ zP9B)fhO9d@Dmw9%b~3adK?Q{&p)fNWfe>R^Z+L{U={Ab?+=oD7UJALM3qWz>FD+*}{FE(#TmnZDKN&n?+eP51woF zBn_HtbtGlxnpoLnvW*~Z&}2K6q(PIdjHE%6ElyI-WRtlj+dOmTS`~CJ3uy==HgK*r zGS`Gx$Yg5_CR-<*E1*7Ojsg43H@OFowKzu~WVTfV6Kj%V=GB;BURlnm!EBRV*G#tf zz{ytQG{|JD2_{=jc23pM|IECrG4rk@oOdIoSa z&wqA^y;RNePp_~i96uLG&Rk3$asguLQZ~oa$dg60JIy({f4-Y$$CxW}tm`x}!w^QD zxog~$jr1!u#~5b&&sNw?Ljrkuuy}DS+{|&@N&fgF?AqLYgdfK9x3JRQhw}>NC~`hI zE10*%binW}kNGQdFK7PdNW&EY3etQT|t}?2+e)6NZIUg3pL&$5$Gi z7})4UAgTkH&^Oi}5hgSh;<05kPbJR?pdeBz=L!r9q;D~QSCySiRmh?g%yipsoe|R& z6V+WRS?DDf8n>1))f7oDfjH4B`mQz45*mUS&IDixdAVdBC$#7|k`ZDNJz zuqa1sux1grX4i`P&w?lBGzfQRCjlnE$UL#rss&tzRUsT=af`=6Lo=h{v_ie@Q0yUu z+_WVGYcMCrM(Rl8I72pV7ucNQ&mV0MPi+V}Ak;kGx=pjxqLZSmtmQq&IVAf|C-KpS zWdv~LY)Y{+C6h0B>_g0UL}XEe7z$8R;>CjRyW{$FYe?oY8Zl4DBtld!r@^M8m6(XU zf(6DS%oW9M4PAoAmvDED7`wUXW&;OY5kzxl#uLsQ4__tzC&DRu17TQQ6jwy*Yg_?p za*c;8c`(X980H{N4daB>G?%VYtge$2kiu}~46TWxG?^rLcbLSr(DDQeuJwvfz^g6w zP^x@^Q#&h;--wUYW2Pl=P<1NAAF>!dK`MfoGPxv>*3u5lX56{Eumw=bg^AR5wjj}Z z(s+fm$l}Cyg&`9lVIO2Qa+q^)2GQo8)bY-QYOkix{BQ$swr2K)*w0Rd72ssyUJL7aRaFNWVm z@b?Vp8vsze5iO1rHqyWwosCeFx#ErHn+A16QqmP!`3v@CR!InpTP%?2RFHzQiry%D zgp49t!5mdBGCb1pD4Ma1XU(8y4Mt>6vkW3lvr-0=Iqfnd(Gihml>=CsQ@&TT!jwVH z3MAGP=dcVZ$Vph{@c3+ehnd5NhVgQ`f$2UVGZ`OQq9UoVE{Hu4Co;vG9Mew2O1g<3 z9pKi)IV_i)Igka&S-cIf0Ee~r2F2prXm-a0lld4&!Fh%4#>LPm7QGa7bBAh~sMrHy z7(z5@=rTDx!#J2rZWMrZhH;oOb&P$MYt#wXko_Yya1C)0na4b%CD+A3L56FT?}KZU zrve7fkPF+WH@G;M$tVoNaB3Ka*iS=FFoEV~ImKimIXuEh3*ix1YEOu>tqGt&;R^El zS@CI3MZhBnghz-2fw4uTWSNAVV#_j#80Hcos(K;vfJc;*B;A6k5@62bUBgN?Li0WF12ta)e!K45bXs)uvWKZ`9b}1U=*; ze~`>HZB9c$XN&i8eUJrXMxJy0{>bt2@nvVO|RBs&Lp8;h4d3nuOMwOEPTE(b)(SZl;N( z4DZTt$e@-&Q;P_4P*XQg#CU%lxXGoC!k`Wj64^RLh-B*!DHGNaGB3k^c@dcX%!|!b z{{q0=24%QIIjtVZyoSqIViyQ9KqAvyhQus^%VmV!Xv!Im<^^JD)p7K+jv;l}Tu0-D zl{3Dky)yWQA(msh7r~p!#OIDMcPdW6G-NtfPoQ&x&T{{RZdIL<$z?AvfS62%=&|H|>ew z$@a2$ z&G7Mo3K>(#o!-r3-iAF32C@QJQg>z`V+>@B+bVDd2`w<|>*S*&C}GBAZbneZPMl>) zE?Z;TGgPAUVRHtn3}lU{u+m|HxtY;#0A_<%Y{p0^Askt3#s+XhII@Q5%8*@1yEV2%o3XJvH&o6#nWWsnAoNSODqv6Toz-q{mnUM z7HWE#;e%1H=(zY~6iXv;f(a@S5ik`FY9Y*B7VefQ0{P-R7fm!vd@-+^XuuIIFte3d zhFQkM7oX*EG4EMoakCvJCL72c_Jso1$cuo`<_Jre6Vk?fr~Iv zvt!=co!wTVl4VDIZUir?w<*lYoZMXYCrYwU-s}8xx8*f@Jp?8mu1IJpX3va`ku&qc zAH%U^C1ut{{%fP{{OUl7G$=&LMnD=2Hu$kI_Q}!1!AfTY zREgQQWo$f2*}x!PfWl_YXo^l;BDR&xE(HcyoSnpjjS41?Op1nk6(k;Pdw?QJ#1;~NtELQTnI{G-O&2OcmHE#qkZPvbw+ z!YyYHTTktRWVIk8^Y`zwfH)I9Th8cD47MIlt{AHR1=LzgtWz|8#)7y z9Ap+r9xJ48+ySSEJ0Rkm;+*0|%nsg;g1{XBu~<&E*}fZfsslr6wRi+b^)|6t{3ybm z>>{5>xNcaBdkA62P;Opa%?RvADyUdPYBqlXmlw>)shdIYWS*&2?)x4Zn^TcXGG1f;xv9Xpy zb(dmXS(2^6Tv=iU8=4VTf9zN*&vi_>hMaQj*283H`rk|YN7J}qe+|S`?d>3`n51C; zjU+k8HdtkB6B;74Xy7(jh3<3%_W>(3@Eq$sAonW@gf>I`S)IJv+f^;ppVio_y*`r= zO=T2%M!=}{JF0no0~gNGjOv!eBBvUqGVcCR$wN=vU1c7&#ocoxp*QY+L&6<#_nQ*- z#ocd7xGU~{Tf(*y_hR!xdx`rMN!S#3zbavO+&xdi)YiD$Di0kcuBmHfiF>BRJ4@Wp zO1QSfJzc^LCGJTQZYseoLfBp6(osgF`e3W4-rdke)m}GrIwhB?x0-~0x@S!;_Noo> zfSmb-v=fWW46T*crKEYrvPji5seLG4&I?;D$#JOHsby2!P-G=>PtC&WR_)Z7Bs5al ztrVr@@jPKF4(^&}OJuivv!_T@o?>J`oI{zt+0&LHYrK^~+?ZWTypC*cxB(q!DO-5G zN;!~)AQBE{VzN)%W9=8f7Wu-qt9!F=6X$r-ow;%9TAv%gF&mMpQUIbZ*-241pe|w- z8UojBOTRF{2kgGkn_*a&Fw`js#N?=e;V|6E+F+0~LoECn7QzJ`X2j=-Kpw}BjEH49 z^JI}YE)|ZRNnoutE*xHoJLAHlX4LT9)Owq_f*3;=?2CI+YP{!yDELRw&%r;EiT#pC z8(Qk+hW3y@Pw{#)400ueBK92fg@$UclW69f2i4w6u8ka(OJ|)XA%}aeH3>O%2J_j* zS28*vG-s5dxsD>CHp-dCW|}%3*WPYiA?_r$b2F1udkR|JX*}02aX;xyHy)59oVe`P zV%sLAndGHQv92{=%x>_u$1aPylf?PS&@pmH$To!|0?+*sUd91=gps1nc7_~fxAb(g z!F3Y5F5BL-+01%`csZ6Wb1^%75-S(Pj~Em^3faN;l|~VQlTf`ueBh^cNCRz|G~u@$ zaTsJF>6{wlm)>UjSuN+skZaW*sLH(JwUNfh0zL2AF_2))Cxz(GNdbh?~lH&oJbzLG{jlXgo zM|>JATO^y2v57<=P7$z^^o*3*C{`G>a{PzP10S=b=56`rZ7<8U1K!OVrzyW)sGGdisW-5DE<3Q2)bosE07u6JIj& zf?eS3k#4?doTzLJ0}TQ@u%1J~kMS{xHV%D}G=(H)Y@nc5J4czPj-i2a2ECW?wL3A9 zKwsEc-GxbEJ;^9{m1?K@P<=;S^o~O{LvxahwU`D&H8+l8mSiMbLN(WO&%jeNG9y?^%2e7AmvMq~ zwg9aMf~^TA+W%$mO~CB9s(arvwB&R8^l6)UsMbYp`X_HeE8Z>==(2hZzHg zkd`6XIKBx}wXq;=CO5>S1^B-7O$=7Re#j7RI)Qs%n~+2hClNyuIq}UC!9W8hh{WWH zgoO9|uf3~IomLAlA^CE@?;_k)wa>0yd#}CL+H2ZtV^CmXC}SzD%lGZ7fpWIo*6s9U zdy@$@pyqM}4qwF2>{dEOFD1%9q27`@%cRN4ZhJXH-lsthg#1Q{^+?3s6-QxM#tM=Q zQIr2(Ofnjo8>XV<`5ZBn=W6lg<`A~zWW1Aj?VwIudV-5;vp|P9(Q`3~t5h#B>9q6+ zaZ2Z)@X5y~4KKVI0Cl#N@WW029olVwG|5HUt$vpIKLy$j0nLLe*db^oaN40YErHMu zp}dC>P`OB2?L`J6zD6vur6M@vV;D4ugb~_ECC{)N_J>)wVRm`5>0cD(D&Y>cOrmBC z1sB1?fzgnj&U2if#S|0=&tJu-Ws1B=n`CI7?(exD!AyL0&%XQ=)m@k9WQ@Xekr{2; zqc7*AYLtTY_-q?5Lh$a@a&)ImAW`x5wBT=ZM)i#Xc6?AHpA_ub*W$i*2w))BIFnNe zv%z_qpB=V^qXbahbW|YqN<9Mv>RiQO!mEOz2ZJ}~Jwraku2*LK7>mYwR$ou@(LB#1 zfqB48hhhRxk&khX1tfY5McbCG#)5Sm_hAskiI<1=C|Hvb3+8!lOOG2rH~Q~94+gs& z-H1MEpVL6+in`y-;)L&5a=IT4fFY5kqHbi70~6d%w=uJr7~&y_Zbl8KR<3^G(mUO@bIjw-Vcd>f zch_Tg=P!?nZmXNZLdV=zx3SA`%pU_w*}<$~g%=s+wt$^{TzK;Wg<@VuI0*{5 zU~n9;i*BGkBSn>qE|Vj-4CTG0W)vu+M)HO#Z9&rW&@3zdW__GKjUNmyQFqHa6o%1) zG6}Mjqes9WoGbbjG_gory0Y8MljY!9W}7VQz)0=FQ>~NSzS-|N74q4FSyqHcu)}=? z{3Gm6A;RToj{5|0NgBXYf|g~uy;GztD@51EQ*`6`6FqJQ?_{beM}NlSphwVhbV@&x zMSmi}IhrP$_FgxRhqwj*!W8|WZ^XLiYXK-nZ<2&JmK$(&5#;n4JoEbPZF(Bbb#1qr zck3xSH(B`|=aX)6RdBuNJ=m-c6D%V$6G$VIQkz~eTu_XmgssE^B^j9iz4}ggVG>2e zo8UzY_7{isU|74R#}*DOG|qfZse+JM&pb1sr_&@gD2o**OwI@b7OJ#_CBtp)3WFD^ zpNi;l2CsceJqQ+Jdjl(-oJ`l}q*tQ)aQ^k5?k)B`UAyk^P+Y2uMGk#2AFjiohv7JU zF4nttq?TXedO6o$=D#;-v;IkvfpjJ2yq}^e@42b*?zyS)?zvi%4E3Jt41LcvL!aq( zblPpuzZiK3o4J>5fv1VJ7t4^>6MAJX>&35oTY4d*i*Pb-e~cSQJa&XKgr5^NP*>igQv&pxdfZvLyU z%;!>KgN=WlXRpGR0etk??DMC-GIRgaiW_MY!{hRxDubeHbD4wu%o9&5uHxtNv; z?hm(ff5_d>D}HQ}djVa4+a_x7lgq4YEO!{+5z-kObu84NQ;D}bgD%X0WC|vXL);cr zh)oK1tF&UZDVwDz;c%UjPkzp`VMS^4`!v5N`njUCDVm~aFs)^MnWV_Gd}Ji`5s~y{ zg)!jGe2l9^(i4@KnPAsS|_j*4EmIz`& zw9_`H$~n{}CiML_089#=9K54SZ>m8jq785Y+v8J`PV=AdZH-vH((dYUpvSEMi5PD6il=a-@5|$*~xPr)enyY94~;2j|tUy9+=O{k?)+t6XPS zVC~>~?}~~(D?izME7vs-I^~mP9(BktjlU=jWIlKsj-}dEK3I=%xf79S=GB)J`&NGK z%k#NWmsrf)VJCU^^oN6K=7X1i6J=>Gbbk9>9B?DdaG{@GRu;@`>83jeoS-nYz;N zr4IaC?HLdI$#0lc%fZaa{UjYe{U!5>#(c)n=jl?ogZ_|~oUnpji3PJuq8a>g8-sCn zYlGE#VC3=}AUKHTWev>T4@Q#Y3e0$*)oGhHEf2IZEf$UScZVL%MH`}HAf>65KeI13 zk+ds1-Z}x5!y&t(V~tx*J-1Tc@5Fm{t>Oo4Y+`zoQM)gs-HK5?PDYCz z5d#eY%X5cQzGUMlrCkuyDnpu#h%2=R)VH$F$lJ$;5_6CvsJ-)bI+`IFj9E2Nx71AC*wCCx~5v( zhZ_He@H{PA;+|@Ocm+i()b0e!h*4Z|jUgWkuq+yhnY~L|%z67@@elpodp`1+FMf6I zYka2msPGhCq+dRG`tGmY^*0|o@wxN|rd~gM;0y1+Q*L`p^ln z{quu8*FPB0yFlH1J=w$SHwLd#SKoZ}SN>%3j(@!O0y-MJmioukRhyyU`TV=}dSCzf z!7ch?rhYDX9rYI)?cW&CsIMQqAb!KXWB>HsM?U(Eum9setoJA7(IeJEsdA3Zb1%N;2IF79`UIIbRza+JNJS(*}V7^V-YrtbY-xHrH*$_Ih ztfyb?$eP)!88bZm-y7ZSOAW|1fYt=B1Y9CX_Z=W1!B2$XK_$6R^w9v-F)LZflpME` zQI10e;6=2)RIj^3!Y*|NK+X)$M|5Jt=s`(qtbbAkj@OSZmG zSNb%oD;-6|H~mPss=>>N^#J#YaE8Kzi@|Gmq~a`$n~XycnVI&qxUa~%7|{B95sJGo zq@niZatvAe9Mxqy%II?@;aZUv6VvDHpc5-H0+&8#*k~?>^;ANin}jT-&zZi8J|}%O zBA!2ZR)V*R_Q#QwJZjFhHjuB1y(z5R$3bt119 z=&|s!^_#(E^Rmi#o6XB=29xHLi8HVGUSVBwmfyG<8F#Ml@znK=km(c^c5Fbp`-;#B zX*z6D%yH6*T(@41lQz>b0^XVKYoz;9mr=^az$(E`83f`dwYOZ;Z=pad!ufFpTVOk*D5#Yikk{X*f zwnj%hbBowgPcM_lQ7m{bGyRgSn&gx+rvm3QnOEUu!Z}P5JMk(EiUSf0bDMeu7pjs! z53ic#&+Cw#5K+1#dh}QSvBjJUB>>(BFI?lC3Tp`DppKjhYXEuHsjxtl@JK-MLEg9u!xzbc)`qK)dEuC^a(ZL5e%U@imYaP6cT7n($@1JR=;cC^pMk zmV+G@6`_!|q2Oiel;C6Pk?aXTY_bqfB=9K(zl^w~9RP5nPb<=O9(NeQ)#BKSDmFE$ z6dmQ(R5aKt9~RtRrsU-p2bUkf@b_Fz$YrvIVMz;N+TAD9&3q( z*9hg>DmyOIgqEm8V}`w=l)!v#wZ;e|cAiL0Cgma@ILX(G)@Dy(4@Zqu1gID;R!8cU zw6%cUNoH)&r#VcKFSX6w|Eyxy%7v^Y6IJxf+2_BWIq|IGMNio@`Scr#TVf(wig{25+gAApXJn%S^4Kt;?ix7Jywz=+f{VJQqFK<`Tu&`3|}; z^n-jR2@nB(JTDYBhk*2>@td=b15kk zqUA_mmqyER9hAsV>|6(1rCJY}>!1Y5ZB+wo!qVgoMeiaD*Ius2yGS6(^=PN;_e0SX zTZk{~9Y;D#cC3DCob6cQGWa3y*+47i@`Mqa1uA;weYcsKBFSd@rU&n!S|)e;cEm7{9O~r5n_rR6&{%r`3rB_bf~KG)(t%`p z^CLPl+J@w$ydg&n*&7vWZe^r3u26SaD3)7mB`{es*xkv=?rNqG@$}Pn_pRvHvNY>CS4l-d#rA zC2$vTv}+={J(1k*@+G=xYIHrqzO;Q`edtsF`uMTOfB$L3AM5!C_xM{ zy~8T_`>%fD%fEU5zuX^xfRbsI{Lv%7dEzgQfAeFXpkRuEXmTEr`Ka!g%ILjcMFvDR z>*aZ>eIlyVdKn&b$S^JdBi9vd4P*AL0KL)`l=&4Cmj9Kl$+}|}hS*usT`$Mc!?~;B zNX3cRjn{{wTAx_i%e9~92QZroCA$areR+xK zWySw$nl*wBqWg%3=Tmbl$aG6c)#Q33F@12VqrifxZ6o7qFs4O;k@u3t*Ec60D(~Uf zG0HOvq=P((vJN3c2SXPYj&u@{!}WnqR7(4a<}Qp5J29;-7#hvp-7aVQ8pHE;X<6{e zaw5F_EFrSeL^q_`b3ePNw_mbJ%;;zIy?J2`8ib#Q$jA}5I>q(BLwMJY-0cw+r zR+};A?(CT=ni_4yr38gR6-!4Hz%U+78OLv+ZG+SUrYJ^U}!Uy z#f0}m!?|LxpT>-vOM%@V^PX$Mzs&=dA#$vyyk%Gnr#;J7=7>A0QIWU~<2uY$B1W=+ zA%+`<(jgz@ITWpPD8wuza{zaN!M4YZ7mst)X1Vdo9XysAwZDrwqeG5kTQ682c}Cw% zd-+wK#Q|5O>`TZ{!=$H0cxZEun5D~9J7 zSXhzC`v&m@+)j`uK&L2CLGnE$$EGMhVr%IixQZ(0Kzla-`CAUn;g6-+HMdvzp95R6*aVh$ie%;IzxhB5N zW0G=6+bgAiSSw%Q*1pK3GGt90aZT)-l<;DyRd(nr$g3>`-kA!rLn_FSgQJ)6c=cEp zqT3JHF+q3+kB@ZW$AK2#cniKopbIAEq9bo%IZ@=I@i#2+iR1t;bo_151y?U7arU}u zlbc+?iP#A$?Y{NFU@(>4`Dlv*)GlH6$%4gxSw)H}$j4D|Cw&0RK{wFzVEqyk(s{{p zAL>ZaMDjGZ{EmGlqDzwXB06izxzZ1vcn&XMXCAWnfneD3l&vy-AujRFl2;fc_p1GN zdW0t^N4FAvK?;E^0%dijY+YF?ci}3w5GSev*y=yQE>QbM#p0OUCzft{B?;0LskEr= zbqZu<&+*Kjn~Ed(^307l71wYc(OYgRUJ_omuW&78BDxY4tP&hhvF&;w+2Wz_K!tozQg*il z2gnOWp6zD>>!$Qk2kdw9M%@p%5BQ`}0eRYTWFfS)Ur{Mm#>ZzqXu9LGg;4VJqa2=d zCyBoKj5<@ej+QqnLD!93$hUPp7t-Wk!-X8(mvLbW=P&IjzJj|V0#%+XJ6R`n<(C@~ zy0}$<9pjnICx-craCnYYHduS^yOG6QB@4MmrCBTC<)ls!VYOA;>+nMBS|LJQS5kw` z>&hM-><$~1YvP!$!`lmw=*kY~1%cr9#9dv zN9Ye0A+T8b7e9{XF5jN}x_*4Zetd)K>rJI4^Qgm=Lx{pl_YRJzwS6s@5}3GV@zdDiFbpa2Ok;< zLM!eJ0!tvD%_I6EYE1+$!h38de^|#w4B&Fw+)m_gU_yqx7R3*8+X`PVg+*QG%k`Wn zv_0TNCEP^6*pRJTBZ4C+NG0#qXx0i+*}OG6Vgp8GWUA?~Gnf3qXW28r z^7`R__2b~5j@_dwo`b#%Qd*LEN1l~2NRho zHT8v0m*F@xJn!KPoQI=xfCanza$yg9lq)Vou~Z=1%Vi4da9fUBQAM;HS?5vli|xhP z$ST9WWYQ04yD?qj7yL{}L^{Mu&LnISqbyD`hn;f^0EOqj@#f;t=HL3-d`{xANeWot z{~OOoDp^fj5RIO&NF zV~{C?RV@PZ_y3B?Zt8e7)A6belu?zV4}SAs{_KsH*4xpbNU@H&gI)wQ^T*j9Vv77i z_L&j=wLRmFwITL!&Sj$lQ-=4g9Kkr54oQ$tFt2)CC^>O=U?dlnBF<HJR*wb&Zw`=4uMlB5Rl#zFX(MU+%wF+!BDy{IH!@!!q;opihlnB&Afr3#r$sXb zNgks4$hIoR$#k79%P??wkTb(17Jvl1a|CI$aw=l(A-nF|GPO7NJMK1^x+wQ?S$z7Y z?iZ$kNkpvyJz;uBN*TC94gZB`RYILWqG~v%#-kqeVn-M~34V2id-;j8=h`S)bQ0sc zjT3R&Vu!%mILr-eB+u{w(SGCL#3}H(+nC z2kiOOD+XHw=5lAk1*GG*@PtVbYCW)jP)+$nZJdNj#8nTn45Y%EW-S?6MsP*Sxb z^KtQDeGn;kP%Z|3d_n<4wduTi3l!Nda197%nW}N@Tn`?a$c~#w;lg?Xt~#rZta?J@ z8YS{#>lkz0NfRHkJG5O9fj)%%Gbk*7519iIItX2gCdpGItECNS(40~2(MfG`)F(wh zTimxcs!{Q;LHVKhYv)=Ba|e+L_>%@B@Ojfs8IZ;qb3sOZ52JJ)5ScLm zj&fAo?jXD>>~$pHX9{k&^7a6jE2>*jah31goe%}tK(SdBx5~$);#Q+S{J)t&ADr}o+xdp0DQe>Djt@dtc60EpcUMy zr8DUimD5i78W8rd1YW&@GHTgytwr_@sco?q<*z|)0GEx(2;Sn#+Pub7kUc}UgF$O7 z0u5JzwK@fCvT(3oKeoJy1eh}cfcfvi7HTA* z4AEH_}kPlK|HxNkF);Gzknl5@1}O1T?~!m?@^!lfWtgz(_!TXbxdh z*Bl9uwoM3XB!Iq>xI{sK1_`M81c_O5sK_AUDvA3wTH$Y@ZYjyz`Uq zY*;$A41lZXpM4;B4?X&irGag38Hkz0Esh2Nqm}_fx0;rLga+u^S!lrKLq3Is26jR+ zhcQ8&K?BdzGLX=~bF>UN8o(v^e9hI_Xuvw zP*h%lMaeCB!X-T-N@1#5!JVx(`0aD7HnSK2Z-Ca=ZI*XhU9Xe1Ktj}HTuxPSG93V+ zD-t|;yd}{g5nwzN190c$L6BNba_QTpnb*FyxNx&z&B8XB_mN6-@ zBv>U}NYf1i;-(My!9uv}=8k2RJ4|tIX@&Xl7);dOYQiu96?eRzL%(4rnD%6H$1GKg zrV$I3qSh5*xvI&Cnru9yR-huxfAyZU7Apy|0K6@65((%Oof5lCYiY5NNicO&8~+pQ z5(C`Jk1#;kp=YF98VoSuh*<_0V6u#MhP}uD7b6B6Z{;r{w2ej=IIR*F9-PhZStBEs zGZ+=Hr7aWzR9L3UAWMPMVXXkT3fIyaN>h3VEMLmzq^>Tt`Hc=l{1rA7gM2N%i(f|z zmx+5SaLj=9j0#$xc8r7eB!QC_R15kZ4BDNuqA2mr@Bk&eUE9^#6<@@e8agUP*|IAU z(X1|LL#`9TC^Ng))Xyl{y|x}E%0}^YFPl@Sm0C)BS-RxPp)2AN?K2n0#l}G-1auk8 zHHik!TUOu5W+R@jWvMY~OqtN$?R8sXLQz3stHfyUPtB(oJQuo>omAJuhLw>!ywp_4` zX?bG@28IKPlIUQk+gQmQOI)KKIb#QMWm~kMezeuzZzd8=#Uu{BSK+8R7vZ$Q9D%<9UpOTit?uA+$i-i(AC5v-}b!vhs7k*3HA9XR1^T9CUP zX4o`=88YvzTAho*Av@zb2x!g(l9qt;BW1Y>bkuV(U)P8J*k10%|8AIV)S3oEfdEFeL27v3*ev9T_gMG)PQ zDS%-F(f4q4B{krbLG+zW0U!#ZuV)GXUl4sMQvhg#=+U$w5KV2N|1rq0k)KRk4oq7P zU`imFP%MzP954f(_3(nzmMLTn)d4Lv+}K#)^Wm!ExGUu^dh^{7fFC*xCh%HwkkOh~ zN^6e!5dt^M(YnNP#1`JAf+N}^Oy9N3SwcAI<2JJ$YmW%&{iH3dM3q>6n2@Y+HF?$Osbhn`M96TZRdO}>K>l1MaIU!5O7o>EI zf=LbRQ!D)&L-s}~+}8BM_QneZ|3b~bu*1Kwi!+A!ylELDXc*);3AT18MR z=NWT^kxYFfQC`a6NE$snf@wr5mdaRom4BBw7fk@_g72L>UmQgz=^a?4piQ@q1VSRo z%We%3@tJ&~HG@_)t8uP=-j19S84wO<3}^(W0BqPkOR5$-;>h@bhETUN034t2Fi1Wn znc4#(`J4lk&eQXTB8$I;)u%%{+AVuCDhUyyis1i z{Nq|7pKenrhl^Wd=$;tN12}Z{v69^#rntXq8#&9$nk;miX(GuEWsZA#T*b zBv%?>8yG)8M^E~zd~5*;_ZM{4sdCG2WAVAyn!&;_&$bKP`g+uk4UE$~o>E=Bw{i#` z6**rl#Tp;a(`5Rx#c8u-^pQB=x-(qG7EE{;*?UF{Lt&sx8+KqWxo}OYKl4~3r5Bf$ z`ouv#^(qjbrxAo*EV3LwsdF^@^4sP4OWXiKI}vYXI08oPc()L` z9DVGokL0hYuH$ZU+0EcHCkN}{w<$LJ=x#fYlnAFR~0CD9@mqk&bd@qUg%K;PC&kj z>wKF|;o^!3r_6P>G7&zfh@px=KRl9smvU}3XQd{wgU=B!SE3Ce3b_6pkztiT>9gn% zV&vHp+CB&Jr66VI&ymcNN%;62i9>QTrVYT%<%aan^jKT}f=htD#TtnDOuGD+{AT+! zQILEAznyHMTKz@5hNFp#9tXG)?eOR-j%bA>`c$n>kRA30m*^k*xIz8fFZSlt`Ww{0 z{pw%xO#hU^(m(49>R)@(zbgfys{|qFPrT?#f#@oMD0v1%R|!PPGZ4B;5K5lufpRdU z&-6gq8PaEZpu`Qyb8wZv>tTRCuK9Vq9~0M?w(>lUAs~vYQdlpYwT@^%9xV7pTz`nmXuII zNyA`Zng~WfFFRC(d>ztx3ar)1IRnSOG@r8+8OBD$U&Ka?zmQ!@?1iY$m1-GoATFb% zQ{NYd*oL9q*bPo6V>j0msg7+w;UE+n_hF}raUVE`ILbX`xXC;g@e#NQjHAsl4u2Gr zEv!&}iBn^_wA6bK(x&GMeWU=Su)99&ScM%(BKM%@2UW)o{`^CKUhwRom|+KhL9wv| zR$u_e);ZV#dOaKK@)dEp;3bYFyhL`bmf$5$*WrUhc8FXrr|ZEPc-~mo0ZJiEV6sLfRsicHXZUBe)kHl#H1APZlE|cjviZxd$prb-Q5~bG z{!P>wwZp!bIP6>LaBU?&-W)p8ws{i zY}mF7)mT_Zx<5mFIYU|6%wQ1g~`X^-70dugu_A%~qaX;n&vaG%Lg^ zt_$pC9prk96{L^r+>n-@t=eaZ7)yCf1&-m0<|x?C^$5=+T$RnOM-}>9GST5+rEsu4 z!9muctC=ZC^SE1t zR^g;R?s7E?xorgMFUXD33dI@(9CG7Q0l7VEePs|NtQAJOhQWH)+Q4n>!VTQs8qJ0i zBb?)vhm|4|u|I~tmbL+!l^P@44t5=OIB`g+jxm0P3fOwXn*It6(WFmNsTD9rlRnb| z25Hh~TEH4+(q~#Q%KBr{XIe6*DoCGxWp5>14ykib-q6u{2m$ zA6rrHbw9I_su#5K{I3#R4IByAtPhjD)JWnT2DFpRelB>20nGsIVf=Rc^>~}?Yj5|z z0`+7D6b60h8~}$H|9wS~?j;mi&tv$zbgh%CTGtK8|3`H*iUOhQg~TxHN&w=lt}J7d zy0Q)MK3y@l9i>67!}Z=!>+meW86~(RGBaM3+KGJK4u;+LU#A|L{I$1!^D`G{1nt^Z zz3-vVf9+lW{JZ!4!@t@>)Xr9oiFn_ypZMORpYq${RFM^9-{;=^zHk52pWX8-pYs*5 zaqaugx1K!x0d~u2vz+ST*V*^sQ(t)UBY*eF-`VSm@aydR#GiiXgD3nJI#oMHnQw&v zf79mz@_Mp04Q{sYNc`^q?>#?wv{LCGW~s_+F}XA66BpVLi&RBpPSAgG#5L}W`O zd1dWQn3vo??czAWnK@?Fjo$jF_kR3rZzMJ4xM_C*lE^Va=Z6K7R6^gu*M@W92CPy^sInw~gc~M)l1EUJ=zd2z=)5 z_Z63s`(&2FW(u#!(c4Vn2zNDZ?2{uCQ2@;c6%0Yp`T=Y~@|uOY0Baak45`_= zfZBvBMy(1d?PWHs(W{z&n+f9WWzDU}#65R6HBUf*Y~cW2(WD3K9c)O_$4}&mY6XN4 zqP&^*S^6OSqadYSU}ps>?E*O~NNJa0TY;$o#!=ZJYb7c164K5X%m?ep8#{@8knweR zs_kf^+Ml#zSWX&TrKxA)>rbU)Qo?kwDDov)GPn^#s<3BPQv#w`ej))9jYZsnyGPl8 zPEB?^7s6NRI$Pk(E3StN;j49>E`+z}I#uAvE3T7;@J3xv=Q$XJ>#00PPbmJH)t3ot zCsaXq@;5Icn7fYrR%1!b5X{O)Kk(1rJUhkA-0{9**T02EO8-XsKxZfr)}UQ;8Xpvb zQXs}*=jnR<$H$b?gadm*I(25(WA`a?K8gT}8A?gX%uz}yB?MKK(gEg==He&v+jH!O zvWKs8|0UfMqTI9^00;GWrzvwn2C$c^f9&u?8z6 zJQuHWb@68Wg1f`T@oIM$P6W5OJ6tNax;r%V8{HkQ7dtj!W$*%gF;RY5l&h%ctlx=T zW3#*vpKY4w(rFeHgbgf{UU0L8G%{>B)>I8SYH6f7;j87u&Ly%OXLnQ^BFjOOSqc(Z z6o-ckPbVUf8-ZOhgY_y>OPh#2WxH)Ar@UwS;n|r)PO*czot*OMZ+yqeDV#-&4ACs7 zv^lOTOV?t_r0q<}q-_bwq^%*Dv^69XVlzxKX#42DJUn3!+)`W7%nimt=d2)=1?ZX;qJ+GKI0jnod^rlkH9)FzUwd~MMw zD@hp#fyKr`7M9CkT9r3miylW9D``S@2G~Nm@819G>y3qvC=`lJ7kuUDc`k%4D{!$AOroQR*-@I5i7_*f7S|8 zL5wN2f(-C~*2Qd1FB9~cVT~pc&gs;02lDqckba(`#ULlg>0*%2L?1-dXwxRuGk}a6 zYcY`7?Ro}~Pf+Hk3O`l`$eeo93}iB!8<4-Ot6=;k-7GA>plL9(B^%5_W-x~-S~QrM zhmKmur}(Madcd?(wisiGa$h`{Qxq-6n4`Sv2Qw$el48seFue(57S0LU@CWYBeL^&# z(fqC~oEV2Nm13MPP9V86Q@Hj_baLVoCC6BkYz7gkgM&^|Y8%TPGdU^GPiPVtOFOt8 ztlgrjuxo+Iba4qw)SfO&s)`Gl>f;l&z8_k<*r;snx{frrt6s>kft-(%f@E}D*}|%P zRe7vpZ93WJ7|~}4H*ot{db)MkvHQc1`o(`yy+zd)l2yT~O_O_t&-%r~;Nx05{UfWz zw#ZDSUo|Z#bM7;+L&|C#{7he^1+_P1Dv-qrGSn%FNvBhLi3v_<1sUp;0Bid(y)%&` zM6L2ko0^l$k$>+XnYm3rgj{wY66iz{I#@*l2($RN%S|Z(3}etjofFD&q8U)IS`;V! zJ!T8}=&c;HMG;@S-IMH)W!FDoxhS6W_n0n(u{-ry^W&y_(KH@;G)|^FI_7Xx`T3ieBgGjL6AaY~vdqy6GT=XA1sU+qT0sW< z97tm_zRTeZAia$VMr3qh)f%9{Jkt4|BiZ?;RjB;GZ{6;J`2Q zK?45ygcWe!?K0ycXIXXPfA3`{S#?bRzq;C`wMYH+sEzoATCAlLZsCFYR)om}sCO+B zP_Zu?ERHQBF`B=h=l-z2pW}Yo-yh|E%HJR19$U17Dfg!#9)0jI_owV0X=R#+h2XrkiLDpv9qx4)+43o2gyeNg_QdKO!gqR+rPGJdikX=;|7bo_;<2N3hOO4v#5k-C> z`YIM==%3f7C|o2<$~$_-vUIv|=CX8(vL>T8jGm&!%hC~=^UD$^Hl@oF^gjz%Z(5g5 zf|085F|m6sOAi^}XHO^_gBvo8w%Z!h(g-BO3^r6xfM^9-GPQy%nW{iJ+|x!R#Apw3 zS|A}t8;_F$Z6Re4(;x_$6!6yAWMv^P(eWLhe)E@7BV&{3D2N-tQ_(R_-3V}1-d!vR z{CkUzH<=qDksX^w$0bCxcbsS%T1G@mhxWgZi1v=ZX&OvML`w$qpHoDWi}z-Xb)Xhu z7eYdbXx&0ag#Ato!L}C4U9FH=#IS-BtQy=Jmx$*oj#G=`8cQjn6C|3{h@GS+8q8KX zMU`#Y)fNMDQUlC^-`>|UK|n}T6=@_0Z6+MfbI^!{L)WWjA zsnjN(K7Y1q$C|30^{Xe&RxLIO6{xwdE}X4ibG6e=)y~?vsiv_m*|}K$tHVw8=Gd zc&PaAs}wZr1Tiwd<6g29C$rBIS9l{0?K3Q_!9;?c=s zys#Xpz;Treb6;nf-J1LM@_k$060}{A`_^*ya2`5@XUJ^0pC+-6S}gAm2KV18>6@+O z;~}R#hP&lUqtb32s@7_Us_CdPooOn&%V_pcH4<-zq>2jJ-0lult6teMj-;YmcBooK z?>T!7i2{k8(v}fBR4w+Ws_B$M8KTHV#8=90>o6e=iw{*(mLfK&aYPE)%Gi}|p#U7J zM&I}#IeQKwGbM+rSyj2JMrlsr8xdTED!6MkbCvQ{!Aa3+NxlQRqa~R&Jfjc&G%f04Lv9f`t3@3^D<-DI2{b|5g}HZuds}le%REZGXPHAOcGnsw zIji`vkctnB(K1f(vSNVjc#vRk(rOBV4KW04_jb3mogXflrHkedt#*QHv4VG!=<)Ql z8GqoSJB}tNE&_o{g*Uo0AHKO%fS?G~d+=9by<)}_XWJ?{!o+dTk!x#}w)V(G;Gfpn z({$_~LwlU^Pk6?tI_aPAj`4JQxod{yg(r@SSEIUJ;5Oqaqhn=70O0|~M4IuRT9)0( zH)9x6Sd$Nzkey7AhqcB0VHfW=lgHzCXOgOM0Rg+6?tjXD1ZR8X1;g}OQluSZiWAVi z3HcG=bf=PV;jTkqPyGP+-Ozz&?)%;8)cJ*Xnxco=Ee`L?r(GOgW#-{eS$u)i<7Sna zyk(R%+s-TIT4^#;5XwNcW+MgBBb1qjP5?*ZZLzqVQ>3zLvQZqRz-;GtDlkSyiaT_3 z6a}nWfQ5)f(G|ow+ts3lT+T^!9tu(!*a9jjNS8NSuo~&|h7Z~b(&dehSpkBBx7}LQ z5sjph1I{fcIpk4x=uCUWQ9coqz?Olr+&K2lhjW)(|1i|3(U=*cH%{_gIN`k1MC${zaUoe$5=efeMh(&ObEWz7aa#zom;+B!m6N?ZND z!C4BMiEHLN4;y)&;1~Y|Y!J@qXY_Quu!x>8Pg|VZmvuGAT0ydf$rr%zOr!>1 z5-pu$0Puo?yM^0UO;V9lsAy_I_+2fEF2Gtxr%(W!TN-ftcVaJMZcUzj&)me@QqO+M zg^)X&*ihElEA}JE8meI`ZRSE4L+ibZzl1@w-kF$weG~zcOle2+)JK6a<|8pb^_+nL z*huTu1`c=>z))ImUxWmbYwG32Jhq98PAx;H)G+aF@5}AWMR`XmQGm@oCQK#hHBN>w z&N0|_dsw%2N?mbUx6ZylrCZ1KCUxt)S*Jm!Bmhvikjs>e?FG4@9h=foy>3+^M06~7 zr0CgLorH$gtrUmE)P!yz4i%@CIhmVQ;GPz%$eXDE#>7&b@f%g56IPJn^vA6r!|9J% zLCWd*{=Ai>IwH%36=c|xi%mYtn8$J_3v^+r(ByRFM~e+su7%fF`H{z1+ZCGZsKqbJ zu$Og3k!b`GdrtPZ1vfpo`t&LJ;6er&Tv}~uw2PxBnxo0+m~XdDlc3D~sZ3%05&vJZ zKU4la*qdhTto073{l@rKbU{sX#n6J9&@V~hsMn7XucW+aTxwh_4tm&yHdPQ$kbr_; z%d;JSI+0^!o?IW?MnbiTbp>NytTs*Dlh84z0 z`SFS}Xm8HFw-T~@KRUvvRt0a>r$+r|9NCB2Jow_w*CRp(*k%0U>`(|S0Nr1l9m*t) z#od%;#xg}&FGZkmGQ4SuIw(S*Ns6$dqq)6Uq~MF_likj0#4onnlkCf2%Zp@r2C|?` zM_eDTSPJkX=&U-+)mV$mMxJa0bjiw-BX`+;Le=%g7HG&4ZzJdWIXi_`rNhtCZAr3$ zPHATeFg(h=a&YT@mU~Mlj_=0v5Vf`t^XM3j9+5&lGt$!uo|5!Lpa)N&n*T8JXPT|3 zRg$Oo8u6Y!ck&!|N9i~(vT7(iigpaz_3VF}r9n%?%9&X(UQ8?Oes?AL`+Www zAa9Acu={X!B|0>sA_;&ML+dJzs&qpz+9;{$AR{EB(ruikVezGh!lHCRz=%fF3eMC% z2GS}g0V}Sp>P+}Xm`D*jKtwKUm#9h+$jPalv5STM$xZ-bo5k5r0AlZ}a;00MXk4g& zuj5BslqVZpiHhNXmWS4LaqjCS!lYYiNh#+xYWm1pLZ6k%1|x`ajE^Gt_A(^&odG** zqQ=Weh(RxU(qx{q=*rPDB{6AL!o3x$1~~ZMDdk1rgHPznF8qaRaXbsIkLKCuW;q;4 z(Gk6gy-Tal$l?H@HRW;mAMA)|r>NT43+IwBSel|mNf;{q!y&1b zUq&Yi>*yZkm*L$`J|&5aE61=9K@@O`6Buam&6*X?KAuo_n$r>wuy}RCs^~vsI#m^n zNm5b`*+i$@=gVY+(YCAh@fQKt80_1+K7$I3Gb1+ky?L@)rW4;eowr~JU8>T?LF z#Xb2JE~K2%({eVT!jo>>p4SscnDKD?gBRsg!COQQzd6?xP*-t`QT$BzGA%55UlPZYbXf^(2q;B*U_n^z1suN1X zUxM*!w{evmZW=O6xI6rvnM3F&vu1D7452wFT)xdyJao7R{5ejm5ulU9()v?TqoP`9 z_mU`{Z6Eh7`nG&4Y-0BkM|;``Td#cCqDE}M@VOa{Bz|0J$P6VFNtE19eOqX7BNe#t z{fOjV_fnrb#=gB^W{7A;x7M5k0r`q*nd3SMV^Y5{@PaU+Ox^~ZP7L$ZIP+VzVtbsd zy6`i)BINUSBYWG7WhfqAkrTpTKuFU*vK@jax3x>ZohI7@D|#|`g)hMH`;%vNX%?6D z@j_H&G2uMytK&h0YStvqbzq1vF$2wi9AqjP(9HaLjU2Ge01-4mnG`35SqVZ5L4=bY z(#0vws=(6#KojVi5el8oH*sUj@InK~`6 z)^*PGIfubU&DQt0=c6*<=wm87-y96ktrenbtI&?ccJ@vipUU>Rmu03)G*g#43xZ}< z?m@wGxWhxgj?Pk7@Twy!`Q{dQXm`hD^LZZ1Y*Rtopq}DpU2ipx;{~-mEn6MeV=^jn zLTx3w+svI>3>fa_+&l7n@->S802@;!`fi$pu%bkO4HVs(#pNy8xf+GdC4r!F88@(8 zy^xKtkA{2I`VX2v{V#mFTg#5tj)ok~>hP3~`!&(#TwC><*G&aY>$u-lvD)+Jsx;%N z+y{QeN|p=1ULi;spEgWQmy6#kCW}lZ`WL7cD$THX5i$jC5wNKfo%ctvTPLhUc9{Oi zEhTPtt-vidRv_fpa%35x4FlXd;mjHTl}YZ8(vyG9dJf(s+2-7uB0ST~NB@J`WmJJ2 zxM1tn#|`to3x+z8uo69JCEse4Fm1W7up|2e)_^6V@h{Ku&`+z-0?6!;^C)Fi%Akj| z>*09&O~%lfnp6aF!~OBc#yi z$zoDy^Z`mpIzY-lKFDfFO3H41<(fUYyzZ2QKi4K14e}VvUFSr!ZqbS}tTFoTXh_e_ z@@CH?dd6nK6xj1dJv-Z-J#W+V_`&q|U3eO7M1DNoS}>l@_Yvl3UxJAQ#NRg#&?{$% zE2pE~dr+;c%e6hZ{Z#rRtJLW$DdBJ>x)+#dUZ7Gz?$S+cpYd0nx+@I}cz zaJvEvF~Q!t*Ad#Vb@Wa!-w7$1H5t)XfFeggV@;kaG_T2CLSB-@0eS3&U#Hu<1kd{L zw4F7VujG*IrK|e%+lcdNj=_SVId%|B5128ar8x$Z8flKfq~H{Z3-2=1nVl_%a%bs# zF&kno;ukX|DW~mbG<9|0n~1=eR9@p1tzXSc1G={y_Dk^~Sq(?%TF*QdlWB&s4*f>< z7)OsU;fbE5HBL~YCy24}WC~?gQxbnVm{gShrxT`?n&0Vy#DFRL2tAl4r3srBXajA! z986_RbUfKGl-}1i29|>NgR3j|&gTOoEY9xzq>)(>=Q&$owL>k+TuFnr>My)zI5;_I#MhM^J-4SbaRLskX`GQ^du)Y1c?=)e zOGA|GFyzZxOF5cJw9udn%E-^8l$L3kmQs#v()6TdT1u5ympM$!V3T#SU`O#}rVuF+ zJGc>5I<2jEmX_PvYvgqrFBAJ}?0$j!Q$nKggDkN|NC_G7F_Jv4%vg&L^UP^yX$NL` zCU_&+)5^#wAPr(#VUlN3fhEtvd9BM+B!1OBOuu*X2IFVbXF2;@Jl$6u0te*9N#_DU7cN#kwLSQc`39))aEgV+J9wkmJ zBhgs&{(C-Bbot>x*)>oViO^&l93kHrb(DOXth!Cc!4Yp99InqY;*WXAR_d3Pag#41n)R%wahcIX5S!f6pA=& zDC=46b}if-5Xplqg-R%@OsTpVOxBo2CCxqt1P$ZGT$x0YO+qu;poFUpP0_fDI3Z5x;!>e$Slbq&I1X9Hz*xq za8F*xYsh(1+~SYtxB=5p6G*R6umY&R&Y*_ zZN=q%xidZ~4$-21%9}CQ(r{-C$O22WrnxibbG#J&d4U5)2e{zRC`D~I6G+-BUT}`; zfmh7JVW>QagBtMm%FqZ)lUCJ}1xa)Lt58_^F=)Y13ajMLPWw7-vn zo+gG<_Y9{lObY6tt?`?>=Qmt+KwOU)4e>vzGorqvGa~MUQ0giFxu*gfWPFD!%YrJW zncl2}PD-LS<=HgCZgszlzw!kDiIw+YeZYGarYrI)XnX?wtHP_ZK83A`DcNU&`hNcM zxS9{Hj`Fwb1^6lxRcAhlS<)?D!)3whsO@0*8m5lei*RjJ3pX)St2rKi1MiS6AZatX zlIf4MF#S!hdc z_P3q96<#q|`%B`B;b5Jppu%3#WQ&G!2jo^4gI8Rw{FlZ7^noAb70^&%0E`(S58*3$ zy0lslTKD3U=`fT)&(fOWL4l(xklX5t`k;pf%V8D-O#r|JTTuQ+d~gi_BtTEDO9Dj7 zhfd1wtbJ0Fg~YyYyB#guv%P`@0m3~|0vLvPQhP>85eXP@={ksDG!9A!gZ4pkuJYqT zX(p<+^v9tnA%{RkursHMiZ8ZDZ@h)FX!6${&S^3$IH3_bTrETG}Os7VK=XXFw_pIowM2@;{i=5R0-(^Y|NehxpQVkt>xUbAHNmk zD+xUrY=I8Q%VGzs@l`d^4tVlv9&H?mDV?Q~0b5PZZ=ZZ8<}s_U$_8g^J10che@>VS?jCvKHcil~F; z$z!QgWt+U5)v(c4M4=wa%`ivrKNIFxR8pYp+dKf*ML@4KfKIf%avC|H`|keOFjq?2 z#W3eUlMKvt!iHe3nkmdROv)XI;0@}ggE>T@7l3)q4(AZevAC@i%p*`T=o`%P3bYc( zC2lcJfo3L;r!1K+Wpsat22&0;uqBf=Blrklhm(-z_(!mg4CjbP;Y~l3b+l-NPn~s0 zK|34k$Z(Dg|7lo9+rN!hhx|OsaOYMOf&1iI_5bCs(SfymY43}Y7V@C zznJ0zo4Gyg4_?C>CONGd^n?R&lmSdiA(1~ySCEr*CII8Oa%PuJsPtn1! z)*<1Cn|Obi+v07U9B=2fb9OUzkXpiF9ptQ^h{6drXx(S*8V+w_4T*`g=@Z0S-2OBh z%y7_G=VzoE_a4B(5-`9@HW6H{1oUbz7^dq{?Vx@{m$=duUyk@(2-hmzgMIcOax&jm zB^^>#owyQ+Fa*^rQZJ?=L9uFup9dzcsFt<1@Bt+aUerW5U`(7@Rt+|*Z7|>i{_IUm8EM=rqPP}`-MdDQhPK)XtbNYcMGy4yOUd7tWuaWcxz5g84U1<%dRkqTV8$xn#A|zMp zH)(-GMrJ*fa)vjID>5?Z8T2PwLw`^{h}PgVxI{*_015fA5VbA{F3`{~TqGm+b0!F4 z7U*R%va?#KBCwE*e4(VJ3Zv96M!0}vWIE5-g=MmnP#OD+yD|-Wri;DYovYg=dO35d z+S2{r`d5_PoS*k$PvaHyC5}_(7aES-6gZt<^|oPATZN5HTuRLa`YW zC6V(T#zMU?=(rL^yglf-f)z(Xm1l?{8U&FhZa0V7BuEq5DR6xV(eX#1=*_cLs`(?#Vly= zQ({3{t)3DKN}0WV7#5`OHM5|xrI>xff*|@cnY{txEG%d&!-9<2pOpo5z(|lwSn^@l zQH<3t%YQl+@t+RKxEHatX5oLJz5s=f6RHBiLYhsFbsSwfRsPTPoMdvNrgP;@wvs z2b28FcW|101MXkYuhq34tj5r)4=`y8gyt)&CESwJp`g1QGs#8i5K7@{N!RT$MZ>DV zU~=IwmSnX;V^!9ix~iPn+^{~z?|LC9sgAYX?%IZjSBTtim6OcQ=)yNQ$vEVgv-kw! z6bMBa-Y+TtKzyqNW7;DODOVpw07YIK);+8FrLV!DjC99`P=UgdQ%t0A-J&k^8V>hk z8wZzB3lmtNuMi$CZiA9Qh!AdCR1~gcX#dMRx{^g)x}Rh@&;C|W$beqs`?l68-b)IO zUj$`DXOo9A-1Is#Jr%scUcX7y1ke<@YrlO_=gKFoI-wM=h+6HBT|L*HlUw`UdK~od zs`MMcbOlCpE>gJk8g6}c5v3MNkqYXCjDBQ|3>bU-qe0TiA0;P9R~Vb+6&WB}E$l@I zsG2}now;_nrDuae@ZA{YROus82d82Y4b)G$bNB%Q4KwVY*}S5qHwJ-)^7Q;*ol}-o zt#1*h4owtXrNwovnn$|hJbM78$|y3oRGqNcr(X=6dt9m#bW)kF{Zqd>VW#UIlgiar zo$n{YqP{Io-S5`KQOQSO)h$8435%+(k*uY!PFVEaE%`^-?ky7U-Ep^3L2n7ety}PM z^`@DR^QDGGdZ~d%7)Sq+ij8MOZ|afb;6`9vgX&yOmez+ zrUFUy2jUfFP@p5m$BP)>5v`a03pMjD7iProsTf!^BONv)GMCmB;NU^%4clR3Mh5hY zH*EI^76WcZ2)5A^|7=E{op=+}SlwP?g7dbYLo>pjV*y`RCa{^zr5ia9Y#M#jV9s%(_!`6x;#cM&Eyj+y;`476HS70^E=R#J`|BEe5!48nnEaKJewKy7A3^u#g6Ch52e_#l_FSPujkIsB?L zu$jf5^OUeGA{NN8VJViZ0sDz9t5=hih19q-0Pm27Eol$JmDwSop9Oc&#J<-I8 zQnbupnPH6rd_$m;Ph6rBRoOO3JSK3#J(z^}%p7m3{klq7DLp_0kIhe3t#4 z#vuta#h<&Ku@XmhxB0?}n?Y8KHiNE;ts+WXQGQXO_DG(SJ;tq?+RM#Fv~@~9+$ ze&MFaPEYKRS{Pe@V1>q-VO;8?Yih|C#QzP$8*Th}MPM&-tMk>^2lACR5}OnG0+A4} zD#Nxkmr?W+z{iz<;yNilfO>1t4caOV0)+D0x{@pKDA!R!!oXR0FnS!IS+ z_z|T^)&Rap9U^VIT{0GRR0z}B~;@n~I8WdIo^#f`!L8?8wZU)&nJqF-DMg-s!6eX@S_1)C(P zfemsv^X1#Ig@oKNZ=h8>-ip=XC>;)iQK<*WTqV#UWZ6dlAy#igQnZ6zIux4qe}jt> zWHcLKBF-1B*h*!3u=mYH;M(a7Hh4j7L*kq^ZqP{^ZVl*-n2=t0FB39~u+Ex1EQ1=W z)&L4aVnd5HnAE(MPJCZ5aJdcB{Mh}tu=QkqY<@b6AA5kE{ZNG5(3@~$s|}!3=gk4h zx>}xq6>jW5%nPC&!FqyMxn?}sI&uD2~oP zOQ5Y9Q4zXsPcNIRSoQ=?E)%I0*Ia6<*R?YUJvihkkRt-vw~? z-5kb*<_3G^#@;2}YM0#D5;Is6h@F+^Xf%-h2Pp2sgH3tv8Mgv3jJgwMJ97SFQ2$_k zO~YvnrE-*;_Z;IXHdCgeDG!S65^-6nkLmivfFdr1h>A|91iQ#cRhNMS?F|7)Jecmzyr>JOuYDLLSl??_fM=Z!}-~|{PHUov7 zl4wkY5R1iU7`W((vl;j=*$0}z<=rODk&BX)#MZ`^0+)^PEZ`cm7)A#!3tYsa(}r_` zgJCYKmk}sDiq&Di5+?X^RXmLeh$F@hlh{wOkoc6`=~| zv)$D2+3uyC)Mq(G@Ql;^F2 zJqcZzjnJT<2{;yFpu_WynF#d}krkouLJ9Ds$Ki=gQACugkob(1upUQNzF)~>dFJM> zmU9QK6VRBx-HbGCj>g8CXe_bN%C2(;joI+ef;4Rz3+)&k6b8cw21B?|hQ_X4;~=>v zLu2JdkaWg7qcM?)AY%4E<6GzyiB;TSH9YgS?w0;%4vTOyz|@QIIGcbB(GmD1xM9tb zg$SY>Heebhf?}aDE(0$_CmuC3*wZqQU*l|Y&GePnwTc&TuYDYD%4p5{CF7i|=~YWRpyY zgw_=+O!8{tTMRCQX|5*Z$6FW?jqCg;b(skT`s4@0c&|+UU?|y7|yWWp%?XsqsYuM`xXzxGOgH9@nQ0%gVIo7 ztifutyB7c^Uy_iQ-EP@WHMBmB0%U5y2WaZ;q`B3BFw_u&m2h&$y#{%DQn^FrL$(?P3wgAx*2#S@APuk zyUnZ_c$s0G3Yt%@#1Hi)#$6L>dm6@F7E5Rs*PhvRk^R$;53+c!+;~Nd z;YZto<&_4_M#YlwD6fvn!uT-HUB|>IcT$vlCNLJ@+FiXamj}?QcCIrnU=u|2+mpwF z?l!cpbs2hEnE}i>_#?et2YTw$q{6r~3HE5Y7mV!cou1}egVEhNXE_CmRfxzmX{=pU zQ7q0-DZZ<<%EB_(jf=^MwnV6D!y;jFW;r5mjmTYc+MNH@bZpghY}IsZ)nshdWNc>{ zn$Iq4pUlVZ>)8(m{rVvIZSMYhNn@bHF#JL+@-z~K(i7i5*=xPOpV$ZLVH0Z<*;q9( zt)_^W?dAJvlN|pZ!N1H!XITViiIP;+sdDmF_v*dY08tJlbMwQC_>ef4T7_SYR+wG2 z180$4qj34HfSF$r(BM#ZOB=?Yv~t0bk*Ub~7#mX*;r3S!6SWE!#1G0 zq2qso1$j>b;iprDy0!{N#7(Gah1Q8Ll!XZ*UOtLv0ls5|I@iJ}2Q0WaXOvsb;O}YM zzU({;_6_G*(CZA&f5^KQ2>9V!(LA}rF=`^)tw53^?1dyONYwTUMUL8(omPfkCqt#-2S;a)h7i|(sGcU_=yHSt1G_!7*X0G2@xL%`7!`dQg_4)g^f1)*LX(G~-FX<=+Jqdc^n`n#QX}yM&{S zF;tpsAk$Rg|2AvpWB^CZ$pGiIsel)nmw{)w8DPRphp9IX2g}aQfNjJu4)31BI4XjF zh6TWeY$}Fx?Fr6_7qT@=nKpl7@c?>FxY8c zrHPtd7Xz`VBWa9j2HP@n03wadE3T#gzqPB6uBy87_hY}8yd;nt5(o(z?|VU{1Y#fo z`6$Q@2qFrx#R~E*2@sMd9Y_HYeF+~mqR=490Y5OJEn*SrYF8gx+J;JTro+NGRcpuM z!Wr$b)J}2Q)#*&L#`*2@@p2PsSN~{M_Bvu{)~%7Vk*b*-+=Q1h%L+C%1&yOVf6r*DVwSR&9>e48x$se2jDy%ng1Tn z1B}N2lo4_mz+$q)gP)3OJ2!1;KW8ogW{pg8C%~#5&z&PhEOyl~cfbW_v~0`y#ZY?6 zcq|SZkG$xR8lNvF@vnP;@bKm<0eS%v8Z_w06Zmv9)1nVB)YPFu<#Af_w_{-gnlBKU&#SQvd9wuln68Krt5xzWw z%XTknpSCx+@VX;?Dg?M_$M+8dru`wgPR2`P?FC#jmC1p-9?W zrCOfyuxZ7S1KZXx5we2zY;G)G>*cJJl3U$D`3ufr(_SIiw*ur1nxF~|f?%RtgOnvdZ7ie6Xa*&0(CD?U~ zHt?}TWY;ay*PKimjirlT2xw24nrnZC@aiHxZpmEm>S9(5iGaPj^fEjLOmk#Y4NXnl z0=yQ+Je9{L+y_edCjxWTgC}Gn&=*2@TERT)^|_9=Qa3!niRqq`SYHa|%!H>l&Mr*% z8&0he>9zz*#6-}ehmUM_I15z$*Xk+?5VY&EBmpkBC<1(kR(VHB1aO_|wacT=1YGHP zC%Hg)O6u**9+Qdh%dmV&1UqDBgm29yQEyb{3??M37XD@vXW{!#^7`nnU6_ZMxqP0>39#4XZagGqTzlTf zZ617xm%niZ17e^wAkfEMxDKP4Aah4Xx~br(RF?FnDk+&RimRZC8s5N_`m04Um;OcK z-btv)ZsY4e5&Rm&F}&Qx-j|cd3;0$6{H*ia;nnc5UMUJ7DDb#1@8BJkQw}!HippIk z{Ytn(p>CNCPP;v+kQHI$TzkjK_Fd^v=4;#In73hV3x_eT))Bt7BkuUDe8CU1{qLcB zvwT<272jqFe?=aD z3yTjl3-hTIsg8o~ZPQO%!6$svALbU?s|)?GQ+;jCv#jjk(U0)N4=#SJuWMaKBp4YS z_(b6syh8CtK-UWYpdAzc9Z+sAw!G2kXviE^GC5Mo%(8rJS-9x9u#$0&l5uFd?;0h8 zr54Xmx1W-6p=8A7Y8sU!mN<@4AZpNn^};yX*a_(mSL+Xl^@pqVhvd%sgHs{OS!L+7 z1PB>X`a=;yf20XX5kg=xC1YVq=nvQE5A{B3-vd?Z)gN3E3N`wJm43oe3WBZI1erMW zM?}X}z=bI(&H~$I1>$He;j;dqGy?q*(THZPx%OuW{b3Z18$f^b@?-tc%aHYl=E(XZ ziMA$g0SDEPh8|uqtv_6&KS03s)cON=dwj>IUj2c#Qd3&fY5jrzGvE1ebq{kNYeU4HM`ay4%~2S0pS|`p$N48aDue{aHam}X8nO_??QvHsO8U(NOZcC zyt#Z}=LTnzQqbdmG*(7Hl@8!rU-5kD(zZIev%0ly+m7~*&aT$vcE0Nw_aNh9HOQYI zcM$bZw+4JRhg%%!4i5vQ__2O@6R{c==ct6|$F>{^+>Jp^BYxlx_puEaM#}Wg4n}kR z)V^(YFtQ=xaP9Rhk$bvmEjg!R)%JbZz|MuDi06W9c2FQ+4V$#sHq!_8`~{5ynDcQb zu;Ll0trN#S04hmbW`dH~r`$COs{CV4pFr zZbK%PZ9~4yu6)KseRvYH_zffM^$aq5|7qTe7F~0w1YhpLm4Pb_0<*S?`9pbJ=1N-u zR|z4ggNnuk-1EgyOCAgZe5Zjctt7Fwq}24$mXyQwl$MmMEvbY`Z%<0I2?M5=KwDCZ zKwDDbbZtqgbZtrX5d~OqpfPj_+LG!+087g2W-)Qb9<(mb!dY8V8QlNkKW?GP)eu~; zintJNN>ngy6}jxbUFOnOk<7*2#YPcNSto!|)Gb!crHTshfvFSXlQwl+Y3gw4+SE~{ zzdD!7;!t2AiKlCjHe&FlZ&5Oj-m+W8ALdaHf=}mDK7vRMRUnv9LvvHN?{X$PskR!r z#lfRKhOB^xT-G?LBgI}MwRE3ip{&HFYu>|I1X*UQqf6bF=u!HBpzvE=>1&(&e<_w6C# zU+$u9_KV_<#Wd!smv+JuArzgnE*Rv^h;uEl<8_U11&73;#S||&xC#k{37Chw3c zPjbrlW(0@D!%Jx(LkqcK`J&&Y7hpO~;wF-qn6`q3=bU*6zp&v!X$76B zrWN!L8K~{WH8@BYJPV)hp{8>uM+ajde}_ zG{3^Ktg7*zwHRTYZCNpdV#HdyIkv*`tUO>d#iq5?V0VjuUrXP#SBMSuG#|mM^|Yz_ z9ow_kf@VKpFkp6>XRSi`g63rK^S}p%(mw<~P~u|iI@&n3!%=)5!87LbLAl~{So1OX zSsf2S;hzE50Sx{&;*$Wg#qvhlH~nGqtkpnA03rZ` zV<*(QI)n#^XNB9*TVt`wdQ4^jON@o=s`_QPYScDR%h;G;Od=>95P zvyK=fVokKs@uQ-xiOTH*;zSb_+Fjxwo9G(*9dW6N%IrUhf@Z38hG&R5&7fH*Ha8=& zJ$0a&@+m$$+q2ezBX`CyfWi5luVp-SZUg-!%0=zoLL2Sp1l>#f>?z{Fy>##36ZxK1 z4=T&CEN+1>l$Tp$#K^5QH+FM@XZd)vR&3o$GrqJ3Xc5pO2)Tn_Mwo-}X9!({D*%6q z<`QV|;?`sz>}Fx_aH1oI9eP_(u2|eJAvs@ zDX@|pDr#CODi*a;VaBQN;^|A{#HLmnZJ!d)wNho)<)NNcjEq@D;{8@C&l_9hS=qq9 zh>**XliD{2JFLwgHCLU=zyuGJ+`+eTC3Cf5l(=E3(i!c2tkA&g40t~M%la*D-= zZ8RZoVzDRti$~K{fQhNm+vv4OUe7fi%QdS8z+q2{+NqQZGKYIsEpQhN%j25=MDlSs zN<_pY?nfe*9mc1H@INEHB7~P>7z_yEqk-QP!p8vTL16fu#d~vQ4SpW@EFKl+!RvUy z{bulg1dtA}xU#Xn)~|2+8qy&@*R-ttMEv-hR5&Sfq$g`=NY>Y_YTj74{vLnMOv^`Fekl8f zx>f7zTKtB(doXy!zz(R6g<=#3En-#&Mt6tU0xau{&Q~>FJl#R#vW%F%+Y!qj!Ek+O zFAWq6_EJI2r2LbZeu#?1?7cKVlpms4u}Szj8isq&;Qtd5r=J1~P$nGkZ`ex{(!?>0 z!!Yrrs3QKwUfRaQcRoUs^9-kXy2($b4nIQKcJdT7Km$se2;ebh@LLdP;cD=?i1QdT z_^pVC=~n`u5K3o3HZFwMB0fHZqnlXXC({gBeuWF*N>zq3v?0!wG!@v^58nK|B~6%J4Aa*N5=k{qQ~g@FT#(6+Q}_Cxxl-clzPSfRE;B zZU|mPJp2YHfrl&fGVpMdz6P8nm&yP0e)u`y;WsXVm?#U;7Xyz-0&)B?=%Q2N{A08| z_6l@4&z^;;yY>^#3ua$0RtZYj&x&sddM$5xh0?^Qk(=9hxmb39DvK`R@ijnwKpepF z1mL82;Q%!_RbxfrK^UqC=O3ifjMfhzus{2P2=-B}xNwlFvNn%X46Jct*yFSWB8uf; zZG2Is61aX-a|>p&zq-Dzkq48}K#hW)pm!a&O1%FB9Y!s8JV_7nu+MvnrVTQ~{z>p- zNrUGrpP~r~^Zcp|C3g+}BqS6o2hhFz6wMlAo?n1+VWwWCYfYz44BQlJ0CHPqvXnC;k0O3){@hU)=mgip>=lROddUW0!5a%hT^Nx^8 zAqNl8DAE|?{+q zrXLS@^hRYy)op08I>on-Q0djj@QBS2t{t}{!xsTl0n^0!BUBi<1d{yp;K^OLp?Q5v z`T7l@ovua^D`=MR2LA={p&|TD;H=P1`dh$1h{}dR*U(^>8`0)WuhQJ4B!C%H^Z}j5BLc1E`Vho cgQvsG*Lv1_fb)P;5B%mhrG9ffQu5RP2Op`{)c^nh diff --git a/wasm_for_tests/vp_eval.wasm b/wasm_for_tests/vp_eval.wasm index 5c3e3ad86fa7fccd55717d811a08d4b1752196a8..07669c104eab4d3bafba9f17608bf56f67d0cc0c 100755 GIT binary patch delta 165226 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

    Bava4kIX4^OvMJl33|5F;UD*5G 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 Ce@l}9 diff --git a/wasm_for_tests/vp_memory_limit.wasm b/wasm_for_tests/vp_memory_limit.wasm index 1e3e4edd247660ebde2f2c54e58431775d0897c1..13bafdbbc7a7ee6a3b78e688ef61c30fefea985d 100755 GIT binary patch delta 147000 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 diff --git a/wasm_for_tests/vp_read_storage_key.wasm b/wasm_for_tests/vp_read_storage_key.wasm index a00d7f195c2ffd460e9f7bf8995f54bb6d500e53..866f9db3070c248d6ecef306206bf8aa8b1c05bb 100755 GIT binary patch delta 146774 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

    sY zn9d;HNXQcx2>q5#%7B02P}XcANsoM-l{+S=|3Ohp&itlJQI5ZW5+oEn;PgZmVx{a~ zkR5WaP>+*^OHe722H&(u9CDmlYvug|{%V{TxU*KS9ueNz!F8(|{ z-iy(Q-F0hn14d1oT?oe0cR(`a_i5Y%c7T*S08cs`W-h_F&Mn%s96yGp=4vqTSdgp} zkchQ6K7vY1;HUo9a7Q~?M=_HiT`kE)=2Ixyw-e6z<@uGx|I9Pa za_c-fU_l^ONE&slI@_2NgLp*k47l+~jJrt>H5#vZ=>a2BCOy;y;+3}aP;<;YpXQTg z`{YV7+JhX&&IAY84gU?!)WTizm45l`Srp-CZe8ADg59ij?ua1}00((9M={o?=?|CA zwPOS6gfLKTL9P5g=7=RgOg!?5c7!Xtwu|^BIu@B#o1z*gRqZwvEe0npQU7wp@?hTO z$BS?pK0i-6w$@!Eg{x4r+3`^3NWrIUTKRQ6L3=4OA$IAwHILYyeNdNkS?{tE`Ij18 zvg44U>^Y4`5L$yJMI1v2OOH?d9F?|SzHmo?kRBjPte>(*ft{N?z2@AjR8_xl4BB zzNweoW57$@Vd~++8 zV{l!&Al*6u^nyKQyQT!PMW3bn_P7@?&;4<5mSt`}0AWFRwIjY$;E3{VD4BNNLO%))y2W3EB^OoS~kc6*P87whaB@HGhQaZ z1?khv(9CpkG-(-xt!r!U2g}%0+#&~q^peg#&09f7pN?5UN1w*&!b{*WvQDD9qoCa< z8M!D(vSsfIUSgH!mS{>2mqR*kcL#1)I$I`uA?YJ!)FPw@%OC}=l;gXgR3%t2wq{eW|oXpxpQ0WmFmznXJQ-gsg<@Cy9tq}6DNvF z!en1jE1kOS6ft;aAhCO~n52z-4em`=<wt!ZVF^qtP8B(@yH)Kfty=7NiJRr%o0;Jvp|NHgEKKg)+@qF`3{RY$`*W8K zCeF$IStH1e3{E_trRa`JxF@n&tVx~V^8%^m*)dApizBdur}bdBob9VR{bTssyJhRQ zD)c?Rw@UU`bn;#v}xcPN~Yb=H1}>|4_+obZZ}pgs@`o1AF9kRlltA& z#c{;_I;!88h%N69e$JV^9uzL;_t+T`dGPlKhe0#4R&VH%+llS(&uoQ8>D*-eMcaJ083os<1 zmeORQYdzAaN<*Eh$({-U?85;$yrsFz$A@Yy)_+{p0k7(VQ=frjndY{ zU%!S)0f|BJyrRZs^({!^24{VOR>h+uY#Wj|so4%2G(c!np8n?_ey?D*L(ZB8efoH6 zJM2>(XFH_ljutJl9U=gY@PiKHVcT}thq}ZxyhNd9pn@v_s<8U2p~J*rK>$i$&5Y-NR1a9NIerov6xai z8brzQZXrZ5`zTz2lcT8WTg#T0)-QrCGfCoyn?YB@psO{$yi{63NI8Cb=W^69EX~Hk zM!Dmcp<`jg;YLB6Y|%hmpMyBN$b?nctB~dVTM5MJ+JX`ovJX+EaXPet@`bf*j`3%+;9IP6l!P0J&~UBk6BF^@ZY^yaz4L1!kbd zUh<$dXrMKyZ<^`PE*Qx`3%-7USE=SlqhzIr(HtPsgo7z))jL6}-U(WEgdzhi_JTkQ zBeK459B8$B3mEaBh1EO*t)d65%^tLxN(EY57K0W}Q_|ZR zmN_A^dNC@z6trF@2oY#OA%(06SEY@u8-aUU-{>u!pF!1};jF1;!dV`wcByjf(!SDJ zDM0n505$9Yit|rDB|uGa7*qgt*CTg1U1D@RKvCy^2~g2b3{c6>vY)-5153NzyEktv z41-I7v8F>`wwg_$uN21U#>rsJrc0r6Z6~qNyMVDNG(yM0*e{%g(GYlW`0FLYUn!8i zOkjU97_(mKHIQ$;dbTiDPGKxr1}1T|H1fc8mQpT|=DxujqjSLBU4qU1Kqx30WGA`& zldu_zZ6(el=C0@2hB3M|Nn^-B4cI3%IeogI$=yz!lV?$yB|y5!Eff_bjWx(o-hRyL zIS|n_O0)X<94F@d;W@ZwPjq1(Fz6UkVzF|fJkl>JT6hlPF@U~J8_blZIyR?MqPgtX z7)lnNr2|nV9cSs%CP`^BF6aoG%A!7aYS9foP_Pf^{-c8NL&u%2dzNR>$}Bnb;H$HC z810u&a2QP&^Z2p)j=`j1F1C)L=!@C5hs zvg`*WYp6iZJ84`4SDu|1RFD(wLML-Cu_2tmyTpqg-fj$!EBl>Bh^5roh5U&@I|?M) z2K0lD1_5l4(|Vo&ynrVe*@dd@erj^3TMp&aj>1M>f~EKyj5hBv?o5X!$MXx$*{1tS_KpH}=^DpxuI^tn)}6rx_x4pp)OD{Qd>U>2f+wSI}{~ ze2@zY*F2Qm%p>RN_~XvgDO@uh*O#74zomzi%}v4+vuSfn0YZ9OAkDLQ&$K{s8*gKv zqRHb*U7)sW`7M*gUx5g~ty6V%?ryvoI|y@eB}9$nxi!9CVY=8$#^MqWRDOVi&Wb@G z06`aj@HKAeSY1QjN!;|Ji+eiH)B)q7CirxA9H~=3JCD@ik9dp&OzL_rwTW_-oI)c* zD9eQwy>GZ*f(6!@uQ;1&#j)e?X968=J5YZ)f+spPATF}Kl46C}Anr_Fa-wzC z?U6X}0T0(|(zTWg&*G)KLe$bOR&v$Y59vsUgE8N4J?&u`$bSqv{>OEIm#qFKc+pZ?h0f}KhfwP@C zz+D5I&$Ol}NlZVoO&k4rv-q<`f35TW^%6mv*EmXP0bY`?Rn@ zPUZ^MR%;2%ih4FLSqOqVzl;fVR2(^bOAZ!M>d@K7Xfgiy*IM_k<$#qea3UI5a0QX% z?C$cip=VtvjP_x+!R(60J5r6!0M(qBftn6FL!UXp6XrxvgH`&Xljx~{-UTI0+|~RY z(*q#?lPY>o@Eq+@XsA1=2LZP-)3-$!CvsFH77*12)g=W=qbPWeeuB+{FbQs0mEb09 z7`(fQ_5$N= z;EjS+&G-1(LDM!_2+d=1re~nQVUEW#ks~psL|w)gky(h1 zkepCjN@B$~=qmQQtUhu!u6tU9GGI!}$pl7y(*0@I3d4^VOmF8D930xeO2%v9QGR+S zRwTo44wKZA__mG`Beg%swW}C<{Z2;#vs912*->Cb_NyHQMq0Kpk&V&e=>bH;}s9h;XE6zZrf?#LKVVGdRi;>k7y~AkPY3DSredzfGm8j z4}CM}P+jk3^$n@>1m1>wcXDh+{@z0i{JqhbHZQ@yjr$b#h9S-CN=p;okv>q$F5PQ> z6Zx<;Tq_NAp_FxkVkzLdZ+3{u*c7^V+r zL-}KuX`JP|Js;bZPP#V-!?QR;t)fM)X)x;SS>!?V0rx)9LFY8+!^d4At60jCwDSvkvGAI))>AX*!(5P9OI<)ZHh!8WKYV z?K~mYI0xnu2VwX!l_EN{jn0r{8cN&0Y@@yfn{acs35C*x3t=5do#hFr;ukv>{9;f0 z7X|-|G5?EDDL>d9oRgx@u#CtDLaAlEpPq$#4=&Npz zp9zN=SsTVHtkI)k^olrX9gvI6sUOm+{HC2bX`66r^FA)P$rT)^5eCPAPPZf`*0Z!- z?Os2$v~6ui4@a(jTrI801{Zl}pU^j1j!(-zb4K>*pOaG%Kv>aTasj=P2M%a;OK#o? z#PRn*^uO5lF@tX5GibETO&e6p3NoG)6Q31iQ`{BLS%KRTM(B=Of!i`x@GUFYlJ0@7 z;1{=|5ZImn9qr2M`(u5yerPot?GSb&;v(BD{CP1GHdt`H?S;2y?qceeiI z)KWRYi%rmf<{003t=#=K7S=~^R1!p|jM10ZqK4#Ydqf~dlr>R(EM9sO7x49Q35ryd z=nbVDmy>y!Xz~K)_`~0LJbzjJ6msKBu0wsv<=A@o2*swoTy5I&zF;*6Hgsf&?R%8xGn7|{KXZ+OSS<)-?%&iC)>pjo9+kTEP zL)(;6|1!PjvUd6J&UTH4+Rd}*iEfrF@ie!8>qdxEf;V2sa=h?D?qnC+>+xm3BeNNT z?iarM36jlep1mR71?I8^+B{{dab^Ll`b-DaH* z^=7ccxYN}04p+kg{CB+SUb>jM5?Vu9JlOL7X^0F=sUF=A&t>K1w68{W^W8eTOVVPs zt`noQs!gcZiQQWz0^$Vfb?Tqpa_64)iP7km;JIo4M7<=Wr9Tk$;+Yw8KWGR|_3t=-5P4P#q-7;DtX zwss?Hf^F&d2?Ln6vO2g0pk+S!t6MfWK*ljSmIp#vw`muGKi?g2+pD8pxT`{9hVdqC z8(Jjq7aj;MVuoDgNp3umQ8AY$#uXG1~E=rpo-86&Ej4VSS6n{^MWQ)wSq%iBpgi-D* zLmOvNZ3~-#Il3I?$c3vMu3#Io`O6XdYonE8H2PZ3{qi)(#YH52Q6t3`7_EdE97b3t zjL;YV)$jjRK^UPg9xq;p5z8<_U;Nhz<%Sa0DTD^QZ(Xydn1LLSK`>M>k!u}RxYnJL zxz<@J_~7brExl@TLK5V_^XZWm*zIsuPB#)xduXzKrLCiVh1NLRNNfB?0t0#&v@4l9px16?>VRI`-yP7argX)aw&JV# z!VoAfgW`28*tk5Y`78Vevpvczz$uf8If1`|3u!t4yjFCnY$77ywn|Iwb>(ev<7UIw z^cHlHPq~;b6X-Nf0$ia7t}xJA3Th0vC^q2Q?SU)uz!e31(6(p5MX>>we5&LFFdZ&{ zr-Vhy07@&Cfm1j}GmF7#$bl1TqoC0yFuHS%$@t@^{v`+7Pwq(BgOeUUO0hu`V>W17 z3pB0eFp;1sF>rdh1E-g3$CQGXJH3v9lPn<3AQU*^M-EL8aXGrm5tBm@4S~|Z)ee+6 zz-UD15bs4)4vL159^hGbE-BWXh|i(H(Qq>$Hx#T+1aq5NK?AvP3vXxah(*Zg4GbhI z94QOtaHoc8^G`YfIWeTi)ZRg@(daBfCV1%G#ybzU28;u3ENH+uAS~$p3Jn?u+GvnF zqK?C=$KhL{(hi7TjIYlGmcK3{Gz48)gD`8^wZU0#IO=Y=7G0==WzB$as>V%l{}h&A zz6h4vb{_}J@(PX^D1d3wl=Oeop-`t&&cug_>V-EGF(w3Ql18LmL9=29Va~z7VQKJU zmCV|?j~9y`7`v(QLx*3iQ5w6M6+lsp-OLIgT*hu@1yDBh7aWC3+t{K4*iBE&>B3gH z%-wnTUHZd^{@HPI@V#5)kN#aAEqMI3^e64NqNWEgZx_7Y4}>wjUfpgF@LKdYoN}tt z_RB3hrR-HHL;Y!Z+2E?Jd05>b__mlhy~;6Bg0qEK24$ zBE&kA`#LTIA{OqQ$)%J&*&XS`1G#xTg#ax7z7|}`G;->Ly9Xb0;P*B@W^q?wFy2NG zK=eX3(SDBKYOkj(EX!MYt+&j2x&lHBZ)ZGW*!L@~;#;fqdHC8Y-H7k4($nC_mHrF@ zgVOUjCZ-f|_<&NhWfMw!xK!#vWmqoag`meHq-9fR5@!XGU1V62VLo9_+{Bbfqi!)nRkTF`2JSz~VWsDYx4bNRg zwayCWtsrCeKzUY>@mfS{wz_k6{%QH)JYHP45hRlX19Ufjto6?4l=_m#jJ7a_x%_DS z*b`{|W{*H-bbf*cT)CvvSz3|jJx%I3)#vhYuF3u1%72znw!*;FnnGOV25M!wzBf1Q zvJ+%Cy6iaFz-5o&syoQxVO5?dk1xKmN6F&d-#U8-ckv$IyYbfbJ4$^gcrkgh7t{7) z?eR^`_<;hVjn@2*QvVjy2S=-ddl+5NgNGZz#k!$)VO2>92Pv~uxaDqb-&y+n*)@od z^u|?rQiTmDhFb_#crSp;=uvgr-ppv53 zA=mT02I*MTu;n1^N8V@`_ersa9pE%=1s&kTRm#-p04E1<9pK~u4si0+C55;ScybVT z96aH6-SB{fmy%^4(VWNjl~jPfdL!)eI-B*%H9iAp!4U z_^+x5-!D1vWmUd-t<5R;rl3VRtvFqPg(jUU=tQF6INYRkqM###P^Ch+Rq3&OxKrtT zKHLSuYzhIfVBH+*Rj!*sc{q9bS*3U6tF3RHRf-dVi(Kzi5r7#DyTzv$`OlKuRHHR^ zAi|}3uKy_{;R&3=(tKWybr&HPasfPHtu#Jue}g*v8Q3=kJM+O~+hg-@J+!;DViyiy za~64lvr_GVw?N^2-|ldZ#Uytx&z>BXi^5rVp#A5;tRUkYR?B0_p9D1EVTAm2kWnz(3ePD;iA{tM9qTxU zbzz=2DLw>rJU;xZ6d(R)4}!A`&u$RH$DoyZ>)*~Poq~znf#Ky!0n2NY0;U%#1#B-> z3K(Clbh;3}QYm2mBIJ$R1uQ^6s5{5wzvV`Q7m1Zx6@QoPD_!;<$zJWUx<24im;Dmi z3&|!_{v3IWRlX|zG+9oHs);`(d$!9e-i(E8UlpTKG;VEGjFu5HpqgkNP1U(74i$&p z%20Kh=5ZA+w;cPhGV~)mEysC}9O`)82-sSC9=^ZFuh-#hy)<4e>(P#7WtX^Rom)^1 zs&nGipZC>r!YzGJ{lkRUs;05Sb+H57i#%>f6;n2f5o)>69LiB;tvn8ZzpjdT9(niV zqtmUo{?n(nY3Zvva5?hu=lTE^qQ6qma|M~BI@Y|1k6ojDPOj7jmzx@5*J>pNps|*|{ zg@m$4mAA?Egm!CMeMRzfn3}*bS(VnUG8?jKyAl!cG|J zXkfzAllFuNMs2K!vIZIVp#r1@3NrFU*Q_8TPqb?V84;*T^5-WRw^LOtatgAYUXACi zz)7C24Yc(K-YkW|oT!)`6pxBlo@ko8#|kuftJ_{5l-<>(KhnWu;zb zmeZ=oP5oiYzVof`9s3<0c|e2Y;O62Ln5Ag(3ZR@yS73s(wCxo*M&P3bD{zp46{_$y zj`P|Q+^Nh#bRm^{!fzv%mE{l4m(g;_q?paO4s3Ww6Axxq&@taLR?souIwoWOB#!9v zJuB##Zyl2n_DR1IBE#Fz^%=<8L%H(>L&T!?Ma@cXwf9=ywh>OK111J zVCUr2;u%k*P+mOcQxq+p@*|YdQ7sZG<&G)mdX9xtPVnNY-hWN0fc~q>9MzB7BSTFs z@Hv`v3~h?C#Y5we^>IU+pltEb#wlA2OvjcTH!@7i3r8jdGsAEF&<+aOWcwMD$HJi< zUFL@NaUCMo&^{(NJVZEz$?kZrC~XLfh|1kK633NLatL~3i;Efo<&rTV)h)S0Wy1W+ zg^D?auR^CrI@WxX>Vm9o(xgxq6GHV#t}#%&yLRn8qs^7iSh?7)Y|Xlkuoh{viR*@W z?4q4Sb%?!#6gOW4T!0d&RK&hRM2(-zD1i0AQGl7h_JsYJtBo(HHpxXb4F0jZ9!-d| zOdsi&Yi{x+^%hl|&Z@=Y(m7I-Z7CBAyE_r3xb*He9}ix%0^7I*I1xyTOYiQ)lrSz7 zbnIL)hDh|;XM%3qxO28Lb4$@#-Ny9?%V~=}90)#GQq7$9hc1Vpx)$;8mzvDSY;9p< zM2Vt+Al|Aba$j-W=MnrAtli3y{EG8FkLXuC>hs8c#W|lx_A4Iod6D76J}=6BNI6kY4HQ;Qd3Ly;{{MmT5Rscf zZejCA<>ai~G&{;boH0-N{4DuNpC{x-k<+wRXNLT^&mSOvjOEvFr^(Oz{1o}4KJVtS zWBO+e6S(OYfF$Yk&*DO1sDP_DmlzuP-$IcRJGr@lB&8GyY$TBG6nS@Kgpe~|p7@(HcY@G#+D93VgL^V8&yv3u0e z6#03dpCo@&zQTfO?Kn4`HnX<+9-$8^{n{Vp?c0PaUSib<7kW&8h`R1V`)(S~{4YmPd2=@&7*_EqTXR77VR} zN6UuxU&^C-0{tK3(YmE7=gbHk6~<6WGvd*N&?E@R`@x7IfrkivgCSHVO5?0%xs+SA zK!58pKFpzNZNN4$#YLewn$>Wens#Jojd^Np@1(ZqG1D4i8V2I~OQV=+JF6sE8PdMt z)4)9g;tm}Z5w2U@D=`~w-jNnztcE)q1RpM}F# z`L;#;*z6ZNxxiVNMW*1fyB>Ab=#b;?4rknhaQaEB&9$qEKAiNc^X+=iRBe1g=T6$% z305khWDzgb zznGUoLTLX+tdy*vStDYlWCqOfBwnid58mUFDs1mxSuzQ6>qhd)f=#Y&}|tXQd(lbsQpMB{=4u~I1~D^@DyWTz@@|CE(=GwsQ~dgXS3vDqeJEnuZm zPWGfgb~sxfoA?d~9$IFKoClBd&|xa9WDyhVoOnFJ*2H(XK|qzQV9~^PI6_pt3x%8b zOoXug=|zaiii?;2%rZoW2=Sjc@qXUh`|l^6coSy@$_s=t&FBK5OcT05D4Vf@MUdQ` zEC%HTLYYAM1VY&&sX#OO?4*LWOqTqo&nV#jEFd}YB0m8#i~swig0@iL4O>-dfdTZ% zC0;7vaDbC=JEQ_Jsk$1Gni8)*RBTHH4;P=6RKOg0p&)Vi0XF$rNd+Ez(VqoUfo>Z& zff30-WUh*AfQfJ;UeG;+P!g#Xp!i>j(Th@Qd~_+EoOY6}`kdT1A+u-Xo?1FG{AQT< zcV1IkbwSSsH;U~h0NftV&*j1anWOmHfUZQU+LcIsyys}$m{cqau0$H3zG=pFCDPDR z>cE`13z5(x1@a{evI~*AiK{dk&)6Y%^H}e;Mx%a<@WsJV_?&)Sh{We$Y&hF9Cf^)? zA(Fn&VKPMzU+XSJf)tEJ{Z`U3dWElu+|5QASL3I^j_$delO%+PEH=2o(-V`?02Jgi zNT#I4Yz5+G%496DspMuwBm`{3Ar={gu`K2wpk)>>f!2Sy8r(719eQvR$TBs!f%_%( zGM7RGEc`nGku!3wCARR}a_?TEcy9Z3n9Shxq*Q+Viz$D!2za(7xo<7CMrt;ZeGhQ} zjSy^_!8W%5P&#g5GHd6V8K{i5J!7?bswMJLNIft2f0u6Jd6_=n`;YF~t)kY$wujk* z%?{$Xy0-*3kjPo_)FRf9wTi7gy-Zf8hAdV*P5GzO>}fLbkD)zI_$SO)R2}zEn6P*{ zw$wEP&gY4{sMV;lN6C6~mZ@&z!xi~O6*-2yz_gC@R4#rZHMFDcM4!1oVT7`~b6~Sc z_swC@A8vev@&k^5IiK#xUXVX9XFj3QlrFNQZIgOf}}d zjTqxEJ|FNLNON_oV=qs@w+?611)0kYS+>@?XIyX+L%D_vGGJ9V@nGaoT&=+LI_|Ki;D zTHn6=L#84C1}S^^k$sOIJovRAe!&C#LCXGnvcL|^z$je2oeog8(0(N7U%Z`UY*=VN zVn&Wo=0Atppkr?x=KP8FR+8C+Q7PNz`kGQRjTOWsr2jON$AdAT$vkwznv6g-Ads7; zXvaaN)e=JyG7JukqLTPuZvh^MTtU9IDg{U8oL zugv~s5h-iO^0kNz>VL!{GN@k?w~SW8o$ce2NcueAJCvsh&I%LjCGFw~(z1@%>G|G5 zLOmHGa|rm6gnDvajdy9~(Nfhyj#1lR=iTg}qEl5GCe2^Ky=skO(a(ejDKR`07u&(f zjWRSwy+LHyOlYq;W_xxg?Lo4ap-f&r)7~T#XSlMNkf#+OA?Zwge?bRW;Gqgw-?4E) zuN5#8~n}}CyGf*q7D+( zO%xNEL_J9qcib(PNGgdtMBEIbfQTdtF)Zh2mWyIYqA+K{{^zo6l`@d;Cn{4V`~Z6L z%d%DOA!?AQv_@Qb?j^35xWs1zhR4gTru{NMzsz1yIbk7vKy)_-)jK+>GE3f0#*yIF z0`R0^&$mo+^5@mrw!$uO?&sA_JtC?`6s@QyIWd$#r2Oh^V^IWie&yBKE*&JQN>pR4 zgI8PS2jrd>L3Oc2HPj(`3$m4VC%e-_Mx5J} zYrkn|3zAgceR!m*%kCdGPL8@~1l)q;1!c%m97D=lHMaI2RrK6oG!xY=2l}+JZ5y8L zXUG-DZB72dntv8nhHAsy(II~kp<(wQWk$J1RxFX%+FoeqdkTLEaT7jhKKd{*d8rwvAW znGg%o zq$7Nsg_k**y*yZ*S)q1C@#@AZK7+3*7MvuIA|oo`nvc~PRKekEoxMKLf~#d5Q43k< zY>GR4t=VL#1O0Aqt1i$lUa9Blgvas({B`v>xR|wD){Z5K1qXMCD+gz!;U@`~Xh!n= z5MgtHmgcG3>6`;Cw1p40rpoSg2F1~RW^{%3xbmQX3@%7Z+C!*;S@sfYAff$)9MNJJ zgOH7-Kzk zPqeVN&BHgs0{oH$xVlS@=ag$Ivpbl@Qk#RLd*6ATkYk^*9puq^l#!hcG@hu&c3ct1 zmaUff&bSrv0yj5oZR^+vzDp8F|O92X!#Bml?GKNHju=e;J_{%tf2g8FQ+I&W?1Vl|g- zpb5L4O=bc}GrFv|k9dqtxEOTS9WLgwwszdxZwiafuwBLyGRLorP>4C zg>gQ)u}N2vx!cx~{v7*hd-FP4eAGGuY8Pi$(&Fq&T71+>!rYwH8A^hqiqHs)8@uz3 z1eQsp>yE&=#b(>Soc*}9mH+{v7Kbwjnoy=};9g?N7Nn;9XjWh1hN8xt^}9J!dB>bH zJN3f8gR0f;>(ZjZY!Rl$t!n)U^J(oouakTt30U=+^>>g^y8e{j9c_kPH_y)jMHxSm zmXG-)>`|k9(x0L7Ne==&gV}NAS%5Qvw$v@ac|cpuc@R4Oq0I-F+yqTADYH(L#*l{4 z8I-Nge2?>p_<_Sp9$cab{9A~OOpyuhEuhrW4#>Mj6B@(01q4|Gjd9ozYRoZ1D(?tk zHuki<=Lz=*H>UH?`NRG7)>CqFo1|Xi_*%X$2568R8insw?Q-Ok{%rr7WW~Cxn-?g7A*GU2OsWS9p*>2Rq<_U zhl1r|j<0ggguxl!m!g>)iX#QwSeW1O4o;cq>>e9w&?(P9W7_)<#)l@K=&pio;XhS` zrm>_sZ3me%_3TF{(3ev@9ixGJn-&l04El?NU*iI$t>FUQisl3R+9|Gr5u9);+F`24 zX(;xW+|6Q0GwS%FQyph5#fxk^=Ylxlpyq{N;UhOP?Cd;6-@~se7``_?R;rk3Iu>C` zXH7bvUhUz$*ppX#cyjC~t35n*QXNo^$0)-(&wQxY8udCSA-PnJcU;TsG>%}XTTFwE z0`$TY-ex={IC78O<~~kNLvBbi^KgeQ44~*#9eNIl<7S!f@XHz2t zm`q%Zj_D0f;;1$S+3ier_gJlM4Xsk^b%?v9u7METiTaWqT*X6QbwK+81=Rr@E6am` zJw{n;>FKe`3RDz%89F)q8(2$IxtEArEvYcCaiQLuL?81aaH>{=3M^Q;+DU7t|jbxM>nJmfX-GEhnfmGu_;9yYD#f(Q;Im0q=1=aNH~qw7?LEEB(W5y zHl>I|O)17VX3K~}O(|G^*(4TB{Heu5kL-vC#dxTxW zI)k+!K_RpaDwhIZ~`KP&I{ay*n(eR&ezeKo=AJjd(cCmphJ= zUk?zv(>|8t)w#gA&DXiGtWt6xUev~GlKw;Upi;%Qh>)ZpULR18tWY9gS z6;CIWu;3Dw*r)i8Hm-YeO=qj9Y?VYhE22?C73a)M8c0AXP*wM)!%nL?%)3SdBuy{_ zt9k|zW?C1jv_0St7v^dh*q1FDpe)vA7&Z}ZDFjvA660J#H|W|PRSE>R94He8#RZbi zY48ev&izjTmz~TGY9MyiJEGpXbWB2RML_Z{olUES_&@9B&sK)0BIMX#7zdDq`pIkP z52ZTr05Hz?q)Blvt824INTVrT!r=4=vq!+Vj5ROJM@ctgbDIYo%x|i0Aw4V8!_a z*t2Qv`EoaH^%6#cyjm+m&I`g9TH~vJab2l{Fj`Xx(+<7kQC^K>f8xLBb$ga+hw={( zXh9WOP{%Igmnq`Oq=*>|-{E9|b_exoE+if8h;}FShe6zElFIpVWa0%?X4BnOIvocb z5U_%w`i+yoNaYT0<&;JiVo3w6wL90LfoFF#(0~R3sG3R+x9~=AmZTL}$K7iOSFj`k zrk?Ty1Pr_L42h7?!yrOpWG$`m3s*OK6h=JzzE*ztL<=9ySuBo!pgGY1osxT;IdB8q zA(F$KqbZ9`5?*aI+WPRE`OUzs+L2aPTgmEevkko?EZ~DUmb8E_kR7q+3qmnCZk0Nc z0t5jyxHLS|lz~jq3{oC(U5>HBI&2yIHnfusCM%MUWDd$3$+%f0WKi$Kn-k!!>=b=Q z%s7Y%sl_bC@(eS9Bm*K8Qp_lArlu@9%%l<)V3$q!3A;$r7y-V-<-WMYx`ZQT5owJ= zpxqjUK++|Ja3f=*X5|w6%V7?fP^bTyMe*vjb~?fvgPyRt=9dq;$cVHD@qophJ*v2C8pT~harLykYqI5C zlPOPRmRbJ*7{Nfb6k8s3odA@X%Pr_LWXy8Mis_KhA$NoFsT}&}LoNDDw{{k-kd3vG zBu%HK>94Qp#LFpPyEoPb4Q|^*KmNrJT0*^5Z60A}mm^RfHSk`%7S^KzfRvR7w$*e68bilFWlHo+wp-4gR?{^=yX*c^{uz@=v(({db9;mH;Xs$5fHSCm5kKj4^cgkiF2u@lmC z4Q(FlR-?gcCR)rG(72OzuVw8PwfH`ugEcLmYzabeUKkA_*qUcIw%ZlJ>%X1NJryKj z_rXEu3^r8>D<^>&YlCatYuyC1cUr>7DAs~WJ_RpGFp~InkaL4sFJl?FR`0rHmRI?0+0ZP4p9Xh*7Z`a%HfPSTcN~X=0F+GfzSp z(!ZMjtWM{$pNo=lKh1rRHzD=+QS&9`8S)q=&L=wD8Hq%JeJy(QOK$n8%=H_DzD=pBa zpgKT70ND{-YM-RtIf0T@(Lb~{ZtBeVvraW66Gdhfy#I0y6#kF33op?^&pg$ej_TLm zz(TcdCNxPCUVaF_ZkUMSZ46(ck$Jwg#?&PDY{etMP#bXLEU6s4NlW5Qban?OT4Jys zu`}bb?3mMKrtw3+CV(`J-}9K_Xz;lrb z{lrNmj^#ia_XwV)*!#`8AdNggTrY9kGZh~sj+^OZp^&D-pau!J*)Sw=+B0{dx##?< z3B3VVZji36EqH9lHoaal8DY|8^2F7~7Yz z(!(8u5ppFJs3(W8al+S%^<)q4+qIKF576S~I-O4rKr+V-dk-wiAo`QLTj)9AnHigp zCkaFUH<^9N>A^wPk!;AA-xWzCbX6ilJU7Zx`CD1*Titc9cxh5mONw+fux#`E&(M+$iVUojcXRx+O&c@6A63vra z_+*B=E0hjsFN4J5Ak|GG8qHOHx0EQh;n{tGlAP;0b;Ysm*P0st3+NuBb<R9<0!$Qk&P4jOH05qm z;`LJ1J;7_Dw!>=)A+unzZx0iy8%SGF_r@DQTj+j5X2N8ldkN9%gLc0RHkbLy6trn3 zp#+%e0!b54ZZijV2_VyXz9nD`$x5i6KruFq1QQs>@RATU?&R>P`ho2xiZI~}kr_Y* z&N_3!*5FO%W{!k=azsJAPY=FbckiXLau)4 zDc*%7MBSr3jcXbbl%ujPSjxp(3xnJ3TAYQ!B}?+JA-DslZ5Qx+Cq_u)Du(;XmgY$) z&`+dG3kCBDDYsA{pOA731@R?yh^ehF#QeY^U_uMgk2vhBfH;nu`61yA{Jfj$Ht~B0 zzt{5n<|X;7`Jr~MO}U7?hv;AoYa)1aRXfPCQ07x=ru9M>Qa=TbtCbSft|J~xW*dlCTInBzI z!8(RzKw%E;=-^w~sjb^FDRn2%%0fXK#J9xvN$oYYCz85OgrFo~=_$2chX*eZq7%X9 zFZQXXW0VSkDaP`3%9QzCXZW0fN?fQ;TsQ+hEA9jYQuuKVU; zI^Yr|%e5~`rhrk!Nit^9CG(SHb){3NDV@83u_Nwo@+mcAYO8!!?k3fov*R=@57(R| zk;Z|pcNL1Br16@&g!|E*Y1qHp=2n-JGECkr5KAFr3v0A2hVm0Qw>=?&b7POp^g@H+ zvd2XpRhW32WD&KA>uHn+Kcqv&F;fMr6c;Gjmh^fW8(Cx90mozv^Wt(bc!~i+lV@mA z=}Ulz^xX~H%C_)@ri9>D5>i&2qz-pu$T6-?x_WSYwy=g-^g}NkElzTxfKe_+OV3~-g5mVPB%K4p@vM8zVFYa#qeooYG`XK-73pYi7Epf z#Ffq3sAj!;)U1&=5? z(m}MSQv`TP69T-%5Fk2z*UU>21c;=_pze|c@o96| zSAjwRn!w%`5`7K|sVDqNQ3ED=f@V?JJbp1aXA(sKlH~;NN67GD z&#Wl1YR?yf9?ldNvGZgIep;+9(C-VyDuaXRu8(ypzqs=1*6*D@(8BLdzgh}&f+i?f&;$hw+%||FNU*vf0RlwJR>pz`5F}Q9$ccdV3cPB!S=V=hC>k)^ z6TnvNDr!UQnVY|-7~k{SE@LP-k)ELI0e5HUZI+CUj-j~C$LPt<71}n0k|ze3{**u; z2u9<$!C-CzPI#v{!Ximx983WGqP~Y42Zfuyr z(k@_$1*7Zs#%Y@et{Jg6ji&()8`BM zx8k(`owZ%Ag*9BpPWju|uTki?^H?gQMs|kEobkv(f3`CS4Buf5b3fONJ*yXbb|}X_ z)e|Dz;4hMi%2Rw0w1Qv($6ZCFbNt?0L`BapsG@x({)ZV+?F^2nEzj~ypC%=YEgsl> zGp;cPpPNwlPq+W@YnGT?!P_BQllJgaTmj^*W_zy=GSA}hMP+uFAtu5C@%p+kBE~rt z5en!fiRM@OR6<;jc`MfrFt-x)^IHU`aK2ffg6;v7AY5GrZ;*shyp>BIZ?>s`A2+v&XAZtw6 zykMj@M4rj&aH1oNOYR7UP+Ny)138TKo;p3q{c&;knTf$QwSoEwt>?4=ma|HMZ~0*c zQg7l_+Eo^{^oca-)3BmPH$?*mBrv@5F%rfENV5vPOeic!kJ^Qq1?&KJ5|z&zO-F>x zDs+n(MJIEh39ehaNEtH-n$FHOBd9}RfQjB1bP0sH5DI;A=&dTVe~3B77 ztp2-n+*oD_N+h3WUsxWNWL_UIkv-^T)Is*m^pQvwekk z7p>#iD{BleM-I9+;_JH_=;N+pty4FsW41OK=5Nxh$ZmBlgHy*MR*-J)O5)2Y+rqbQ zAdyjZhP`2o_wX%SR8-NZQH&aLTX=6%>;Qn+X{s*DV%kb%a?_SfUspU=Oi=vB z<*nQo>85P})YG{!7#LxInKs>H>#Z_AG0blPI8-*PcOV#Gg@%PKLFZQO)@oF3Kn*bC zV5xQq$Q(#5mq!3>A4okwYR^(v2g<>i+NeGT^|;S~nuWO)eTz^l^{c47vN`s`dQM;S zTeb=&HDmNXPm9z+vz?@7m1){0Wh~neY6~%XPc}-h5r=o<&Lg=^NRG&G6cblx<4%6C zj@3F~d0i>ePnLs#!C^x9Syk1o?#%8XKmceK{%g!{C-W=oJI<4gffMAm^ZUd9QAo%PQL{)W00&`=BOC-1 zSC+|lM=;_p;xb7uiEjd>oy0aOauGVn%w*H!BkeLzV29B{GmCi0U#RCKBs2=csuX5r zPe}AA7ADXD2)NC*r*6hQwhO89U?><_tzdJWMA^`C>K!_s(HPVd+^||N>YcgJh4jdJ~ zAZb*&I;xPqNv(pmt_Y1{d;xi(hpKafv5-M3WR3`o24gk8Zmf;DY}y0qEE^-@e#vrF zTrkQ|#YY4n3D?$LJk~;N0dr+{VbyMgM5k^QLA^oQjtI~Rr7O5}-SH6tOxQRgU=$J+ zX!?#SK>`EXVMhe!>WIJ)TaAtQP%yVniHsSomLwrpu+(Jd_ejQ^l->Fn6i-#aG2Zaf?>R* zU~@VEH)AOof3#e7nqZh?0f_Q3lsmVxg{_e68*92USmiE+W+FH>4$k3R^1?N>MKKjV zHKxK>V?pld@d>dcnxh6o!hQ_!En=L=yQ-(!r7gWZSRFGboe6C|!9>lE`4g818VldO z@!r_oAkfPV0v+<*2y_!sL7wu#cKb&-ZsQ1aU{boAlt70ysT+mf z?I<|>aVBIU2iKYj^u;VAh&P?%5V=KBmtXpU zt6b!{SWHKr>#j2}lZGQE&(;52hCE*^ijGlH%9I3D!-TtR*y+BqJL;TSR0jIi9Vn|v zKwN=|E-Z6O0`h#ZepZ>yfg{hwYyy4Fvuosemz!LWXF9pvM@??kR;f*}WO4BXGhGDS z1bdvOq4;inGN+dD*h5HWW`ct>F?sH0LWKcI6O~V8WDvndl0XH9HefJM3qfy$T|LrJk;jaOu&2cDgCf$x9 z{{L8%rZzf0w`n206iQQap|qL*8&R4}d;XIsOcU16${u`2y8nVs3Dbsyd5D@-AyXscF)BfGR;&fG*qApa5gjD7uKH!Osm{}e z*G9~!5?Cikk)^8?3gfgvFsu}DaVVsc;zm^&-)S*Cj?zd!M|vxoqYLFK3R+!J{h)fu z)zf&1>I+4%2)}K(|RG_xXP@Pd)gO-{fv3<%VjTL3VO{4G;^BYqpx=S)MjJoY0 z9Z4Edhibt*OOp10-Y}**Xi~P*((StOl5&b0l>?$!dK5@6ae(q=;<_AA;Sk6F+Nc}q zmw5TefP{;wiEhdm3=4^FAdwx%oCinsp-$%pbCqxGDC)Fis2rvg+h^4WIcY@?Dlz(> zEuwrWeoS8s+y|=$dvMMoPUDaflO{VJG&VtHF_lJ~*6EAtCM3-qZ&l79bpt;Irv>l= zC0Ez*3lmePadT5Pf!eg#2Lm;%A=ZzZMe~jp>+9H&lW1_4o|Ef$!@Or12UNfDE}M}Z zH!%*J*iv-~19w0K9w*U$;CP2X zvx6pzijYz@GluS%BH#hUsv(ZphgMf&j+iC0)1@ya+3$A8=%fY_Bq_8e%f)ru^w{iL zrjkb0AdT4xf)WL5pD58tX2(hPm0I!#sNm?04;5$N>&k{DRjyb`-DfchZWfH?HsuOL zR6GlfNdTu9_l9P!UCTb8uAxogv%4Qft)hY%Q3fX+m3a7_N>>@Kfr)W5VJpcLY>b92 zU!)u{ja}>8DD^9%i=dBHRX}<`1GORaY88$Kcz0~8&1IBi7oa1#=(8!%ZYT)4YvsFd zFAQ{AM-A`51w@bETO7YIW}J>@;++=8_`G2X)Qp(TlN4Eym+4%^tS1QZYt*mV5r5l_zXps*hIKe%d6LEiDpwv|mi?o_4KNsElP_aMy`p1fucB`cghB`X@ zvEnOcG;_?-eAvDUuOp5*I{xFuZp(sWxIn7Sb?PedFwd`LkSaELJem3oZP0s^3bB(btn!qO2? z+u+7JUH~{P&8h4@zRTu7yrLy9@we6|@uKXtWM?x9%PR30f<S`o8xaXOY8+CKii(Mg z&u~kmGpKP;DwOZxa-tu+diScUTW8ATSix}>L7oqS&cWt=zurO18AUil10n%vqIf@TQ(M1AGng^ z^R`1(?JJm6niT}Bm>jwww8cn>Wofm=8=6skJD~XDBcRO60?pw9_LYP*>^!TEJQ)hl zGV-gonLUHEzQ#(<&f9L6f`0c$9`$Sn@gzDPo&XqNsb>~&+c{3IyfoGA(?0(qyE@)#B; z!jg?Kd>fW`YCQHpsv|DFxp3yOQ8c#^$Xx|W!MLrIla$;Z-iAjow|}}459W^9VWT*#iv0st-;HX@%?SV}oQc?UQ5&Pq2}P?`WC~&C3#e5`pm`b)2{a|JCc(7>ty=$4 zzy&`dkZM2_ZwbK3bwUFBLR?fsc1~aL(yOo;uyh+I0ceZr0K-yOU$s+{*aylrQ`X)X zLgT^!2mlL6XLmNV*XPayyOIRYFG{a%T)naO)LD#_(G>TRQPjlF0nQS~rW!a845w!D zBW&{|juvr`ajdMH_}yKs4tIE3;61?z$Hp<*)M9y1zt}5a#JQAIFBAaV)S6Pr@RzWr zyyB_CVATCUaoU;6fS)<})O_rYM-4b*>q<;cTP7>NAVK2q5YpaSvP*Vl;4hgM?&LkX z{(<7qV*Hb{jr^+4c2D~sg&ENY*B5*17_k+YXET@UPa6Y0&IC^s@Bm?}QAeurovRkK z+-C>LIoqLC8N2p8hRNI~vBCpse=>*3T)c&qO#m|ArQqUCG= znmM!0#WFXs$D{7p{!R{N+bchz9$40vD#MTQc#9dW4Q)c%1SQ09NdSuWPDVZjknwzH z&WmN`2~;AdguKdTO>PA3P=km)ts28^`&)!p+c6hp5TJ3LzZf^Y<*L@>kgVK>L}sNH z+V@qbq#FrnlSI#(Mr$6LNTr`{2tEKZV>7oAcj^P}-~t1a>P!ZIOr#jrZqrz4ZFRJA z&>@>@1C!MuS2*lFu2?m-piFH{`Bb50u=`0fo#MNSZz$Anz3M>JpKU; zQrJ@SP0*tP4!Ar=6|pz>(O^gsZLVCmSt|n$Rn!K+D^_#M8iozY&ZHzhp4AnMG^>JLG+P<)H>eHMsui93 zB26%TP`ZLk*WIKEHfM44MT0>J^{~(=BC869?Aur!jtMsgXIPBour1nPP#fw9voDoz zjb;b@@oG{i5N!=I1#zuIO9*L_AhQO|R$UHO=nyQjR!np0^O!z15p08RUs_j}wUl%mEc*FoetVAK7np>Gmj+zSn92+4Hu)@p zgyA02CpkxEb@yuX8FCDF9BNoOI$fQq)6L2eIvRS`mCZ1U0+yLVFYBw2SUD=Sm~dTK z=XkqH+}>0i;&gS^3Uqa?WhQR#NN9K;M|)hVbmr;?$T-+9*s0o3eRK{ z%vd@H19g3Jw|bjjR)))@9wisDF*BJ|iGCLyw_)l{Z_qf>9InOF~n+LuaqeOCgKenG`9>iWb%PqN&ygj zMP7$oB-xNOw;EJNs`0G6ELs?Kvoe~QmGV)uQfkb~kZZr47mlX|CMUtoHo~SX!8T5~ znWf}}p%xmIoFy|WDy%l@W)eV8W@Xe61fEzK3Y@BL2}TVqD76sw5C|LFXAR-e)AACt zV$EV!)SBj5k;U54$hvt}5=k|UYRpP^;se-5tuZY$seu+oGoorYD{{En4g&Vht94@p z6fgTOp#{5Tuwf2JI7JI6AVit6ij6M7E8_W`bv$NL&`eSFGUl5qeWM0Ekfn(d!-_jC zqguKSnDL} zMDz+%SdAE|(IktN4iRItM2|R3em=vBh@RwFrV)|2)beQvjMrUx{VvJxkUkGq&lHJD zuO5O{O^-;t=@IK_PIa0t`~)-;hlWfh124$Ij_^w@vX<)!ALAEvC9g%*x%_ep6Sf8> zo;K+~&rocS*%KDI@hsLXLbpm63>_j`7qc?`l-=PB@zIcBZEPxaDek2GLMf+@Un!#yfLgXv#lf7@PC+ovN?>1 z=dopum(9DlVvN)rlg>k8QxtL;EmefUe3FySdg7&%Eyh)4QcxvA8m#uRQ>)b^$SnvXPkVkHb{CHI-u8heoBPR031dcUnY0n}wW=-ImRVKIPJg@}=W-U3@f>u_nKS;^ z61CHyd{F5~jCI&kp@zg58K(|mwcD^sQ;Ed5OA=$l92to*#1ax?`98t6DSG{%6?fIy zuvuMgwoNl}eOk`wTjUA{(4nPmVbA0kZ#wfnFo~#wq-}G(98K5#D^n8D#%O@>9o?7{ z(5e%5?2{vxdvW`~`Y`#B2kxXBwZI)G^`K`40Nxr$h6TxxEI=-aI0d@GbJA7jQh=%H zn5+U{z$T*)6eGx}vRSeUlcF`Malsm5JfEiJ$wly= zYtQzru0)Xsnuz1ZmuxsRE11}>C9{G>H_Vh1&y;DL3^Fm5^KLq72s|ONN`CcLhMBLhW~QQYg|NnD%whNkQ8g@lkYJqfcEUlB+zn$w zG{c+&mV&daw%S25cw)ROUuWed4nB8q5YVX{OxtX_c2*j?Jjt{alm^09GBeHrVvkW_ zGTr%Sl1S9)TH?u7Od+nrRRAj_=^bX>swY-J48vv84K7TzO#vEBMm89a?1YAoHwS)g zbFj_gPBsf|uWS}2_C^V7Yx2dNY%81};xuHOlV3||S}QCsXi%bq*vqJf_kQ%z>zoo| zZwpF@gP>VrCFJ98RtrHwmISW0T7W+rb_?ZdQVC|X5RaW~CA?S(VJGkp<)SM;SwyfY zer-t}5icXwP&Kf{p=vl*5o``|0Rn%gYB2axdxY_6gLD`mp#Q378`WgGi@hx`?p0mN z6?6?vol>bCg2YBe<>nM%DN{BuGgN4BU==eHHr~vPDH{k&+rX{_w+S|bDsdY_;($~x zUycT=TKg{zSO$m*8jY0?2{$lrC59Kx?ma0nQr(GGwH^+;Q5_ zA(Rsx0=k1I0Yia1j$=)df6x~DZMm!p!FlI7wnK*L4qLZ{cis7@=TLXpVM~em^rMak zxTQ<^UrM8Z#;x)KtGG#{K)d#+JuGsL{1!nx}+x)V)$k7x~| zoRfwFBon!sUNp;)62(z_MI}_sA)=flIgNUfsJWzaT!C%rw4cr0G$PN^9+Rm;2=i(l z^LoOk8?jILIB5_oL*2Nk1m(zBNFkh2N{gk}y}S;k&BrlhFr0>P9ocYrA?K2g08&Hb zelp{F0uPnWfs%dOPRKk_SpQ=Zq3}Eic7ludxWAk1t&yhMj z$bB46(t!GQwL)^;e-{e?D%1+>3?Gbxy7F5z!ozVG4}Y{=nV&>$r{9C}y>f5-8K)RF zt~h0=A^w!6$!3>5q4HR+@*`9YmA}%}m+;``6dd$`3kQP-^DU-%cb>dJCFW2@JM1!n zemkDo{bT6YgHKjm#s!dDN zei5jT6)7!m0}luR$n6Ffho%c3rH8F~7S6HOJUfenwrOAgW#I0kYHn-A0U+alu^a<|MG~Y7L0Pg8UxX*rWpMIeTK_Yk`HOtrSg^7|u2J2doA(p(_IZ-NCJ*K6rj+PR?dfr6aZ>FB4CcIZ| zExt9t-AC-!peoo&tW29>h`mG?~Wsy%2|Q7Q#cfT4Ln-pqV2|v01N{4{%p>YY8-6i?tUOB z+ugY4)kmDOkq_p%$O;T0^Wd<0!uzGMcF6YOcRdQul2Airn=M3yM5gC9+S4qC0R&mY z#CmTUOjwH)oaFeA2ZEl(Fa;F~chY|$D2+FrH1i?Sk}tP z|Di9b7gH|Xj&d=U_n5X2ope{)3dmTm8h}%gjSKt4M!h9x)Qd2-*{ByWnP^WIpA>p> zS>V(hvLGV>%i+c^5d9X&n#u$}XhWTu2(mF%48}-AT@!^We2;(thKCk#>v9afX6Qom z&PLAVz!$uaa-;KsievZ=p|hbho6vOTNYfH*xvz@kRYyEC&X17!AY!CJS&I&#maOk1 z;g#%Fpylv6z^#ceM68aMCY&nH!DN=~MGAQ_;mF#7Oq1D1T@ste?hrWW6mMdQt3lRi z)51=ecRN1EEFj6L2I7qnLTc1;Y9miFM6(HCbzcbr(aNAFTf3y96kDpr7-Hfcrsgtr zbMItb%6K0uznIpIyVTp%B|=Aa$%x>id&KnxG&Zny8cQAfnxZif_rIR;X6#;eyk!mZ zBCk~4lJ(PhqvjdEP8!)2)9I2wnV`-fOY%IR=*e6SJ3GC73&=_qM^{0Mqm#)>7KbE| zq{CIij@5wd_;40DBgL^FUTiUtzSBb?eyHdWz?}-+oS?qYC3neOh!y7BVbBM zXnFOc2{%|1us||@z0MmY^@9#2ln*2sd%)SB1>vE0njL9R(VOt9x$s#RbY|cI`_Ew) z5%0;nTjg>r*In-DF=)V~53X#nOuejTjJlAgP>C)!vlGJRX39kI@h0I!@$qR_H-NF1 zrgRiz#SIge*fG+ny)Ba9u~}r7MZXp_Kn+oi)54-e!6clR-#fT;C_3*$`Idcq4iz^A zGe7hDfRpDf@w@<`!DRRVVYht}{@CwFi*o*Pxk_|d&R>$-AH6^4|3=TlBykMIv>;Ea zL(wmC{-XRn?}<*#`yb1H?ETSq^ZvZ)_r0Hf6@xrln{cqQy6W*dw!)i9j=SF<1qJ^< zuKaQm<$wSE(X9o4P42$vFAM%~?&HyaEBF)n55GS;zUXhy-5=dw^hfi*`~K(~MSouJ z-L6vW%9P6Lkj&k_f5!Ct_B`StvPSq>z~ihw|o6JPTUWDeJyG3<{9Am!Q6^>6~CX%d0u-a{58V8neYz@ zcUd^PtIxkFygKj7c`tD)=PmPHIUkA3Ny1->%SpoDkIV6*u-{*wyCC{RzyHzdrwbKt zJ*9k=XAVyi{toZUd6q`k4fvlLe@n6AT|;m-kI$2Y(V2LN4byPQdnpq>pZ5_j{B(5D zpuhIQB-cJ-XJu0TjQ4D=e>y7MPSdls%piQ6@<;zP=r7Hmc4~CO6#phx(#NOxCk*ds zfsx_swyjrg*tB8u`e4K68{V+Z^Y%vneTqLbcOdE-@{i9SZi!AA@=wb3N1KQI6LM!q ze=x+yoA-TT$nPswmzFEuBxPuVMtG92#^4nS@B8^Q|BqTaE4>x(^@J(j!)HmazdEX~ z^n0WBS$==@Ha>3PgO~R_4a@?)_f0e(C9{TROmey`wpsWb;jqtaIdp7+H072Z7Wr+v|!SNP}U=I^`d6@GWF=hT6U zb!iVkH-qn|Mt9Hl7vtuS7p&WSHKTa-x|@R|#H-vd zL`&xQ$G2AoE8ZZ%+0o=2f7Q^L1O=0?yfWcw{HmG*(UQ6T z@x$j5)RMTE-y*-4@vBB1h~7NcKV?pmYbVhH*oXLSc~W2VL^D%l*!1%5uLy@0}U#U+ypM zNfNaKc$Vqz75+y{N$dyx=LL(vykFmh+^JG8og{n?@4~l9 zcn$9Yog{oN@7eTM5?+$|E?jm}CVVyTi!60;M}_YroNe$Y2n*Je z27l_P@co46Y4s)v9^gIOgZ+fF4f-tM?2vw$un<#{|M8>3PY}-bc$n3Hd?x)=!oDSl zetH_)(QBfPSNd-+y^1Yf5Pf9cz7M?8Z^`93%K$CnrT;Jl(&ncg9k;JwHl* zjqr@6(c*D7x1s3TasNh&d3xMm%1?jj&uZO$2*msM<>=F+K;Gr6{58=>LVt1lt`jTX zO!5ZNq0nConu!)q_*YjqEOf{-*nIsqmS}LzhIN|+Im!0&mgsX6{!_W$=HqHFJ&PULbt#o}dExG#G9`a;a`c&%{&?H+C2j|bzP8dIuHU#} z{pKsTy;yT_x*TepFbquekS-_7r|k1Rmq&Awgg?N$mRP{k!K0BngXc`1v!X|P{l4hn zDt~$#!A;95-gMDS=R|K=?GMj*AK~93{_FHhiz=CjkMKUri>^DxAC2C+#$P`Bx;Jdv zwjmvt0NlTA!}XhIEw325`w7{#SfY=v@n_S7B)*o9zDZ0e39l+dg>(Jd)Fga6^DFpo zgrn2X^%ry|;W>1rSIr&KqUnnM%Q^m%&Km%ZOUZNrmCjE;jz|Bx+OPB`kR!59RMFEb zqM7ITCk5lKYGP!r&he{i24G1 zjc|Ox^Af|mc(uRM5usmM>sLCTCFgolf0}1?^e=1unbk{Aa}=QJ4tS1T(A;E&C%Jqnnegd^ zRqnOX-LLUanVuB;S)%zrt>$q8V~$WL{DwcHGfDSB()iK%IDGPhXGR;w{mJOj-|$a_ zMf>@0_>0cE=d6k?si#TyS{{g;U_Ws*%1gdI!~4mZu+nOrlJDP)CV$h9P6}2!Uhy_Q z3HK%8ck!;FPr||?8lYfhwCJ_|Wx2@EsTcVpg`Q+`KLvSTCfP&L%1fZ~ z4(^-0#Q%qU{Z^uH;JJ+FJf2tb9M3b%)4}s^t1I4*c)ri`UwJ;yvxnycJn!PUndenJ zQ+aYcKR&zSJ;U>Dp0D!!DbKw;@8Y?I=LVijd0xY_{On52JD!&j9-rsPs*3kRp0D%# zInO?xd!v85%)hTbeQm|-1^-A9#Mk^VdAz<#~eVGd!Q*`4G>$c;3$QhBcK@Zv!tE^Q_`ImFEQp z=LCLd@yz7uT@$Uk!oPT;qm4zx^Utjn@8>)}=J^|*@9}(pw%%E5O^8u4!!_;e9*rrCFu+ws#WPt-!^l^57N% zj}Yko#)7uXct6B@*U~~)+l2(YDFk-sy4p@B5Ge53T>Icj1jZG(zJ2hy?&$k_{IA1t zKky0UAWudA`U!t){<5BE%P0L$TN7?()g?{X$$Q#_Jqn~vAdocSN#4^Y98n-?LK`bK zZ9<@cYr-9$^1n+HuDsuWF!$8H!3X?xg}y(&i1x?-rv5NLJm+HP%g`|+*0=y4M6yY{pG zTk>7I$)0;xuBY{4^6Za({-^$}od=044!4(o3$%E6-}Z-Lh;o&ECw{>{6%I1G?ooe! zX8`uk|7CmoP2jU-(S48lKLHo~-kA6gsz)qGj!xjvrF}p6GL5@>g{o zjHCXYsOM)!-Cy=sqUCYPh7nGk9lid`{;JL!;;2PL?TQ}yvcF-@BZ|7}{EoFm9p>HN zRhZ(nZzF^V)NA>z^E=k_zARdKz+W}z3Pq9lR-$(B?h@}Iv^P%tLEaBW4=VAIIPqtQ z8mc9UpC&X;NF{%r_bZ~>S4g~FQIz~6qV}YTD**PvIB|jZBl~uK#Xr)w=l)yO);M*=EA0!uGbwMbNP#6_7bx>@3OhNX0YL3C~T>JF6w%sYFdH% z_t3u5H~de{*!TIL`OAKmd;7kBdZuN@qMomUZ}Z-p*wobWEj?cJwa>L&9^Ka4I+A~? z5`DP0^-cNL^+eP9THl=eizw=AU3|iK;KP4=!*w@o8eMdYr?t54`pd6>sc%x~*HU-+FnR{kd*0 zTL0OWdAZZ0PY<;2%6%w0XRvi%{wv+l=D%qPqFsZnPer#*X*~&^|G_D(C*=Dp(IbD` zQj1!LxYKb|&43wS=l!S>EgEWF-o2L?*T<8W+~R#F`t70CD+YvwlD--VaQ%(Qc8(0S zelho%=%L}(a}Z|brnbHz*Y*_6yn5^PH$>s*T1KJ|jI>TK2{*ZMi9Y<-Ei&31A6C~*G3;+NC delta 136900 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 z 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 c$%P|*XXeJGQv0LwS)@<8c;w= TxResult { - let signed = tx_data; - let transfer = - token::Transfer::try_from_slice(&signed.data().unwrap()[..]).unwrap(); - log_string(format!("apply_tx called to mint tokens: {:#?}", transfer)); - let token::Transfer { - source: _, - target, - token, - sub_prefix: _, - amount, - key: _, - shielded: _, - } = transfer; - let target_key = token::balance_key(&token, &target); - let mut target_bal: token::Amount = - ctx.read(&target_key)?.unwrap_or_default(); - target_bal.receive(&amount.amount); - ctx.write(&target_key, target_bal)?; - Ok(()) - } -} - /// A VP that always returns `true`. #[cfg(feature = "vp_always_true")] pub mod main { From 60fc86cb48c0adf45b4754c764dacb47b8ca0e45 Mon Sep 17 00:00:00 2001 From: Gianmarco Fraccaroli Date: Thu, 20 Jul 2023 11:28:15 +0200 Subject: [PATCH 117/120] test: fix unit tests --- test_utils/src/lib.rs | 2 - tests/src/native_vp/pos.rs | 5 + tests/src/vm_host_env/mod.rs | 194 +++++++++++++++++++++++++---------- tests/src/vm_host_env/tx.rs | 50 ++++++--- 4 files changed, 178 insertions(+), 73 deletions(-) diff --git a/test_utils/src/lib.rs b/test_utils/src/lib.rs index 6b0352bcc7..a12fcbff17 100644 --- a/test_utils/src/lib.rs +++ b/test_utils/src/lib.rs @@ -18,7 +18,6 @@ pub const WASM_FOR_TESTS_DIR: &str = "wasm_for_tests"; #[derive(Debug, Clone, Copy, EnumIter)] pub enum TestWasms { TxMemoryLimit, - TxMintTokens, TxNoOp, TxProposalCode, TxReadStorageKey, @@ -36,7 +35,6 @@ impl TestWasms { pub fn path(&self) -> PathBuf { let filename = match self { TestWasms::TxMemoryLimit => "tx_memory_limit.wasm", - TestWasms::TxMintTokens => "tx_mint_tokens.wasm", TestWasms::TxNoOp => "tx_no_op.wasm", TestWasms::TxProposalCode => "tx_proposal_code.wasm", TestWasms::TxReadStorageKey => "tx_read_storage_key.wasm", diff --git a/tests/src/native_vp/pos.rs b/tests/src/native_vp/pos.rs index b76b4dd041..077385dc61 100644 --- a/tests/src/native_vp/pos.rs +++ b/tests/src/native_vp/pos.rs @@ -118,6 +118,11 @@ pub fn init_pos( tx_env.spawn_accounts([&native_token]); for validator in genesis_validators { tx_env.spawn_accounts([&validator.address]); + tx_env.init_account_storage( + &validator.address, + vec![validator.consensus_key.clone()], + 1, + ) } tx_env.wl_storage.storage.block.epoch = start_epoch; // Initialize PoS storage diff --git a/tests/src/vm_host_env/mod.rs b/tests/src/vm_host_env/mod.rs index 1766d7bad3..3b2cec2c2e 100644 --- a/tests/src/vm_host_env/mod.rs +++ b/tests/src/vm_host_env/mod.rs @@ -27,7 +27,7 @@ mod tests { get_dummy_header as tm_dummy_header, Error as IbcError, }; use namada::ledger::tx_env::TxEnv; - use namada::proto::{Code, Data, Section, Signature, Tx}; + use namada::proto::{Code, Data, Section, Tx}; use namada::types::hash::Hash; use namada::types::key::*; use namada::types::storage::{self, BlockHash, BlockHeight, Key, KeySeg}; @@ -37,8 +37,10 @@ mod tests { use namada::types::{address, key}; use namada_core::ledger::ibc::context::transfer_mod::testing::DummyTransferModule; use namada_core::ledger::ibc::Error as IbcActionError; + use namada_core::proto::MultiSignature; use namada_test_utils::TestWasms; use namada_tx_prelude::{BorshSerialize, StorageRead, StorageWrite}; + use namada_vp_prelude::account::AccountPublicKeysMap; use namada_vp_prelude::VpEnv; use prost::Message; use test_log::test; @@ -436,12 +438,11 @@ mod tests { let addr = address::testing::established_address_1(); // Write the public key to storage - let pk_key = key::pk_key(&addr); let keypair = key::testing::keypair_1(); let pk = keypair.ref_to(); - env.wl_storage - .write(&pk_key, pk.try_to_vec().unwrap()) - .unwrap(); + + let _ = pks_handle(&addr).insert(&mut env.wl_storage, 0_u8, pk.clone()); + // Initialize the environment vp_host_env::set(env); @@ -454,15 +455,19 @@ mod tests { // Tx without any data vec![], ] { + let keypairs = vec![keypair.clone()]; + let pks_map = AccountPublicKeysMap::from_iter(vec![pk.clone()]); let signed_tx_data = vp_host_env::with(|env| { let mut tx = Tx::new(TxType::Raw); tx.header.chain_id = env.wl_storage.storage.chain_id.clone(); tx.header.expiration = expiration; tx.set_code(Code::new(code.clone())); tx.set_data(Data::new(data.clone())); - tx.add_section(Section::Signature(Signature::new( + + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.code_sechash(), *tx.data_sechash()], - &keypair, + &keypairs, + &pks_map, ))); env.tx = tx; env.tx.clone() @@ -470,12 +475,14 @@ mod tests { assert_eq!(signed_tx_data.data().as_ref(), Some(data)); assert!( signed_tx_data - .verify_signature( - &pk, + .verify_section_signatures( &[ *signed_tx_data.data_sechash(), *signed_tx_data.code_sechash(), ], + pks_map, + 1, + None ) .is_ok() ); @@ -483,12 +490,16 @@ mod tests { let other_keypair = key::testing::keypair_2(); assert!( signed_tx_data - .verify_signature( - &other_keypair.ref_to(), + .verify_section_signatures( &[ *signed_tx_data.data_sechash(), *signed_tx_data.code_sechash(), ], + AccountPublicKeysMap::from_iter([ + other_keypair.ref_to() + ]), + 1, + None ) .is_err() ); @@ -545,9 +556,12 @@ mod tests { let mut tx = Tx::new(TxType::Raw); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(input_data)); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.code_sechash(), *tx.data_sechash()], - &key::testing::keypair_1(), + &[key::testing::keypair_1()], + &AccountPublicKeysMap::from_iter([ + key::testing::keypair_1().ref_to() + ]), ))); let result = vp::CTX.eval(empty_code, tx).unwrap(); assert!(!result); @@ -564,9 +578,12 @@ mod tests { let mut tx = Tx::new(TxType::Raw); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(input_data)); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.code_sechash(), *tx.data_sechash()], - &key::testing::keypair_1(), + &[key::testing::keypair_1()], + &AccountPublicKeysMap::from_iter([ + key::testing::keypair_1().ref_to() + ]), ))); let result = vp::CTX.eval(code_hash, tx).unwrap(); assert!(result); @@ -584,9 +601,12 @@ mod tests { let mut tx = Tx::new(TxType::Raw); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(input_data)); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.code_sechash(), *tx.data_sechash()], - &key::testing::keypair_1(), + &[key::testing::keypair_1()], + &AccountPublicKeysMap::from_iter([ + key::testing::keypair_1().ref_to() + ]), ))); let result = vp::CTX.eval(code_hash, tx).unwrap(); assert!(!result); @@ -606,9 +626,12 @@ mod tests { let mut tx = Tx::new(TxType::Raw); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.code_sechash(), *tx.data_sechash()], - &key::testing::keypair_1(), + &[key::testing::keypair_1()], + &AccountPublicKeysMap::from_iter([ + key::testing::keypair_1().ref_to() + ]), ))); // create a client with the message @@ -642,9 +665,12 @@ mod tests { let mut tx = Tx::new(TxType::Raw); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.code_sechash(), *tx.data_sechash()], - &key::testing::keypair_1(), + &[key::testing::keypair_1()], + &AccountPublicKeysMap::from_iter([ + key::testing::keypair_1().ref_to() + ]), ))); // update the client with the message tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -681,9 +707,12 @@ mod tests { let mut tx = Tx::new(TxType::Raw); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.code_sechash(), *tx.data_sechash()], - &key::testing::keypair_1(), + &[key::testing::keypair_1()], + &AccountPublicKeysMap::from_iter([ + key::testing::keypair_1().ref_to() + ]), ))); // init a connection with the message tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -716,9 +745,12 @@ mod tests { let mut tx = Tx::new(TxType::Raw); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.code_sechash(), *tx.data_sechash()], - &key::testing::keypair_1(), + &[key::testing::keypair_1()], + &AccountPublicKeysMap::from_iter([ + key::testing::keypair_1().ref_to() + ]), ))); // open the connection with the message tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -756,9 +788,12 @@ mod tests { let mut tx = Tx::new(TxType::Raw); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.code_sechash(), *tx.data_sechash()], - &key::testing::keypair_1(), + &[key::testing::keypair_1()], + &AccountPublicKeysMap::from_iter([ + key::testing::keypair_1().ref_to() + ]), ))); // open try a connection with the message tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -791,9 +826,12 @@ mod tests { let mut tx = Tx::new(TxType::Raw); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.code_sechash(), *tx.data_sechash()], - &key::testing::keypair_1(), + &[key::testing::keypair_1()], + &AccountPublicKeysMap::from_iter([ + key::testing::keypair_1().ref_to() + ]), ))); // open the connection with the mssage tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -833,9 +871,12 @@ mod tests { let mut tx = Tx::new(TxType::Raw); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.code_sechash(), *tx.data_sechash()], - &key::testing::keypair_1(), + &[key::testing::keypair_1()], + &AccountPublicKeysMap::from_iter([ + key::testing::keypair_1().ref_to() + ]), ))); // init a channel with the message tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -868,9 +909,12 @@ mod tests { let mut tx = Tx::new(TxType::Raw); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.code_sechash(), *tx.data_sechash()], - &key::testing::keypair_1(), + &[key::testing::keypair_1()], + &AccountPublicKeysMap::from_iter([ + key::testing::keypair_1().ref_to() + ]), ))); // open the channle with the message tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -910,9 +954,12 @@ mod tests { let mut tx = Tx::new(TxType::Raw); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.code_sechash(), *tx.data_sechash()], - &key::testing::keypair_1(), + &[key::testing::keypair_1()], + &AccountPublicKeysMap::from_iter([ + key::testing::keypair_1().ref_to() + ]), ))); // try open a channel with the message tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -946,9 +993,12 @@ mod tests { let mut tx = Tx::new(TxType::Raw); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.code_sechash(), *tx.data_sechash()], - &key::testing::keypair_1(), + &[key::testing::keypair_1()], + &AccountPublicKeysMap::from_iter([ + key::testing::keypair_1().ref_to() + ]), ))); // open a channel with the message tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -991,9 +1041,12 @@ mod tests { let mut tx = Tx::new(TxType::Raw); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.code_sechash(), *tx.data_sechash()], - &key::testing::keypair_1(), + &[key::testing::keypair_1()], + &AccountPublicKeysMap::from_iter([ + key::testing::keypair_1().ref_to() + ]), ))); // close the channel with the message let mut actions = tx_host_env::ibc::ibc_actions(tx::ctx()); @@ -1044,9 +1097,12 @@ mod tests { let mut tx = Tx::new(TxType::Raw); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.code_sechash(), *tx.data_sechash()], - &key::testing::keypair_1(), + &[key::testing::keypair_1()], + &AccountPublicKeysMap::from_iter([ + key::testing::keypair_1().ref_to() + ]), ))); // close the channel with the message @@ -1094,9 +1150,12 @@ mod tests { let mut tx = Tx::new(TxType::Raw); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.code_sechash(), *tx.data_sechash()], - &key::testing::keypair_1(), + &[key::testing::keypair_1()], + &AccountPublicKeysMap::from_iter([ + key::testing::keypair_1().ref_to() + ]), ))); // send the token and a packet with the data tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -1143,9 +1202,12 @@ mod tests { let mut tx = Tx::new(TxType::Raw); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.code_sechash(), *tx.data_sechash()], - &key::testing::keypair_1(), + &[key::testing::keypair_1()], + &AccountPublicKeysMap::from_iter([ + key::testing::keypair_1().ref_to() + ]), ))); // ack the packet with the message tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -1219,9 +1281,12 @@ mod tests { let mut tx = Tx::new(TxType::Raw); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.code_sechash(), *tx.data_sechash()], - &key::testing::keypair_1(), + &[key::testing::keypair_1()], + &AccountPublicKeysMap::from_iter([ + key::testing::keypair_1().ref_to() + ]), ))); // send the token and a packet with the data tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -1295,9 +1360,12 @@ mod tests { let mut tx = Tx::new(TxType::Raw); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.code_sechash(), *tx.data_sechash()], - &key::testing::keypair_1(), + &[key::testing::keypair_1()], + &AccountPublicKeysMap::from_iter([ + key::testing::keypair_1().ref_to() + ]), ))); // receive a packet with the message tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -1383,9 +1451,12 @@ mod tests { let mut tx = Tx::new(TxType::Raw); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.code_sechash(), *tx.data_sechash()], - &key::testing::keypair_1(), + &[key::testing::keypair_1()], + &AccountPublicKeysMap::from_iter([ + key::testing::keypair_1().ref_to() + ]), ))); // receive a packet with the message tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -1475,9 +1546,12 @@ mod tests { let mut tx = Tx::new(TxType::Raw); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.code_sechash(), *tx.data_sechash()], - &key::testing::keypair_1(), + &[key::testing::keypair_1()], + &AccountPublicKeysMap::from_iter([ + key::testing::keypair_1().ref_to() + ]), ))); // receive a packet with the message tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -1570,9 +1644,12 @@ mod tests { let mut tx = Tx::new(TxType::Raw); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.code_sechash(), *tx.data_sechash()], - &key::testing::keypair_1(), + &[key::testing::keypair_1()], + &AccountPublicKeysMap::from_iter([ + key::testing::keypair_1().ref_to() + ]), ))); // timeout the packet @@ -1655,9 +1732,12 @@ mod tests { let mut tx = Tx::new(TxType::Raw); tx.set_code(Code::new(vec![])); tx.set_data(Data::new(tx_data.clone())); - tx.add_section(Section::Signature(Signature::new( + tx.add_section(Section::SectionSignature(MultiSignature::new( vec![*tx.code_sechash(), *tx.data_sechash()], - &key::testing::keypair_1(), + &[key::testing::keypair_1()], + &AccountPublicKeysMap::from_iter([ + key::testing::keypair_1().ref_to() + ]), ))); // timeout the packet diff --git a/tests/src/vm_host_env/tx.rs b/tests/src/vm_host_env/tx.rs index c9ab18a08a..3f16d3554d 100644 --- a/tests/src/vm_host_env/tx.rs +++ b/tests/src/vm_host_env/tx.rs @@ -18,7 +18,8 @@ use namada::vm::prefix_iter::PrefixIterators; use namada::vm::wasm::run::Error; use namada::vm::wasm::{self, TxCache, VpCache}; use namada::vm::{self, WasmCacheRwAccess}; -use namada_tx_prelude::{BorshSerialize, Ctx}; +use namada_tx_prelude::{storage_api, BorshSerialize, Ctx}; +use namada_vp_prelude::key::common; use tempfile::TempDir; use crate::vp::TestVpEnv; @@ -102,6 +103,7 @@ impl TestTxEnv { epoch_duration: Option, vp_whitelist: Option>, tx_whitelist: Option>, + max_signatures_per_transaction: Option, ) { parameters::update_epoch_parameter( &mut self.wl_storage, @@ -121,6 +123,11 @@ impl TestTxEnv { vp_whitelist.unwrap_or_default(), ) .unwrap(); + parameters::update_max_signature_per_tx( + &mut self.wl_storage, + max_signatures_per_transaction.unwrap_or(15), + ) + .unwrap(); } pub fn store_wasm_code(&mut self, code: Vec) { @@ -155,6 +162,34 @@ impl TestTxEnv { } } + pub fn init_account_storage( + &mut self, + owner: &Address, + public_keys: Vec, + threshold: u8, + ) { + storage_api::account::init_account_storage( + &mut self.wl_storage, + owner, + &public_keys, + threshold, + ) + .expect("Unable to write Account substorage."); + } + + /// Set public key for the address. + pub fn write_account_threshold( + &mut self, + address: &Address, + threshold: u8, + ) { + let storage_key = key::threshold_key(address); + self.wl_storage + .storage + .write(&storage_key, threshold.try_to_vec().unwrap()) + .unwrap(); + } + /// Commit the genesis state. Typically, you'll want to call this after /// setting up the initial state, before running a transaction. pub fn commit_genesis(&mut self) { @@ -194,19 +229,6 @@ impl TestTxEnv { .unwrap(); } - /// Set public key for the address. - pub fn write_public_key( - &mut self, - address: &Address, - public_key: &key::common::PublicKey, - ) { - let storage_key = key::pk_key(address); - self.wl_storage - .storage - .write(&storage_key, public_key.try_to_vec().unwrap()) - .unwrap(); - } - /// Apply the tx changes to the write log. pub fn execute_tx(&mut self) -> Result<(), Error> { wasm::run::tx( From 85c7559cc51c3dfae3dff146c40711ef842dd679 Mon Sep 17 00:00:00 2001 From: Gianmarco Fraccaroli Date: Thu, 20 Jul 2023 11:28:33 +0200 Subject: [PATCH 118/120] test: fix e2e test --- .github/workflows/scripts/e2e.json | 1 + genesis/e2e-tests-single-node.toml | 4 +- tests/src/e2e/eth_bridge_tests.rs | 8 +- tests/src/e2e/ibc_tests.rs | 62 ++++-- tests/src/e2e/ledger_tests.rs | 220 +++++++++++++++------- tests/src/e2e/multitoken_tests.rs | 20 +- tests/src/e2e/multitoken_tests/helpers.rs | 13 +- tests/src/e2e/setup.rs | 1 + 8 files changed, 222 insertions(+), 107 deletions(-) diff --git a/.github/workflows/scripts/e2e.json b/.github/workflows/scripts/e2e.json index 74b08e0fb8..bd516e8932 100644 --- a/.github/workflows/scripts/e2e.json +++ b/.github/workflows/scripts/e2e.json @@ -10,6 +10,7 @@ "e2e::ledger_tests::masp_pinned_txs": 75, "e2e::ledger_tests::masp_txs_and_queries": 282, "e2e::ledger_tests::pos_bonds": 77, + "e2e::ledger_tests::implicit_account_reveal_pk": 30, "e2e::ledger_tests::pos_init_validator": 40, "e2e::ledger_tests::proposal_offline": 21, "e2e::ledger_tests::pgf_governance_proposal": 100, diff --git a/genesis/e2e-tests-single-node.toml b/genesis/e2e-tests-single-node.toml index beab8edc2f..b17ab94e8b 100644 --- a/genesis/e2e-tests-single-node.toml +++ b/genesis/e2e-tests-single-node.toml @@ -4,7 +4,7 @@ genesis_time = "2021-09-30T10:00:00Z" native_token = "NAM" -faucet_pow_difficulty = 1 +faucet_pow_difficulty = 0 faucet_withdrawal_limit = "1000000000" [validator.validator-0] @@ -183,6 +183,8 @@ epochs_per_year = 31_536_000 pos_gain_p = "0.1" # The D gain factor in the Proof of Stake rewards controller pos_gain_d = "0.1" +# The maximum number of signatures allowed per transaction +max_signatures_per_transaction = 15 # Proof of stake parameters. [pos_params] diff --git a/tests/src/e2e/eth_bridge_tests.rs b/tests/src/e2e/eth_bridge_tests.rs index 1d15c5323a..e0cd8c5192 100644 --- a/tests/src/e2e/eth_bridge_tests.rs +++ b/tests/src/e2e/eth_bridge_tests.rs @@ -144,8 +144,8 @@ async fn test_roundtrip_eth_transfer() -> Result<()> { "add-erc20-transfer", "--address", BERTHA, - "--signer", - BERTHA, + "--signing-keys", + BERTHA_KEY, "--amount", &amount, "--erc20", @@ -335,8 +335,8 @@ async fn test_bridge_pool_e2e() { "add-erc20-transfer", "--address", BERTHA, - "--signer", - BERTHA, + "--signing-keys", + BERTHA_KEY, "--amount", "100", "--erc20", diff --git a/tests/src/e2e/ibc_tests.rs b/tests/src/e2e/ibc_tests.rs index e071c2bd13..26947eb2ec 100644 --- a/tests/src/e2e/ibc_tests.rs +++ b/tests/src/e2e/ibc_tests.rs @@ -88,6 +88,7 @@ use namada_apps::facade::tendermint_rpc::{Client, HttpClient, Url}; use prost::Message; use setup::constants::*; +use super::helpers::wait_for_wasm_pre_compile; use super::setup::set_ethereum_bridge_mode; use crate::e2e::helpers::{find_address, get_actor_rpc, get_validator_pk}; use crate::e2e::setup::{self, sleep, Bin, NamadaCmd, Test, Who}; @@ -132,6 +133,9 @@ fn run_ledger_ibc() -> Result<()> { ledger_a.exp_string("This node is a validator")?; ledger_b.exp_string("This node is a validator")?; + wait_for_wasm_pre_compile(&mut ledger_a)?; + wait_for_wasm_pre_compile(&mut ledger_b)?; + // Wait for a first block ledger_a.exp_string("Committed block hash")?; ledger_b.exp_string("Committed block hash")?; @@ -216,7 +220,7 @@ fn create_client(test_a: &Test, test_b: &Test) -> Result<(ClientId, ClientId)> { consensus_state: make_consensus_state(test_b, height)?.into(), signer: Signer::from_str("test_a").expect("invalid signer"), }; - let height_a = submit_ibc_tx(test_a, message, ALBERT)?; + let height_a = submit_ibc_tx(test_a, message, ALBERT, ALBERT_KEY, false)?; let height = query_height(test_a)?; let client_state = make_client_state(test_a, height); @@ -226,7 +230,7 @@ fn create_client(test_a: &Test, test_b: &Test) -> Result<(ClientId, ClientId)> { consensus_state: make_consensus_state(test_a, height)?.into(), signer: Signer::from_str("test_b").expect("invalid signer"), }; - let height_b = submit_ibc_tx(test_b, message, ALBERT)?; + let height_b = submit_ibc_tx(test_b, message, ALBERT, ALBERT_KEY, false)?; // convert the client IDs from `ibc_relayer_type` to `ibc` let client_id_a = match get_event(test_a, height_a)? { @@ -356,7 +360,7 @@ fn update_client( client_id: client_id.clone(), signer: Signer::from_str("test").expect("invalid signer"), }; - submit_ibc_tx(target_test, message, ALBERT)?; + submit_ibc_tx(target_test, message, ALBERT, ALBERT_KEY, false)?; } let message = MsgUpdateClient { @@ -364,7 +368,7 @@ fn update_client( client_id: client_id.clone(), signer: Signer::from_str("test").expect("invalid signer"), }; - submit_ibc_tx(target_test, message, ALBERT)?; + submit_ibc_tx(target_test, message, ALBERT, ALBERT_KEY, false)?; check_ibc_update_query( target_test, @@ -428,7 +432,7 @@ fn connection_handshake( signer: Signer::from_str("test_a").expect("invalid signer"), }; // OpenInitConnection on Chain A - let height = submit_ibc_tx(test_a, msg, ALBERT)?; + let height = submit_ibc_tx(test_a, msg, ALBERT, ALBERT_KEY, false)?; let conn_id_a = match get_event(test_a, height)? { Some(IbcEvent::OpenInitConnection(event)) => event .connection_id() @@ -459,7 +463,7 @@ fn connection_handshake( // Update the client state of Chain A on Chain B update_client_with_height(test_a, test_b, client_id_b, height_a)?; // OpenTryConnection on Chain B - let height = submit_ibc_tx(test_b, msg, ALBERT)?; + let height = submit_ibc_tx(test_b, msg, ALBERT, ALBERT_KEY, false)?; let conn_id_b = match get_event(test_b, height)? { Some(IbcEvent::OpenTryConnection(event)) => event .connection_id() @@ -483,7 +487,7 @@ fn connection_handshake( // Update the client state of Chain B on Chain A update_client_with_height(test_b, test_a, client_id_a, height_b)?; // OpenAckConnection on Chain A - submit_ibc_tx(test_a, msg, ALBERT)?; + submit_ibc_tx(test_a, msg, ALBERT, ALBERT_KEY, false)?; // get the proofs on Chain A let height_a = query_height(test_a)?; @@ -497,7 +501,7 @@ fn connection_handshake( // Update the client state of Chain A on Chain B update_client_with_height(test_a, test_b, client_id_b, height_a)?; // OpenConfirmConnection on Chain B - submit_ibc_tx(test_b, msg, ALBERT)?; + submit_ibc_tx(test_b, msg, ALBERT, ALBERT_KEY, false)?; Ok((conn_id_a, conn_id_b)) } @@ -553,7 +557,7 @@ fn channel_handshake( channel, signer: Signer::from_str("test_a").expect("invalid signer"), }; - let height = submit_ibc_tx(test_a, msg, ALBERT)?; + let height = submit_ibc_tx(test_a, msg, ALBERT, ALBERT_KEY, false)?; let channel_id_a = match get_event(test_a, height)? { Some(IbcEvent::OpenInitChannel(event)) => event @@ -589,7 +593,7 @@ fn channel_handshake( // Update the client state of Chain A on Chain B update_client_with_height(test_a, test_b, client_id_b, height_a)?; // OpenTryChannel on Chain B - let height = submit_ibc_tx(test_b, msg, ALBERT)?; + let height = submit_ibc_tx(test_b, msg, ALBERT, ALBERT_KEY, false)?; let channel_id_b = match get_event(test_b, height)? { Some(IbcEvent::OpenTryChannel(event)) => event .channel_id() @@ -615,7 +619,7 @@ fn channel_handshake( // Update the client state of Chain B on Chain A update_client_with_height(test_b, test_a, client_id_a, height_b)?; // OpenAckChannel on Chain A - submit_ibc_tx(test_a, msg, ALBERT)?; + submit_ibc_tx(test_a, msg, ALBERT, ALBERT_KEY, false)?; // get the proofs on Chain A let height_a = query_height(test_a)?; @@ -630,7 +634,7 @@ fn channel_handshake( // Update the client state of Chain A on Chain B update_client_with_height(test_a, test_b, client_id_b, height_a)?; // OpenConfirmChannel on Chain B - submit_ibc_tx(test_b, msg, ALBERT)?; + submit_ibc_tx(test_b, msg, ALBERT, ALBERT_KEY, false)?; Ok((port_channel_id_a, port_channel_id_b)) } @@ -716,9 +720,11 @@ fn transfer_token( &receiver, NAM, &Amount::native_whole(100000), + ALBERT_KEY, port_channel_id_a, None, None, + false, )?; let packet = match get_event(test_a, height)? { Some(IbcEvent::SendPacket(event)) => event.packet, @@ -736,7 +742,7 @@ fn transfer_token( // Update the client state of Chain A on Chain B update_client_with_height(test_a, test_b, client_id_b, height_a)?; // Receive the token on Chain B - let height = submit_ibc_tx(test_b, msg, ALBERT)?; + let height = submit_ibc_tx(test_b, msg, ALBERT, ALBERT_KEY, false)?; let (acknowledgement, packet) = match get_event(test_b, height)? { Some(IbcEvent::WriteAcknowledgement(event)) => { (event.ack, event.packet) @@ -761,7 +767,7 @@ fn transfer_token( // Update the client state of Chain B on Chain A update_client_with_height(test_b, test_a, client_id_a, height_b)?; // Acknowledge on Chain A - submit_ibc_tx(test_a, msg, ALBERT)?; + submit_ibc_tx(test_a, msg, ALBERT, ALBERT_KEY, false)?; Ok(()) } @@ -842,9 +848,11 @@ fn transfer_back( &receiver, NAM, &Amount::native_whole(50000), + BERTHA_KEY, port_channel_id_b, Some(sub_prefix), None, + false, )?; let packet = match get_event(test_b, height)? { Some(IbcEvent::SendPacket(event)) => event.packet, @@ -861,7 +869,7 @@ fn transfer_back( // Update the client state of Chain B on Chain A update_client_with_height(test_b, test_a, client_id_a, height_b)?; // Receive the token on Chain A - let height = submit_ibc_tx(test_a, msg, ALBERT)?; + let height = submit_ibc_tx(test_a, msg, ALBERT, ALBERT_KEY, false)?; let (acknowledgement, packet) = match get_event(test_a, height)? { Some(IbcEvent::WriteAcknowledgement(event)) => { (event.ack, event.packet) @@ -881,7 +889,7 @@ fn transfer_back( // Update the client state of Chain A on Chain B update_client_with_height(test_a, test_b, client_id_b, height_a)?; // Acknowledge on Chain B - submit_ibc_tx(test_b, msg, ALBERT)?; + submit_ibc_tx(test_b, msg, ALBERT, ALBERT_KEY, false)?; Ok(()) } @@ -901,9 +909,11 @@ fn transfer_timeout( &receiver, NAM, &Amount::native_whole(100000), + ALBERT_KEY, port_channel_id_a, None, Some(Duration::new(5, 0)), + false, )?; let packet = match get_event(test_a, height)? { Some(IbcEvent::SendPacket(event)) => event.packet, @@ -924,7 +934,7 @@ fn transfer_timeout( // Update the client state of Chain B on Chain A update_client_with_height(test_b, test_a, client_id_a, height_b)?; // Timeout on Chain A - submit_ibc_tx(test_a, msg, ALBERT)?; + submit_ibc_tx(test_a, msg, ALBERT, ALBERT_KEY, false)?; Ok(()) } @@ -994,7 +1004,9 @@ fn commitment_prefix() -> CommitmentPrefix { fn submit_ibc_tx( test: &Test, message: impl Msg + std::fmt::Debug, + owner: &str, signer: &str, + wait_reveal_pk: bool, ) -> Result { let data_path = test.test_dir.path().join("tx.data"); let data = make_ibc_data(message); @@ -1011,7 +1023,9 @@ fn submit_ibc_tx( TX_IBC_WASM, "--data-path", &data_path, - "--signer", + "--owner", + owner, + "--signing-keys", signer, "--gas-amount", "0", @@ -1025,6 +1039,9 @@ fn submit_ibc_tx( Some(40) )?; client.exp_string("Transaction applied")?; + if wait_reveal_pk { + client.exp_string("Transaction applied")?; + } check_tx_height(test, &mut client) } @@ -1035,9 +1052,11 @@ fn transfer( receiver: &Address, token: impl AsRef, amount: &Amount, + signer: impl AsRef, port_channel_id: &PortChannelId, sub_prefix: Option, timeout_sec: Option, + wait_reveal_pk: bool, ) -> Result { let rpc = get_actor_rpc(test, &Who::Validator(0)); @@ -1051,8 +1070,8 @@ fn transfer( sender.as_ref(), "--receiver", &receiver, - "--signer", - sender.as_ref(), + "--signing-keys", + signer.as_ref(), "--token", token.as_ref(), "--amount", @@ -1077,6 +1096,9 @@ fn transfer( let mut client = run!(test, Bin::Client, tx_args, Some(40))?; client.exp_string("Transaction applied")?; + if wait_reveal_pk { + client.exp_string("Transaction applied")?; + } check_tx_height(test, &mut client) } diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index ca7bd974c7..fe8e25294a 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -154,6 +154,8 @@ fn test_node_connectivity_and_consensus() -> Result<()> { "0", "--gas-token", NAM, + "--signing-keys", + BERTHA_KEY, "--node", &validator_one_rpc, ]; @@ -456,6 +458,8 @@ fn ledger_txs_and_queries() -> Result<()> { "0", "--gas-token", NAM, + "--signing-keys", + BERTHA_KEY, "--node", &validator_one_rpc, ], @@ -476,6 +480,8 @@ fn ledger_txs_and_queries() -> Result<()> { "0", "--gas-token", NAM, + "--signing-keys", + DAEWON, "--node", &validator_one_rpc, ], @@ -502,44 +508,46 @@ fn ledger_txs_and_queries() -> Result<()> { // 3. Submit a transaction to update an account's validity // predicate vec![ - "update", - "--address", - BERTHA, - "--code-path", - VP_USER_WASM, - "--gas-amount", - "0", - "--gas-limit", - "0", - "--gas-token", - NAM, + "update-account", + "--address", + BERTHA, + "--code-path", + VP_USER_WASM, + "--gas-amount", + "0", + "--gas-limit", + "0", + "--gas-token", + NAM, + "--signing-keys", + BERTHA_KEY, "--node", &validator_one_rpc, ], // 4. Submit a custom tx vec![ "tx", - "--signer", - BERTHA, "--code-path", TX_TRANSFER_WASM, "--data-path", &tx_data_path, + "--owner", + BERTHA, "--gas-amount", "0", "--gas-limit", "0", "--gas-token", NAM, + "--signing-keys", + BERTHA_KEY, "--node", &validator_one_rpc ], // 5. Submit a tx to initialize a new account vec![ "init-account", - "--source", - BERTHA, - "--public-key", + "--public-keys", // Value obtained from `namada::types::key::ed25519::tests::gen_keypair` "001be519a321e29020fa3cbfbfd01bd5e92db134305609270b71dace25b5a21168", "--code-path", @@ -552,11 +560,13 @@ fn ledger_txs_and_queries() -> Result<()> { "0", "--gas-token", NAM, + "--signing-keys", + BERTHA_KEY, "--node", &validator_one_rpc, ], - // 6. Submit a tx to withdraw from faucet account (requires PoW challenge - // solution) + // 6. Submit a tx to withdraw from faucet account (requires PoW challenge + // solution) vec![ "transfer", "--source", @@ -568,8 +578,8 @@ fn ledger_txs_and_queries() -> Result<()> { "--amount", "10.1", // Faucet withdrawal requires an explicit signer - "--signer", - ALBERT, + "--signing-keys", + ALBERT_KEY, "--node", &validator_one_rpc, ], @@ -672,7 +682,7 @@ fn masp_txs_and_queries() -> Result<()> { |genesis| { let parameters = ParametersConfig { epochs_per_year: epochs_per_year_from_min_duration( - if is_debug_mode() { 3600 } else { 360 }, + if is_debug_mode() { 1440 } else { 360 }, ), min_num_of_blocks: 1, ..genesis.parameters @@ -702,8 +712,7 @@ fn masp_txs_and_queries() -> Result<()> { let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); - let _ep1 = epoch_sleep(&test, &validator_one_rpc, 720)?; - + // the extra argument in the tuple is for flagging extra checks below let txs_args = vec![ // 2. Attempt to spend 10 BTC at SK(A) to PA(B) ( @@ -717,10 +726,12 @@ fn masp_txs_and_queries() -> Result<()> { BTC, "--amount", "10", + "--signing-keys", + ALBERT_KEY, "--node", &validator_one_rpc, ], - "No balance found", + ("No balance found", false), ), // 3. Attempt to spend 15 BTC at SK(A) to Bertha ( @@ -734,10 +745,12 @@ fn masp_txs_and_queries() -> Result<()> { BTC, "--amount", "15", + "--signing-keys", + ALBERT_KEY, "--node", &validator_one_rpc, ], - "No balance found", + ("No balance found", false), ), // 4. Send 20 BTC from Albert to PA(A) ( @@ -751,10 +764,12 @@ fn masp_txs_and_queries() -> Result<()> { BTC, "--amount", "20", + "--signing-keys", + ALBERT_KEY, "--node", &validator_one_rpc, ], - "Transaction is valid", + ("Transaction is valid", false), ), // 5. Attempt to spend 10 ETH at SK(A) to PA(B) ( @@ -768,10 +783,25 @@ fn masp_txs_and_queries() -> Result<()> { ETH, "--amount", "10", + "--signing-keys", + ALBERT_KEY, "--node", &validator_one_rpc, ], - "No balance found", + ("No balance found", false), + ), + // 10. Assert BTC balance at VK(A) is 0 + ( + vec![ + "balance", + "--owner", + AA_VIEWING_KEY, + "--token", + BTC, + "--node", + &validator_one_rpc, + ], + ("btc: 20", false), ), // 6. Spend 7 BTC at SK(A) to PA(B) ( @@ -785,10 +815,12 @@ fn masp_txs_and_queries() -> Result<()> { BTC, "--amount", "7", + "--signing-keys", + ALBERT_KEY, "--node", &validator_one_rpc, ], - "Transaction is valid", + ("Transaction is valid", false), ), // 7. Spend 7 BTC at SK(A) to PA(B) ( @@ -805,7 +837,7 @@ fn masp_txs_and_queries() -> Result<()> { "--node", &validator_one_rpc, ], - "Transaction is valid", + ("Transaction is valid", false), ), // 8. Attempt to spend 7 BTC at SK(A) to PA(B) ( @@ -822,7 +854,7 @@ fn masp_txs_and_queries() -> Result<()> { "--node", &validator_one_rpc, ], - "is lower than the amount to be transferred and fees", + ("is lower than the amount to be transferred and fees", false), ), // 9. Spend 6 BTC at SK(A) to PA(B) ( @@ -839,7 +871,7 @@ fn masp_txs_and_queries() -> Result<()> { "--node", &validator_one_rpc, ], - "Transaction is valid", + ("Transaction is valid", false), ), // 10. Assert BTC balance at VK(A) is 0 ( @@ -852,7 +884,7 @@ fn masp_txs_and_queries() -> Result<()> { "--node", &validator_one_rpc, ], - "No shielded btc balance found", + ("No shielded btc balance found", false), ), // 11. Assert ETH balance at VK(A) is 0 ( @@ -865,7 +897,7 @@ fn masp_txs_and_queries() -> Result<()> { "--node", &validator_one_rpc, ], - "No shielded eth balance found", + ("No shielded eth balance found", false), ), // 12. Assert balance at VK(B) is 10 BTC ( @@ -876,7 +908,7 @@ fn masp_txs_and_queries() -> Result<()> { "--node", &validator_one_rpc, ], - "btc : 20", + ("btc : 20", false), ), // 13. Send 10 BTC from SK(B) to Bertha ( @@ -893,11 +925,14 @@ fn masp_txs_and_queries() -> Result<()> { "--node", &validator_one_rpc, ], - "Transaction is valid", + ("Transaction is valid", false), ), ]; - for (tx_args, tx_result) in &txs_args { + // Wait till epoch boundary + let _ep0 = epoch_sleep(&test, &validator_one_rpc, 720)?; + + for (tx_args, (tx_result, wait_reveal_pk)) in &txs_args { for &dry_run in &[true, false] { let tx_args = if dry_run && tx_args[0] == "transfer" { vec![tx_args.clone(), vec!["--dry-run"]].concat() @@ -909,6 +944,9 @@ fn masp_txs_and_queries() -> Result<()> { if *tx_result == "Transaction is valid" && !dry_run { client.exp_string("Transaction accepted")?; client.exp_string("Transaction applied")?; + if *wait_reveal_pk { + client.exp_string("Transaction applied")?; + } } client.exp_string(tx_result)?; } @@ -1496,8 +1534,8 @@ fn masp_incentives() -> Result<()> { ETH, "--amount", "10", - "--signer", - BERTHA, + "--signing-keys", + BERTHA_KEY, "--node", &validator_one_rpc ], @@ -1594,8 +1632,8 @@ fn masp_incentives() -> Result<()> { BTC, "--amount", "20", - "--signer", - ALBERT, + "--signing-keys", + ALBERT_KEY, "--node", &validator_one_rpc ], @@ -1765,8 +1803,8 @@ fn masp_incentives() -> Result<()> { "--amount", &((amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0)) .to_string_native(), - "--signer", - BERTHA, + "--signing-keys", + BERTHA_KEY, "--node", &validator_one_rpc ], @@ -1795,8 +1833,8 @@ fn masp_incentives() -> Result<()> { "--amount", &((amt20 * masp_rewards[&(btc(), None)]).0 * (ep6.0 - ep0.0)) .to_string_native(), - "--signer", - ALBERT, + "--signing-keys", + ALBERT_KEY, "--node", &validator_one_rpc ], @@ -1897,9 +1935,7 @@ fn invalid_transactions() -> Result<()> { let tx_args = vec![ "transfer", "--source", - DAEWON, - "--signing-key", - ALBERT_KEY, + BERTHA, "--target", ALBERT, "--token", @@ -1912,8 +1948,11 @@ fn invalid_transactions() -> Result<()> { "0", "--gas-token", NAM, + "--signing-keys", + ALBERT_KEY, "--node", &validator_one_rpc, + "--force", ]; let mut client = run!(test, Bin::Client, tx_args, Some(40))?; @@ -1949,13 +1988,16 @@ fn invalid_transactions() -> Result<()> { ledger.exp_string("Committed block hash")?; let _bg_ledger = ledger.background(); + // we need to wait for the rpc endpoint to start + sleep(10); + // 5. Submit an invalid transactions (invalid token address) let daewon_lower = DAEWON.to_lowercase(); let tx_args = vec![ "transfer", "--source", DAEWON, - "--signing-key", + "--signing-keys", &daewon_lower, "--target", ALBERT, @@ -2054,6 +2096,8 @@ fn pos_bonds() -> Result<()> { "0", "--gas-token", NAM, + "--signing-keys", + "validator-0-account-key", "--node", &validator_one_rpc, ]; @@ -2078,6 +2122,8 @@ fn pos_bonds() -> Result<()> { "0", "--gas-token", NAM, + "--signing-keys", + BERTHA_KEY, "--node", &validator_one_rpc, ]; @@ -2099,6 +2145,8 @@ fn pos_bonds() -> Result<()> { "0", "--gas-token", NAM, + "--signing-keys", + "validator-0-account-key", "--node", &validator_one_rpc, ]; @@ -2123,6 +2171,8 @@ fn pos_bonds() -> Result<()> { "0", "--gas-token", NAM, + "--signing-keys", + BERTHA_KEY, "--node", &validator_one_rpc, ]; @@ -2142,7 +2192,7 @@ fn pos_bonds() -> Result<()> { epoch, delegation_withdrawable_epoch ); let start = Instant::now(); - let loop_timeout = Duration::new(60, 0); + let loop_timeout = Duration::new(120, 0); loop { if Instant::now().duration_since(start) > loop_timeout { panic!( @@ -2167,6 +2217,8 @@ fn pos_bonds() -> Result<()> { "0", "--gas-token", NAM, + "--signing-keys", + "validator-0-account-key", "--node", &validator_one_rpc, ]; @@ -2189,6 +2241,8 @@ fn pos_bonds() -> Result<()> { "0", "--gas-token", NAM, + "--signing-keys", + BERTHA_KEY, "--node", &validator_one_rpc, ]; @@ -2277,6 +2331,8 @@ fn pos_rewards() -> Result<()> { "0", "--gas-token", NAM, + "--signing-keys", + BERTHA_KEY, "--ledger-address", &validator_zero_rpc, ]; @@ -2313,6 +2369,8 @@ fn pos_rewards() -> Result<()> { "0", "--gas-token", NAM, + "--signing-keys", + "validator-1-account-key", "--ledger-address", &validator_one_rpc, ]; @@ -2335,6 +2393,8 @@ fn pos_rewards() -> Result<()> { "0", "--gas-token", NAM, + "--signing-keys", + "validator-2-account-key", "--ledger-address", &validator_two_rpc, ]; @@ -2425,6 +2485,8 @@ fn test_bond_queries() -> Result<()> { "0", "--gas-token", NAM, + "--signing-keys", + BERTHA_KEY, "--ledger-address", &validator_one_rpc, ]; @@ -2461,6 +2523,8 @@ fn test_bond_queries() -> Result<()> { "0", "--gas-token", NAM, + "--signing-keys", + BERTHA_KEY, "--ledger-address", &validator_one_rpc, ]; @@ -2484,6 +2548,8 @@ fn test_bond_queries() -> Result<()> { "0", "--gas-token", NAM, + "--signing-keys", + BERTHA_KEY, "--ledger-address", &validator_one_rpc, ]; @@ -2584,14 +2650,13 @@ fn pos_init_validator() -> Result<()> { // 2. Initialize a new validator account with the non-validator node let new_validator = "new-validator"; - let new_validator_key = format!("{}-key", new_validator); + let _new_validator_key = format!("{}-key", new_validator); let tx_args = vec![ "init-validator", "--alias", new_validator, - "--source", - BERTHA, - "--unsafe-dont-encrypt", + "--account-keys", + "bertha-key", "--gas-amount", "0", "--gas-limit", @@ -2602,8 +2667,11 @@ fn pos_init_validator() -> Result<()> { "0.05", "--max-commission-rate-change", "0.01", + "--signing-keys", + "bertha-key", "--node", &non_validator_rpc, + "--unsafe-dont-encrypt", ]; let mut client = run!(test, Bin::Client, tx_args, Some(40))?; client.exp_string("Transaction is valid.")?; @@ -2616,7 +2684,7 @@ fn pos_init_validator() -> Result<()> { "--source", BERTHA, "--target", - &new_validator_key, + new_validator, "--token", NAM, "--amount", @@ -2627,6 +2695,8 @@ fn pos_init_validator() -> Result<()> { "0", "--gas-token", NAM, + "--signing-keys", + BERTHA_KEY, "--node", &non_validator_rpc, ]; @@ -2650,6 +2720,8 @@ fn pos_init_validator() -> Result<()> { "0", "--gas-token", NAM, + "--signing-keys", + BERTHA_KEY, "--node", &non_validator_rpc, ]; @@ -2675,6 +2747,8 @@ fn pos_init_validator() -> Result<()> { "0", "--gas-token", NAM, + "--signing-keys", + BERTHA_KEY, "--node", &non_validator_rpc, ]; @@ -2707,7 +2781,11 @@ fn pos_init_validator() -> Result<()> { non_validator.interrupt()?; // it takes a bit before the node is shutdown. We dont want flasky test. - sleep(6); + if is_debug_mode() { + sleep(10); + } else { + sleep(5); + } let loc = format!("{}:{}", std::file!(), std::line!()); let validator_1_base_dir = test.get_base_dir(&Who::NonValidator); @@ -2802,6 +2880,8 @@ fn ledger_many_txs_in_a_block() -> Result<()> { "0", "--gas-token", NAM, + "--signing-keys", + BERTHA_KEY, "--node", ]); @@ -3090,7 +3170,7 @@ fn proposal_submission() -> Result<()> { "0", "--vote", "yay", - "--signer", + "--address", "validator-0", "--node", &validator_one_rpc, @@ -3112,7 +3192,7 @@ fn proposal_submission() -> Result<()> { "0", "--vote", "nay", - "--signer", + "--address", BERTHA, "--node", &validator_one_rpc, @@ -3130,7 +3210,7 @@ fn proposal_submission() -> Result<()> { "0", "--vote", "yay", - "--signer", + "--address", ALBERT, "--node", &validator_one_rpc, @@ -3359,7 +3439,7 @@ fn eth_governance_proposal() -> Result<()> { "yay", "--eth", &vote_arg, - "--signer", + "--address", BERTHA, "--ledger-address", &validator_one_rpc, @@ -3380,7 +3460,7 @@ fn eth_governance_proposal() -> Result<()> { "yay", "--eth", &vote_arg, - "--signer", + "--address", "validator-0", "--ledger-address", &validator_one_rpc, @@ -3615,7 +3695,7 @@ fn pgf_governance_proposal() -> Result<()> { "yay", "--pgf", &arg_vote, - "--signer", + "--address", "validator-0", "--ledger-address", &validator_one_rpc, @@ -3642,7 +3722,7 @@ fn pgf_governance_proposal() -> Result<()> { "yay", "--pgf", &different_vote, - "--signer", + "--address", BERTHA, "--ledger-address", &validator_one_rpc, @@ -3663,7 +3743,7 @@ fn pgf_governance_proposal() -> Result<()> { "yay", "--pgf", &different_vote, - "--signer", + "--address", BERTHA, "--ledger-address", &validator_one_rpc, @@ -3888,7 +3968,7 @@ fn proposal_offline() -> Result<()> { proposal_path.to_str().unwrap(), "--vote", "yay", - "--signer", + "--address", ALBERT, "--offline", "--node", @@ -4569,6 +4649,8 @@ fn implicit_account_reveal_pk() -> Result<()> { NAM, "--amount", "10.1", + "--signing-keys", + source, "--node", &validator_one_rpc, ] @@ -4586,6 +4668,8 @@ fn implicit_account_reveal_pk() -> Result<()> { source, "--amount", "10.1", + "--signing-keys", + source, "--node", &validator_one_rpc, ] @@ -4596,16 +4680,18 @@ fn implicit_account_reveal_pk() -> Result<()> { // Submit proposal Box::new(|source| { // Gen data for proposal tx - let source = find_address(&test, source).unwrap(); + let author = find_address(&test, source).unwrap(); let valid_proposal_json_path = prepare_proposal_data( &test, - source, + author, ProposalType::Default(None), ); vec![ "init-proposal", "--data-path", valid_proposal_json_path.to_str().unwrap(), + "--signing-keys", + source, "--node", &validator_one_rpc, ] @@ -4641,6 +4727,8 @@ fn implicit_account_reveal_pk() -> Result<()> { NAM, "--amount", "1000", + "--signing-keys", + BERTHA_KEY, "--node", &validator_one_rpc, ]; diff --git a/tests/src/e2e/multitoken_tests.rs b/tests/src/e2e/multitoken_tests.rs index 0f2b15d877..5d713b2aed 100644 --- a/tests/src/e2e/multitoken_tests.rs +++ b/tests/src/e2e/multitoken_tests.rs @@ -3,10 +3,10 @@ use color_eyre::eyre::Result; use namada_core::types::token; use super::helpers::get_actor_rpc; -use super::setup::constants::{ALBERT, BERTHA, CHRISTEL}; +use super::setup::constants::{ALBERT, BERTHA}; use super::setup::{self, Who}; use crate::e2e; -use crate::e2e::setup::constants::{ALBERT_KEY, BERTHA_KEY}; +use crate::e2e::setup::constants::{ALBERT_KEY, BERTHA_KEY, CHRISTEL_KEY}; mod helpers; @@ -45,7 +45,7 @@ fn test_multitoken_transfer_implicit_to_implicit() -> Result<()> { &multitoken_alias, ALBERT, BERTHA, - CHRISTEL, + CHRISTEL_KEY, &transfer_amount, )?; unauthorized_transfer.exp_string("Transaction applied with result")?; @@ -69,7 +69,7 @@ fn test_multitoken_transfer_implicit_to_implicit() -> Result<()> { &multitoken_alias, ALBERT, BERTHA, - ALBERT, + ALBERT_KEY, &token::Amount::native_whole(10_000_000), )?; authorized_transfer.exp_string("Transaction applied with result")?; @@ -131,7 +131,7 @@ fn test_multitoken_transfer_established_to_implicit() -> Result<()> { &multitoken_alias, established_alias, BERTHA, - CHRISTEL, + CHRISTEL_KEY, &transfer_amount, )?; unauthorized_transfer.exp_string("Transaction applied with result")?; @@ -155,7 +155,7 @@ fn test_multitoken_transfer_established_to_implicit() -> Result<()> { &multitoken_alias, established_alias, BERTHA, - ALBERT, + ALBERT_KEY, &transfer_amount, )?; authorized_transfer.exp_string("Transaction applied with result")?; @@ -216,7 +216,7 @@ fn test_multitoken_transfer_implicit_to_established() -> Result<()> { &multitoken_alias, ALBERT, established_alias, - CHRISTEL, + CHRISTEL_KEY, &transfer_amount, )?; unauthorized_transfer.exp_string("Transaction applied with result")?; @@ -239,7 +239,7 @@ fn test_multitoken_transfer_implicit_to_established() -> Result<()> { &multitoken_alias, ALBERT, established_alias, - ALBERT, + ALBERT_KEY, &transfer_amount, )?; authorized_transfer.exp_string("Transaction applied with result")?; @@ -326,7 +326,7 @@ fn test_multitoken_transfer_established_to_established() -> Result<()> { &multitoken_alias, established_alias, receiver_alias, - CHRISTEL, + CHRISTEL_KEY, &transfer_amount, )?; unauthorized_transfer.exp_string("Transaction applied with result")?; @@ -350,7 +350,7 @@ fn test_multitoken_transfer_established_to_established() -> Result<()> { &multitoken_alias, established_alias, receiver_alias, - ALBERT, + ALBERT_KEY, &transfer_amount, )?; authorized_transfer.exp_string("Transaction applied with result")?; diff --git a/tests/src/e2e/multitoken_tests/helpers.rs b/tests/src/e2e/multitoken_tests/helpers.rs index 27228cf266..176692c508 100644 --- a/tests/src/e2e/multitoken_tests/helpers.rs +++ b/tests/src/e2e/multitoken_tests/helpers.rs @@ -15,7 +15,7 @@ use regex::Regex; use super::setup::constants::NAM; use super::setup::{Bin, NamadaCmd, Test}; -use crate::e2e::setup::constants::ALBERT; +use crate::e2e::setup::constants::{ALBERT, ALBERT_KEY}; use crate::run; const MULTITOKEN_KEY_SEGMENT: &str = "tokens"; @@ -24,6 +24,7 @@ const RED_TOKEN_KEY_SEGMENT: &str = "red"; const MULTITOKEN_RED_TOKEN_SUB_PREFIX: &str = "tokens/red"; const ARBITRARY_SIGNER: &str = ALBERT; +const ARBITRARY_SIGNER_KEY: &str = ALBERT_KEY; /// Initializes a VP to represent a multitoken account. pub fn init_multitoken_vp(test: &Test, rpc_addr: &str) -> Result { @@ -113,8 +114,8 @@ pub fn mint_red_tokens( let tx_code_path = tx_code_path.to_string_lossy().to_string(); let tx_args = vec![ "tx", - "--signer", - ARBITRARY_SIGNER, + "--signing-keys", + ARBITRARY_SIGNER_KEY, "--code-path", &tx_code_path, "--data-path", @@ -135,7 +136,7 @@ pub fn attempt_red_tokens_transfer( multitoken: &str, from: &str, to: &str, - signer: &str, + signing_keys: &str, amount: &token::Amount, ) -> Result { let amount = amount.to_string_native(); @@ -149,8 +150,8 @@ pub fn attempt_red_tokens_transfer( from, "--target", to, - "--signer", - signer, + "--signing-keys", + signing_keys, "--amount", &amount, "--ledger-address", diff --git a/tests/src/e2e/setup.rs b/tests/src/e2e/setup.rs index 7bd9752768..4c39c8e6ef 100644 --- a/tests/src/e2e/setup.rs +++ b/tests/src/e2e/setup.rs @@ -840,6 +840,7 @@ pub mod constants { pub const CHRISTEL: &str = "Christel"; pub const CHRISTEL_KEY: &str = "Christel-key"; pub const DAEWON: &str = "Daewon"; + pub const DAEWON_KEY: &str = "Daewon-key"; pub const ESTER: &str = "Ester"; pub const MATCHMAKER_KEY: &str = "matchmaker-key"; pub const MASP: &str = "atest1v4ehgw36xaryysfsx5unvve4g5my2vjz89p52sjxxgenzd348yuyyv3hg3pnjs35g5unvde4ca36y5"; From 39113c694d5d3adf73dfd4103792d2545154db08 Mon Sep 17 00:00:00 2001 From: Gianmarco Fraccaroli Date: Thu, 20 Jul 2023 11:28:44 +0200 Subject: [PATCH 119/120] added changelog --- .changelog/unreleased/features/1606-multisignature-draft.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/features/1606-multisignature-draft.md diff --git a/.changelog/unreleased/features/1606-multisignature-draft.md b/.changelog/unreleased/features/1606-multisignature-draft.md new file mode 100644 index 0000000000..9311b2c4d5 --- /dev/null +++ b/.changelog/unreleased/features/1606-multisignature-draft.md @@ -0,0 +1,2 @@ +- Introduce multisignature account and transaction format. + ([\#1606](https://github.com/anoma/namada/pull/1606)) \ No newline at end of file From 9091325a0c13a06f5ce7af5607444c65642022d0 Mon Sep 17 00:00:00 2001 From: Gianmarco Fraccaroli Date: Thu, 20 Jul 2023 15:11:05 +0200 Subject: [PATCH 120/120] fixup! Merge branch 'fraccaman/multisignature-draft-rebase' (#1606) --- tests/src/vm_host_env/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/src/vm_host_env/mod.rs b/tests/src/vm_host_env/mod.rs index f6f1d9315d..5ad6850944 100644 --- a/tests/src/vm_host_env/mod.rs +++ b/tests/src/vm_host_env/mod.rs @@ -27,7 +27,7 @@ mod tests { get_dummy_header as tm_dummy_header, Error as IbcError, }; use namada::ledger::tx_env::TxEnv; - use namada::proto::{Code, Data, Section, Signature, Tx}; + use namada::proto::{Code, Data, Section, Tx}; use namada::types::address::{Address, InternalAddress}; use namada::types::hash::Hash; use namada::types::key::*;

    `>IF&ilboPF&pw-QOELDBM-`%MtTB(GBe*3 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�Gtg?HL1XvA*Lny>J0yTamvtWclun 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¥G0%+>(x*O2l97 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%0DtaiFxj{%J+*<=3V z$u_$aFFDJ^{1UoyySwiqbi@t;-i)T(N{1?YBR2K)FK~0Y5>liMOkm=;7EMoR9Jcu< z^bvdf6FLg(hy)RTo1+<Ro!1<%^BPghMKHDym;8kM>6l|$A8%$$ zWjfg_1RA0#F%JdWQZ!ZP)od@TCgl(q=&LCoGPZpuMhZF$%7`gqdNGpy0ZIg0p20 zdO}5Q{_6yFI=X@yfHUgCLH0wT+qg%!)gIjkAn^A2@MAu1r9c}cNz{hgoDzxFi?`A|CMD#$6`=;L%X@6VkttRAe ziQZx;oBjjyE_it#Ja#GCRiciLSVBBIQ{4D;G=Jt(50_UZqFerFs3ydW%8;H;MpgP0 zA3`PE_-J|6@EU>FAsE(6+0W2oi)_!NA1z%ds8e@fNbZkLM~od^Y^_H{*k*z^y zMu-|jBry|>cABV-%UB8DJlKa?OxQ=S0qwFN>`H#KP19%kC)qmbiNRji%-Mg2s*v2< zZ2S3GSzdRHh@;HqWY}1Wo^7Q+P;A)dEoMRHuy)dAKcLbO6{#d$V$o54NM7!eh@~@j zO{CBi%44`xRSdY)p|*g-i7tgEN0*ICctNb|anDM$Jy~>}VHz9;Ws_lt#tE);GUZU8 z(H=GhRx+Iiw?Nf&4&2Jgp$^h=mHj-kd+LN)DSInXy@HrA1Hde|YAepfhOP8R)AT#~jd zt?p0EGR&oXGPtnqaH1@ceSmBG4xu+@M?C-H`T%M=%)Eb4Qo56H;_iIszZPS$$$oCK zxomGK<7Y2v>_8dBToTTw!HS?VZU-#!Ugy9r`0#7C>xe_!MUjutrXrks1&BvW2%o*z z;7OqGP^-S*ExWXyIHc+X9W{xACqv>RN_kd@IKnOwcy*V4phK*yAGFoH#g5L?dx|Qz ztEh52MW}UY#75|arnPQIv2t^)(t4L)jog_YB0nKTlSdVUV)l&;u2E88b+lQx;m1rc zpEX^v%|_jzw0af5%f`ax^alG4Mfy)DNpztoR~k&Zt!f+Kw~fe-8CnLmZ-xIw!0hKZ z|41#y3)q8pv!ak!5a#>~HUEX0{{q)Hw7$Uo1K}VeU$7v#`4?bskQZ#;Fb3diiU7&< z5nG$4t;a=s^lKGHOzW~WxF+=9KBbG{H5lvA0gI$6fm^dL5L60@yIn0aC|?3s4TCFg z*f?H0_BW~J@mSnv-Qq&n_HJcN;o_O}{c6o*-YuKsyj!*szFYDcPCi<^!C5xR%i4KP!)bx+=h+LI%cRaPyRg3W+20y z3~uiB@L+gh8zOflS7H>&DDz7w1&qJEz@zU4t^|P<-i zX9kD^Ii&!564cKSHBkzyG|Xxjbb^!O5IcxM!!GYMI?;em#1swpFy@?244r{ac%YZA zL3Cooa4g#a`iVjq5r#rYd2ARTp$+9KiafK6OdD0OO+i!vJ@qy9ZWus#5Gde&H*#tH zEZ5_y0*R=C>?f?IZyRb5d2vicArn>T!h|NOFe0#)Pxkq|f(i=&KzVLd0kP2$%nicU zq6aNXFmXB!fCP-#+~V&w2`jon{Z#aMqT;amsFN4)OO{-~H`p}ni(vzyyRm&$6ge(y zsWd(O%GSbOOh@n;fW0*W9J>|Q06{3s+=|O3g|2|Cu=%WUIk2sAIT+>;3c!gd94ycL z|BsYAvO1ch#whMRT8Ws|7qOB(f^5PCqS{g$B35i^c!LJa<#v*U60F$H7RPNAutvFk>8Wx`ULZEWUyJfWATCD}BT4>-jhAY=8a@Z7an$ zj9eMLHw@3 z^Gc_rO&w-M9V}W+@mwG@tgrP~i*F>b?g*I$$mKuci@@&fdagm4Wdj@Gqv$uA39aeN zf}aoSOJ$JT!%A0x6%>qY*5V-PM__y4X!bV~N&LW4TE3iSyHNUq&98F<`{}5Lt2RHv zTV1r-mG;_AKEk&0erwqdwbL?2(Bh}~)SoKFPo-0SQYZ2L^bpPTSuYi*0j>-{r@Pn> zu)Ik7j+_^x+v7Pd=MFL}11 zB-WnW1=FbY?4n(z>Mjp{UGqX)&kom_?R;r%88mCd0uIQ) zuoT9&09qtIXK;v-W`DECbipi@xIVPw{LacY#I9<*`N}34OPhqH6>x;5wX=t`oP?z{ zF!bx!`pGY?fz&BKErvhe$wVug&}W$}nq6UKP3>;#iPi?#+WOA8>A4ZzJGFR&bMb!ARf@p7mYYSz{dH02$Hawf+JSa zxPa$vT(g$*SGuFyA0|O+75@FmYBt1U@p$2&X`AC=OUu@C79U#VpbUL6w-hfLn_xWi z>`#}k&PF5_;kS@T^}j5|<>mOizkp|?JtKd@li`;^PkSOlV*Dyu0E5t%|N=DsB@VpSTXxOKstjK$==BM1Pb zP}Ss%tKyJ`N|>AwN;-n234+TXl=z7V5aVF0CS3mG~z)$|VI_G&Ay<@=YV#_(gj0vrOtmQRfA~ z3udN2Q{I#t;u}M0S*lyTo0R zR?I=$ABQxhbL9F(iE0kdUp0Z_uHNfU57#G??W9j|s^CJ>2UbibD@YG4=UV66k{vvL zi=WOR{f{Ap1%j3khh|?H3zX&;bClZvxDB}t+8pE}$@9~UCPZnRT!9{KbNcOX&qv7? zR(5zRe4uMm2X1VIfkEv>PaBUq zxY|$cZD^~wfz8tob%Fk`p>M@ii~Z?wiaIDN?qlqCPumdb@Q)m~eY3=UN;X@wACH3K z>1pZ5Bl|wHGG*9QND=+%$z?*PLdNJ%Pgp@Afv^H5iyU9Z+lL5hN;j5GHtul!aKZKD z?Ik@NttaoWhhz2R*(zA5Cofleyq^55(i8P8d4(QM){|S6PW2_PRyy66yjbbsdh!yb z^Y!GVN{`f&o0T5uOJ1h*U|({h(%HV`dZlxH$qh;m^(B`oJ=}+diFCd%0Zv$u{v^2=k2msoa=F!g z8ILFYRXH>WOdu z=GXq|yWe@+pTE=BXv^Zarz*Ej+;!lWr)IzR;75-|)<|2HrB{_#Onev3@f!QpKcOzt z4?kOcF&jCp14mTj=ey0AlTXv5>E+z689J?E2x-3}N-?Bm z%1ig(pops(DS};=YgtUs>m8yfE>0@hM5NoUaSKO_2uIe-mQtTg|NigiBlQIWs;w(M z)}B9FWCEuE3C9yV<+?gqx?$erR&JNqaq-&fylL%LOSzM}c$<79<67?a0>1T?_WVB; znbx<&wX9zBEpibyjNW>W#)Z8>)=;g@KBHTrrd!!wo1>nFvD!`14c0izml$*s1xaW9 zieISxeJrdEvv8sI_pz{gmSMgi-xspS^5!0Ia0eP2wc@jKj*W|KVnGx z{L@$5ThFr&FTjZe^1L-&P+)WIF9(5?T>sRc&XP0D7<>o65t(?j9nNO0ppA6SSwV}q ziuN2Eg0KK*nHx2jJJ3y2hp1@1&HK6;gXp(0{a~qEmC6cKy_I0qtI{>EOs;U7&ET`e z)#N*emo1e+^8UL~B~08kH8nN=iF<3wIJvNLTjU3_#c$+W(%|oqxh=MdVs0^bgt38A zZZ`kpalBTizFWrkc#Hf+?zx9wu1GQZ?{jQ|;>y@Kx+j~Xq|8ZRme@PGq>#5+%gZ$6 z19j+Q7v*m!IGYWYVP;{8rU_5GhEnD#e(W&Qc(Jy!JEj@w8TmnfRhY~inqGM-24n|%G_f(9$)B%K2y6${YT6ErciN3b8@65Gk$xSDfJe>bmdpdp zZV;G)q1=$S%k=y?MR7Ho*Aqhk6K!CT_}@GRwg=+U_&M93I3kK4?8KcL#UZkMbck7n z)=e<4g@qq@rf4fgkO&zBL`B?ogXROvJue7`RnvrlcwhN}G`XcIVuuEW9i;!f_=9!v zIp@VJ>7{}Z=}rSzU4swArvP<}XR6cW71-)(+~rYF1+H65BmSTM`h4c^29cCo<$HXA zL5pV+AOFZwQcgk6L!7j&d_*a8c~~h_@`Fk_S9D0Jk#p1mJcpTqoHgDHPebA3ZypzY768jV()IsLBZDo zRBeCfYNNb}^|U#SBFubS_!$Z11e=cf|AaOl`VaG(LBfXZ?KXmP66ipvXq8xxL{5g% zB&nbi%7>`Me=iCuk4_q=rf+=4e2<)qba8_Xff<;QZx$TDsaTI%>sO>Hp^Y;RHp=H&2wNsqed_ zPVR4RF&$Pl{X!&xDRyI+t6T5kDCgg-q<2dpl$P<+wWccRKSnB$<8CN;SOH6UAuriH zQSo2n_7~QnD&;D=9@HJHwLTlw{n%{`tI-3h_cYrkfhrduK`?+ zut$xt*E8M5HO!MA#fGK%Hv+)=ogg&tVK%Jvb0`UHo6PwDPyl=Csx}_v2>CT_T*y56 z4!5jm{}0G7b87?A*nW7Tlje2RW&bwzLKJy=&d5Aa-&-2tdA-#(hKY%?4WYFiBr0J~ z$ajSPbb%o>3bR{(dW<}twRsn83to=E!H9GZkRb)PcM0gYa7>Ygo`zz)856EZ*5 zqUOYz4@hoJ(E}hsZ%yCyxqC~Or)7Uaasq5y?@vgscN40kBa-XssyQ1FZ$)g%dT9+f zA-Nu7XoZ|VA-SHrv)iIwp|MFp=dp1(QY8l}njHyn%t8qU4psyrS~$u$EyyE!6H< zk=eK3ou}SbQU(1okja6il0E|b*a=D;4tbOZHRC3JD!ST=yTTN;6$L4F~& zq@N(WEDI1?Nl)lkT@91N9Bp$W#I>RVmGmJV*^3JB1H0iOuNUpAq<4v-+!C$uRbdK;Pzn0p|jGf_;P^A1x&bKcZJpu0a=Bg^hVR>G`BT%fDBHx(kG_@6C0SKe}EL z{>yLSKi*g`*A$w_YYI)|HQMnEvaj)4%WL2XIICo`sz{Folh^<|g-SB8+`(7XZID(H zr5tLxA+(ZbGetgbgzad(f|P4ruOrCel_Y2Hj4$J!2v7snW3P}xTF7&F{05gjUqA}2 zL}A&R9?Xcz@*ExxlP)DizCps{hi+*ydo9vSzskv9M0$+$64Jv&FsIF3`U#nx$F)5E zHY9k3l$-0x9PEJwM2VeTi*AsEmlGu8lgZXxkr^k?j`zt6Km5-gYHDU|KoZzFX3;Kp z=)^1FAMbNDP!_H2cS>x({#_>QJ35dTmDmCMWK`oQ`?d&1z}hK?cG!#6gw?)=V}HwG zX}o_tUJ8;V4*cVMRL}#qnrZTZ2bUc?oodgY{CxgnkZ(Qnm_p`^Ws(+zZwO^!PWq&; z5No2$6Du(0l2;i`; zaQ`R6h`e|54?3VM9|SL)X+#X=Oe4bJ+p7@R2m4rPEM2e-y@Ks;_mT$~Pm=Nkln{Bm zZqEn%c)rI!&&%6zisv)~dPq+8PBw_xxzF`(i4J1fhKo%Xh?$O3sxB`(s-mQ=iH_@d z0dLG91no_yC<@W$ z;U#O7z`8GJz-(lnBNgj@Oz9jxkhD?PE-kdzPJlUZ|=nG#0zNkoz;flA2xmN&jOo5qIY&N*)R|Xm3sZuNv#9(7Upo=t!|*&XG#wCCu@|> z_M?U(y|2GVpOtxazMFK-gI>b{KZMw5kj7tRE5eA(l+eEr8`F(}guCG$nS8dlo^&Xe z2I&sowdwm8<|EDt?2JS_AOv&d17V2M*&@?YQA!;OdPp4#R!BQTE{}Wzp-{UPco+T8 z;*&pa73*3@$L1Fg?qB*DX=hgZ-YWztR^n5uG4!c=GA5EyMVN8xIeKJSp zSkjkfZD_(_*%tTICj`kcqfeL-aDq7%Ep8%rgA`*T@M9*c<$x*uT!1$qX3RkMOmHfv z^WMyj5tv5Q!;l4yJa=u0s^?pKP_xJI?3&0B28XPX^4|2S&hx+j@FvmZkD zbPAxf03%R`leJ_co#!SId2xUJIuKRT#vDrQQx`dV`y#Ecy>yX4)hb^k2)0VZ zCezy!KH2J%rvWwj(};h{U`iTAA0Ebmwu~leL@1*kOhM;^ z2+8?nz%~5e#?Ao3btxT4x&1FHb(VUdxo0!&XT#ah5DfUCN@hoF@d-*e!JFXVlAH}I zF7Fjry~^t&f(I!~054AJ#nk(XHsQ*fRu?Zr^aKj_lhpX$+2*@o>jeQqx)5B=*q-v9AOzj*kc`Ap+K1iIt?m-n8$_n+VN4<9)8 z$>ImVL9e{$)9%kvHWJz{^4+TUXhU(eUn zuLt|#A)x1ap6$nEIU6!_{4f`u5Al2mLkw}TUd0E*K=6SSuB-9?61NOjpWca#xu=bz zz0Di!tG&$^E1mCczC`Ka-ez>qJ@*j2XtenXszwtE&Gd?&eDIFuOWjNEXl|bP?uWkh zz4vERU;2knN3U&;V@{r@QCu^h%P6iq{L z>&`h!L@=BR?3!7M`{j4OJcGzJbG3{yq0KAYa;R7{*NXI$m9jptzrAj`JslK(DgY0> zRC9M@wL1@O0`27-!4+g_DQA=W$KGDe#Te2%9$wgm-# zAKBx)LTzKch!+>y=3|WSiO$3}ggQo%;;VhZLSh?2t<8eMmJrM(_NCU$wI*%R ze9?eSmr&AwhnWyBDntlVC014V(VD6wR&u;B! zic3lvp^g#46N@v*xq=JD_A02!1K!?kT*6%0b6s_=R*?)oj#8pe_Ly<_E~KOVV*1z; zC7cVsU}aLH*X-=Ji{vu=UiA)5;Iy47Zi`{>>Qu;Kjl#U{Gn8>Ed*WhKhe1PYi z$3qBvFPn!KI4`NgbsF1)BG0TP+V6E0cwrnu5M3%kG&4ywk|ZkTnd;K}>FhdfOU5)t z*^0E8q|+2>f0Ul7O~f>4H0zLn5D{~Vwk2Yk42_5>8Cqvk+!Z<8;w6hZjqiIVMQg6T z;TYBl6X^1xvEI2=s3lJ$7*-{>3ibA+^QcDJtwNG5F$mB@q;>}0(LPrlnyY-4I>2g|&rgSdH_QyP z0V`s@G)#@mib+j^Mt1twIG@@hbOgMqA0!8sKyQrFs|k4F7tZWrb(p^~ z*~L0cpm9Jn0zb!5#=LpVVs^^)2|!OvpUO zntStDy1o)Hd`ADHK0vtc($ch49QVa)-EY(*kkKpz-r2g?P^|Nnn!k9B1=Pt)WkI zU#HYA+~AKVdPr2V{*9oHOp$aRon=x zvSMiEDDgHZceVkNR@Gpv;+_S#VkngGDiJsx+b{0p>^5W6*+49P&23S#Z%EPw&-OQ; zWs)XMT_#OM>@tnY+dqOa>Kep#10LjeZ;a~2PE`2C%jhJGQIJqOeb4a>J#u8hOinuS zt?zAQ?a#x^bSddM{M!Z2&oA5G90=y7AlzG$tEUzR4iX|PrvCC_Zm0gHK3sUi!%?9= z{Gkm`8MB}do!KL4qqi+>^sBP5utRMu>`>-;p+)x|OENY!0g^V|T3cA53y8&%cDf5V z64{C2iF@2DnYTRckosUiXj_9~>`((7p%f?#q;FX&tIt4sbZ8R0&_H^KfPxdG)5G$M zACSvSvXvA^7c>n6a*#|$N%15{!99>J3?q@y^`yfB1T1IuTB^6K{9~GB1;lw$x36W2 z;fUd82w4gsgv{j=q$2~+F(bQ-7&fZ8eor*tAEWBg|89L>jJ~Ib7u_OnKVkll0Xi}x zJx|r~4z27!`sff*;{1Yqjz>dN*5dz&?KwI`1=1r*8Ojs=tR4%Xt7386*Ymudk5it` z4Nmg+2);%N8=ffy4+Ib@<+zA;;k^M*eWh6meDgs1XOvbcF-X->}ks4sM|mpH`9@^tDi#jkI|n7G2^oo&b- z2qN5Z9VG(tx6;wRwD9>HNDrvzvic8b`+Red}uO%JeEJM4JAsn zG)m`&Nx%5eN5A{{k;nh^>BKOYRl)b({o(09d&}S6^)AHWgDUv?7ysxBzkUCA?tc#j z2UPGEkNozr&m8^o2mXkHX$sP*d8CR%dSWTl$Jm=v*jM4*bl+s!(->uVEFq&<0h}t} z45}+u-wF`DeF5^HRO|WAL_1J9t1!U%uVLayJRDtv!xFn2?@C1=7~!K^qTl0qdSj(v zivSu>uLXlS9hN|b3D0vp9}Um5JRb?qTAO*E={o$U0cYbihHW%|}`3TKHwe=d-IKw={KsrcHm}UME1LwtagUMz``oLf^ z?th|F6I_pHey|;GU_6WN?UN;HjRAU}q$?i zdjH4}%rHGbH?cN%d>Kf;g(=|L+|w$Y{rXaXNL%23DcO#ypVz8hB5;Eg$6pl8A?J z;y3LEFy1ysY`2(=-ELqTmG?5qyTR(H>~?3gW@KMt|8Cy%%M{)wgE|d)wV*buM1rdA zgyt}gb`2`x$z*hh#mnUm_C(|ogIPK>@13Cs(hVAhtxNg|TxR7G4ZVTnR$DI~<(lzI z>yj-Vzn&dt z1vw*m*e!Bq0`lld-ZDG>aVm}@X_eW6QB}y@)i{@ptoicW9>is@5HD=ys_r~ipLmxX zvX^VL{MtMUSLg#^4!i-M`7W;EPR^6e0-Z z!VJCepV-4GaQj4;d~toyh@$DuXJ}ildyCpm|6dGz8K(5U64pq*k_60+gJ=b1Nq)1GOb=Fj(_8wN(r)*@r zN6ffQJ+7I@TqgD`6Nagd>ro}4`x<*fJXk)mXXeOI1s(1^KH!|r_0T4|8>`7ROGunL z0<_4 zxpmyOMBrl|not1H<1T;DWZkO-T=KaDxrG#sS zi;`I@;pMzUkwWLxc7kzVX&1*Ml=k5FqLe#RKc8^y) zSHZ>jaVSL#@i?h8>&KO+)7S2bA5anIX8PmrmK8nV$MNX$oza){Bey>Aqi&}44l+$` zkDckdd>^6mw$hl0lC_C87;wq(%b=3kb42}`Y?|bkF{c*o(UD7#JLYRExsQILDwb<}#EyROLRn2`a@MSH>- z;9s=L{Abfc1UyK`(_&Xm#{IXHPzD;e!p(bph+8T9O+ZA4N%M?n3j4)8&CB`N9Vq4IsI$OA z=+RkRN_s!^n=EOS$X08`^p)FgwEJ`^Ob@ADfXU=vf`sg+2kM?nDt>xy5zKMVq{x(? zpIch8j-Q`fn4d+{^G2H@BnTfyrU_3gBphl3$M!LtVL!-ej5#<>{4$-{N_O@BEy>=#8tyj^=MjUGB{rw zC5$+!c(2-OybtEeVhIk))pQAM4w`={gh-bN5+nfA*gY9Ht|&ABL{JC*`3S#Zz(ZYd zk17sgrqjif-1;P?UiDTcm96U+|N6e)k1x^97%hv3c6#K1FMVM?5@!uEn;h_Y@`2ej z-`U67^PhZSk!vYN71ZQO1p$s|$ic+~0fJrxiw9e{bPbvUg3U_%tvfHLe|hy{GME;h*1FNlIz#U#zF~_6ex_31EJ@()WJ(JD+&nCCw@l z+^XC)P*#s$db}8F3x~+Bv-7ouI$2OqAR=lMAEZi(v^$&Xp5$U zT>)tTmP&qp*cEuSj}M5ECKV&S(%;y%vM?;j=Bqx$ZH9%)KB7O3$xGQx3*x;HUVzXK zB6-&^;$}~LdLA~2faC!fj08wDNJp9A2rvW*bjCdVZm|g!O{K^a_@T;?iLkvG)i^|O ziK+eoI>;Sd#}gd=tKU|O+PLA{kkhnj7;AzfB}q%03etGTLwI^2#W@CAQGI_5te|UI@(bEwgjO( z;MbA@yarH>3Tz*dfqJUnFE@W7ppD-!s>XetATs_~#eJR0R^_25B0b?w;P&bjQ;kq1 zJ2ap*^_4lkq$jTXjBgX~SxKJhs>x4a?zjwdN1v>{c||Ve>IOcRf*XNwrn4K;8l?( zgsq?l?G%AtfdPQw)IJ&&vyT<&E$ZQaT2V6{Iwf}1T+>ZS`nrLM@C0B zlo6MA(+-kLDOrkKKy(HTQ~{>>Jg{ON(1I|4@pxR$-ik<_40??3F==`!PB)ULpHbhO za)Xa>3ftD1)C3T~tFB5CsJW155g}j!&A`aYLI@NZ zPzFW@4TCgwD@M&b=;`(Mdm>y3A7}#cPXP^jA**HuB>&kAUUAap$ zfQg3FU^e&5)i=+86Zwu0Nl>UdP7Q_iSiw5jY%JB9s5<9U%r3_ViH*zTZi#M?sNBUi z=Nj9h>$C-RiK+;M>_TarbrmHjd;vFJ#of$1MdhMXp$1*=(s4Dtg5=AmLyI;Q05b)O zXNy<@FoQB6rlAfS{hEM6feK~_#fG7Y0`@LtnWs1^-OX#=Z8%VE;XrpA4s?4Q==LEs zx;+kb=Qyx}CJYC|oB&5S$=2)nhuvn9n^_c(yB1vsE}Sk8|g2TV!I zs&{)Fuy1W9XE@MpIMCg~fo|Atek^c+j|Lo=1^&T-6=jU&`RBobS>b?nKsdkv0uFQw z2YNgXbQun;K*gvke#$HFws*{=Z^snCefzAR42KfL`!1|A{ zn1iY$93Xb#A{?;AWHTfxAnhu=p>Uup-~g#`02~qsK=T6*2)xv#xNR%+IMC&BfPrlW zPD?$RjS+gqx&|C*Xa)=imfGAeRo^@g80j$_Sn6>gD6PBwY``6D2OAIulchNhG=u{* zZa6@|S26(yEP`$(7*gjUox*|e1;YWBJ#z=T08%*6D@fcE(O0d+ zN)RgG05Hx^I#e`nPrW>i*;971%CqBFeMzv|_;tg9o3sxstXzZx3$_m&tonVxaA3Fp z)((AHv!4U=xYl1Y(L#!_5zvzj<&jY=*Hb3Kw5$BD9m zoM2iw{B^?p?egr(A9cyQ$0C$L2#CgW;YOa6&dMUlg&`XehtS7=san41$3~ zS8_CK{87psIa~@VrVfILu2K}B%B6Zn?ZOevz^<9;ySnHgrgHf~OluKAzX=CZfxp5R zw9|7l3KPS~2HE7jU#LuW-X&BG}&A<$niFJh%<>8WlC*MH7;%15!P$-oB2uTk@o;%_B(&HQG20>L3d_n9yR*bZf$DA zP#q|$=qL0Qk}0|lwd7%h&Oz^Z8s-TmfF~4_Mq*PaC0RGw_*ICR$k~=?3PGAPiTSZj z1|jh>gBYUwM97lft~))zrmh>8e9^dyP&qeoD-T6ZMM&^Xf9Z@~L^TLA18vT&Re}JD zP>1>$EY>_*Q@3=cJ_ZX#M!&^>XuZ!T3jv3m2SU8PMZi6GG(Oo*z#Tf>baw(1lB_M$ zMi?+*`~Pn4f?4|wPmz?nWr!GKQr8L#V}FB_1<#3kcm?pDvnW#S=fVcyZT-n3d72p)?>UL`-$aTA=54xH+?Ud;n zz{MYYzH2(BS(C%qigfq>=88o}G=o|QU)}dF%;!fm)zAEhra8-1(^-Di;G*0y1?NrO zqHJe0>52$~p{pDt%YP6g&uFu%nEXbRVEm` zLhI+~(IxcA5u-~w?;It>QMw!%x`Yfl8i-ph?93+dSa2O%p0(nhnONYTX~XNv_{9K) zlCM3KN#qy1lEWk6kYxseQk8#j8={Y|Vm_o7b5I5!`xJDvRfcF4&;Tegu zjP(0PquX2~AuEIKRJBPY@11ko+R7<|8e;OzkY%cA|Dpp2s2r*8+jYvc-< z`&I}pBIeK3Mk{iy<3u?z=`X;*3bPoM?!h9({Ak_b&rOd{E%CpH2AFQo5TmW(l4sMG zD=0?#Wz?XlYrM5YC(BwD?G^eXMcP2cKeCaS8QhEuRsjJB5T}X^S2a_E@gOTfo#hfw z;aLMt8zKK2_kmgHWRExkBiC!xBd<3?f8UeNp_WX08*`>@LYO5@53#^Ih4Jj*7_=ga z4qK5a(3=)EyPVEj(TF!^{t1u{ zuR^G&dnZZ>G^{L3)f7#*9Ry2m@9YQF*}lb{J=)gU(6eJ!l=R}X}r_VWk!Gwjb?+yppu+_Y&nvy z@HGq-CLyu$CQA{3#jFjZ9T=uL8WLUNJ_G5#a1hsijrL3AQV(9{*OQm|^<+D=*_5He z!-}ujc$cjv`q+l4z=;N?&0;DxMYdOg4R4696Pk)7^VXZNPI|o621CJ}Z3U2~;q+Tr zBJ&!Mr{VN#Z3RrxaQY=*0RGb|(>I)czO4!~JDfgN6mZjon2ljg?Ph*4b;GdjB*TzD zfJ=xtEOmqKw+|aNf&JP{k+i=i347ceW zhT3&+=pDw;y_w!2=-#9sXxBT`-~eI#*uiRP=k2C^m)?!g0g|@lg4KDu>EDM=Q8^50 z1yMN==LJGH#N`FH$*-L1TuPAwf)`cGKw4x-r@s z&YG7%R)H6-`6aWLd&b-J20watmFxGlOR?$rQkdg4l^THeJ-DpmLlNBI6L0^+qdEcJsk-(G-Vm{2UL@^)# zpb*7;{DW1@ppMgpiG)h8Vvcd_5{s;lW{5#TH3yUfVVO^kV;*w-Ub^bQY_x6%=bxR(MI?@wj6(j#+_6E-UzZE7+1FOWD2B zoERV1_PEnlQg$o9$Iv|ym~WteK#<5V&r=C+kmDg;P}(4pjZ#kR91H1DrQH}#N!7p+ zQW_}Fs~m#-k&wzt>9`-|kW!2akP)`2$CX3B&pDu5$UbV7IdPPs-zR@qc}!YT%$>v+ zEU~=~Q`vSXmu;NQ4H6PoIg;Jz%r|U+h7sm|JYI=PourqO8zhmA1O!ATTWb!hw9-6p zf4UIA9Ka_xMp!@+*WA57&E`ul2lmPCfZHb7-dw&v?R=%w$2mE-BU@F7FLr_J1kly= z17Cck#1_EQRL3pAGZ)z_^FfL)Ps_I_*VzTwvAeF(mt2RNhi&pduW75fpO?o;g+wGs zM(1JtB_UhF)#Qa7B%US4)`g@~N7(d4N<#o(fT z?TN&1_O6BhrWS9?Y~!RS6|*D=2r_1m$!)d)xyK& z{Lu?mA7Z||Ma-{;aFF*9YrjLwb>O_H4=vY;^N^>+Q`g*IhLE;UpRwt9)?nUl}fe(0*`u9Th&nppL zMAyjEzi?RmMf8mVI_N9t6Y5}%rmPPaYKAV-4EYKdYKAV-4CQ%d=pxNfo@a(G(hTK! zdT>#z2N$Xb7pVu(Uy&#~*nd&02N$}F!mEmkGV6PBfyQC3_x_~|)XEA!4mDyYyUq7L z{9ar-Da&4H1jqzt85Msglj}0uR};($+Ekb035f-bC<4R&?Bvf&#b0wBY)9Q3n=FiR z5)h*M!C%l>OhOM@DqL_(1d*4gXB`BzvjY-#V!Q+MQ?j!-XIN+$k&(>ki|B~amyMpj z;9@$YrlAHRGmheqDMes4a&tu>H&@`Ji*bZ%MnEVw>T^z@KKS^EpnOjL67!f8IGriN z(d!8Z=QxV^a>D#t(A}<b6$ZpyBj zbZE+1H<*2f112bg6S4KS#@Z#vm zG^nSFE2s{N73ucScazLYHFN(4*p~5QFaflHO>veA7-VjwXkP?vhuWcSF|a)kV9Nwn z&x6x!gEqoyZ~Rye28L$ujK*0mIGhQIapS} z*9$y&y}+};FYuzNfme6D4?$-n6MU^tPqIZcNRN-=j6r&YEkt2t53dxAt=Cam_Fl^Q zeT~Y=caYAJC!Vc^gRJZK5f6ie_VqBxp%D=;_M!C>e}M~`W+++V*0XfSvYMc9%tYfc zMrXf_rPC`WZL&iaCq&+?`nFbZg1d-_Vxa=sWIl@)AP^1EYXJiUqItf!c-KcOHb+10 zydY3fNjx(7q7gC3uKIelDPlDrSN=F1ai<{^KV4!+x#90&0Du zvb)Lr9~WR^-3#pTs2&N=9(e4#@Q)(PO{sfJ@|EJ;0VG%Hkv_y`(vS7w5Tl8azNISt zSJ1uS!IVF8JTia8-y}S`^*Yr3JODYYiR5A$L^iCtC3cs+H^Q0G4#Syr()K~y7Oe{` zC=8=tX9|w?aadHM9_Ng8#T>hb{l?y4ef?ZtQ|3>8Ug!gWKz{qV$K5NP4Gv(O)cTl* zzPoJF7Fhns-DQ(zGi{R`xjXIGqg+a_XAca_x(@63d1agb96Bd4@{cN=*crdQoAg?o zrIlWWv$WEmKm(`rnK-H`-HJ<^QWUW#^&T3!V@f$P`fa5s+onmi5jX2eRb&$XmI@nj zC5^D-WQ~6>Vf#B=nw4&C%^%uYV@pCnVnnD9iJoKr5e#?J+_}%~eyT&BdILkCS z@teoK_1H(l!A8|%$C&u!>woR5|Ne=$zwwiy9$K!6uYKhkC*Kp!JE|U*rHS7;@#$~8 z|6f1yq4WJGuq_SA{FxBj1ZeD9HO-SaP0 znW8|y<6=xV;=R!)^}q})C_RSXdyRB~MkJ4JEp8CxLzOO{7EV z$L@iZ)bSxEnrKM?+d?Uw{pE+FbW@o5Cmux^eEb;I8dgna99Bft=QXJr%gqPW_@_!~ z)EGh2#q%doZtT+|r+(vI(Qf~EfXD6rahk_*|2V~?^N%MXdDa5ti$`5eWOkMNMsSOL0P3M!tGPssv$V|y@K34SoP-j{rK?=M#W>xgqg5WKdxVxAr+ZX#1b@awI6X~) ze*ZhA!s*BKFs+M@WwaoRlwts1K9OUEX$)cgvyId5z@OXLtBE0JGVhcoIYC4+7$B&f| z{xR8rdu>-lYA`C;p5rfbX9WeiGILf?pew_+0z+5hlQ`CFB?Y?n7)G1j*NdDmtA?(O zuNDwhl%s{H|JjboPvOix6Dj{;Iwrwu)TR|dLo>zP%d#p^5TIBb08QlIdV0XqW2Gcp zqB?VN@?xd4aq<$S2jk?WN)N=z%}S?ZuKgpOin;cW^kj)kjz~|GxDIC!GRJnzf(_y_ zp(lvKsW|ecx&PVP1Otu;MOHudp8xpre<6+#;L=aze-b|s6J$%Sfgc_qxrV$ZdQs+I z4*%Gd3Y_rMo}&&n%p7@6h{l5eW$-4)lmZbvrql{VP*n*UK>m1?JyF^jJsvV&;`#G> z#yGZMp?NU-c>A;1YS3x^iQ+_xbi?Uk@_~=#>~4{1og(N4^*Pel7POgM?ht@oJ5$5v z5pY`2X6!V%D*$_djx)BQKgU*Ji3;@g_^%TXyX{D}@w*h#O5;W@#q_EEAX{N zE5Hzh32cC1L%v7*<@9^>--JE-sYV!xjFGO>2blesMySPsq-(NIp$$w%J7qA$zBC~E zW_;-l^T$;HC0MN#%CJo-l;SL2CH<7*k&6pD?i8=$}x(+Lb25?gp$dX{jTPI1?jqIRrz)!EZPk zTV`0EGcWVWVwp7yf(ACrz_Hneml`xoeqz_Q3gPm(WE-_)n`_gWuq7YJ(cg+ z5*@+WU1AM7Wo@|-lZDd$#%uKkK-Q5SXO1dG?E_t^&DHfq?R}GtRZm$NYg1m?nmv+% z(xj6QX>xlj#|Q*#H1?ZIaQ z$amLi&*$w=T~7`AwBSq0;N0Rr$?Kpm4;Xsja*J zeK5O%)AhPx;!b>rhgetP74%&YJo6_ns4Shg|<_8f9 zR^x-A#{#Z8+#y zD_A#{z;sB3Z}^2i-M8;5Gk@{C%BoV|%s)M^l8{uNZ^?$|S5_`V(vg5?c;??Q^qu)1 z&$l;TP1USVw{1yPiiiaM3!{a3RdCL$W#J&TabYMjzV9b+^ES}Jm=K8o4P z@z3?l48F*^c=d}QTivJjKxc6Bx~V2fF0MtvAuBj#lG@I{u`X8Qlu42sbKZu$ZH3)k zLXw$VUR>D@ZTj^Ww~^5Qev)GpXNlk{kWjo7jWD{=nWHbMtfq%weM#m1($blkmsXO_ zQTFP}zM1DwR#wa$due6x;-wegCW?t`>b_(Ys?{f%Fh&&AwHMuX?S=fEMmbHQJs9^Z zJlsSK@a^I-dV-QQ8J*d7b7e`!S?(TH^qY0K<^qAN8S6?6f6awL7MZeUR9}t#xqn%Y z;?^}66`HKt7n|lqKdM_UmSC&lmJ9j z>V4J21%b(l?wM(Bu7rGQ5`Hk(LH;jO=cbQ;eHHXev9v>h54MeHY$m?ZnU(u1tJ$2F znXJRqdYNjOHM2);;5^wR{ahzjd&_H}n<7HrPVEMHYN8-tegYSN9;TtpmdhhR<9vm2??4gg`{qW(# zU--^v1GFEeY_aKraZz?EZp~3v;MVfc;4FoUk!vRL=y{A^!WZDm+wkcq6SWAR4u}?j zPG3+ebozW;=s}pb(}9-RX4LJFB_X>Cym8|OZ5)sHP(h2rU-{5h+u^%j7G40g9#0`c z_$amJ?Z1e;h_=1sz{9g=qHT};e_!)Y9Cvmp*kc1kjy~pO%~k0SI}`S_=jO2q5V-tET{gR4mQ-R2sWz)u&}C(lDnx z0F;R+^=6Ci)Di2JSlHb`b33a?e>Ci%9_>nv@&P@1Qa7zftc(ELlpeh$=On=7Z`>%K z&?D$F-2n$6291x3Oo`zFX3QT3IGZa6GPVlyrge*Y9)?ug+rov&(;Z$0q?NM4nf%6D z95G}S=l@u&DB+d%jD80Y;=MZDw5-r4Z!CRPd z$7nNL6(c`X&UqgrTb`63&v_p|QXwGt0D0j~BV|D1K*|0Q+JcptPko-t*$mZIO4G$L zj8?m-O45@wnNI($7utG9&LYj+ABAZb%7D7#nBi?1ooV!#;Fi%67j*Pz)B?V1Nn2EoqILB)mo|QraA4 z9h9*WvC%V($P^7z#B$|DIF>bsC}K(Tq5~9-IM#wC3uWQggZSU@gOyJAxVM%dMvA90 z9>2XG&nwdGK)(;M_U3*(vhd5F?q?^&`)8`3K~iD9U!FGGqBr%!QI!e&gG-Ie_|&*R zQ-q5YSAll9pKAMh%Ke4S9*WwA!tI95L5kXj!d1x4tWH_mSQaQ-LJ=UG5AQfdHHx;k zicU~ep=fteG@=Lh|+fi zK_-u*&eT<5FFZw2bBFoSlN#j?k;_l2meV{(Pi?!3l4M*9hV;FWNPl5+^qs}oJAF4020Ytkwkatw*j}S z<(7AjO0IC{q_epfD#535W1-!t=c)~^6Vz&y@pohyP~<1FJGn_{y<99d2)@?H7A*;%S5 zjnu_yL3cS^LpAMaofbp|qSJy3($&Ff!KaGR#mvJ9*kQqVUmm3^(}UCwKVX=yC5W!l zp)mjk^+d0TSt{LJT6yKsD5?=E&I^7HSc_e!N!^e%k^6p%SVGVbp6mc97ggC7qW&ORXFl;EL*4 zj4Ltg>h87fHaaSoSwa%Kj0$$Zn3NZsUO3*XP~-?9z_YK}79sTXqi6o) zpRjoJa^q{i$_7fv^}TIG1}nrHatai@DloMvPL~lGBOOo><#HkuK4hb>q&bl>awMyB zd2dNT&w)hc%w*WOeP>A-ZYZEb*()D>S}D@>$k)b4tHI-rJ*~OY5LDi;U2di3j7Syz z`Mku0N%b?slNhgOAkC|Zhw-#BT391$CRTO5AL}$nF)^L(6O%>4lQYfC_n#QWczM!^ zVOzlgOaw2&_+stdm!YWFm5K(5O^hbJw@uv9occpI?L!SFiGZX1= zQ?L^Eq=2Vg>F-#<%2vS#tYEBF@JCj#xmEC|R5?5+oV??z(25(oICa<};lGg;~`&fH4=3+-SNMD7b2VxJ)HSq68Kxu!Qna$Ny_NPxY{XF78I9!eh*Rwb!s!#O$NTVI z+8IBe-y@DU^Zm4++b*fk6Jy^Q-)xUFdbH;J6ma^rq5FGueGhmE_^IC*lmz17GH)YG zd3G>V^H6vS_yw>O6n14MAo^xtMY|*j6+9r5EV#&LD?LFjXCAxKlSqT?hQ+T_TKa;U zv^kx{*mV@{huv*3EnhQgu62sK%J}+hin1SIj{YmBh`Sh{YL1u?fmQ*p0=iSdH7r7T zS_`fTYfXtJvzRbJWDC}iI@OT)|DtsPZjL;sPj*|oOmz_XS+d3LBKK=u5e3SS7aiyI zRhyT}eGqISuP*r;+*$ua6m;opDkNgNDoGv7$bFgJkbx1`${3&O*|BYa)yll=?9XU} z?63?=5J5dOk*O(+zFJ)%Q`JFiWpG*mdd5w)!sp4w{#tkdi&lMaHIAdPT0VgIsz#4U zjO8|~uJm40$ZBH&)V4*xT-saGS)V^c3)Yo>vxs-m<&Tn#!nO{-j-m-GQ@^?R zg|z~|{rF|K0>437fv`ebm~I2kJk9o-i&QaP>94c=E7RisFoQB6Di3(zHzGlxU?Tlv zYI6}#1ds?6(%(0b{3c-P8QHG%K`Z%6tAzE8{)saI@8SI{{bL5%CzXlbK1_ydH-sex zT(I*1w(LM{Wpg$&hA2h7S<2LZ5Tz(PNZHCZNK&s==k>t*5_S-c-#$D=fo}K7;Z)Jm zShpfC(f~hPvWmP26CuCzyg-)25T4#_iT;oq*RD4_VYT;`uB8XUlO?V1@7v$n z&x9nco`58+Zh#}LK7b=d2lPR9P@=TaSm|@G#r>L9HjL ztt(*}9wkr7QH4XC_0PJ8&p>+$$oGs*zK`oJNF7;brZ}e3+Q_Hl(>8L2dSA>(1{{|} z=s9fHmpd^lkQHAF7g9vX4^m4Y(<=fU!bBeCqIHi%5#}N`lxz!gkrxzUF7kpC1kZR~ zo8HYLN1^aky!F?a5=>E*wxMCr=RYuD%AjB6^Clcl^FVl&#|M&?5^snVKp>Rj2qct( zE>Na$nDi|`_Dw1+a_GaTL1=Y_;0QImLQrAV>0VwIWL}-TG=11{?j4kZ&g9Q2xUX4t zfgn^M*&U3i(5c;F!#Kh)%s;i_ezCaX0-XUoSI$-X+bMF+(yR1t%0F+{cC<8W_*3Nw zmeH`Zc-7*IQlD{G-a^paNtKps@s7~zd6g3lORCik@qJzn*WDYH{OXPWTie+{S5;gK z_?&ak+}tGGx486bck->F>f-`ue^-=jWcp{;kzEYfbL#nK|>j z_spI>d-h!EKfSRDd2EO0+=5v3%HS0aV&oMLuQN1W$hDcr5x+J*jv7Y-Wr}*#I>M`! zX3Hv<<>YF0@aUvm;uk`Y+t-5ga*I?RpWH689b|-Zncv+1r?aB;bZ;c|O}HVu7-_SRlIR*=Wpd4G^z6%|0f}kygUUt^}Kl0=RxagXw6K zpuTE4NdysoPBLKE#t@e4Z6?Ir`w%ug;azJ6TzY>H-qHkPJ;;cy5m}grLCCt6?fR*5 zVIW?OH<6nCO%=@#q{yrTKPUenz|rwnz>4qBsm#6=`G z4P0fX+f2^{Jwk(!u)0V9PZb=p>3r_tZb18DNp^#b7Uy7`7l0V0(eXY2fuaSDC_JJX zj+YVR8_J5pcw+}SzUiPS4BZJcbwF297)ldnVt}~fq_rPa!@~@vpkJS;9rg!+X!-RD znm)=lMrIFaDx@e%OD<^$&*^BcXozzRQIvqALe3e)h^TaQ%&B=%P~T5{RdKG6(s=|f zu^;oUrYijaeMvIzC1Khd9iRD4`P(0uAoUyjg zs}3k|%11OuOp`U#i1l^ed4LBTF7`3ViWkl?Z@|`i8LCwya+|xqnK0H0QXp>)1je;| z5pE7czm+t`cpZxJ@hAO^F*_FhVJ2U>*6%+>f(0{k>ddc;UezgLZ^+_@{LCvaa^YVp zmC6^bp<&UpjDPo&`23^$9t5sQW(3q*u-o2>RJ zWWt_#LN6uqOc7NP2FGEZX)%wJ7z5}jVLB41m+Vi2lOkvC5K8#n6=LPA4;e`cc<@#1@&$buXGq3B^5$`a4W=T?kG_}S3()- z{W{xUIr1JrweAFptFwH^< z_n3Fk6S@89p~(o*h^UwQ36l|`B~e%(VKPFrCkpE&OjssbW$@1oc@KdyL^Li+xRWp$ zmEy*SA;hGCj06o0(9_gX^mdF%32H3LI_4Fehy23`X>P!B(^f9|uue#u&G*K<1ePws zEk?pWgn;x+wLF<-i^DQ5J}PWXii5(ICE~;cHM;3SP9RO?I;9K6)o6g0D7n9xWIehD1xbllvL2q;Ph<;2I_gN!8hv3rm~zUCUFo6Gli$|i=RLw_Q> zifMn5caXOh?f{4nFt@nE61gHA1;QTkmFva(h?s>QM89lGNxQfxsH^kVqu*O(hIf5*c0^H zW%165dD{@#(LFZ=jEX`Sgu*)HCiXC+>``imq~$@RD2UquP_%i=DA<}w{U9M@*`t&C z_-~Zu?!oJL^ZX?u_Z!-gXGNAuAH#3x%s>Z#%=BlVy+CHyZ=l^khFT4@3&>ERfnEeM zX0HwOERZ2efuu$dg|Wz#>o#2k(XjvXX(qQ(f<%U3KIOdF>@5zLE}0oj1^bKM{-U?9 z=;hLcpLV$a6C6;Ojmq&-4rn`3RHn~-;bnEMOF((vz}Ob9$UPiHGh0w!$zunThWu_5 z11al*=&)Pi56w&{D#3!Foux}Muh^NN*q)Pl+f3N8vnm-wFY~?)b&WLqmdv#>VVg^W zY{^8&)9}n5ND0Nwm9spV58$99VnD55h#?|^@_-1KRFSWLfCxHy4apFJ@XPQg#$uUc z2*L4>6ko%6=x`WqrIm^;e&Yz3@-N$wkcFC+HX1gtIZ4qjslY`Fn=)NUc4>b0B6l?OXIS}Uk z)OD5YG>Y^s%wtYgz;ui{``{JLGBnO$p3uLnbFy&8$WxX>SvOXZTGO|v+4 z%EHWK5U0+=1j+dGp+n<}w2g^?A?HAL_&pdzJh2QpGQ6Soy8qOIJc zKt<~91}akTMxcm7r7+e$=N_@bgh`?Pym_~UY(1yAbbfzwxs|p zBSp-2WHl%=Yl&G6*hhM@Qu$hJoTgXiDsIlD5NXL!%X@oo<73&7PWB5+2%Bu00uBX< z-U`Vxk4Xeyc_1(-oMX~Dgii=8C2)e3 z4bGEZ;%uk0GDB;nFU*6}Ecs#C6w%4J_}V%FPlg~^sLbC3u(8aG`NkY6MQkI#M#Qsm zz0}Vb&okW`%*L_-^{cSYW|5|F!6Q=zn^$e~{|XO7-wohlM0esZ@Gv6({nJd0C{_Gf zCWh^8Idi6AVut5J9)?2#Sv_oC;P?>}Mhfo@Immp#HNq9fv|OP6LCR#%6kX!WpS`HVAx1jf zZ;iWDdYKB=kE4?VRWWv<{`*nYo^ZJAN@?^^*__g5L`OsNyj>@+n|jrTraiBkYzzrw z%EP1znm7=LOp{`KaLEVx;GjhYg*c}6TLctpuJ|p=`#3bXNyY*wRg;Q}z#=%(qb_BC zS9ceCyNqE)ID!60RJoX14V5eVbKT8#Nbsvdbfh@9Lo+IT!UU!p4CK96skA|(hgE2v zuA4gGz7k6q8+bsT^fPO64~8ZE6(t{@h?#99l|~q5tRWiB7Fb0whRC86 zlCMESZ1Tv<8Xr(Ri}sq0&@Na58g?g;L-LDI5t~lg7GGY<3(a1M+YCWev>3kJ@md6z zTlCEvoKy0sv%o!4ZbgahBwUp&A+vN~fxw`3I@k+`M3>I(_IHy=+)ALZv@qVXk~9t2 zLN^WALN^WALN`TQXx<*oxFQFfpKU7lnu4vg$lmEJlPjH-1Tg0peidXBEwPih_JfVG zAWk;t0vNMaK|+|RC?TcznMgtbOhSKgaz&wpM>>scEod|{M;b*&fqtfGX+{dINVt|u zFNsC9P%TWJs1DU;DwrceC6_?GN=l1Z)U?QD170>yv4~|ujyNr1S##n{(ZMD!*r7Q7 zej~~dk!{HHrgMmFL*_Uwa)elhBKHY$l=+CRHgD&Nab&hrYz~9Ia%2Hdi@uADrNO;) zuG@!X(iL_f(jAuvO~cW#_-dqCtxirSO2PNDf6Vi5tc74+#I%S%MhW(QAQBhI_ zzo@ZGrBZ*fNXcbzk-G*I*%#hWNaywjMeZNeI4<~R^QK5?>N>N~QY^<`gYp&`uxY)l z%hE42>9`wQV<_=jd~3$R`|h$HCrsnzg^@t+ChyIm+`ew*-B}1Xwn$-nciulz1lZ_5 zlz2BUZDre;l|>-?CzpG(=wvK5@NWE0RVHbb|GK$B=f*p83is}GTRpp5AM1A~*_(9T zTW($MbN91m2`!FUk{gk^G;8O0m)9LhrnlT}c9)*gKpg8b~>-$ubO{_E9Mp7N~ryN`0Yg_^`tA zeeQ$GuH1CMU2B)<&m3?+W>@Oh54bOqoqpt?JIdLzsj%~)`w82gsxyb&%lKJz$X$PC z$@q-5*s`pDw=Jub-w^&Ga8(RG2wY)cU3=KQH@kI`|J`H3cOm>z@ZHuz&tdnZW2?e< zj=H}{JAHEs-+14B%(3hA6(`-79X+j3{|9$i#_rJ5(rP+Ct!ee)bjS3J^+}3(Sy@(E z+jVNf&@kxx5=2yoeuk8M@Q1WIH;OGyhOrk{sIL%vq(WU_cN9*oRONR5PFlH?Z_mS> zh6~|2_|d6~;JNsx#^8U!A5VWR@Hw$~>A5pw@OAjlj=`9G&) z!Y`E!EBM(#@ZG@i3V#lmbA~AWdi?SHJAu!m{3yW&0;a`2*odF=f++s;gW&rI!H)yS zEBpj-87{2w*9XDdfhSUal;BDH@fPd^j#uaz;CPpQ4_FW{Ea3Zt;9bD+7T1D+6JqJ> zfR#z0r&Xyj_MQ5QDs^|+YVcR^yJk{hUzJMQPRV5b(^FJ~{jM$@rmh(}^PG%zJ@GH_ zxpeSMy=s`6?le!)%?+wrFBz{sR(jL5D<|L5PStdFxH^}d7YtWpOM3VBSmi&r^`a^@ zU+*2RnoE18W~?#f(V|bSR+sV)$vdQY{4BjcrE2x(tJU1>BhxZgiWJ`R6&+sNa_{=K z)@6cAeRDGWx2)~@{c5$xo~hp$0dl{qSJkNdz;RWpW<(V5sN+*`hyHl2IyW1VO??UN zCyNk%)U~W?f%TKMYWCDH`0a%Mb=k!k>l&g*kelGemh=36$PhmaSZ}XW+lQWYcE-8^ z=qggl;8^GMf7>5O+B&>@(gnXKCGHT+#02fbt;UM@! zz|$mfSMc#g_-Ek4k3IZDaCDs2jLYF>>h+CkfgU$n)upG>MOPE{Dz!UPzfh?@sehJL znX0hsEV^zu@mtQ*LmJeX&bTInL+P-qA0uw8e0PREFhUR~tm(%V^{Cc<#^b=vy`SM3HLk9XY4XQbP^94Rpbjh#2X0*Cm z|8BIZD(wT7q3qUsMyru}@)$LwEDZa$qi2m#4Rs;>YtT;!s|f3LW7Ony2ydY0tMn@k zN}sJhl(B9i>2uUvW^@ntS1YTpY)}()^9gtUv=!H1-`?6W<@&ZeTCWqL-%Wl(0Z-vR zf(t1%{B6{gW0`Fh=_zB?i27zyVjMucY~xK=64qEV!e5+8l_v#P%ovA zkxo{(w@aZ|Jz+eA+A_=FVP%Mi3n-0=QolT2jas;MR>n}B48WmFeaQ>b5hjHOxDU zNqY~DDn{^q_@&3fc%f<`hBf-o1a*z=>Dd$2dbkVSH&I<=k1SM9QonZcZ6C>43veyC zCfs;j4XyTUMr`KGlnV9saVhWu>Vd09yoDSXN%G0eS!`Uz?n|2mgNj=z&(GZU?YN z0Bo@Yfk z|8I?Yo~k`}r}|d%5K%j7^);*2hYC-xRgH;L7A>HK!C&1$r}p$K7Zi@JQ+nfd4lGeZJRE<>?50*#alCsp&-yr;~ig*S;N!68{KRAQBSU@^g zHmUTDg4&?&cPh4xW@fz49$K=HoO|?_?^AcB`w4SvOUti@B99lQY*gR0?V7?*KCdoJ z*ePBApqi8($GqqKptSThmfvPQ??H9cp08&jHjR5q%KL1^ZaDBG~seRRIAzPJA$C;1Z~l+TD6_tD?y7d8*(W@$MLI& z6CE2DCFsycvJLUi>hLPXvjdCdg|D z6Ym9T0g{|w#ebuIO%iuX5IG+qXh)Pd10&ZTBu?NzQJC?FLS1sIFnIh4hvHaI%(tx1 z8aND@04g;P2w6(V6$3(^{Xj^Xff|_RQ-myc{LlQdcCjvA5l-}e&?s1hhM+>$bA@NN zs2fJ>zQ-Cz0 z$y@F3>R%pAPPE_9qkoz_nXYFD{c4;!ZLO|7l$>sVO<#H_IVAz@=$;M9tln}c=?!7V zw%S=fS}l-s*P&!n<&OyQzdY-lwbn^J>~M0?aG4&%##^%)ixutGdwRp+w5_mT@s{vYl+W|;s0 diff --git a/wasm_for_tests/tx_write.wasm b/wasm_for_tests/tx_write.wasm index 90f2c3c8d5b31952f100dd4442ff0b5e798317c3..632d99b37d47f8289e8c341141f824cc87ee4bc5 100755 GIT binary patch delta 138396 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 delta 127181 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 E14eGR9smFU diff --git a/wasm_for_tests/tx_write_storage_key.wasm b/wasm_for_tests/tx_write_storage_key.wasm index 5d600d185f1a13fb14405ca7bad2bd103649b875..a0fb758ae9663ab199bf18cb5f5f1d18fa7e34c1 100755 GIT binary patch delta 59 zcmdnERc7N>nT8g|Ele#vocW4u3M`IK7;>lg_Apg4vQB63Wzu3~Yd7j;0%B$$W&vW> K?MA(9^}PUGr4wQR delta 59 zcmdnERc7N>nT8g|Ele#voVki@3M`IK81kq0_Apg4vQ202Wzu3~Z8z#=0%B$$W&vW> K?MA(9^}PUGoD*UI diff --git a/wasm_for_tests/vp_always_false.wasm b/wasm_for_tests/vp_always_false.wasm index 9f59d000a464458e7c344d54430f94f8fe35f92e..751f4a41d96e57caf9e4e09d8fc8f4b6537c2d08 100755 GIT binary patch delta 127519 zcmeFa3w%|@wLiXR_BqKpIp-uNPx1n0Zyp8|2%>0_x>OE zQ~2b}p4l^N)~s1;&6+ijeaA0q7Q9r`@qqn``17TkuAV4uOITLK5<*1qXIoJlf%#`w zg+ju%@dB?cY1#G=A=1AQ2ShhqvCfL6EUz#9h!`dwPQNKep8BZh5(iE_?BH?JzIfaT zKUyI?`~Qe*#Z%&G5#B7G5t9X%&v-wzVw*lZ^`~uYc48I3bA#~Z%ayK zsvU4u$V;{+W1(iRA2Dwd;tj3dk_+&>7C*_j6+=>E>qsm1TRS9D=Kl!GOU`oTNJ};= zYeuM93CD_y7!a~sUE7oMoRDnxQc8HCnfy7X&b7SMOwU4&-J-3IMM5c0%tCUzJSKsn z((43q8zfr+s39eg6mQi{_C)e#;aUka+p(1Go!jQx0Bg_n+S^5|8&SWQkWwlW)&z9V@0W3kvII z8e9np#XLFJ>%4fK)f%+!xH{L~qZw!mx=IGkKrzI>(5gboTZC>CFhlcLN65|LIS8p7 z5Fmv%o95tAuDPV*B`LREpFVrYT=ed?Azwr%`+bkdVKH`c@$FKzvb!SLkrDQ#GdEvvf4bpEH511#zt;Re|_lBMCY2#LyLsy z^p}qgjY>y`$3HMOS*6P4Afnv&KYv(k_D)=0+@~Q&_U=bolt4TlHdT{v#V}I zj((9#*twbs=?zW)TWn2_IHY^%mW2U~u-9vOItBnBo$YO3nEu-#hmF}PY5~m}XMIoE ztyA!8mGFJYWM=gtQ*_PO4{ajXFPU;pdFiGM+y<|C7Es%iKKl4$>z*gO^#fPC#cKJu z`-9y#wZFFYajV5jKA*nv_-gTd`mob#@cZKNGfm#kz2|-CwCW**rQvM&`+J*i`Ri-% zzhmIcIbk*+T>6cggLG{>EK}RUu3tRVe!=_8Cb2S&M(|=#@dHl02*qzay}Cx{QhO<# ze&KYWx+ZkeFXa))WP2U5Rmm}S#4T)rFqjH#uhXO^-GcnoE|XgAMlz}CzEdhw8qN;9 zLvaN##1SBDlK==Np)W{4;UED;#27p2vRo*W+8$)f0_qwJsDB`!nYPz&AXN{9)G-)R z?_fwf@*$=F@TCzT-y>hDnPez~{sUGmgQ#zlDVRQp_;p|Y21@_z%QdySbl2WWr*Hf+ zS!BbPuf^{Vz7k~ZM{n*?QTpnyFza1k`D=lOr_vXExf}`i{I4NoAA2t-ta;;qeQb-4 zHQT@TWx-C7x^o9p7^*2lEp~1|cj@&89SHU|OxA}4=Ex?ni}UzFdgKR`&%t0nAUpm> z1wb;r8E_U*0?7#ke-NBkP95=?1?wxPef@I_)~?f*fOel-u(p2V5fuL{g0=JXam{l3 zFVp*7{Y8|Rd-XN78x}!1)q9(M31y|AaVYv>^v#xErYHV>W$l7ndM&3gE@Ma(lVt@0 zj5A4$YHQy4y@JZVC0%gce(A%m-5}Pc!`EFbSDA#h*A0!e-)a<~_p62Jm#>>CF|dzY zaf0dRQ!SSmnpb_VnV4W+j;ed6sk=@)LR z-SgFEzVhoY6nlBM67MFJ?*Z-Ii_$0EUY)-2_S!w)ZP4%h*8y+Omn+T7#`$}D*}<1X z(&u*9)U;nhCw+C^oqRek(7KXePj2K_!by-t)MYXiM8oRyT4oTA2)ym*QBTQ z94M|%|FCDA+5CpmWK+0#~0J_hbN1!^brq#3%}PrJiPYS-d^1eUgbOo5=>*0 zCT+-Dm44yjFV)_<4zI^qR;vpO@wo??kX( zc$cLs`wmEMh8%;VSKfx;chb8*eNtavaZe!JDuNn%(i0y2amHfbkiPNJqxR(MMQaKk zn}#k3tTSW6aYZ%|7XD91e3l7$L`fkR#iYgu#VGw0fduo=+dmh$sm6fjni zu`W;mi9p^YlA>&h9yMuQv8})$XKtY-9E!7GNMVvIYes>fETG$wB8a{KW-v6{9ve@* zvk9F@V3};K=y}4-&_dUkiFh4B?rd=~4zp*9kFh(1TdeFc_L>);-zFoQ9t2kwdt25# z+TT@BwG1&1OB^CgVqSX)W8|&}(*COoV@A)bj#!m`_SF%k+sW6+=Jjtt=^AhSn&NF| zi2WCD?4^1PwR*u5CR5u9aSE~q742eNS?LR2uU>Q8YXu?9NY=h_EhZDMzc)I)=*@+Q z7r#}5c*9$F;CI&FMyDe?PO>qHNKb$Fi1hDv+!x*=YU5+ly>ARjpa1r+f|3*db739Q zIo}G;azXK#iGw-Dx2l*bOm0m-@}C)KH@$Nb<{0ALBZLOjpaGr!o&ofS!9Whm2XgcK z2FQ=YKqo#b-TbbXZrFJrP~N^X(}bQ6*o5hSXA{2q_e`aSe8@_F_2Cf$FCX6Zd%SG> zXvN^lCYZ{KG{%!YzFlm+qC$*LCwHH$D^&i8Wx`9Z+>IJ9pADA;fsFPK7UF`zkoM1m z7s8>U`_b=-2vEVF9u**i|J5iE9FsuH^Bl!WLuRPd{d#mQs?|M)U-gx^&x9?<<)sR3MGk_B*Na!+zN`Q+$NW?|l4 zs>xAWCcc1<@xNX!uFXm(dL-m! zl}5Wn)-4%oAXbo#sREb=Yx#xHDxnIP z1&%A!ovCdA{EYTm)qQ2HSZWvVf-=+QPL#O&-Z1fZv8DU4;o`x;@472Si0bfO3QX_* zNrQMkH0VwDx5tT>gjnn!H9@>D7I#l;gdZ`NhqlGy?vew=?J}2Gq!S-MNEAtw*g9F% z0zmf%P2&3mgm7f{FAfzm7v`(j2&#b`Spj1n)qV6zZxV}^e*2! zOq{%@sl0R3C)?P4`rRkl*yiQlZS2K-ImBN)O?;6)m8kaMV32FM@aoRLti5c}t=GZG z;20a#<00R)zu))Ej{b+&=wZu`PZw~Q_@kzaFLL%7^*AVsJplNNk&iR~sPShZ!}Tvb zeEoG-y>YiTSp2)ELu8-#8xI#X2b$_IIug!+>R6sWr#*`Q>~L`@iYJ;y^>|$zL-F29 z?^(3_lxFb&Ogw++5e7pEdDG6PYahSzf>pa$zww%OKKUmcA%-B^nMa7BW}eOQ8%9mb zbvrM4^_N@MY<|?_``r z#9w+|HO1dXaa0pKQfT)Fya*sD^9_LFPd^g=^XL6jkuB#TfJN0Z2JzL6Zmz>kLQ;T4WM7BcZeV zgCB`YguLZd|5ra2M^r9)Bx4)}PcV);x?lP+rYLgT!`(Z6CdOm><-c7fEB%Ac6aSWZ zzUDkpnUiOBtJm9o|9PU*-cx+H^-+BN%P!d4Om~xh$?5o>1-nc^&d2wBxr8qd+%tXW zBcH4+`t`d{P!{XW%e^a$m3+wwd7}G*`QjTz;Di@15+4citpCC#q7gx|LoC>D*`u5a zcW_8UxQJn7vOEkykABm|lm!=c-_;>{M8n>5Z(^Q-*{{4z94mZ|4(1 zHH)4^!dUOtC;f|lD^^snWuc&5AyuSX<{$k#F@DHa;gsW5fr{%!lRj=ma%=ZRzY~v% z>Al+lWLovu092Nz^e~CvXLOxB4$%FPh4; zY3O#i^|5-i)gOO@P-1!a2{(w_2r0Zu98r&MY!NXi46KHDeFfV50zZN-yB7LqtP&Hb zIxb%&4y|1JN-tKMIIjwxs5<;VuM+!zjusv4pEXNHu`=|cly!bGAw&LeR*T;Pt3z)T zhoHH0ZWLSXLj%WC5Bz+(!}|UT#2USJu%eEye+_ZvbqtF4{>{h5pc6#jXjR za7?NgylUWnG}JtsmAV4@+UUWF#o#;D%b?4z2a~3*v)Jv`?3x|sp9mgYpqIA4iYFu-jevvUhL`ze>@xQ+8-~61ocb^OW zev3G4SO;p#hy3gO_`JKlMd% z91E;^5fZZ1uX#xaZcoVsI~cFH@mycuCab{VOktb~7G%u%3_s*-VCW{wy9u zZyfnDwo4$~@``wI0GDI99>C?cx5R($RY-QHU%O3Q#(ukTn>Y)&tQh{92+6g7@trqB zzgXhSH`zr0+&9If5`!8i0|x!O-xSrQTCqCexE*V0$3L{@)i=ctfbCb_5?u%icZgwR zko|T*TTGbW-)l8k*5nLhe3&nJ!10~@`0FqDv)&fdQ2q69iw^+nn*S81l^MeJVp~HL z3gUO9WBEJca5Vdrcd&hgZ5jI>Y{9Mmo$rZD_f=>2UMoSB?wS zV*2&J$m7cDR&RNJ9P+R%KaOTE$&WAg=X@ZJlKnsT@B9FE5&GirztgAWzyEh}5Sw?v zhr&VE9RH#CQ7lhQ`Mcw?y8AC5ia!Z)Yxk;;MAZNu5QBIC8xTHC)A`VJ-}Qg9TWm#@ z)OOQ_-y>uX*uG85v(PCR(#|J3-?Zh;3@bu%_P&zrf`}~lFDsDy172@Lj`T~za%u6( zZCUc!UZ>w3W}o=)hh_6I?EQ#0{5WjRsl^tUB3H~vKs{lF!`nxDV~?s|Dv(Q6FRIRJ zF7!-K4i({bZR_ZFVm<_Hr>O)u zZG*co#Nk1LQH}+exYe>^(PMXCy7Ujbf9!87#da?}@2d8fmdUT0XLW3L@ykrPZU9PL z3JbyPgQ*4^_MYm}%tk96o-fWv$UpvP4F9nOt0Ux}eVrV+uOtF9Oeb~1E94haA+GF( zG3wKv1T(ntDOQPW&iq&LVks;;#<(^i!HirL!DX=`CXuAZ!DyCQw1WVr0_6nEaLSw9u6V>?KYt71FbFaY$ z4e~HmlHO%p)$t_P?>J6N?;0e8yo%OfIF;TXbV7YDVU12mjm)LorBe>bCETYI4$38L z&#uX02Ula1X7#@ zQji4_{v;r}!PbpLl3=UDV&XQT{4l4?SwN3v)*jH9eo#8g_$^kcG^vLAB-@dW_$JtsXB-97CM1F zro@#0r)g3c5XC?5Oi1KFVvrK~Ttj{e`dhOFIWa@0MeIs01!q`DdL-K?uu&gm#W2nQ zmP}}j+Gk5kTf(DjgjAu2N@1Orq?7i+%E4heU{%VGu+ z>3szGBC=`!(x%W+>1JDXN2zXGRO1LOu_9WB7uO7q!!;4|ZNlHO{a@)Z81$n$mWmBkCY03~3%-A6VVIODR zX-~0Eruvy6dhDr!UIsFMI9os0VzQNk@pG7PzeV;97(b`l6?Q#j-jMk*;8n*I$LR^; z#Hp50cp76@H(7DT-SA3|vreYXVWI1RfYMab@p{G^a`BOjH|F98Fn&lbeh}l$vsk$< ze<3l#^4f8OU_M&Qum;;(kTx zEtA|BuhRZL9t(+l&5AUBHh1E%F{$y!fLte!yTKh-<5uKgB82~ zPhwiS?a(^EZ!_Gbug#Nj{|nE^A{z$2zhfR|Uw3bo1^#ZN+pzupn-_`d$*}y}adM&z zbA>YS6CBvL3E6D^tFi{Ec)Fr&j2!9i-=Y?+aQ0LCuW*!Ka*nJz_#a#3do##CK2uK5 z)3B13ex@s^+smK|VK6z3in>JvI#(dcMHFlROeqMKd%zUyOk%1ID3)P?Sr3bP8eka| ztTFP+ybLMJ3@OXJ;>n&omn$S5bi`!Ys2^YD<4=$Vy?|epv+E@7Ite1!-y?fTnssIV zNk5ep{>5j?*r&Helk|3BNSPoMIWp#ykP9aRbL{jYOf@56tNoP1#Jr5d4Ra07L^;aP zW}?ZlzbBjnQcMo28QgE6hs)C-&Vi=Dq)gUsU^EV`6yjg)Yq`8H^*lq{B3NSLYkK$JKq{w$`H zvmkF(YE3p0Ookvd5x;GPs7z%Lvja)SBqWEDbTQ0qD@2t%xCIB*tyn(Nle37oHut#T zZEC}=5HcWK$*_2`tXB~{R0CQMQt-H~5YN?k*a7cx#9N6a$7m{t5KId&pS>fT72^o~ z%#aT#l~|B3@DUibG}uSLp7%tXGm=93NywT(ErV=IV~bJk-LS?HsAcipKzw%~ofGQ} z!a00lG7(=GAy5%o?L<6SLk8a*y5PV;c%tKpe++zUr~UymW9-wEg698ip!rp#0W_xK zk9d8B+@l=v;5H5nm58?)X*SX}C;gSr(#mF5X42RFV0q6wr#d5AeA)diE zhRgxJMU&;uAafD%EzIB>&EOl+_!eUAt3ahR%?gQdK?Y%-Ce_3Xxrq$IP-^YeQpg7a z%vFgDzLNO5&>{;8k+^&JZZQ+wQqZPiZ7SZTirQ3Rn<{SOo8dF(%)m~MkoRa|t5-X( z)w^f+?(Sb#$qEtUiQfJKOsA4R_K!VKI+*nS`+>4X6#9?W$f_7#f_Gx7NX7laYT1DF z#%fuKTt`*QsdYuvy;MXUk(`6jTZBllzpPr;993aFEZSAG1I_TD+n3>3tG5FRgWiG; zgz@B$(YYE@;E@S@FoD0)-~yv2sp83bz$GK}iR>h*I6E7}LRO$4Dh5uZ`r$>;C~N?F zS;Twu5$_@50mfbzBG@!?(fiozE2DWt??fa^^reVoiQa)oj_8XJ z%Okoo?9+(ej?ABdYRiWHQ$+voD*j(h^mckvH-jWuPVYk`OLQ#Y=ZM~m$e~$I??Eg_ z^d`G2`Wf_%X8SIb{0u#C+VFp>2R^a5CSmfwj&IR$=`AYCBVjutSrYEdC*cl6a(ugO z015q@)u-`oKQdSRV`cHpk)KSpe--~!J^L@xnX!0)g@7^UpJ5vaTLlKG|FS`%pTgFY zFKk`;!nP8T>>$yJNKV+6B9_xv{?+3?P1rh+nWo1-7@14=`($DJSMg63wttas@51&k zw)q#^e8N27f2PP_gh>8ZP9r*sFhXpD{?1yo{fK1EvCW8NM~FT|W@atg4T$ANh$g$2 zklI77PK9@X6SKLO>L1Og{i_a?u~2s4X#9jJT`-x(k{f57x~3TY+%Z3OupBwiT|kh- zQ^A%3G$n6>1RojG{q$>S!if*pH3)UME|+ZA)W$RO&z^OQl7KWU161!#q7p zrJaMQw4=z|Z0!SiB85AOTKAbkJBrk&QYhkk2gxBBGNCO4$b{w$AQM_NfJ|u9-pPb& zLBxDA_2!eQC!b7RvDW4snO5ReJ~MU3K06bQJ6IMAV|sj64(g13GJOgIhm-%@!Ll|( zBD7-wiO`e*BtmNjkO=MBJBd&;Hn-%HXh%MYw&jzkKh`=WN21MxNYuv-C;v#ojN=3) zKf^%z;pBhHJp1n|t~H)^aP8eL*0LNpPqbSpaGq$dQs6v+lL{`vz+;7-NT3~@C-FRa z?nI<0C)Mr0zrh>XredJq}se>hXtj~MeASL)CyW3tBong3vV;jq9s**ZKRKH(__sq9Aju%n= zqE5f)-6P8fh8N}G3g=-#shCMgNK``YzysP1QjPx->{h z1Sw#HASD*0fMbG`LVrELIicXi0>1c&;EIEkwJ$Oj_9Ms3FNq5O)Z^s{_-rmXUT^WB zI-JSo^W+zIVPJ8PTkA8r6&tIw$G&(dB)2(&=S8xcBY2V|srQTgyPm$QnBL$J(ub1F zT6>{NLbT2LI{KE+db$Wk2<|b{R*nEC*s%{1a-(wEVElZa0Z%j zf~-oF=+~R#szyJokE?3^ur{uS=!aY5*!fbxi%>MXo8JQcT&RC>q7Wi!G;5K5HiDrg zl65bS&mz4wHv)yEK|zzYH3hUZU%M4l54@Njx(Bx$>eQL~Dta5dulGjPV)qc>Er`zo zGP+dIUuex7oWRye%&|!4pgd{rz~;I+*iV#w4SjBur?;yJi9EJ2N5%Z#orr;=a1Inz zHYXH03g!Wd&4B{TzFvygy7`%UFs=l8f@nag16W?^qB$59vZXumS`#c&Ix)waX5MOP z%c_`S?mxl-owHFoP@W;E|2A${!Y-xkj-`Y@?<6^HS|J#5o1NKRsP7oT*oaL-nR9b^ z#x6~`O|@hN`;zezusd$baS(IWoI<+3Xzj?AU5$^H{?IY*5uZ934;P4oYFyA;#(d zlI@K+u!;SQDcth=3ELZ4(ZKDEW+SlUIa&HRnm*X|yn8 zgR&V7Le}KApX*aZ?l>gk^iv9lo&qI;;yC`alBFI;DV_z*oDABGIIzsb;d`>~Lq3(5 zV)ZiA6X{T)2VIUoTu3O@N{AF2JR@yRY(Yaa!@`NdTU8Vm1Ux7^LrFS4bsY-KL z5LSOath~&Nkhx-T=FPI@5sVS|hl+EVal!1QMYuLLKRa+~2l_=kG!Nd#om2iJ#K(qC zG0oY5ffIyOg-Kj@3kEQ$OCbt4T&7cT0ur$?XgW`#Ix2*m;BhT1jtc8o2SkfEHL-LK zN)J+Td^Ui|U5a6B{wqkH!ZV88I9LEqGXQ!Kji%a)G0L|Y!-wYg5JJ&R^KrKiKrZ1Sb2Bcy_Eqo*UROcyD11T1KnY}nT?j!NL93M9J7#bE^M8SG!q zJAFV73x?+>w7n6+>W{G^j0J&%kv!z0@z9@kLC3uyyux|8e#`ZaZkDF58RRf5=3a6G zw_A0dtGfwIsTmWDfg2L?yJ{Rq*<2}28Gy`nu9-xbF01u#I#t%xW}xaWGZTxjfvLOn zd!T4n+#LwTP|kFG(7k!gVtSwR7?2W1CbX!nG_oT^Nq-0b_baP1@qE+kye!TQz4Suf{sXbmc=P5?#K@$%insM9G9Xt$Du%~dZ$!x zZFZ`(A?av|?Xw+ToW*Gz#$se*VK-~Sjxc@{q>a^FXHet}5La{ImIxf)AyalBp+L{y zOq{2$F&hUsea;Ui4H9B{B4;x29CvxnFea>>W~7ncm7>)+$uz%rA~rf>gyGCi$QjSv zeIe%%hMPmqp$xZ$9HT-)&J@^HFxDQ2I4J2yPnWS8*le_jnQ^7Sg($Gp8ek(kA%EQI z*p85Mx$&go*nkQ;JNGjjjTSb~(j+w4HQ&*aOMFX3nj1%(MsFa|?g z>JskMmdGOT#@+@LXGji$R&jd~(J28OP=2p9ADVezJK%j~c5FQ)lVK>VSZyARKhGVn zkuA+)H9ptqbHTb(S2$tMia7OK&OTq1AwtxjI z^*0YPNAST%mEIzq!<^CA3GF(;9OBmrWGM;nXch*ijuP{?Oacjp8$P~=io;W^Mg(wB zMG)BfWNM{<^S7l_VGbk?m{2DCzkFLZrp!4tLTNX%N^@lmFf(NPET`4(b ztnpN{K2+Zy$Dw*m{BRa=ls@lgt5GYQ0@*BaxUeCGV}@?blQVhLkcR@n6`8}EJP5iL zchu=?%b*T%KJhLd5bc6yln0{&fizAD@}#0G>rl7U=SNM>-y)|z!-!*exzmQuDBN%h zLkgb}9EtO$bLOPzi{=@79J>aBE1cjoA`f?F&nWIu>Jth{6l!1_U}F!cWk7zMcEn{P zJnh(1*m@8G3@k!*y{%7RhS?Fpq3AHLrQ?Tp@~@@CS-Am6FvH-}{A+N- zdmh0oBpf)yC=Z47)AHv3yc3u~J-Wr=RJO^uJjr-z^dWG`vzI)m$3x(fXD<=aXD@M- zl2i^igokmo+g8K?<1ea0Vs+#1rF~jh2&jIfSbaM9eL(!%$v6BL!SR zY@9|Vp8BY7>GaHPw35n(XOOTKQb;4ta>6jo*d}@&lf386$OS=yhmBB#DO6-gqDgBC zHG%6wyq3c`439=sfS3_PUO|upfMR55eI8q%g&uGo+gz!TIpUl-kG%09k(Ye;!pwu?~?7a)k zZs8Wu*L4&|7mOmlpeFrY(o|Vng#&{s1JpnWQDliGmJOo@V*EzICSzn z^2RVvUtb%U$2+`-=|L6DimSqKBwyiX2xixg=iprm+^@?3V~b4t+3ptjl&b}@Hw$Qy zf~aT34jl&~lGG6jomMu55l|S)!#{nNtj;=>K`W{lG%$Bl4jPZKe3-tYfoQfzsD6U` zN_bxkgb(ovqMl9X8(L1hQX6S!Otq9rgZ;KY??B<*BEvEgWqIX{ry^SHSe#wW8%Tm# zgXOu5<4TdBb8tC36LKb-#Vg0kJX|c2DWV!D%h{-OKVIR7>L<%H5F9vJo{nHr6R!Hx zcd(p@VB%!?MFgq+o8*xQ4=@+B9Nc7$K}ij*`~|<3BlZ=32E{wx7t`Sf<;o`pQ>6!4-ZW44lu&4H z0Y-Ze18!7acz;42t_ljz)!q+`^Kdj!+LH7`@w3*LKhi3&y^*aB1K1j3j)V0Qn_KC@ zaw6HSRbh1kfwvVaZgCY$<*xj+d7n6zk0Ow4@)@*}?LCB~VkBj`)mAZmQ|H%FVMJ#G z_%X5`7X{**&R94(D#XtqMOvLO$fUCOQb5&Wuz_ucZAPE35 zREKYwVeGUgh_6%rrNX^oAU6d)KF&&+3sX6LZl+R@6nK4^CG(Q<#evx3u1dfRuChp? zQ*j8P0C(^!)TtF$2!f$CD12I&j~n$x9Sh|d5ZN*p$>3l;CL@~U#jZse45KW$!^fAC zDoQ!=;RbIE9r!N!T3P6K-H2;z!e|~8ASyl20!RD_k17RLsJ>H4O96DFoLL zd%S^(FwQ#KB~NVv*M|@}2Bm1NB58xU%$zGL8?zb=#G7Cp!#i5I6tYQYk*EmYGsoZp zqY}3QIJRcYO;#(GHVGWuH_oz7K$oCcP)I+_a_Ae0l5WwNnp_T47Ptr)nEGj%OtFq; z+5{1rY8``GfsLZ6im`{E4q!b6`?2ZlNELS9~GyaKS!NF#+H;V)h(Pv?m6=1Muq z7aJwM-RCdgD8KbNYAyFyT`!0FXKa#{g!r>f^1Gtge`%8(CEvZ-k3J;pWzW0*ehL9va^{e}_j)a_35Xri>h<9|Jn!ZAu_6-pJ1&x67q@l4agn?xELQrL zrscW#jIv*{MB+Qj{@0ht6>`fj{qW`Ti=`dE>eap*EUrdd4*x~+`R;FBE+^w#!oT!? zbA?uehQ_2_f_^=I>eZ8=1AWiuhd zdv-fB36O7{qyJakLw_Sbkm6#0$W?$Fd}Y}`{wjGqzP8)_+pFYn!ZpuagJzEMwl7DW z6EV37K5+5!wf;%h$R7aB-fQF`D*GK?ED~Xrb`di3OLx7f6NR_^+OJ+NkKflm+jA(# zUFTtbbfwJyK*$pRPpie(#=cq>4Q>w4+#Ze)qZjWGlx9*-p%1wQ{x^$d#l9jzc8z7c z4b3_&qgn-Sm>jZp-67eRne&=evlig^VHq8M)x zL~vn`XXiF~0KGVPbf5!ePv=nSKo5!t8tHFbEr)&~fU)-`!uh}^7GQwOG?8ofXk@Mf z_ypU9Nf7ek$- z@23KBIYER%JqUsAzqm>c9U3_?ArLEUX@Cp(1c2Crp_z!_vDhEJ8k=k1^$)mF4m+s_ zy2MnGjKKtmhx0C$Pksh!?VVU6sX3w^%ro_bLZmQcwEm<~7Jh-Zu_+Km!N@6@z;tv4z=QZRTnV z99&+dS%lV_m(`?jD0_>bMFUkz_X%-A8O+H7B0I_2^2^JKN8Z)!)q%eAG-^ zFMr|*yb8RE0et@XE+EbOB7pzvGxxaeSy?pKbc1o{E;yVNA;VV&vyZS>X(XD?!BB2i z0K>Auf|Ws+1Fi)Q@q%U0NQLw)00!4Ms{%#^G9_uIjge=-?vB749-HPC4T8M zxD|QpW*PDS<7W9+d{u41E%HJHg=^%$i9dFKbB+AAJaE}V+>Z&S4n8YZjVC04y*aDe ziML5!K8*Prc9{z}54QO4-zFEKeCl?X+x`h>$z}LfGf2NNf+)BoL_vJpA@?v!)<$9@1i zefimPG;upyj+%gaAkVOXu^H}OVWhNzb0mzloTP=2nwP^LK3CSF?3B5(t`hk3*8VA0 zHaF1kZk;P<$?myx<#$9WG%0;1Zjp*Cbi?Pm5&y40l6CMIW83;X`A~5=^#okAoZ$Dq zx$UxTnZ}s8t@|*CeDE8C`p?A75L2X(FuI6XN!M+LNwbAp76-pG?SB|G0}qjeq`K@}t3Jez{goM47wR%I`)>%t*P@Ke1O<`?wH)me9{b z#3sh}^SA#|&N%5x+;)zpb0Jw@jD1ah91C23$dAK*TazDGSn$e^k81UL{R{7w6Hb2f z^k~H*|i$C_i-z%F=y8okr zM0q4Xj#{41kE50s^5dxG)%-YW+3vsH3*>*3pA6)GpC1R<8}j1-drN*CVDHS21MEG1 z+d6=KW7j}z-^-5!>__==fL)jm24FABj|1#w#CEu!yjLbq>dh|;lsDwZ0lP0h4%nOX z~LP~wjKyg+$fejKnL$d3c|qxo^bekMN-l(+hC-v^Wz z=O+W~lKeQpF3XPt?DG6Lz^=-V1MJOy+kJAFf602;im!_OeZ6cDTe?U5Nj@k>Tn+Ww zAe*rgiQq8IfxUNO&Fd9CE(K!%+UY0n$0ydV@ejFQP7@#a^X`}LA(*{E_Bxv`fHws5 z=i~#Ju$X~&iuEN}sIb|%#>=>cwIDsg*n+&?xFEyg`G_pT4}3Lu%$AMv8j&7X{pO(WpmoiXLxYc|;rocS zu)}}{EIQoq?Vt6atdDKrP=a+ok=)>4@gQ!WdCtG=lm}}E}u4IB$xd;)^UES z$4Ku9Jx0!X0%xf@{rjKD45rB-BL{Asar2WpWArI`UL@lj^)G%(o+>u_+nKy z|Hkto>fg2*j+no1#>?@~$TED^;nDL%t>5??kqGPWxN+5Gh~KkW*7~PCgOLayd?@pq zFTwZEPJc$0G{8~Q3C~0&e6y8GOqbiaW67E>w{gc(Lo0r;9*+CE{YSUTA>m5wIXORl zX@5mZRgSY7+{FU-cf(?-#FW#y2t~%o1Du~Qx*gtHuMCz-ksIMg60RnCix#fSd@Gjg zBwjn>fn;Nq8ysaK!B&(ZEH}g}qg^#b;Tzc@6XTZE6u+)j%?#CMK3wgsoWBkxSV&c? zA?OawVq*&E$z#+{^d7U4`7NQ7=fKtf6K*;pQc?lqh@K~-ld(Z6Xg!v|g4R3ZS}e!H zAHFx{*dSF0Hmbxys(6Z95)5(Nn&c|MV?R3zor{HR4*r|XIn65GAiE-N8kmq!agSRb zPLC2o{z@4o`rn(MKX)1af&rka9Bcn>UGHp*1E`4@pWA?Wp6;y6S;!Z z8*wf{`y1r?i1RImPehz{ov=CLbO=>L-(Mu(9R=ER3jy?A-e)E?gXDeIS~E!AmFkfJ z`+5AsTI`5_k!KH01<H9B^^azg|0l{S@I&RD2TfvFA9aBD%e#)%h;M{uCb0 z#KRK^e}-^DajSDK!exn8=RAbH(N^c*5H3OZJ%qS*+Bps3tq8x5MJ&x8(VRmT-C3|N z%n{MXGq}T_*kYIauikEt@K@bwM>yNO`*~S4R?rK^J~rQtsbP)y7#k)tMk4Te0eoU@ ziuG2=D7Iv=all%(ki}pgE$KDM)gTIv5EeR+g$B8!5Vsy6+*Rn-dy#ogvZWB4_~3Wd z*`49wpi=x*;jbQlxUIk`$6qb}a3z6r81)JMFrRfM;cqDZ#vm@dA(nFp6wQv%EVl%T zrbKVFsY!PGy<27b7xpSk+)B02vSg@r)eCa}?B0%y$T*EEvf7>2QvGu}p-ON*QaV=# z!V{zL;I1$-ruKc+s8}F8RCwnr-IKUuO*?9h_&kZ_R*?TmOAAn=k)L|(;6kGTKgF5z z=8!5;^^@fj3?V~(2-$SYbQinaRj z_@#4TVRuTWK@$yYA~+`=!n5W=H{ik&<&q1r4BJk&Xf$Jpityv87@IP!zXPprz&Eil zkdy?U{Yc@Q3DTC}15U`N+vx`FZq#jcz{8DtpKZBnT;U|)WGK$H9%`%veKU}Kq8zL9 zRB@DJb)G7Y=BZ-;*$3?+ziEnH^#!)I2Bg@loS|3Q!ZNlHn^Z#l#6$^w$jjJ?#?6C| ze(9v;{w05w6HY{?Upyzh>e+B)lyQLw-)}f(e`Vzx4)8%_3`@=aTcB6e`hLDrnl?TUNxH68aP-wmwH!J1u|?WsqD= zcXx&d?*b1V%4j$m?oO^MfsUB|^xve;h}fsVx*2hOj|NQ}pp173*& zjgGxF!xXM5RKu{fhSwExYfa2+ob4dl`@@qrR378ma~%imtVgRsT>X(DRgWy(T@!&A z=&Lv2Bt*BQkOLfn@AyTqb=*8TR$Pd{<6Y3R!fE!SCu;T{U z&PYr^H%C0Z+Xn4=f+Z1d4&gj1aa8rNBUBM-5aI0=aqQ((;4u0JVbPD^N(+lCmz70$ z3ZmRRVSR>E8KhzU)S&Z`h&!+cl9n=D^`!xEBiJaVAk3P~tXtDi>v0aWZj_TrifSn4 zS}iGB+(n>V<80ngy7CHG`Znz9lM`60Mx+JbRJ2-zGtKK_3>#1_ok>5n=vC5tSMOYNW%e&3UJRSIm_maue6 zm3MBNn}pg{5hqGLTjfc9Upa^+SwRS2j8PG6H3{*{axiIF1}aAAst|FK`idZ@9>NVy zMeZawrs&*6vJ5KFCOS|8xajBdw?b}IR~(t4s{9ML$x*2U#$#lNX@oWZaDGe)=Ct=> zd;`^GQwamw4C19Z+%%HP3NMI#R2MZsz@T82I+DB&98wLsVl)Q}Rp?DdmNMFi7};`B7i;3efXq3 zG6RgzrLHduvh~akO zPVyROITJwgnnkW`*=@~lx!}SDi#j@c+r{~g4Vf9nl06a!4q(dNDxEQu-!AElX1EG= z8bjD?Wej0CDTWwT>eV12icH7(-(R8uexX>NZl3IxCm`-?I z)}$Jdqwz%0l>@l=`TUml3_7X#Cvqant;;xtF$|1Hx;Oq5Ls?^sI8QascUv(06{`tq z#0t-zr(#fc) z3T5} zHXTtxPXhHIa(gQhG#s_PERe79r&@H1DLN3*13+8YLdU}no1`ZB>)yhx5l9Bf!zuzp z44041(3GN@^r&5of+XpvTNw5W+#Ya1SkVtJoDg67$0n`M z^*q{A!)fe%RSkzlW?S)6esd(ru>c&2+}z*`7lG!0Bras5{lbZpF<)pV8jX=L4D}Qu z5=aUZ4-YM(&s&|*V7|zNKxdSa<1~l!Fr_hLT(Df}KVZ4iJy{Oy%CTLuoVN4vp*rkv zou`dH$N;xQYh#xc!U6~kor*z*&M>Wp$nq|Y6`CsL+Q@20}BPU(DI2G%O{$%d>~9zxmnA{7hCL#R5@)$ke&HV zwHifTJ-x`(uv!yPouK=*je>e<4?wdFu#<)n28I?mSaNm}1z2a^!%hOS@G9zBF zS)#A}0nSE66H`p#wX1n4X6~GXV{9~12~xue!Bg~^%d<*&<|gWQz9Z}6jXdua%pU#w z-@%D|mwj;x3u9ELNyNisHOZv{Jd4B*sSRCbx=<*cc}|cym`Cf(L00s#ar3}yh1Sgh z#;`>>k5|ofC7z<1g9L19(^=yZnB!fT@BAOWD=WUl zdZqj^?_mSz=k|xA!9|?IQbX(Wt>`(<1ayoGCVUTNK^D}mgs9i~A`Mwwy{IRhSTAwC zlx8}EXfkl}xtJwp{Z+KSHPgZkNP!jJA@H1(dxyZa7sF`+df>E#%z^cCAMe z>{%Qp(@DJh2$x?#y=9YNp~Fnzq#~PyVa4SkmazmibG()Gbg{8L>PnfvO#@K_f)5c|&nlK6~BC2)1_A$WNEd@Ns zQuPc*s44~xs+PfFD#e?g99s=#5ZK#}Kv&_eiz5+WhK!!%+`J11HfS8)kM)`{&`BJG zXiO?jsqus2Zzp zAm%T-)2>csWCd1_W`8=}+!ow$09nSERD*IEcX+V65G*LE68OY%Xw`B@L#d$!Zk}U2 z0-O90H5vo9mLF)50Wt)82>HS67P{jTrKmKJqVhn7s7V)7C*mW~nE65pJt5)IlEz!k z%Uy~numh#57D?G#CyaE}At@tWbed7Th9Yf{c#T2QAn}4-JXpMnk(4K1lrCILmoHuB zgszsZ(t*!dQq{XzPRRoeo)nep}ddJ8Ym6DM!YAhpP zRA)xMVwA6{LE;6bLjPuMb#~rTJJw_*0+Qi!IfTwMw?&P_?5mcI$K^N>oM5=Q-tYQQ zR*%9IdYpAB9I$w?6+|X;+<>cwgw-UdHa8=!-hbmmxt~M5V-leyp^T6Ps-py0ipQwq zM*9cvlJmu3{vEsI_~Tn3^Qr~T4c-(}G9QP#fs&%dHd3jaJZmIlkPJBdAg6G+AdIS( z6^_#5UWqR^B4$5=85mim(TMz^+FB*qFZ zyCs8Vw?NM%&3J5Jf~9$?6f6x?!7nzubPV3 zJKzD!|M%T8nyR7^4PNWDojN^AS4|MN3mA&-Y`vBl^-^?{f(TJnOEW0ql+x0Tswz$s zjUyG~jN9rUt05Z%s|RyCIyYc5SThl~j;^EA9g`o3Aay%7XTT>IIN*f1m@W=9vQ(U08>F?hLgFoV3;y> z6=BgVbv8y~z9l7TwTy?q08CISPBS1EI0sA612snZ`afjDD~h7 zGMU-X7;a`=Nd%pX^)y{mj7NFFJDD0HrUuM0Sp$0`TZ3Lw$<)9e$kqV+flDK#8*RE$ z91|n?p2Y^Z!SGPZA;u|SsR2$5@Yhw1%0!3|ZXrP?+QXzi#fM=LdjXKqD3byYy}q3z zVn(_uP(rk_d)V;FhOK}U{LPrkXV8kK22nDDmnI%Y%?w(a#u>D9PZ+d#O##uNWH5vu zAV4cffvK#~!X+<8_&9KVK^?&sLO4_Kq9fs0H8n_pGam6H)$|}9!`17i9n@?bP4$3< zsUaPJCEf-t%SUEQ_aZ)49h;5!AU;`rArmKvK3wb46+-7@qMhmzm`rJV&N0&LC;{b5 z&07S$7jfXj1K->#S|fC5fYt&I|aOX%Fv<6pw z^kbNs6fk2TK}Kzq?L}>v%8JmTR2!vQ;26qs714$>C`G{qqY~ohU z;?fI_B@9p!yo4W}3Wc805qgS~(Gk$Mn%5~!fsRNZiEKb-ppK}8gHr1`mD3J6y#S4n z8qhIXC8%0WTs`8{0}>F8sfj9zrASpzBpon>SmNtb-{SU^*vgSG5e+Jf69HiKuSS=huVE%sy_S*8OB`$R^A%L&H-Y$;r)kmmE%#96?T%_YdSC{zlN z;Ne+hLAq@0SsE}LiyCkW-JE?V2fl+4WnYOwuLg3ofu$D`(N2vmeCpWH3mm!QqY5Z< zR4Rpk6#YuE)uU3WN)6z~&%})R+|1Qy-JwCpQu<9N(Dnc+$gfl(ICH0GVy0iCx?d?N zDa>Lyh9D?r-_kk64keKp65njcW)gLW!Z2koL+fIb#c>!5bkn#4gk4(&SgbqKv$`8d zv^p{e)Ic5m6kV{tKxGOo`jmXg^)k0gcP)<3!UV(%bZsfRwv5&*8=UFddUS1l(6t=x zg06)a>8^#=)ZNEdLC@x1nVyX)^ncK`*>}V;$A1}ESR<|n(!|y+4Z4;-UF{7Rr7{Cx zZs^kKw&jm+DGYEb;bJ7@K&ab|Ny2)Y9>~IAzL3a_Pw+ZIozwFNhZ!6>TFMaW z=3-3Kgw3==JA(AI3odj_ZF=#|A?3`Z-%=|^9DzSJf&CoAMOvGl=PZG^I4R%?b~>5ce;%$B%r!Ok?WF#*FPV}|~Z!%X)-1-`FDhp@C|JrMLjb1z7 zGak%SfGy^kxP7;=*mj2)^U))zU|w^0AgSv8?-$#rL=Ism9inFV>x=DCc1`kne|xcg zRtmeW{x5rP0%zG()%)L}hdb20b!$j$rj z5J-q|N;s9I1I3s^f>mPFQiJKgCfW#3n`b_LDjJ^+Hc|{~gHa2K*eFPW(bq_n|M$1{ zKIfiW-3dYPeSDwKOMg;l?|t{#XAf(wy{5f37-%Y;c70>PT%fAv1O%5u3#d}+!6AW# zG;X;bd=f0rhDQ*DRES~Yl81vf0I6QY3%B5K#Fku!P`L=FR9P-id5V zt@2r$BY25(Mhel3cy8nW3;18`TMe(XS!ij$CLsOyeO7zG>2O|okr*C9XG9P>fAvsA z1Uk@-HHvH(VlOE8tE_$HnYuNaN4;5l(B=S6RgE++X+e!7I`w+2SrFC;xiBf3n_*+%qoJ&!h`J$lYH zqAT@0)`+gxbG{Mr9*0C4(JS;EYexI@9B)Q9>3OIT?Z-!ji%=TTZF(MVB+mr^>erzQ6=P+ z#q~>dUNLg_#Jk6)zx~J`94%OtTozDJmC+II@B20v2+)8}A*$+!>vNx6*nz{UaAo*l z^1hRHU`BQJJZlFIsmjYw(t(5eVc1=EPaCQ^K}J~HdLyDFHgE1W0NdjuL!<8E_<>?8 zv4G63pIaQ7`f+{mv5%c343QTcZH+brulGTSuqQ847-ibf{&)tM9n3@cVJqn102g)$ zzz;S&X!92^Pg=Jcx~@;VXBtF4O^?P;ZC{ixZUAa-Jx*-GIuwmDvCLW&U1UWUDXU;< z?wZD8Ph18?XIz!@Kn3mBr{H2y!$*a9It>U`{6~K?Q%KY&sA_|u#4{9R`9oQzSPAe*Ch15q(n9SxkVwN_dSEfNk@;T)S~H^s~0hSmf+ zMNc+q16>mq(eg-K@~{iDvPpE%=F1kZy~bS>KU%W3plAgDTN6Lwvn&J5eTU8uhGk&S z8h$9+W{{;wl1U)lgYjHSJEwq~tHX}oA5W9JF3U|3vUzZYwAM^_J6K@RF5b4Le2 z1i%jNP!Kp#6YNosJ|}p_g)ZYiHTE?25e!Z{bxqp0m);UxKyvNvkvt-06&Z95G(=65 zs#FHyd+$L`AGv#MY;0!sJ(cJ*a$x}n<)Sd?&GmzsxgO_o)@G!fLS;QlhG|Ovt*5zR znk?hXIw-5h{{4{r&G7o5!eulJYsId@O+{K!<|Lbu<6LxfHD%0NS|%Di(STZ3DZihi zjyBqKnEmlMH!NL8AycJD#p0Z3o5HJjGNEZZ@p0lnT|*hOo|XL*Wv`;_zFKNs@3-&O z_}&txxPNou-L;*?ujm{Y?0&JsB@10n;F38noja@|_Ah@}B|8~*=L#%D9YZbz30A>@2w5+_ z=TwACSiivf${||XmasR`(3{YCSBR;Zp(jTP;{yMl{01Ts0rnu`_++rVVKIR&MDQ-+ zwU5Y95+S`pC8V->v?eA61;im00C^wL6Ffesr;*ofrN>E!nSrphKEO`Jj@mF;T>yiC zT??`j=^0Mzpb_3*v`qsU8%?i}=wL(Z#A1%p+AL7|(;VISguZXuK$eZz)V}tf~4z-U19<_9YOcs@r&@MYSDN$i)QUEW}%$DoHuTekbS!Pc)cKlp?$Lo=>Y zy~N_t(h=-=OR=4pvlHML-6XfjM7#IwKOxYb8Xw{zfmW+0`(ZY14NY5^Ft$|YE1N0p zz%H93ZT0CKF)y4~003-~1VlJ07V46pbDvA}Jzyn%N}wY-JV$GAOtX$d>WU}?3!ITD z)RAD!yR(T`=pxM$2ipp6*wcOX6u|t)Y7J79`qK-OkZyXXi!kF!iri;(L(d2jeo1oz_+*{@a2Ivo%3YJ? ze~0`v?o}RhgrT&*g!VbPsF>wnS%S_gq#thVEH0<`$E-3khwhW%FZ8@a%@L`R{ur8` zQuO{7!ea)MiNXBiTKq4AdCQ2-7)$JPw!fQhpZ&Gncu1dTqO86mu`xEyS_vql-?SPZcF|k?tgKLATg?d!4eoa8pX}foozv~d1s)(l*TJ=BdT@=SJM{HzAssNvsXGlT8Z^vZ1Q_voLCqfc z7I&rH9RM;Tt}^Y?>wtVe7Uss(L9fa(0Q_az9V*iO@WvtDP;Sd;1~5|ZB$=&H${)AQ zcK`TsTPr{&1(r&D1o%O{ttJ2ySF?)l0_d}pUYkuN$<-D76b{mH=Yo5YKT{{z^L&MT zJw*A{4B+R66JO&H>69_$f(~*iW6I?ljokjijPUVIR z1KX_TeagklI!v2DP~ycoZilelMe4DgG>oL*m7{iqjptcv~3*dZMF2~$ zR{c;S^;6D0UJW*)8xORr%&r6~J&#OJ4@tsA`r$7UW@~?uIw*B#i}(ok#!o=uR?vfJ z$X%`nmO9R17SXlad4&k)ji|=|d?&h$=cbXu^5{imUJm-s5ksVz>i@o1-QP>n(1mPe zns8k?;kA9bJHl)G>_Q0%4g6}FL4Q?)Nq(Xm77*hi6Q_a`9>`h*3FPC>T#Z=m~F1IhjrGhHc9XI=^6Q$jp_IiYoNb9qphDe6WYeY6qa(>Hcso9 zF9Lf!JdT`Rj#ost8Y08MMgpIe`tdYGs9AK1HU}zhe3#j??H%|oHZ6YNn*9#_Z^q*k zr3BJAFfJ3PDD2y?PRP~bRp)q$_&%XLyy{W74?2P88*McF=2pO`h1WBQmqF8Mu8W@H z?1F`yNQXPsd%|!X4dZ}W*P%1Om2-jh)=#PE3Ai@V5c?I$A1UOJE*DE6S+sv>NgE0M zE%zXo51TUJ9Cv}Au?ylILbZMwi*)@XYXb-Zq6NrD9gsyQ#Qw@qD1S9^I(-OjM%1N8F93xq#QI>2-Dw;1ZQZrS(pF+8Qq+Ajp}v zt+|Jo*JGU0(V9W%rC947jqm$n7X8dr0psL1RK3QDyJ;prcUJkLg=&BcSe}MU*8mnp zosd#MR8QbOUL?Y{TUd<|j5VS`5z>Ymq>~-MH~u4qcw^XIV?LkqyK@%}j0F#O*fDJ1@XdrkpaFa0*c>;g(3DE!p`8G>7zC_gV zBzoHg`T%Vr&G-ZG6FD8gb(wnLI`uiH4G_baX#)&A%G(P+zr>A_Hf_liy$DogYRPEp zO&ZTsO`f1dX#)zZa7X#0ef$LF&vVy+l%~I60fGD>7uN20MHm_%MQc@YH>CQ_rg%Ll zBoJE(Wi~frm$L{ULVIZm3l9O5*g9zK;TmEelBg}i!`l6ITrIV!&i`!?Ik$}@rY&p6 zv$atW9T;fFhdW8aE?Ft>H_!l$|0&Qb^j5`7w4+RflGA(%d(KoS+2c!?qUlg_rAjV# z_VUGA8@-gEwTGNq?TaPjTCE@VeR)UGKi?Ui+kjSIvW==~s+FZGA*E3ZXDc{MGZ_bK z^q%K|8mG7kQVKdEc&CPP5xTGjD*Uv1YZ`j9c9FXH) zLHWG_`_X15M3c&)5%UCawX00xRV^Hnr7AV&5#9@KcWrvpwz}7thXP=QPcl&6FNFdc z{k=jHc?rlhBRCdAG}%SegEs00q!gSmqOI_iOK)k@H+m=*6Qu|YD7`~QQPqC?z}CqR z{4!fD7LAk#t-6{yCg_rMXo=Z6SWJ{$FE-v5(r7%e_~5bR-u-(U<&lCb+!?`bdJ?J@ zaAJ@PLyJRcAna7HBHSBUVuWX=diW+p0r(`=#)tmt{lE3G&wlC9-;-J}%$<Gb}??&%zzasy} zP3|<_IHphRbGK2SM%|UyC;k&|fAP+d!~ga#M}GSszWlHMT)4xUpH=hLDBYpO>q6bP zE~u-9=j+^U!4knjNS$k309KG-gc31B_Nb-I+!dO&12tqhv3duAA2d)Fp>6Y6JI)bTQ+7PJgyMo0=3sZo5q1sE8M0K*XbR(E8B6}uTTAD7V){tg! zR<^bPGg^YbCMYQpvQoI7YF^t~sjLU^X=O2>s>B2Pfn3Q^1OJ1eB>H6`_5?iPuN1Ln zvB~;=G~)ygxBaUsG~`m}jL&U9sIp9%-V7`Vygyov@OIs$tjL*}CL%W}gR}%UgfdFQ z;KP@9KFk*{w=eqbjgMWP|JddB1^ZZM20N3-yoLVfr%hPuzr~D*ArxYSDVwqEoU&oc z4qM5wBv&+RMe}LNFUxlAkC#;4)oN~hiSz(h(^Jjnk`$UC5ID?^Pfj;+iz@?b&oAnLmHRF`7P6zn-c&x*9cn4o81Vnwz%67&pS3I(_UK_Y_Q z7)?viGeH(XPl9ZsqX5|r?oAyO2u30hK|RraLA5mfR@j!5xTwSAgY7BF~UoD zow<^rkj+&`PGCw*PDtm+;Go+@K$WMpZ#mPvL)V+ z3xV#KVsalul462IJ02#9l0eC)p3sqTK})#zK!BtlJ(Z*4(@*>;Z4?U`W^37M;)ZM#00GFi*_&K8iw8Un)~x}nca=;o%;`x?F3Cx4 zE?xS_dN8>(+*L?#Fp{>u1TuoV4F(mNT+lD21{H5|fj#XM$mBx(v>-FNB;Mv?!A5|i zMp2!XT)I^X(1zQZ!X3bgke#=#fDs3n7bc02zrgIP;sb%ch0BOT_T@oI>n<<}9RoK| zewmAO_8p|-{=ul;F;1Z51qv@|oR4v$K8%u)g4^%}9pv8*j1xS~I58+4V?=rUx%d6+ zBC}0wW&t8NbO77iCL-1Gwh8=vJ&j&qn@DyLq)BLTVpP2d)d{4ozyr$HWt`}as<#!u z*P39PU}RU)DIR+lI~+IpzC-6w@sgBb9D8bMG@YtSf(4*_7>QBT=y6GoGpr?)RmckO z?oR=ul_zO}7nF#`HA!@8uiNAN|xQ5680cCS>YFKdvMZEX>rz4d`zN>{eq_G4BCNvLLE&{1Rjsrg)# z*r{)<3_9GZ@3H4cjgdlB;JKdv&jI~rma*vv;{t9b=|9h;RO8&8aP&9BLo zG^WE2kzW(Pt{>k2SXkuXrX)uMq*RMK0z`@KS}qrLL>~%KM^bwQl9<*JawlJ9XqwN# zZW(WYiky^eU5d+LwhdYEVT?Fb6E4yV-h?`3qnBmIXtNJ+4IPA^Gjq5qR|%x-O&A=m z6)u8R@`l3=4p*YoR`*^GEAQaPS>l47$@5_UD5{D|JU$4e=82mFnj$Vl$GZUo2KF|d zJ9*;d8(qY64&~qFSYDzl>@2h{@d;`xW|{aP-^DCH^CTzb?=Z_{1-LntN&-R+FqpbYr0F_*&6#BA0aa8^QkBz~h-azW6)zd$AU_5`Pvq zx{?;M5b3W-~>dxXcZEog=@k>R2yJFOO={GfE#5H*ug}TFAq=7jdZB zBHsX`KqY$0@_~NgUQIw{3k_!Rp-_!cFY?jCYuf9>B!cIfKP;HMGL0w)FsClpmH2~J zkVQ{qignK$MDXoIUetU8;NzCuP>^{+>VO^i0j@`ng@Ffph8WT(8pRM`axeBDX>SS= zQ6>H(N-xp)`BWgUj2A|>i8cY%8tSzoy0Nj37a%}q?UD0Klksha@88OXV(fn<{uz@& zGmkq=q7-a3Gp}~1al{#~-g}{c?DUU|`~w)r%`r_MO_OJa5bFTk@}`ENh?(eXce@8|FS`GhG@!8gA2dtdz3hyMMc4^l9pfw@mZdj6U7F{Nro^n1n($w*G4sPcmUfO zJwP*%c}rH}Z=#Cdk^0sIZ^?tZ%ggfdwElCn>pRk15d+=F3BMGC4!^S*8IjZ7*a_R! zScst&f1s(=#Dbc7RL~4qyR!&*LPg5>K6wDD#;|VxVz;8wo(WDk(;$>$MxnB{$tE0V zfrvIV<~Bj(C1AsrGfy6#{M%&EVO6t(f0cN(x^PLJtQ^9ViV(6ft|~^1SgUlU*a49d zpha4Mj<3lE0ivx!x&}}e2<*j4=f&B@a%c_yt(EvLvRgZ~{{?l+#YBQg&2;nRnVF0P zeu=z3+uNBbfT4x@QQJ7SB)qVmBndTl4I~mTh*T4Jc^UNOuzwDch<l1LTikvft@WQxj5B2Z!TlBz;TuBLtv zL=px0Gi!sKTGr52?-FGEhglyWP3lu4b(5mMmo(00GpKq=gdW?Ac*EckP}ld}8m%Dq z7i-huD1grCLmsN}SM}>= zmdH2pHL{lkAg!8)<}}Yf{2f1d5npRnv85BomRVUG-Na53KYcITi99ilz)gcpfpd^m!020GQWv5) zOe_0sq0Zs4lyQZ*Xx{n=LG$HzZ=u$;a0LIAOEOWta>v9y2Fp$bl+!KAY3+sy4F;da zm#A@uuFl44W6eYc_LOB3RlT?XE#NpJvcZLpOOho##FAf~%r&d4ZSMZy zls#ueUw&nIU9mEmTv=YvO;#Vgvi!p6`6H!kG3A?&6`hO}$VdYt3{gBTK%T5#RX&H> zJFhBVu$K^UTp++{7LKyqb!K!StLvX^Tf9f4r!DnP#8o0uD)`5a(bn&qJ>S>3FvNw_ z>`ngBRpm{|h0>hZkKBTSL|F%xpPxFa617_XqKJfbccF^y42LUl+Ty}G5wCQ{qwOvv z@)a?|9dq3kAnQW=oyb@Ui*?~ck;6Ob`Hs+Y1fh2p#*F9+ie2d*F&HPyMYJs!>;2&6#uU_N0_<9yq94~@ivet3G=%acP z>TE{Om8KV2t1udem0!KBbVN_i?au1C0X|;O^R|_eIVHG=;7Ohm2ahRFr}u8x9ZPFv z-=#mOs{jy@0Dc@U?AccMvVQ!E{dj~Q3k8v;wb~JTqLjFP1Mpr2nQIb z;|(SS1f8{PlN6ki^4nIGqi~L2hV{#qY~&=#@95;Q!}fO07q4~qAjuF_Ypr|Bw$i)w zd^28Fdfv?s`;{QZ?3kXw=W&LRxEF6LJ*6@4!lhhK!rFz1!Mn))e36}At_EG9gxxM6 zq7&@2P7HHE(GV3@5F59TiTY-0y~Vvb%ctl7G^foa>Ikz!%)dpD7_>g&cC&(>hb!%p zaJ0r%N7GoeE){p}jW^sUWd)hxsmI(loT+d+jpw5k#5l3%Y^d;#mPg?PPMO{-|BUa#+_1xKx* zvzYGM3?SU=t=7TCBQ@>><6x(qQYTDZ(9@<@|KwYCw`&NS1lIQk zA;<6E%J-69-}CpmQ823Ho%w=OaEt2X?XEglYPJ~GU;5&wmI4Q4=a*&{iddmn4J7e@ z7FQ&xfO}Np9o+PzXGKpP9p>z&L}sx~@JHSxQOAQ0B>`)amjaBBw6hW|*qf7^Bjj;p zckG~1XEJPxfJJqo+7SZKF%yI5bg`ypkl;ENx+tuAO5SQjgpEIoVa?T0afZdU>;C9@ zJzi;ZYgP)02HE0cT9ssy4dbx%{mxe3|nTGfk$xcv0aodyZ@wzp(JfKIz7vU-+Y@D~>E5e@Me`5mds? zHvuXQ^sIt(qRnY`3{?68sHE7?N9b_XIb|P<;H#WsRTx2he~skfl-SsviY;AdsEGp% zd2(|f(4d?9Q04Mu`(S&qOa2yAZ<)fzUXcvv>sFn;#l3&ETV?8Yt>`7L8okujqH`Sw zTC^|wnuMpeur91fh<7X#O^cR*fV4XX8hE&f|)tI=(x zVIEis`D+&t2s!O2j#LtX;PZKt>UYpigj{4{^cbkU1>6392I)JX1!Aa&NN-KtFvuzf z-7ub(4dZJaj7LMg2}Y~s@ttv&0}b6@Vc19Z6Vr zn(zU~!I3Gy$`F%ivX}}*8PzeM*-VF2r+mYu^)%{w*nO4X(%v6mkH-f?h`-v;U$Iws{!Jbj0!u6hF?PuKpmk# z4FF0}AQAaS5n?D%LtC(#TtETgoKS%0BG8L~i&}R=`2`e^1x9Gut@#oP)N&|bhbasN zAXX=!K!VBErk>Lz-oV<80%-HYatH-%IzYrNAOrGi)cmedfZ+RGD3Ih3^O;ZpoC_#m zG{8{6lz^r(WVjeQN*xp^3-sBV)&~^m)e{t8`{_mjU9Kkera|3FQQ&3FZHfY~Oi=)K zNV|Y&KE2S@v;hTxaemTCALulx9#ZQp#l^q2H3+=;23QR4{p#YXuA>aA@$}Yg35O68uojcn?ej8Z6y}T=)TAITF|NUM2_`cr;l9%lj zh#3xS@!J3g!2XjsFvTHR;lO(yeNXY3aDX~_97seu{)=(oe1D(_9N3}_dt(81DcuGV zG~hs2iUaiS#BIQEz}6#=19IS=f});)1Fy($13!@8243m6fjkc2>c&1`o0Ih_;DB{1 zzYRPW9H?|~fP1x#En6F4|4cn$xOQ^iR{J8hKwZ1d;WWbFvek*#Lr3(7Fqd)|5Wg9h z#DALDRoCW$MBap#M^!kW3zz&g&P92GrAY$Ai_e0Uf_nt#4A=VbttdiJr%{SRQ9FTX zppN!07FHgXf3PSSOQbs-B~C1AW1i7$K)}tgW!Dsm!`F6F2(RzrhMb?cvAiZp-{i@g zaY=^*Rn{Jo4VSHzc1qTcE+{Lh33kDt`&8x)|G8v`B2-cD!bkA)&+-$X+ ziqM+2@z*jdv*w}vD|Fwk<>jA+(6s*swa&kjv5rxlT$qZP0T>G0sPZG5h_u`q@lE?3qS+KD-(Ems{h zlq)!|NWq16NXhNgZ;%)2FXyx()oFZryJ*Vl)jGJ(!UBZhj3TjNYa=QNv98suHZ^K08-NFL;l$5LmVWcXvnvBC4FNCff^)& zg3YVV9|c})WeRQf1Xo!NH-v5eF2<$HamMuYQSfe{uH+3rD+O0xJU%XAZpd45%IsZ$3x9B>Zs-tVL(X8U;~o3jt2-wUuVD*BTo(I* z6Kw7XqB_!=DEIu>Q)7+_!o3S2O$ zzHvM;CH|eI@!XSq^AI&pvTPNHy_bOoaJLJ9Qb4Z?&{GhvGHCod1CVM#?1m!Yh!!Ke z7D3}9pqEDiSU|CysQ7h5E>pVNSgZh6;>JDLNJh>BSRG{mW)=(^#b8c}|p;5MXcDxOVZ!r1LYs=5ipOtUb>%GyxBZr2Wu^LJ z7-rpHovOs}GlXZ$R${f8XNvnV|F9UEeK^Z?0(@$*@60b{(0(^V$*{2iCnp{C4E`&B z`6Wz{uJu;=#+8Gl0s-Ox!6Ij6`gBxp!TSTMfl@hOypsp}v~^^$y`dr>|p8x~hfdR|00HQz|yX4Yb~EvpK&MB@tCNh>I`EoyOM0n=sHl z4Jpr0r^AX%ww*5C5cE4VXZ%P`YK2C~A4+T|^!VgT8{6u-B-Z=1n#j-XKIv?+pho-3 z0|{rfuk{p(?Vu9B9TQ+mqPD2uu3Q0x0r!qVrv?>e~kEc$;!LqubUR#U14l1V=m zk+lVOoI@h~jzvCE60zlXIftx<_7hPOjkSq@r7z2{lw{6jN zeg_7`eP#6l0jUGBJP@F5)aTLN!BB{U61TPjgK4xP2;?lKDLXTXUwd9qFVS~qw9jjn zY&YP2eiyQeV8;ch9W&$UuxGS|VW6Sth{@Ji4WY8&9_A^-y}mBYU7umD$e_Jpc1K=V z9bO2Q1COC@XXk}dc;Qw(d0|a>VY9nPpEt3h0+!Del6|`S0|?a@dfK>)qYDxLANjDU z4V>bf@%-Ti)dp*>p$RJ5+*1^0GHl3PR;UPNnp-Qu%ga%c@lmXP_$_=XF;QbJ9>3Z} z5ST<}ijw{+SqPa*=7en=yO30QX@f!>5HAtrMzp{(`NXKQ5O%V~>_DMhY zY>8HoEe3{PE6CQOC!TA;Cwv4JLu~oOJf(3t5T4U|qJ4TmPtSTt|~tXhz-0y`H1ICWi*MdQvnw=T%`*9a30S&A|b8QlNYYh?_9dA3*~2eMs*i1 z36wVXC^60T*1Ga>gL}(v_OPpWa}_x*L~{2Q`09p7a5SUly55m|M$HEjp<(AVjLc02M}7qr`v&$>{v zJ73lLYc&4g;(eY5vaZw0d1_@{r6%DPS~=ec#^`-Ct|8(GD@Ff<}D$W(-GG-5H%i8=CU@l;MgF(Wy> zf#mdZP#uMSN<}DsO4MXmpg`F7MO7ZCu#wK72Ny+wc=UMUQHXlG$pyAdk&DJY+6?4k zdHZy;9S(AZ6+IHYU~Qlcfh06NN%*s0|Fcpc3FRC~_;ZSlBs2p_XhITntW+ccO4sLJ z>&e1vW%gR+UTb7Qy;v1RPzM|>b~n(NP;hp80}E@k0Vfm;U(75{p^hhk_>3grEgr1{ z(TCjWIKksh2!?nJ1FN_I2gWf404OapA^tIZH zSpQxRY`y#%^wu3wMf;jut_|2Yf-hi8 z+}Z$bYY75@>O6%uiVfP%4DKS+90Ym>2-6ByxUU-9;IEy~e}WYg{DwdQA?#)&O7JY^2?% z8Xsqm(Drj|rAR04+Uz0%twXDD4c2zUiBN;R!&DIL9U?Dn*a7k=KrBF?wxh$#3E&3| zHr!gl;ChzSU@+Rnj|VxmB3?ci8LIG1M=PT2oXxT0B9_DY5Rb2r4Cw2L|2hgMd#2mv z+gbr=i*4sk?E*mO>JCjD5=~s)p@~DHiFT2yhfc0gO=+_8HwjBqNS)lZ!$zI6n~A|c zK|d9;9mE0LPijwE!JW(-5HBqmanQs@pzgAJ2jJEWcnXUhg;=WrxL;$y-GS}QCgWkd z!g_KHfOnHXU_tmuz+E(rQ9Mi7G~5pr>kXV0zb|zTsvJ97bdj#8{y`PY*g={f)DVpW z)#wWa_k)@z<3O_l#%LU9R=^$x2byq}EPZEKC2*iw!A2Fpfd<}yE^LPL+*vdR5Ptlh zJ#z;uyiq4jzCqT4;lGytq}`4cR{u4ff}aZGU`RhjsnOpsz}KQM;@nt`vA)KVQ%YW& zGT9$zPjT0Bk>bvxJbQ?;*UQ$%^JuSJcZj^%E006(Q)r%sLD_;1G}XIs!Z1zZ9YHdY zI>vFne=!TPnFaB28HkTYgGg?8??f&6W0=**tV~#Xr2DlGnqRGTfz^az^oBPI=6chg zp49QLv9kY(ZNa+wm0mNI_7D$sz85qC@$J>R+shnss;1pbP+9{#xh+{t8~BBvA-tm2 zq!aK48!W$0DdLTLZ}^oNPiJ2O^1B#|*!TT<;!^y$o&-0W(K9(02W=%@3V*KWOAsLR zd?APK^hAz6peO3CaXmRh|FE8DTe9YYinp&gq@>j|y|)0vZzUl{+; z*w4P>A3t`Um&G`eGV-BM|NVQv_0b3a`9BIK|LF+W=*TY}{pRQXAe;?UE9@2{pSt%0 zU;DRD-v72w8LtN=qU`Hmed_oJ!;wMN!%8#q8}pxe>bL&s(ccX>+he5}`Mp2=^*{O|XF`^Y!<{{sbM;;aGa&lrbY+FAIN5*$jM z)AJ~v)n~LyEH@ue+n+ARh1P(h(nc2Yf&xCA zsx^gbVFyN?Pna|bXxD(s~vo9)n-DqAch|VVG}Sm1z_OAY!ZmbA7IYhAfB&8^}u_r z64ksD8(K{$P>SHXKbGxpF>qcV-nhSi68PXD`9MEFhsQNR-f@2PBR7Ddxr(u-AzifCAx5C?iFetC{ z^tsk?Vjk>jeTicCpw_}4w87J#3Dd`q7TN9~mEfL2pBnlEkfR`zR+&4ikwaG@biE_5 zdW5d$(Nzdt&-f}4C1JE;E~K9cp_CXOu3Cnujc!E!K6XqeA2g{NU3br(tz(k8I510} zago+ci3WjO0tFpxYu9L06Zv^h5J-K_rj*^aq}3#DU?PnV!dW#E$2Dkz-r(_hiFm!CLqlC?)snunDF(^7aG&Uw#+NaO86z{MJ7%#E~_* zT~J8!z2Hdg`+^@W@I%7`Lq;QWh)(Q^TN?&hAdRY1~~J4_LdA&IaZ%9WN8S zgECb1sLn0mAZnY5v#|IchXta~35OGa=zBn7KJ&~t%zX^tu+E8(b#J!Ag{RWV=G1cG z>tVM2WQSdiy^huEY{%eP?6>2;NVntXHo|8gU}Aj7vyiE08=)2l>aEFMKn4~N0mEUy z)^Peo0momiX9CjiQ34`xrJfLl>-B_67-4~GyaK`TZjl+$5?M_K;(sfQx>tDJ>o-V_ z`s6>5yxu2ufy$LW`FWCieDc#I?IwnS_~Rrw#gAw@5dQ(`5nn?=zi@3)Db@+&HwI!X z7H}yl!-`>M6Ykd;#b{w=m`%)zF(6aQ7GH*z^+HQ=oUl-gi;J<9sZ}KSF5cx>@=VJ- zmo2kqL(p(62MwUKhlcsCYVb-Y2b!^?9%u@p->cE(&FXv;TuHLaGbR#pF)6h z6x3B#w(D>v674&5R;r@Wk@rtLHa(g0A~h30j2HRbuYBF}BDMG+)w+3+nh7A?WVYs6 zk($XIU95;XhAm)4aLh_s5!5(Lf$T-*=c4_mwvZL6>33L>TFQ!;XIa$Hh{1}_+L=}z z!4?ttisew{P#Z6OS!l!dNaw5|+x+R86=ZaScC8@e)v;?>LAJ%>YsFzYGnn~37hfwY zu)TkmcHtT(H~n^Oi4>y5L9-6A%Q3&@t`*><=*R=V_~p({SDSqAJ7MT>zT%gKlTNUJ z1$!IQNLjap2VX|nf)$vdsGAv-!6q<+44O~|ofSBK7KcB&mf#Qts_;$)B@#FIb|nt! zApZN_JGVBen9sVM|C3W#Hr;4vL6qEd>kwhJL#6>PE67c^4rZDsTROYbR*;ECtf3WT ztX9o&rLihcEcW{62@M89jYutH#o;A>j^E#b@)?Q(lpjl>oD;SuO!z^{7D9P(($)vt z)WSKQqG;h9AEnIL6;^afevY}JsC$ZuEN@f%MLjjeUr^$hUh0fxx?*Fw`B-i+Q5G412mx#GUo zEvgHBwrN7cXkrJ|C-DAK@KNSG)ZR7P!sqo_Dl1@3sBF#pjxhUKvx)D9v=yt(TD00m zgx3+uFaQr@sLTY1xxfQ%Cdl5_s3-3;fo^|?Y7<@6CI~9S;MH0ZpG6<(mv3(Rnd)^{ zJD627F(;?DU^Wn9a(WA>fEDCuVP0>cpeWEjHt(F`0@`NPmf-3TghYJ(U|@-?LWUWVq{}_qnfSvVMC}A>o;Fe}$LVyUbk7e3x#>S>1%Wo2=KX+`65^ZF9LSLKpJ`9%BM*q$yj=d^s$ik^O&3d8KVS#`UN0__wds{ z11&I8z%9j#jg08j`G5VclbpEi_?cY{JrGC_$Q?o*Hqu6)0FUF^H`!;yq`m{@F`d-6 zFJ@8;2OyL-r;CaB7Sc1Mr$YJ==?O~{-r*pb@$ka|(qkb#P5L<4qlTtP&xiB`>0?X6 zqzdEwblS|?>W3*kRMwO(%~&Us$B%iYWi?!;AY<-)F=?HhXsSFq^6p=G@K4hf6B**pE{~4~uboM7>TLS!ukC)^5Nx8#!KYs5QGF>4X9emt^IsT5U zV&bg<|3$pwcV-oD6t8#^rtIBEyM~ry%2M!A?Ee$^nE#ec*}MPx-kbC5lw-=W!Tfh* z%33m|;cp3K;W!})MNFB|3Yhz$lqqYU&Q`%0h(WnC7%S!Jb0Ugzdd7Hp+6>C&cF#e1 zH)J(DYbF%*jGNvc)J2T(`T6Y?m%+4#n8CsL!9}A40PCtEs7lzG>D!=0S>xM$w?y0) z_DbAEhgqaWIH@gN4MI)3YD||$WzFvF`r`d$hQ^}ZuIz5qdWuQP_T?D0ZU$`jb2HxL zV$^vC?vN?SxI;SR*}1ulT@Vp}#%l9j)h4QtmcSl%e|4OJFEXS%8#;>g3q;@p%Vy_t4w>@U-qi^V|=b;*@huw~T8JQ8Cn=4ogl^EA{^ z;-j*#8Vh(Qc}N%;RvY4-5JohkZU}T@Q;d3aqPm zr<8RS@07Bx2gMiBx*$QkQ_8xEcS>1T@lLR=iB|;m=QeCNmg*LJc&+ zFz?BC5E?o%!}odO!@L{*J=cjxJ&!2CP`jis&1jbtrU}hik73HKV8MiXA?&99x3lq(A+-weV%O*>ehCTQaict*&-n;j2p& z*mFO|AJfxvZ?syhT*BR9w~G1Zj*DHK!!3x@FkSAD?zF4fopw4T#F3(XEG|x6x;0K( z7EZYgQQwr`b%gTG#osBq`c?iKcdFq>KJz0?l-M%(IMW6qQ`W2z^&TD{<& z25AnZn#$d2hxr?ZNIhux=~((|4wJ(v_32JK`UY#%7q{sij^@@qxsg(LwS9mKh4(ZH z)TvIs_rt~IlXGV+TBJXV!5AnVOWo2lk`TP-ZS-{Z(+6)9#Oz&%Y_v#KyusoP6WUrV2yKp@YG80EgkCptsfLaxpK_NSlDaMJro!UL;QfDlA zw2f}$q7AirO(VfWJl`yZ==pL@%irPIo1PC%_pAgm3F@GTkgTc=@;gqYj{4Z(nTGFm zF1x0ZZAKKXz_S}!{OsA(5N&{?#$^GEKuK870PbFFj01z-2$Zz3Lyw0kILr<(7Sc1M zf5OkqA(C(M$%7pf%Ch|lm1~i$6(LAiloK!ckWRe3^S~YP=Tw|UC z_{Jh33UKTZmqi7ja})VrLvKXwx@`GI?NVJ~u;o7!Su$tdBzW_R9%*(B_D-NnSajR< zgb?KNgv(gH~2g>%hCBjl+8sSr4(5Ey{T0qBj znVojA^u%*C8PA7yd)CUoBF|)4f3(+}v_A*a{&2`D$C=nm$uqt+lfH%fZvHJ4b!C`w zMsujFQ*_8?<>)lW0S5TBvOJ)Boq0d^%IT;=&OD9?!LS&1$=yO!^of+`nOT~^-Mj5# z>q|!4aO1&bSfz+nN{ePGYEi^WrbS07s#C;jr$uuVt)d8sNQ+SKa+Yknpe8NC^#xP< zkEUP5z(`0F%9@nrK8Zkphx3o-KY4(nA&PRJJV?d9=F}TbV!F@e$BZ3Yfd&jisj>&r_qFP=i)a{h^VP1 zVcszc^40-bb*h!df({b& za+}3__7U^K-F3vI;9#bp1cC}feL>;S(zp~=Qr^c^o260_nT()+{yb;wZ;Vs?0V2#K zcIZ>bC3SX9H^)7#2nyi{@t6*V0*;DsY4Lk>_S|N!mEIhO5(YUyK*NzcI%3|XKzCCL zAbJ*@iJ3HUI zwDT4+IpQ&IRgJ(H@0kXV5K(ZK8gbG=IuG_??sw}sR*7DzCs!@RI+3QdIG7jHpR`aX2@fyfARj>%4HN7BIGGky=aU8D=kYG8GQ>eya8gp3a)99F{#K?7$4`xADwM_kWVi}83);ZJ*| zjOjVAc%;{qG18u(N=cEhI5G?}wnmn)CB_7edu6oJ?Iuc1dX}(GED|-@Ms7UEwd2JW zT}Q#phR1nD&GaM!ZcQ}7^s-XvYlwKpf4%phvmurnfrZBA*WR<=l}3i?gOB5-XfYmx zcS)<6{?h{GvDq%e0Gn5&H<9Nq`K)Ze1RtRP1Ts&aGg2NX_In&w3nxIf0DA8A$Gm z>jk7gS&5RQ3Ih0lJ{dNKMxMI3H`}+Nb^tLVc*{3!rSiJ0UxUgd^D|MupH znF#BUouTp2DWcldQ}W!Fl#N70l}mxM{4w&rr=HZu^Ayn4eAkY0bXzQ|=AT{6&hK(X z8t0F(kf4zyVz?BNiW$=8DuPpqgT@r%danp&gdZas8aJKsuStju$tjA4R*ykf&RyZA zKKp26Ph3iFi`F3bDH628z4EJ%R!2Zn!qKsD=3}Cv%dn{7NTKG55Tb_b(7S0Hh}fY+ zZ#sp{o@?R0%(4O8s89K;GA?PX5TwM3#YBTR&W7XRM|vYjb?A%Wfe`j;j1WnLFb$D>-yYgR`>ywlC`CiB&!#R!y`4Qcbud zf7u+d*|=#zuXATI;Gln8Sw-9@=rh-RaE0RiUN_1m%o_`r@kQ74@ zVvKErMQK;ApjYa>!Y$M^G$@M?B~r5^`fdP6AGxroB|c|uaf~I9$PVff{u=~M%(KIaJnT<0`ma+I9Fcw?%lVW-y{V04 z9bZteGt25X=ho@b0JeM;)u^PoTn$~x(W#+Ga|_$3#Ou0hgj~K2shRVQgj~J~0G+Q9 za$PmHbkzvCt{Ufe)d)FiBY$aOVxVONci>#DJ9U%sm$*Hr`j%U4O#d{-s3 zsus#4V&++iG^;ulzk)TxD4B@_;zSY=?ydrN6(*OUW%2yjV};frxC}u03`VGo8&ps; z1J%R~(XF^<**&)ay8N~eYjdgI!!?KlF*`#AWaNJfgEpX3_t=4{1YH2o1>;w(Z2gv5 zNUCmw?B|ee-5ghgccV73E^muAP*YK2%D1&^M5+LLiQmk*h88!VL_^#TX}^J_cHaq= zEn+IKvS(u7vH<5LZzebLFyxvLbOzm|NnR6yN0rGIfV}S`*_3Z{LkJgf%Wklw#jB8n z=>aAikiIG&RQrF--8h44L`@Kv-nB-B@Qgtf%f4cb{f^&3(bnh{jiuUF)Drcq+?lLvbBo? zG{HjM&7KN8aCe6aXcB;`4ZHzv_A~ix*5DuUCwk5f0wSz@RTKS1<${6XN-b{0R*0%-o2T(KJ#6jHC)3ZYPurbRyx zQHKQKBTw?hDYJ#m@H?3KU`)U>sO@D}tMp3;;b##g|jS@5koU@3kCpQCf%zu;6^?Xn7gCZ{o;)g_;b`VUq=R^|3GFVyo zFsX92LD$8EnLfiZY_X|SVwVh?IKeNW-Gwch+Eq=$p>-O8aqBb!KbJOAXG6`tthWUH zAd=`{gXLuvCFZxQ41^T3&dtky?f3~3a79z7*pbCcZxPG9Jvqs6(<#tT4EN^ji|%Ft zUfP+PVLh^L-V6(o4Is={80&)O-q3084P0B>Y3~jB_TG?dPb`*cRCR4gUsMauQjN_( zS=;V00S2X|XOr+$ylOwy{In9`fZB=>xrPEqZFJKn_dVZU`bv$ta-Mq9);p;PzS^J)$fCN=pMTP`(55$6heUEsF9W7dqsXB$Ua#0i7YBs=TpjUzz&^Rfj zCpA1y>talx!y~aM3UkH|&>;wh@=~WMS}{cZuCCWtcvYL76d&sPCURI>e<*xPo{b_x z1YZ{>Ax!Y)Az=~Wf@0VD%OaHLXN&Ir#WX{`3B)WNorGSAdcFx1vYJKrM)wAi32)J* zjfHnv#m8yZvgv!u?oDx2Vn5WmBlFinMi1g^y-BEr>XKD{lQ7kEp_Tz$Opjq1FxAYw zWcCL953Md}?~R@I-k5Lijl$Hly*CO|LwjsykfSQwyG^+i;{$&7R%EQFN84&#mtfaO z1SrHBFR_6Q=q-g}DUKQfDk*G%W-a0qCL=@8 zNYF$7tXK#OcsBL^#|m^s*Gram#eaiLO!tY{7J#^vv_3@4L-y!zHt7vbnVDVco*AJR6A1x0oi_4fxw)nL0yKjClFbn&2g?pBTLqu&|wK zM@UKNb1vdb(qAk5f-kRoo4zH*Kr18F! zO5WTbQixJaf&{LR(5tzRPf+o6%5a598&zlmyg1AuA}brCYy)MuMPy}o#kL(~-e=N} z5P#41d6ePZkky-|Y#n9ei*whe6NiRS&5R~j5p``FoKnz{?B!#WokMm|p~sLfRs#Z%&;<1Q56cwlBz8Hwfy|ezB)71_$^dw_lj^Nx=uX zy)$QLIY_=QXWckJzBgz6m?pnEXDyi`565jCHMgG$3WjoiC*$N56+Zx?Mly#_6=7g( zBnL>3X&*lh&^F!LY(3}6ug&?F;K0W6rXD7Yoc!so<6?s2TEe<+$bU%D8fL+x7@sxP zcSri0?!0A+&&={1!ebeBWqEm8?sDt1`~mXov#9SZ7x{JWyzKWW^5?lk*KQb~Xq0l8 z#rKb`hm$0r*>LGV@ub!sL}0pzM|IBH<6y(<&UIOPrb;(Ol*gZX&u7bfP&`BzL88sr z_@t;A_~` z;@0irI_;K`mTWMlLbn@t)F0hTt1|XYt}jyxR#?*rs4M42-Oez*74+Qhg;{3HsC%(x zfIn%T-38fCyY{)wX$JLu`3BfC01h{01$uIu{D*XH$u^&4S9Rz3q`Yz4K6#8JT^S2q znJ1e%R^bOrE`xs^THugz`ptNpTxvS>j*F?`(0j_i618?^Oxs1 z9wleDz1b9fgq)#-<&uZV8b(<5L2~qZ)-L|SO|;bU!J?bwQhZl<0kO0jaeVM3+%iDCfl&$g-zbsgCwNftO+4=Bb2ssv;Q2b9V?2L^C!gmA^Ow=o z93u?CCYHGX7lPtoqnx?aPaB4!>dKePRFCn~-{uw~Q#z_hA^n4LcVAD8v~h&G6d{*| zOC@^ZugM1meFKY!I>I=(R?fYvfc}*ZjhnC?+`A^yNZK9uE`e0$T`~Fle^nYfTW3iG zN)VqSzCoTE$#VtiD>~O3)|(jC`w=GJcjK{BlXxlo?zy6pz}JTI%SK9fwhIs80#tqZ z%4VflDle)mU9y-a>%u$M)zp7@09r=kv{>r84sjqrp!sXmn5MeU2C8yPz@r~9SR#al z_;Ct}qTGBT@lBflVf>TjW3fXmSbkqi;aGgFglfv=tKoFXcY4A=(_d0oDt=ld%M&UuzK;lHecJ8i zN!bCIXlV@;6tJ?Ggw&->6PIhrfOKR@r2Cn5)>1X4A*e?+w_6oYT4@jm2zNb%qj|Uu|`Ll{cv>K@zPPwxf6{I+BEH)+_bw znf^yK2MGC$-qm+>e;cbIORL9N{D|}$1Yf?O2>kh2USMj%E`x`D6dQ~Noa$@q>g&m4GG{Y z@mp@8%(OIu4^vH{fE8CrKEC4?f8te69k?YFZ;_&p)vf54hM=g=uOsVxODAVHt}Q}M z;reb>SXi~i#nXDpTqX2vG7<1W3Uw0uy0jXuLG^-F7gM4*&SmuvQ_aw1@rm*vo@DAu zTv9#3k9DN)62aLmw=y+-hSs-`lCp|b)Cc&K8qwX)MZH+J@fx2Zk<>$JJpCSym9^Ep zD%&h^8bGhx9AW0I;0fM6|Mn;-pExXJIeXsS7!<>-Q-h;(zkw}Q^{B&IF8H& z)#Im9QY3w~r$$fbNfkRg-vr1yzq9pIr1urn@?9rX7%uG!jr(KOyqvi_n-(~{&C5Oj zWp@SG4qeMok6}&#A5Kwp`cu>RpD1%gTBsK%i}tB4ajmj4GfPpphNBfvSlAe0u}3|P z`j8*BTH%`$sZX~NWc$>S1)qXyhfl5Br%rJ#dj5-Sk@-);uo$-IKlymqCudWqaYBQJ z(ZTd+3|b8v9U_(gB&9@_(H0?MuTuKlX*%X>rlN%ia}?PRX?~HpmP1HwAMuMTYF=O- z=lz$2o>0^Lnxrf4>t#!xTQxprz0}RotYK&=2~A}eX4J*QrNgPmPgsU-kClVVdp&+K zUjc5=cRal}!!NfkJcdbN#h4iBE@ap96`2lk6#R5qyQ=S}5XD!*E~BA?cg=FSPzLIa zdIp1+QPrS1>!P-vI8xb|B8?H!nKDxK7}H_OJDE{2&`Mks{EOYnxMpZAM=og)LfUCC zrXWrG#GYXkH&wV^n=6!MlKu^42?S=D^T z>aZyA%jdd&amt#I)U)n@DY)Tb5Fbimg@S@F)9;C=&iFUe_1Wdf1(?4@TzWcDB;om( z`*?cO%d5K>msfW&F0a-N>RMPxZ^6Af16iwv8<2 zxU`(a93Q{;vBG95@18Xvev z)(4I;`atBbWXU4;Nam(%G$@cQF?w?-#5c_i8 z2VG2`cWzFYk8HA-yg$t5$4_3jb944%V8R*0Qsxf%yc4ipxpeM8196weq}Q{#tJ~a- zl*P6tr|=y)sDHLd#}}!ik_~2Yv>V3dpuXrW@t?^}qRs=4bkqUfAsv(TQg@2U8G5mA z8^FL5Vdq#8+&V?#(J3OZt&dX9B!L4!%rI-s<9xOX9>WA{b6t&X-+QPwq23y=Zcp?hSN|JVv z8vLmybbx+Js2y~$>9Y7lNvo7F@np5(#AV!7ShMnO!L6&b{=_afrobQ4wvVUN4@`O? zN(Q?=Vl~}9MOi;YfwFI+VTx#~eOY6uE%MlU4Rdc6pS0xCg3L^fZ?8YZ2@v3GHm`=F*gHX0h};TB#A-K-7i}pNyNBhAw{fUb;b76 z6nng&;tWX<+!UeGfd&8^EfbQ^e}r*0DS+Us{y7kqN^S+|@?pcgVr8OL!@~rZ6J581 zbaYumULb`_<;%Ftiz8|qApu`KP*d_n9rk~bQ>Tz-=l~S4X#FRHrFf3wtTz%)3}zX9$-E>GPV_y#tgkF% z2J*s5Orhq5PDv8*!inmuGP9KIV+A##K{{da^oEUA?|R{ch)spMJDbUgVm&U6Rzd4n z++#&8Nr6mmee0~Rgm^SC`bxi+*XFLk(sv7rXB$A$k({Kc26Jmn<1}w`utL}z7NdJ< zR%m*2OJkDaifn0EHxm?BEL@ruiLH&QM=O9HjG$pHwASCt()1hbbuSH)uRa&##coghrF7{S)sR{@>kz^@dZE zO1|ouNzCdLxi{8Eqcdnz1c+&dAmQlyeWXCPbLA+|Wc=yUz)6V?gv6B0|F;sIp6`w5 zNMQbdL}$SLKce$J><9m?h>rOM=ZOw#UZXiR0#lsfD)PTS!=0<6J*eHJDW@`SJML1p zO)-b?KVGSfmq^5x;Fil>gdIh@x>-pjDns!?PGl2tNHy%Fnz63?XhwTJq%reeO3<=E z>}u|eE35~0V1usgp$9cPuyF=_XtbP-vuBxn6mb%)!kh9xrdWQno11tQW)*n>GD^=m zuyF+1e5yNN$2HDRk88jrFC)is4G!fZv7e90HfdMo#HuXzS@cAehJ{ za9-nl$zt|dym{4#n$WILwc{F$VGn$KIkI}o!*LCC{~Xsatveskag7BnNE#JGERUA+ zbzDOSx@^K*?tB`=L(}g>mXQua-Csq&9e)b#b=sv?iXW#KPk=@3SbmKackWd@R>3Mn z=O9DG#u;-HvfgQuX>)1v06!SZj8ITsL0Gu=6cLRB5+8--x#Woa4UsnvmV@Hn{D3+Uo=Ny(S1CdtE!Eu0_kUTx--3 zKiGbOz4go!?_O$N^gV#--XMzf*^c+3#q;7e{hk*mPLMd;6)PiWy!hmYK2ttb<`?0LnKwL(f!B36WA*}&2A*sqlPfL? zC`S9CnLgX|-MuPZA=KqC4?GNgMTnApM;0qt@2JeA`X^RCgDt64dPc>5vL=9;7n-Oq zp{YJOHdh{eOlyD5N3+A-RgCdguiD1lAppbrlchTmZq1}34qjAYQenJT7;~rE zV(vC(%pGHX|5q2StJ)wcm$=@^yN{LD-;Cxi$DX}_DbmQ)=Tgc!>r>g(F$8_ttM!?5 zy#UNt-T&X-xkuSm)p`Ehd-tuXTd&-kROQ~P%H!OdKnkkzASpv6NOBY8A;>FqFNf(K z2_#^uAb})+51bn$HW7o+4RV-ex@Bx;ux(pmIU2il=-BND-7rl=#&&i4KwFoYQO9v` zX1SEn`FwwSpLe!$MlRgy1%hdhR}2Wp=BEfmNOlx9M(tYfnV zit*F~3={!I{K^azVr(3*oFm5OU?)Q&!Xfs^H9*T>wY*H*&_~F^ilEORa?7wy%~q+| zGX`}p5zUXGehf(CH84CcVzWHE;`{y%egE#`1W z+&Bqy&=yH)PufUA^by)_qtxLH+WDR33Q32f^NSiNL( zTYx=FIBr+e?Tp zj#)*cg|&^#WYt>S zjLI0&`{|K_Yt3>#-}W%~GzTPQFP2+LvTF0N&nGaUMRY=&FYdLox`D}deSS(OYHK*y zV7SI##i|$gug{Oe%-GIQ#~f5&!N?3rcBc;N4Dj(nP41F=hmyT$O^Z$+HKHk zWh<6_E@v=atY8GQ5o5|)pP!5uOXwr^Ij>*1&2;JkWR=g_(Ag?i#&n-9MD;ODxq?A| zYcTxQpsECD?9K|%#$-C;z8;7zIk$nXdeAJ|eOR^ep{yqYY*;~vysR7+=km^kH*H{J zfkdeb_exX*)aXk56blj1F!_$@w}iEQ1FqRZ1fmhtf_f?JszdLvoq-iYQMMuLjCPcc z0y6wyJ|{t;7LT%*cAE2;&V0@G?{Rh&gqA12m6uOdw~*g$wNa8BJ&k?c%CcbFM}Or5 zHzPSAaO~gK%A|XEDoj$t9B=1}wP+iw0$4A(^?IlkUrk%6^f3T{XodF>c#;_*{s-Bg zupdrRLi95DT?7o(4#`29STIuQlLMJ?>V3>V-)w)D~3un^>{{4 zv;9)`drVhSM$W16QVO+VUjxn6*csSE%V>XGZ}iHx7>24|Vv>71X6bVByWht)6)ctW z=SX2|4P(tvIi(g@_Y7FCz~XC2Lt5tRj{gpnPXdqLrjV9iUt(`R$SvWt*F;&tq9>_j zwm=49O7j13N4xk-;0x8uCYq_bIwS5f%TUQ6n54!im<_qt2PL!vT*7Nfi?je2Hho8W zX*TxSOqx1P!|M^KxHQ{RxwgI9X`^H?BA1hAPvl3)E2b@wqB*oG@(25ctq!YRte50{UWFn3)hx~NaX zYBuAN-#&+xQ&SO29j&5dQ(?hL_6!r7v$i!8Y!=m^!~Nw}U?#j)q1tG=V@R=>zHRKo z`_|H8vgU_{D#Ef&F17r~o04ZEkG=wKwnyFu9=@T8)_PMDZL^gX9Bv-5pdt+iHyI>G zM<3&6xV`pK^RwU|@bdw+lwn@m#$uE#zvibY@YO6zNi(vwR(9qp%8qCifg3ryeTH|n;35395Sz| zvu!G48b~%BO1v}V@#yS zG{b>egERnI$TY!B0XSv>D41xxe_$Z3zzhcDhPA}x}6jw_U;>Y`Q4jq)PN?L zLh;!(I-pfSpUB`=HQhE|72fJL@6+B|Ub8yoO9mhyZtCmPCW^4bF`OZF?Oa6;K`X;R z(SMVT*`UW^jTge$p&VkS#9_cZ;DfIS7nyy%^-y|){pHyQ zh7(_T~nNc8%7Ym!;o^KEcQL+3fi0mE!%MeeW@K4JWvpK@T+fw!p488mL z4HO4zg<8ZOaN|7(ECD2V&aCmZ_>)0rLGHkgiGhI^85q5)LKp(zTJs=j(~Rd4NNEs{ zWDZB#MA* zHNSP5Ko8BN=OQWyx#TABC?y8rX_z|!;fBPq$@Cyfq89!7c*XiWAKD!jr5w~5|MCIO;{<4d zvaGWZldY6uRYlij;>9SX@nQk+$z$z?wMVJ^0)}SAwrwdKwD>o@ zFnx%7QGgGyl?!z;E=S%Od{5gA&#&ZeJn@dTh*`2Hug^SU?vcuO%*GH zO%+P2rm8tbC>w!wMC%|z8Y+fxIJi%lU7`d~jHAkPz@lo%pZ>%55E`KZb{=TNwor^R4Iy4_g~GBKczq^} z({#PTz!S<+Cre$Ad(XNzdnC#0jq;<%7?y z*c&&HrU4}3tO3KDRWt6BUIZP})sS70ED_SK$mca5tP&_#kO7O}q_cpvve2Xh&xOWP zE_a;7EC4p<%kV`w$=v*kV*o}w#|f+aSryiJ`*N#99ua&)o>&bzs7i35d0;1mAjqRE zsOEyLF@NHI6ZDgo6xRZOk_S$uAWK<#MFgC-Yd83BF}`Wq+37Vco+N>a38}zlnUICD zuzPt{;&KV4HcidfQfJzhHNTS5TdG`Q-84@xRW7D?-qSmunQMD`OFlK~F}6fbnVtIO~1A zM27iW45ow+#TDD4X4@mD5H@j};vM(qBg5Pk@f2Ddot976tBiUUQ^IsSDKm>&idV4r zd5*J}>+<2|>jPSMt=W*@Ps@tP)BAeqgZK4Xz1l2auchV_AUZb(0va$NysvklnV)IZ z2ZQ{F2t6nzkMt($=71JGoc{LuO1rEi*-=A|4letIgK`0+2x6rcjZ$ zHUBU;a1+9`$b?_mmdw#R5if)SS&!xE9R)LW)BNm>3+bcL4Bz>?ltPGx3Hr)i*D!8&-yp1@>wK`8W)Vz#cC3CozQWO5T-z!D-HDNDm zM5?eiCg!NPXWIlz%$2h{b*O;*8Xpk-*05JFWWA+XVq?(>RkZZkL zl}(SCfz(EL4dmLB4W#w{6a%@|_R#x*B$9-Iti*CnF2&I#pQ)|n#!|KC7;YSRaZ8q5 zTmQoaGF{4vTIOlUNulq3$~_raF>yNbX0U$Y^C!$0mU#+?YSp4>u*RB>D;dKq5F*Bf z@dTMEwTxBK<(9OmvDPNUN{0q5%wbJ~$efjd2F<-NDNYCk4x_<3rdr(}H>;qyKf*@S zhaQ(sVtNV&-`=q~SOnhjuvvVEk}F;Ti_EyAFLWTjTRtvyhKxbc=EICk-k@k$WIGGZ zpeWptmOu*6fJMC?i-dELCrfy=vd4QY3NMxxdy>JgV^0j0E_ljZdm_W2wRx6>X|^GE zX}~Gfd=+uN+QQT!PEytKsBo%A*svkhTF_$cn6kxXs580mC;9$XK>xYBLcIyy$a2Du zMaFJR*mBD0Y{<-EQ<}9aG?d2Tv0y^?!z@@Z%G8$EJHFPBGm%9a=OLlykf?Rc##z8V|YTqTXQ%g9E0RKlr5`SRtkYr$D6Nf$9KCg&~o8`h4| zI?hAYMT_>1%yn_IrNJ11CO4TUbCJ?$$>26P+mR{p#5*$0JOtg%?|3XFTS+Qkf$Op0 zJ(i;czQ|))=<8>JnFsY$iSG#pN91Y(uuQM!`-IIAIVh_##7M*))M?sXYJ&-9+=>b^0%#ZFF8j_jaF%QEC(uEai**1^oH$1fQe(JjK# zWT0U}&CIK_b|o{B^iddWc`DHwB|i>m(+HM4EWC>AV=tCfq&va5i{9AWAmgsALP7>P zI~x^I28l<^@T-VINO*pFjg6tYI}S7)k351g*=P&BMrL2`)+O-wsv3kb9~V2Piq~$~ zPvIN!Szfpy=4c-9j~(i<3qXWe)-y@56OFn;t-Bafwb zum^V@K^`F>7V<`mRL|I8C5D3AsoE?FpBcMqvobx-FKw?Jp2mPc)#@2hj=>z_QZ+R= zFdrLp8deE=2vk0Civ=>E&oj>6v=cGpnm3$p8e1#!MGWj%A}WL#NQaBV@xpVW6SAy` zENgNIOT8u_peA~>y-pgDp6?Sa-#09Jk4|3B|u8&f(4egkT=ZYu8a;Ar^vcd`%00a3Hdx91CJ8n(E_> zJLW@XF&+&lq*XMY@(p{xf|_XMeG9y@Y-ypP;8++miH{G|+KPlghZq+V6vOvmQSz)c zQ}!THTd5$LK`rN$d+Xi?+gO1U5dt>|xE8wM8U9IgEgXXXYl1@_lVOwK*XNJOT=(?{ zf^XpfQ6pquFxP;rZo^H8yW?C?Jq`Q;^7ceF>M9+{2S1l55FmVU>LkvEmOOau{cC#{ zymO)IoeNbt7jR6;UIDxHm1NMZW}FM~D4Yu?gAT@YKxdr`dQ~VEz%3xlSMdPGN$*hw z88D0-$e+B&co$~%S5W(fqMe3FWsn^aIG4E>m^5=QfFkBzV3L@Ema#f>FmOR029`~x z!?c_>4$6>mQjBXXcnxr`#;*$3_NI5kl zFX%7o&?2j~g9QL=%PjDSDtcoNsi0TyORns9jpW*Xw?O|5DPMluYoK~~4F+XifC?rw z(UWjdtjBGyK@XYVA_!WRxo4nrk--;r#wdTx=>GG$tk*!n^{=r*zXi==l|A@{$P;9< z-v|UhzwEH{U&Cu4z^C#W;CU;5Wv{jQS_+|2L`Zf@VniWbhdO&i?;&(?3&|+4C>%$y&XfuMW@i zaewmOutdI&$*~cau{{?3;;OayDBC1z(xDvNpoNzXLyWYKqL2rCJpbETv#nosElJ4q3Pisl#p^D(}^)qLOw$0DWNX+Fx2=6Lg&wEPm^mS|2k59{L(x_|F`DNi(y z>ys0i@)K$KXFufmk>*1`GI+~LhNBBBnkP7AA2=2s_=j5c!7uk#ljC2@_b0DBlj}?F zUFhaLc-@KGHO=l=GLR)Jb7%NuWdo1L1FuR}=F((kdGNrc$;wzJHD#{(#H?gxj$~y? zuIkQg6*Rl~6eSC)9y`lJ1}n{H@{n<{r;%6U3>VZ2gRT7P$*5?jzv26k0!Z_aam(qkE>()^1)X{tP9_;Xb%7ZQy{#86*Gqbb zlJbxhbO$a@)x2e7QEpz=HTy0`p&9knAwQD!%+#G~*Sq3;qo}0S7`1tnnur3hg)W-V zda6%Ii`DW)0KGsy?$Q!b;4UqjG*VD1vJ%gh>IFcCR_!Gk1*yhefFLrsj(A3D>=}~L z;WSEXo+=xee|Ml@NDvftv=kRDBN<;~XlEoVth9_|=yB7H;uh0>%;D0P{zjDU3cvlS zg}*c;&nYM)SuvC%_FLXG1F}BUuN;R2IZ!2ShfH6toq%*WFQnpd3TUgdrFA5F9{-jXqj=O_JstXenXeX?ptx+r5`+BP=Z5Q=~ zk$|4f8tU_gkbxzpS<9FeN5B;T@mkmf9$`B3X)|$$>e~HU_TH z8}deIfA4&}S-8D<1^U8Za}5E+S41rJ%=ut*a)uZ3%j95le5L{EMv%5~M_WQg#Nwp` zTXOG4o&`Z>%&$bI5W3gTTXvG=&v)Ho|rx0)Vy!p>3?%;$idYk;htIH~I2c@Vxs7iTb)h4Vwui>Vv)>UgDM{ zA7AV$Tj&n~YI+~t=Uv+=_)N?Y_fB)mwSg!FA3&%~3ceSUUoCcP)&PgRdKozFyn4>^ z>In{30@`R8oaC)b+ynx`@i1Ug3Bnu9%9!UuIz)*E5ikg45ezN2)+pLM7Crd{A}%y+)j=@Eid2b}zz#xAo?POd&pn#lwbZ>btk_KHrp7-U zqbB~5{9f$VP~$IScgfLR>PtR-?-wF$vFoEKj^MM+KcVcEY|8&8 zd5k)_r;;zNa+i*M_+!T+?1DtFf{XnX8fU#wJZtk0l9j8GkpDxnXSKU4_gM0i)oxkW zr=|ZClW#-Pl|R#O=UZ4%-r9UB*|x@=zxY)U35o;N1I&N7#;wTT^E=5m*0{;}b0OUK zJIUBt?t+tofFuHle|eT$oI98tIm>-1cRaa%t=r4#t7~06?--pazg}afsIFs4ah+S< z|I+(O%qsGUnx8-Vx^?c>?h7ANr*oN3V-gME4}Nsctv(d(qR+sH2Jzf|xgLNYI2QH2 zuQwUl=q@TvMdhdxMNuu8-sol+sIP8x_va2LH=OG(=k%F#-5py#o-0Rhqt3$|!yG?8 zvm9N|`D3{#DrL%lLwSCt{5{HpR!;W3!QI~6k@s+Zk86T+c$J6qkpL$xe<{F8%ijoa zqNH)2yD@)7A=!MVTbTUo^W5S3lZA40Be0(3SizB&zsm2|b1Y3R+vFbHyu+2F8z`R6 zQRGO=7`LOvnR1igy_xdm{2q&%|0=0(c5l8ied;JzmuG7InBUo_{^y)>i4J62Swi_t zJ)ivfW_Nb}mzCsao89gCpB0l^nr>C@mgK`tSIs?=9BsO#xhImpZMq4Nq_D-U%fHf< zY}i7bHOY-zxOwf-d$za*uD+CfdecDEw2X12W%cTL!AD1jgF_$(cGnc!qSKUBygkp=c zruug|_jCSz&IQiba-6EpG3wCq=$tr;4qi~^R}_7B=;15eWx3OiUVep}m#a)IEZggU z6tr5x?WttXm2O>bZ}Nv%x~-+hxT{7#ll=HfS1p~OC@7yv=3V6`Nl^fS=(GS*}TnlCl~Rn`=KYf`tPgB4cpvg?xEzt zZEjufGqrLw%7fpjCC9h9Rh8068I~VKL!8ylv1H!WZerwoibC?MI6Kb2$ysl8EZKIo z+gMMZI>com-zPZtaQ+PEF01y*tKG8vcSn*JuXgJz<ZGPm%hW#woG*9r`5F>1c_4E^}jX!40$-J(`{?_ut#>1_GDO!*hw zUy~^>0}BQ+GB77nEFzC^e6u4(yiD1#GQ+ZUeag61u)@OPBOH`4MqDXYV2 z`FoV-Svl#s)-7qI*Z=#R=jK7l9!_%6weACpzjIpIc)-QGZ{L2?t{w5dT{rIDzW=tp zJ6ADl$;;Qe6_Cx?c1~|ewr+RJx(}RQ)|)5W|3`6svgzl=;l91w?^u2Fo*k?9?t3ix z%kA!yiyv9xr}yeRc5a`E_uRHW-g85|cl+)ecSg|@$^F;4vDZCEl_6d?ZT9A!`}S?; z{t2$D8|n4EJFnk!`_8@Zh%Y{$HowKAxkbq@I4#~V?h!k!*?w`JzB*ZVy<2zsEmRT9 z*m?6U``;1o+P#10-rd`8ig)kXu`_y%dkeVt-Kpg8^=@sR^ResQmigbEDo6jE>wm>K zX+pVv=?=F=_aE5d*0=AMK*VtWYdhRWbpOts?wm^c{AFRBUp)HsPS=-f?U*P>Gr-%x zA;e0{Z{oKYYFfU8-$Jakd?~-P^|w=ApSdsQwKh}U!SB~)$_%;Yx77wHo|SDtFSRc7 zz+L>-OQtXIo;l^aDQ92!y_9jrwd>!*?=1X7lsC|RyTbdpa8~Al!~9;IDc?7z{DC>; z$0=uD_>U-y64Dp`<2mJzQ=X*#c7;FTceV$grJQ}CKc$=<(x)klTcr*B^_=o^l(Rh^ zfj3NK>W@-BEn6Yx0_YP}IrK*{g4|fzUVfXqyL25(yhuMf+S=v1bNL^yN)AoCMs8d3 z$J6fVO7|MSEPsK=H1OTY-7_q553I2jKKEks{EVB)e|=4I?o9}?ocG@Z*?#mN;Bx;f zm;7XxdrR`v4X!`Q-|VJ(ixp}42%Ji51oggli@RVTz5a~?&nNSCuw12={yOQ~;YN~Qo#T41POt4`IMmFG z&2scs&i{;NHR!Kw@WWLGj5v-4otEqn+Nq9aUw^*}e@SzX+mQU$vSKMYK2j{kt9QXV z;BQfMVtqNfjGDi}%U7c2@&C$bJ-bw+HIlq=D_n9{@{3#1WFI-ZY|@Te)AZ^PPG!qN zVqNnpn;Vw+h--ljtIC<+ncSj{1Jqaq33|>jTzYkU|$y+@?(bNpG0+pp7pib~1P_q#6x zjplRjIQoM-+#R`vX=^_{uPnJO+I8LaQO~C2?eB0;4Lq~Si(@|-O{}biq znes>xfiY9wL|K>?C)?iTHdfNd{*=r7Kw>?cbB{74L|KJNB@Q6+Z=z#@dC%Q9Dl*_DGsql9p!L7js)`* V$0snSGL+zT8+Ivutkl%YEe;s-!*%mlaqi}YVZI5 z`}{s7AI|J~>@_oM)~vN=t(n=&|2+KG?+x#~&wgG#cj<%IOjOEJN=3D8sfemlHWBmB zj;JVkma?>EMQyv9NMZVFHDTP^EAF;pDa+d+VZr{9{Qo@dKfk{CTiPl;QkX2T5#l1w)bB@Q$JO|QIDy|Ro)ZoNwr_Rq;&QT z%Feq&U8l6KrnP)(&S7)sedQbHt4Gz*r=NC(x?SC-d^LW;@6^6))GyTC>bL41b+0OV zqd)PSny*yKpMQ^b8s2R0v8L;UV|m+F+|5%BPXtkdXsfp~-K{4UZ(1v5iXBavbib~T zzP9{s%iGuM-B@K;#ng`U#FA5W$NG(zmL!zgy76bFrTVmWz;z;CqBRkV%=T8C-(z|E z5g*s;^~m!!eiFr2jHIU4(N^r%yr@bI{u^a^iG@jfw54Y|)||*}N4Zw9iqSB;HK{$l z)Xhtml)I@sPc2MZUb~(jC)f7cLGL&%4d%r>N0C(AYI^6X#Dgkn#p$7I(VKJHlG;(8 zy~Jy8SFOp2qn*eRb>8>dBT_+P>1-<*@luDyO-Ghc2S6i^<=EqNu|W~fI>pQ9w6%JA z@pMg=V=%RQwXPL2Eel^ZJqg+>ka*40G~?pCtya$>vP&IVmyGV#4f?{U=&&0GL#d=S znm0@i<&%-{rIeoSwI`fBPtzGkFOnY;0Gsd8iNQG0pqE6`FU!fNEp7q021Hl8GVx@A zQy`HeL;Ms5_|aY}HJ6t`*Aj4M5TrvuQShmq!a?`}VM0l}H^Bw>*#_vimXXdT#27H` zDM`~URx^VjkP8gbPtlA^%#DCUKuZvjfq;0RD>Kjx7h^7nk0b{MZ^%duV3v_`7?A)M z5Y)zR4*Rp#o!6(oShWP|eza-y(LRUgo^oO&y0W}^q)0@fr{TCs!>f?_b9xRDaisDXqo zlaRi7`sZnTadY>`j_-yr^1L3)Gcf?rNZVWa-E?2`fs`9Lqh@%vTqL*LUmKhJDKF^> z=~HG*OW!!-VOnNs!qLK?OsJ>|$F)YIv6j~vc#@kurOZZOaHA32xj zRVPXMgilNQBPUhz`-M+0%70qA(4M~Lq>A9NKRxBs!^m6zB`IBb)J3(XL{qC{w^%J7 zbicFfruJ8!ecWoX5`aIsI(RFQR!8yH##fK}zE1Ii?RDx_M>pEhWI+qqmC};h6{IGT z`MIeZg4CL1G&i+}R3#92+lgc#^ph$HfDJ*~K>`qigj8>s0<<8dH-MBF45>XVmW8xo zD5SojfcgSJHG=_l4hBRwdxk>l&xMq}?6^@!8g%qQQS@vGS`GTboC`O6@1ged-(Iy} zrY{V&ys`P+w;ufEUtW3pe}jRD9Dj11f!MQ0i0Si=uL<(I!u%oc-+cVFL4LorrxvB# zJ`=v*8|DvrfAwen0<~Q7*_z>|6^Q`IpvI?rJ`2rleCo3w*lN?pCqMrgRZUrK2!&zJ z8Jx8D#Y!O}?Fk9UU_*K`lG>Az6w=8i2sz}?lJ+)`0D>|EqdgfjiVwV!8TXJVX#s;H zFhUB_UcYKhKl#NG^l10FbjV3xBz^a}^x{dE{e!vm`!7ARS99t4lPAvBm;E4}=vqLH zCw2Y2w!Z`RF~WQE`%HWX1_DM9y4qX*gLLgRYirm2vd2P{=$J_2>AHdd8IeF7Z+zw& zN2|{CYpchl$E@D0Uf}6s{lqWRudm)GeeJa)V{2~-c!~Gg^7KR3&eiB1Ro5M-uj@=N zyskt2I{oH#r||okHIDkV@2%FA>fZEDAvSEFcn);yGiF3FW2a>+~5ez>crkARt5bG+pncdmUv zOMC9SVUYyQ>n78SuddrjQ#X!SKVIq8S8tqm;}|8GU%%X!Op8Kq2n27B7;y2Mu(s2abgUDfWM+aBbm z2(;eMy>aO&=-H$^S4{vJa_kde-h~unVPmmF~@7B+b%ubmsxx4)&=y_UY_j-FBP z(xleOmNm-~eNiuTqTZ_1BGFQI?XufEwZwZjz5LFT)Vt|F-?4@TdJs!L zbI*Ep$HpJrd%9LX-uT9U-=WkU=^HnHL2XJG-FIz(vU}3Mx^KE(aeeyL`^J-7bN^_; z+`;!RlC*XAk5G4Qyzl;6m4EL#K>u;$D-Yy{&won4_26{WkO>c+%I_r)HK=FOH$7CN zo=9(g=y;w-K3qdU`mGOuh1YsmsdB$!&VKt)hWwOODdc%+srP!Ua1OBN_Pl!%#lrV^3W(v<+_abT-G zOvwUm2Zq#=1=fNndHffX+ovM8;;p2!^F6Syc*z$_u6+DWQYj5N0TJQISZHc4rg1NRW53 zwKDPoGlL6LV_&Uz{m*RECEM?ZT1&j01jx>`_k7L9 zEzkWZzv>Q2K!k}nEoBA3a>rYBf4co8hlzI0ORjn_ea}my%2tHMymqF5H{9F4@$WBv zMU7qeK#y?RNUIkmtX&ycm)EQF z)t1Y<6rH*JHWj}2O|`ZAFG@+j{Ykb8JMO=0D|+s?N2IiWWkiL2_rIN|Oy~U#h065a zFUwa!_iLs5et*8A|NeIh)NTkEn}Yz~iwaEty{G~O-QOq_qt6!q&LZ`5QtA7eq5U3{ z({F!NvFfIrblg{Qb*vfis`Swxc>a&$>OOjEEOF1H^ z4ZQUIYIQ_PXfhu2&W~WK$jl+Hj+uUk$Ox9VgCkCE&Y;M++?+v?>A5+BBE8n6+`L}2 z`+<79gx_Ra5ybrSYgAQgRb~<|iy;1Zyq%fOw`=S!nV6H0Y3P~zW>KG567p!@GhsooyJQ=SL7^b=X-X83Xi7+N>D)8^ zF~e15{SbfwybMcvuzUG%wbCwGC7?x9DU`e}azpQVo7T}@v3P@tH9i0b{|N2uPLP39g{?{y|4P`&*N4pfK8TDs5^If{sDx!{_v zm+tsc$1T^PVkDmSpE*#~$U?9%BLiE0@!Z3|xb7#f{>Eq2LK1dClY9b;I)zEjOPB9gcKDf%S9IP&t z)oY>W7#P4UJw#PkNz(4cyAM1>-KT@KEJ9lBLf7rMJP{=datf{?%6u7|9XULvjWK zxV=Ty?{D4|+@L=VX@I;b`h?^RhUu3aYS6aNp{ia+36lclP@aZB@J~53K)GkC%ZRwuzpK58rw6IUj_H5d}85?=nC4psm5LRv@ zEN=@5J>7p^qApSTf^Ggcma0Q5*FKU_i^3<=qQ342ma5{2?s%m8>2Iq^O6~BY=cum{ zoPCZOUz(lbSR+|&b#FLFb=kX5-;PIv>H8g)lPSyf-Scjd zy#01hUl{-tX0dx_djODS`tErzTyepk;B6+krkf@Dq9R89{uSzdrJnLPU#yx4-nm#U zS9kd5U807^w>&EA-wh(lwyo%1eTmw{0@FX~QdK)7Yx$+>5Uu*U?@g;^x*4gAMA2Sk z3F`)@G9mGD{*;$o#Oj0J#8V{Y@uy57oC8Y|Px8F|K+7_A2HjJ8s9k z&_uE>O!PKLp{d>r1X9TB|Eqav<#zw6D^#*VDuhfNF{z1{CDNLBs=L@%F`Ew7T&Ye) z0r}3Arn~E|RO5&}d8Hbwp6cFtrO?FI?tNFODQeUmROel)yxu3nYWjHa#(OKC3gVlh zHU8ZRJkJ$3=$syQ zWqPz)3hn1@AsEub?{+D-QYvGa;S|$7=0*fQo7mcYN0<7$4CO69GhM9yx#{A(pPMeW z{oHhM<vh z-rxVubt<8Dbg#HhjRAH3FW0DDBYHFbihP+zCMLcHJT+dQ|Ik|1JS>|Aabcr(B8jbj z^)Hm8db?-*Lj6i0y>`7ibOb}$qGHT?JQZGVJ{}%CB`n-5Z|!&eBX3ZXWxAhtgF2wH z|HU3$F0u*=pJclG58j~m{U^G0u-@^!|6EMh`|sVL%l*sOsTH&+xn9knt6x~Jp0y7M zO+}qp@5~5w2@N)Ry#ywH*lN&`N`j?c1{-Or4z$6hRKvp2G4=x zFkcL2kcSnEVG=bC*v{@Lx2Qa&{?Oh0E45J>N{?elDKPU$W)o!a!LQZ9LIwwIQl}7H zy-6Jy#E$UC-U=Z;wdTNEl@8xkPN{j<%)zbx(YLco+1mZo?P`&-p)!B_9qM3z*`18c zQ~v5Z)u)&xuiOam$}~2MaRf`CiP+MuvIC-li}o z)1&2}=_B#y7u=)P7G`I7_ojOw-1?o*^$budBmE}vETwMuZ+=7_T(#qQVPDKi#|D!v zHr_;U$D97h57g=YjlWYb>m6@)`~R(6Tm8Ym^*(is>i1*!<8}W-_YL=}emy+Id3#^j z!hYDH=X#et=HK#=8lA~r6>@`2hgSdDe*<5>yApbwu8)Q!1AEqqJdrSuK;&81i~gS; zR=@rP9qG>NMR%nW{(?u<1PQAMyL1V1%~%>>~Iq!HG|* zeF>I7sU}nCmM7IiQmA{`AJhyjQ0{zM-8gwGt)FOFGtJD06Ff#di(l}4e{TG@FZx$* zRlnWqmwxz1bzuFrTu4CvpWOH#{TKhJj@)a&Uw#J6?C{q-qkd+jkM^K|o#Lgq_+|fd ze_{v4&MTf(-(oI*`&p%h*_`_v=5|o^tLN0$Mv45;-VTf)FfS{F&`|)N^48MARq$H> zus(I9yl{CRGi9rvrj_f7+{RzZpR*Q8<<9iIF6D(4&rQ5s;SAbm^jSUN^7U zjsWQE{@Amy&9W?vV@8$HUrqz2eH_i;XH#ZKI+>sl<<$FE;mmk-a{^A{K zazO`KFed-Y-_;ZRCw8b~^@^AMaqk!d(sS>qY0}loca_V)HNUI=OJvc_ztXkco8MIr z;xBp?euS}bnSa+k>LP#q`|A8p!bM<*pAzH>D?C_TevRIwcKV-J`s<+xto2`1`ah&e zhiiSagzsy;Xm3$?MxLPG@5$G$|94woRkCAS7M<93{jP|~e=eeDGr&%suKgr6Y|hhH zHFUm`!@?Ml4&qsoY=6bSAYTg!(j<>6b%CC_w^gPuhg-Wt`X z1@ak(Ba;o*VF&Z7`?w-qulBYsbghH+w!eu|GRE(JTsM^TzDs|!B`YFCia+uRy+3BN z@BDT_1Tu! z`q$O#5#>AoSLk2NYy=KL+<&58pOjlme)n+=`UK_wSE;^&K{_{~Yy8m(ePZzJj2W_0 z3ym@8m+cB2ku|{sKU1UqyRT98{v|(AdHxkYQRU;#W53yX`~|-zWJgtB@xr7YZRy#1 zwm2dq$&vmw>vYB5c2kIVbsy;0L%c34UQxWGYw`xV0%G;U=Xkbt5w)uE$vMfPS?u| zNZhq+mzvAMC%?^!wmC&@POQzzYjcX*B&T8S;yIXEBi^I8Rz&DVAL$v8H#sJ~*O9#@@*?WfsZDUHcDk6rRu;6E@? z&#H^c6cYx~8EqAhTfi+kh{Sw%5&(<+S(EgL#u6z~(3-4FoXvomcQBJlY5Z9dVDFP4 z&%dTgR~5#c5*GGLX@WaT4?6;5v8majRY4nYC^|vIWFH(p*FM#3_tP+MGj(TM)sx$* zO{P`v)2i9n%uGuq=n`D0nbw06c2TCxDfw8fOvRnDpq1i*vyuq`i7i;3OXWFec5;|J z2MteF$#c;3WQ9D3jZapG4cQ^hcMK@3a?*Uqz(UQJ<~s%$V@KqGLdSs9-WFoTP6!W3 zy=*u9he(dc>2EJ@%9sR*uKo~2`)y6yE#1>d6~w{7|KDC`1S*{}p_6~j2!J=_XkpF> zbP>spz)B+75$Gf`FajOKaz~)O@Z*j^J8ymr6uSF;q7nGNdi`HL0^kjD1|``!&`Tsc z0<3BVMxcjCb#@MHA~rAr{u|Tuusu?&A^Qy!{uoIA{QjQ^=^y&K!CJ|`j&3&y);fxE zu+~l_i?toOSnD5zwQYm3c1`m~VXcqCW&e1#pFiWnvG(uRMWB5spZz!L_Miw*xsz=}?qpj_Br7Dkhzzi(mBeyb)ZN}kO}0)7iyrb1B=a>bA3oXs{rX2v zwtu5;&y(%nWb<#b+4F+S|NrKcK|+-LE0+Yzq+f1w^XCc=kkT^Z)R(ccMs6G21EGk&L*B{7>QuNqrB^wtUG#S1QbVxfWpPPd>Iqi8EX|3_ zqJ6iOuj%H>SPsHEiDVJhK_rW?_E@Wj6b)CtJBA>vzlgmW9}iQbKlKT4)c@f)a{c?} z>xvAH=*S=((U(CuqC10dM2Gf_BWeVLxj5>{#nGl*9BqiT&K|(gTC#G9sw?*KiK#30 z35ltTvyUG_Om#XL_fI)O*Jf}{b$qWl&w$Nvtqrc3uaOVkd-G>bD*DQHXD+ z@W)sZ9CQ4KBmUp7f25N0Z`AEsH~u%-{F`h(L^uBTl-a|S!P;7eNWgNK_2zu2@$26R z+yeqXp7HDd3U%S|^%wmAWcY^7Vhw?4ugN$Sdx>yfz$h8sW+KBgksczonaCz0b$&F##H!^onN4Jbk?}`)emudg-ayVZakL}r!E`sC0^jvX5(9k=R3o*@jl{tPDwVt zjd+Pu<19#t5no)|B{}VihET{@mA)ulUa|`odvA#p6%at$Rdfg7cbhO)r>>v6o)@wV6bUps|MYXam1J&pt& zcEugNB4Uo2M@*lK!wj|yc%4B;Fgzt;hHN3cEXXJ^Z`6bt;+R|&WN2??@DL1n+>}e1 ztpE*euB;Nfg8X0{hldbkTeIvIp!Mbr8`8roZ3d;G4MjX6Ju7K*YTDYQhvv1z!j}$f zbLs*(DCxD~li63mWR;x-so4uTxFLHEj%ZuV^*xym6@x@cZF3`})h^}qdRVRv^q3TO zA!JJD2Pt_2Da;~y3omu1Of*Qr6_rUTBxMwZ#1s%#RumMH^;wt_b4vW9PSsVZ61n(5 zb}ZoXUnlqeu#aT5gzV$!l90_Gt0ZKL#Yzd;I&ztW?BD2=ko_quBxF}hhlK3ZSS}%3 zV%p`f92j;y`!KoXNqg(qFK%|ZmdG9*Qv4TA)g$J|*rX%b45_yh{IG8zOkr%pl)^9t zdI(dZVG5)ZrsVna|3i;*3&M=$lJUNPD-KiccwS-=|DylU$E!;J&y=nU{rKEG0^bbx zUEt9ml4qFcT(dF226u2s{ED2S$!@6NtoFb*j~#3-;@ncep8JV#pgM?bBf^1*`Fg}r z*wzsJoLFG;23sWZoML%4>&twnP@c{DvcQSSvsqu-PL$`|)g?>A4J;7-*S>-QsvsEE zkWFSOJF!Mm9%KP;9`40VWFGD)c7~aUTZ$PV;kE^44g|mX<~h&&28_1IJO^wom_=rq z3h>p%3%#AOUAv&;N@3n5{w1gB%2bUo376f4&^!1x&6`^%myFqvqS+u-%YGvOEH7S2 z?PgAdgVma}7qcVPBuR-5N*Ep)3^Jl-F`J6A*>Lrs`I+qe#ZD?Zkl#tZ=~5;?Z!yd( z`#LNhXfkIF;W4(0Vc^;9m1G+vWU`AE2iuGq+N5ppdw}J@OH>oBW+GWn`~ra|5A1@Q=$Lf>3U+L@WpL*YlbA`qz1Mf zHptk*U4yyAe(GeAf6Q0)FctGp`>NhgMf@LsRZmMLq+w6k!j2pDo3=Yl!sQXSO2QQp zw?e|pB5pMR*UFwKC%7j~CTN$bD7(|hTS+`GYdl3WpwfIpw*lez5|0^L&(f~H;0#^) zX|~-lCFQU{xtEk{VF12^bAKX@gP5EG%o4q~jaar?8fbTN{O%N9;wYLE^KUvsf3D)0 z*JL-~7G%9BOuZ78_#@8LQ_9qwcs^AH`=Y2z`CmFyk3K!KebAPxbJ#o>TkqKGTb%DM zXBW2FpE%NUj%0ISEf?DrB>_^ver@(5@8!9jZD+CpRrv+2x*;`CrEEhK5v`n&o}0ym zYjDACIORs2@mlJNU?nt7sLh*KS zv%OYcdYpW2ZfmsmGlz@78VU3|1Hfr$w(g;z6P;=GNN6gw?lzD9qpjK*E?c@VjJedD%O3OMmdfA?G9${y7VEU3qECejVoo zD0_W!+6%Q2V3X%OfxcDNs zIBjW%R*GcDaI%sS?(>gaM0?mw6?UheP4dc-tzF0Q%RNS>lPjTZJ4{7*5Oe4{cXK4` zI39K!&)Z0=aX27vVx7zt1wWLx2cT>sHG5Q;RqOM63PKkydsk)dws|W-;+HoK=CR}o;cY>^N|Lp z=o8Th94dIRpV+{QSi;SVQ-cbL(X_CqVvih7g)dk^jYP{F5=?94Olz%E=E;k3r#ABn zr!)-zN{V0<>Y#ojPz8_RlnZ4-VQM5RsePmhRR*MFL@cRWNX7#c_0`mX72zYLeg#H?$*GadY8|Q@J=%x4vbUS_UQqr%0*r zh;8b^WlrgZ!G2Tb59RX%ABjjgyFaBabP~&)VHak6a$^ZjSOq4)V*IWKJ10~#djEc{Z8Oir4d4Xey7VKxz|$wW8-Jz#rUOC)1^auWU* zmte>d4(uk3&)qFGlz^HkEZk_BcCYP@pfXuKVI3h^un{v9CQpS~Rd2vCA1u63GaBu# zh!kRW^Lix5?6i`2V0KCc1-eMcb815PXtTu2oytsHVC@K`gZ8dcq20yMcNejd8SSXi z-W+l3rF3t^btT*qag!2mi@2jC?2EW#(1p;V9*4XgJ$f?A+AQl)iJ(%6Dk{Y|iEX9n z!Z?&ux4^&QzfcHey%)4ruJ4eQpotl%?BSnBKe@g`C^INEBS#OGn>K_1gB%P;S-~fU zqXCJD;V3I8L**Q9W%O--{!;y^2(&rOaF}AakKr(A|AwoKv@6ihgfSH-L})P?7{Wjl zP8$(&m1ILpw1eT^lT>nt4a~1mu(kl!nD9-|7eY^FQj%dxk()_z{nwXbsi;3US#Rnu zb%a66WT?-X3{X_V8aLuuF{B1}#T{%-Apq;AH0xrXs++BO1hAml1n{L91jCv+7iCJ# zYqpN&0%3k>6{*5#L4IDu*2OVTySlhUjG*ukmFQte#_1;PGrZlXr!fe{3CTTBMh|19 zB)VK$z+2n^R!jhl7y*|h6cH4~yh4J)m=`68#=HW8f|!?2ke`Zqd4zc}&nAe(IBlmz zDhv3){Ahdka!8-f3f%?yZYck)N|G?*uMZT15=qDn95*a zyb{I}p%y1gH!+?Z0N_6vN;k-8p&K}mTF&S#{ANZ9nD&js%D_}`} z^KbMqd&_S!2a4bRjUHDKw_0w0um8bc>`cGB*@_*?$0D$VCtlQ*wK^d}V4RM+%nw3l z9`n=DoX$V8SU=?#zMv-`xo=-1U+|A-Qf6v|!s)&4mp`uF~U59yKPP6nKwJun@Z z1n2_Vh$J%|o*Mxl>9kYSg1~1~?sSu*KQl1i?qLDshNYw8lkz1cmW} zz)Gc@T=LN0u#Eff<;gila-6E^`kMqL&_5@A#Rqd10k?>q0YtcA2Pib(TCe8h^ zf$9(lH0EVH>aa6F*1K_eIpIy=utcyj^eQtFGguo!3I^UdeO6M7H)0%k){tm9vI$bt z=LKidPtgB2g5AIQ2Hh}5%*a_$KB!?Cl?eXszxo}vf*Mhdb7-a|!_9yXE7AGN5;RTV z^Blo2Ps@tV-H-ZbhbJ?VyvbOG1OQnOXFzkrC)$Uw!81gB78-^jw*mWC{}N3oM@^Ed zbDC?wph^4^x{_JJ*?A%K80G_`+s$@x>Iq0l2j3@N5s)roSjL-?bIp5{XDUm-q`W9U z1k%fZ>!-}5a)%qxZV`IKFadxLAQcA_GiOpx;L9um8Y3f3apk5`Q*eF& z-n4vgE)=7%=~ZURM3~fQ6-M*KJ}k(O_;-$m3I~Q=ycW3I%smSQUOtHDu>N!sIS1x#z={_BFZcdoI*b_ z7W?og9s{{kb^U;d3xYS7`Zig%lbc(Mu!OL zfcwT$gZOXot)QXYPA0QrV}M&6*yi@!*kBnjsIe>rRtVoWEz35S?L|mN(mf15jk`A( zGL{B+>6`{*3i5AMczFg7KRciPA#KC63{K!VS7&$*Y^En7l9=_s@yoErk7O3rnC3{6 z_Vo>_5Ei=u>yV=-TX#ohDYLbMs{2@JtOYG8_6I~HOSK6@^ro{($J6G+ROSVj7Xo6DvAn}vXO)F!@hzX4D zMe5uXo)$x}LC>$Is6l0%LC$s>6Jq`xC*eir;vU!Y7qeArHpX?>33A&h&EiB1y?go6 zR|5reob;M`{^7OMP>V1J%sJ*}vfSZizZ>bh53aqrK%XZl(<%Sx(Yj)PF4o<9R^~r2 z$F9#e?6}bXVYjZ`CquLN=9uCrF4C_3WgwFhLQA7yS`q(_Zap#uZS2Z2tB9x$0e3LE z!yxVuC=Y`s9Nb~Km?5;+E<7RR5A60}bB_=SQ;%Q9F3TX$yXd9R6C}oPW2!eCf@uMV z@Gt7tHQ705giO$WlRfLCV|pCwJs)Wh5vDZ|;Nl8$II-btB@I1;G3&pc0tqt8;$NM(*VI}95h zV8FOUNP<5BJy{v}32>W`1{WhOl^hc$!k!aO7+FaKe|V77P6TSnHqSWYbHogLj_{0T z-4F&J1yWZBO%)&=h#5)U!SL-sX_n_cp8I%CdC&4XyWR$RI+@I@HX3EHz`eRC?-iMXPVr3Z44}HrdO3X8ao~;;HZ%C?3cQUZKI3kT#nvKB4ke$^YGN=d>O>B zB3WwV7?nGrt(dp!tsdNEBsqKt2g8uJ;cYf<6#MPVbkp7%N9DFqu|(D{*K*#yyBDs~)A{7w5B#71L|+J$ z#;bLgQk(qyf2wz#wDN~^zCi>P(w^yw$<nrnypTC+h7~@@bEe)B%_=MLfuei?t z{Pp^4YK4E__4paG-(ur4UAC9`mY=tcN_n|+h40>=KfSlZmxFtAgZDZ9#*6gmy@iq? zf;ks!e{bKF+aQI24!=a7tv33*;`R>R{hf7ew!YtSG?(uyG^@$3;Dep>D`*!g5GuC#?Ikw{+BoCdVv?a zU7sqQ{yHHaCwT03ef~iqCos-&Z}pp5R={k~!nZlT6Z-Vd2ol8P%3z#AQ0jNyp}z#g z?*2RUJk?!)mo8G=ecsrkFCbXgqd%wCcjw>DmAF&ycu1@l;w#R4vd$7PQ4&y-S3V5k z%kqXjihL4Kv5wy2Uv&>A^Yz`M?}Z+^`|i_g6c@pFJfL@0nv3Ae5I4rMC^MGNL;F8| zP&ZNH{s;A9$9zw5oT$f^cMH9uYz)WEBR_rY<_oXedcz0ZWl!RL8G0}5jHmPz?uBiA zN}nFZvuDxAd;MKc>uQF%|0%stg|U?q+sD7^4|>iqFMp78RW4n5JvYu>$A8a_HAl!0yP61Gaq# z*bDuepJs*yIM-X=_uqJmTcIaz)yZShxvx^yRk?Ah`gv}gs&2@QQ`N@YI6c_pcWtEy zFYO%MgV%E7fPFhR4%iQJZy$KIM-jMm|1+3(vhb7z;1w#sT|!ZXB?G z&y53iS8g1z%l$6E9{bDOWLkS$ZXB?`&5Z;0{@gfVAIpsc_7Cs*?w`QIg}Ld#y(l*h z+{<(0!2L;X9Jtr!#({gi-}NV&yG5>yHj8!m3t;Xa^rw~|6SjG zR(}u2?=PO!dsnAyA@RIzcExgI_upXj*PzpGF0gWve& zJ}%+@oj>Mz{T4yppLLI$`HXtv7np7WmOayA6Tch;I~wk_$=~^BnBpdX;{WI?=Ipot zE^9uW=-s^hZp+)366|vFdi!DfJ#Ua%Y@r<{EHsdgWu#3E3v80eW_~E(A5*HU{hz$3 z>tc7x%9W*qO5Ew+^CH*l{K4P$qCU(&{Zjoiq*~#R^r?F*z7WoHL`3!dNS}^GoSVjz zk3{@hT944n-|||1*t%B)T=NPnOcNF4J?zW+mB-R__VXZv&myij`PsC2>$v{jx#HfiLTCfSC<1>sPYb#bmGEX0q?wrVm4EC0@~Ar&RkZ z`VWD4W9ya?Z=y*W@wVtMM!fyxFPwgB_y6{nj2uja1;xW}3fkLE!D-v|xzWxG80Se| zkALfSeUiG%uYOf8lUC%()}aWkxb{`OFZDk9s=m7-QbaP%)eSP zHolH_4}gS!*Y{LjBUZys%!Wm*2#Xwbu)d75Mvir`?j)28g8A*_#-7zG*alTJ(b^VG zepg{t$ADZkPQMv-zo*!Ms<%hob0p!i82h=nB^*OTp%@y(cI0h`&_mCYYP|tVH zF?$$EV;_u{y~sfs9GC6%xYoo5>6g8Y-B{AE zwS~#cRLawbv0a*IZ=t)4d~E-P?m`LgD0Gi;K+mQ^7Yhan6Oy=^2DV3qoq|W@xVNH$ zoHg10(EEd#>1gqwZ%t@ql@8=UnGpFMd=VH-m4`i;P96R`6LQn&1rHWT8Dx0LZR|B9H5% z-9?0)R&g(WLp2uT*ajW}f$)fZRq|6gM{7@da zW651Y$lXis6_W5*!1(fTJr678p{t zA)hZO88S}_{wd`3zO4_=+QAIl;0sFb3@5riIj^PWyJ89~C zlq0j+t2sVgs3f(!SPE$z2?KZ?RgFf*G z9cyL5m@|0#HF)AG3)yT`>S2y&?A$U*y3A~W4G_{~5cDO@fD2aKvY0c&cwMWU8Ef1z zfX#7z(Y?}tipHYH+KmTF?-KMr_%24+r^0NX#1Nk>W494U$-Ibs0i#rw@T+8iH~SC0 zqia>EzwI48C53#%?M1`wUbEj@28KeMETxMen_a?YKl6uS8``qy*oiC`)9R$)gsKF(5a zHIL#k|QP#rXn#J3!WeE5fi5x3CcV5banjZwOSm(1o= zB*pn`vHYeuQJ~tXEOH#I)}z_YMaQ0qxQ!BSiEz0_jOv|6@xO_Og!b3Tj?zMBDxL^> zF|O4aD^~GBUU!#AIqkVoA%;Irm1;wpRB5uj2~TPYz>S$Hr}MC6N}58>~gS^Sn-p z;acM;Nam?UHryNbqLVXP5*?6bz*^vUGdQ*rb2C`rrUtjlDM^+`X4}HaYMd&mFzWQMAZ#V~eOSQUH0H_ByQpora>Oq-AMlxOq?N+zME419W zsfnvdMnOOH<#n_GVP4DNYt;M;t?ks@w| zVZd;m)?|Z#I;SmJDcqq_-mhc7Q5N_4imFdGupv0P7s0LLMo(^op?D$1EmK@t-5^6Q?Q_EBM#E^7+5}LgX_%+wEyIK`?}WiqfRbS0VW4uOBt-%s zVviKmGCo31H4Flm4?u1s<)fF3R}GXey``f}4$&uwiL41YF-KXe7=4NwhLDVcG%jI+ zN>IeviBq=To#$9`D%V-i68&yV`vvzAHJ9aocKbo>1)EcRBK|Bc(~TM`#U8v{&BZb7Ey*dgYIO^+o#nX1ARK^=f#Ae zsE!~3$0L9o%jF##%Ha-}Chj42qRfuu#6Smv5@eHtWjSNO3dgPj+fOkOHxm(2H9Qj$ zO7<0&X+egX?LzDYqx0k1R1#v+Vvz{higDWG_vUuqVbHE5DC=iOyr?YxlLdz zIod6ei9@%KhW$3RB{@Jwug5wq7s8!V7ji7@4Q&rk&7j1LqrvM~XKai5?y{ElOmkAp zj+Pz4jx!%5Cg($Vg7+EUrYAF6)Va=#WyuBZ3}+%*n6<{UmpZ~<+q|P;Khr=kL?;a} zvv(o{i;g9f9CM~Y%N!HJ=c?|JBclt)*K2*tb1JB<`v2jMZC&EV2r`(ak z+JIL9;}KeG5Klhk>m>wI*d4Mn-6?b?p{KMWZmJ|S{Q_&kzxSMFG)T#!ex_=QnOkAr zrfp{GN`or`mdaPl4F8n|g#G8hYS{Y>2Nz7zKN%YXwj#(+!pmqAPz|yKa}|LA09bJT zqk%V5vQ^P@PCSAw%LGQ;@)}G=fU#IPQ8>?u@D-+&FtQqPt7q^i{3we`8UdGqH?_sh zFjhH}77KKUVYG8XfI-Wd>rCS`$+5zbKy>3Qdg@F>R4s<-L%u6@AT_2_mDwpcNyg4& zEGB^-1Q42dAb+^q+r3j))(l9XNvOY}1Y&V{d5>L@G9Y9D%Zv+|rzgzQ%W%ZCraq`c zx@I`0u=S&z(K2PsG|!z7Ntr2L)GcAsaNWA#r$jhR32#LA@WzzXGF}P$O7C7!6rAyG zGh51Jwj@hhCxFhVXMGwy?P%v{|K@zVGG)$9{W-iTZEjAOQ)tPnEo^sIS|l4)bbzAj6C`pr&?HQp|Z%0)A3 z5_~LdBN?;=N5Nn}{?7{Rvr=*y7zjW|#Uy}d9izfFa9s)G2SbLZn-!O=zX@q126|}k@ z6e!A|h)<*N7%FEvhI)^jtqk>^p=39Cfb5W=Ib%L?MU4_9#a$+j2)K__g1Fp5TA5XXOtY%ynAV8dQ@E?)0<7_> z7zCs9Q1Nu?XPOLFlFHE#Wb8%8YTUVll%%?kP?G8#c{Nu_8a&Tg^Dj&`nR#9zctk&% z#!>QMP_naKbUrW8kM;+XacBRmW>o5RvA8Pnw-?zprwQvbEI3eTf=jMevdWUBG*U2_ zjnsinMh^cAhD}kq>m-;3 zxXiy4y&*3zH;*&RQ91bSSZILG*Eu_ku zfyu6f$MoM_^klDNu7gCOj(jc5Zg z4mVjv4VQO>pQ7T*^@;9Or%{5jX16BlW`o4$I;naITe#$dAT#ywt2ptQ)~5)}R%7rx zS#5~SvgR;Qzh}qi32-A3O)`HS=AJyU>Te02&{^jOPuK|z8fA@u#lVzf?oJJ!n9?2x zEWvWn1H?y?8O$)UbY7UOg3ko~PQvk)y2k}JL2QK5ZP~TS1Jl`%b*Dx}C%)27hV~<< zpfDs9W@Zx*Vl3+ok1#gfCefap6rh*Ds(T|aOd%!yi=q5DFB(Hdt1^X%ELrRx2d`)v zV&wj(JOs;G-Yz#3MpIy@Z4xFHahk4pi>>Pe6UP`?48KS&;4UBLFQO{J zOX4ZY+GI19Z)ltooMIYJdT=)S)0AWWj-r>(Kr{7%*#!F-G^5avC=cn&>DNJXDtAiB zY7rLYRt-$31Wq29F+(v=xX%+BIg6)FOr~;KD2wKybFG1-A#<&sq|96sE1OKVk)#co zY$uR3WU`f!G-R^HNy?dQGS_4eXU<%!g6?G@4PnFv&$TAzn(zvlY)!#r>wKyoJ=M8)`e8Rb2Yq_O6} z=K&d>EKUzGz9knpC$a*omCkeL57Q`=GhLyai9-&dqDhl~WER#FtxL`1^qvLnLN3>H zDot60w6gH zFnP$GhNa8c`A#E`6V2{K=eU9SZkioquE?3K6U7We7{N$|3+E6&Wr??m6`sSQ9Ie5cOWc}QC+a^7o|w}p z+?|~SnEWC~ik((1g|AH_ao%Qq!6sZoSX@%CylcT*|Z&C zbGm=@7<)u&L&yQ4=84wLnxz(<6lG;C?>WwX*>}2#k1;GGfGY=2ik<11e8FQsVzx6P ziyFjGfSM977JRZD*QZ-UGS|z9c{(N$qH^5~HWjVJMC27LG#+7Y9d>Kz5q;=-hM-Z2P{k@(U6s@&%T4#A;FoHRanxX=Txd6yEIAUI zt_g?F5|>QFOv@#J;XCw%Nnr+AvV`r6(2yBo_`4wwj;BDL0g^Md#2MRw;FE>(nF#TL z3gSTpaZ^DG6L(Gx@!T5lE3^g#NDZhGsOAQ7^2xgxeiy;tQ=o4UK=DSjI!@R~18+<= zLQUq@Hkxl5)EP-hS7hZc*q2!)AuMi*K&Dec3d$;aqwEnfiev?IRJF?RNXMgS#xkBY zLz*=dkvYvWh&0Vg8BFH1%Zx;4M4D9&U};YIZp{i)hBPaXSks*YGNd5KT$#fYv+-?a z4(}hv%jpKD`+&@3d}N7=q{6x&_CTD-6mND+I}I!8A%19pTNCFbTyoA}79a=hHoyWL z*4`Twi*KXZ9TrUH!yENY@@;8VhWQ{7>420 zFbuJuhMZs$&CPO(X+&~(gpn4)Be2vS6=z!$K!L)o;tg}-Go6ZnM-T{)5C;Ndi%7{b z2|1#cWfC#WB|=mULgWFDC?`qC0sFDx5v=8$sA=#3cNpLcd*%oYrn~!z9I9~kqn#kuK5twW?tSVzdb4EyCpU=A=ZHdFl*0CO9Z;SS}rdNA`EA!CVM zAjklTOm7(yvji@e5q6^~X9SuTh^19W(9?Q`)M0ZSjTctV_?q_0;2VZmj_FF_s(-hETo;~{!u-uIwh0KUT6R@nGDh4^IIqtNR}R&70G6)AzAWDV@SUs zD+GlXQMkXf4fDEO?SQEo=T4LcLW!^~_Co+GUXZCM%pZ` zP4YojGqlE}E@prMaW^37_8{U?n7bO#(uT-0r#u-)xpt4YzB{bb-0buZor^&=Iu}u$_SRzok3&v*qn{(J))bujL2curm5%Fm#mPX(N z6I3E1U@9EcLYTWO+$~cC^2K>BnrN2zVqOo?fFoL9W;3x2vy6!^KFi}`-qXb5W;;ww zHjp{&3k9x`7XhKo7nU$Tq>cGOA7nuc3O3uO1DkERk8F>+m0U*|Txftzy@)>Sc^h+G zXq}?KZu^-1`$x0Snyt&SxfMH)?~buQoobNv4c_!dxrnpQi+O9e_gINamK_bb5xl70 zrZ6XSEOYr^D9HwSuj{Wpme=I<5}0_nBB7<2Ju@~&4#W$849Aj{lvx+~uZ^+us{)c5)ZaLKoKQkixDqoq84$2)n*~_KBi>g8e1dnO0d_6=meM%cx-hF<1H$$#fp(3%`z7-8f^^(7^8*$EGP0XE%^`a zV}HJ;1;mJDSWKiIpM~HTYDymFA2QC~cb~~<8He(JCjXfhZaI6{dg~S@s|6XEzki(t z#F^~basYpFu=Q{<$M%CyA5G@WKC_XbnruWOq%B6>&>3*>5VJ_~a3Ou;4me)i0TJhT z=Xft-cJOu<1nvNc#d506_T8vc9T-xp#Untfw~5W-hY)UO7x|Hd>uOuw{RlgUbCu#^ zcM6a1kTr?$y!YC9I7^|rOEK;v$<|=*BQb*w%?PVM ze4Lf%I;LD>PC0h#VX`y*@1y-=Xk4(r24brAwvkj!Qn3FMGGnTIWL_jF0Ha9iB{mW2Ir_uCTgh`ZmBu)V~+z`W2= z;(kpMHpShqOV|^4&yp~;IqtT}LuZL=>RMUio+9zC68DP|t}Ss-l5j(bd#r?;N^px1 z_LR7Elo6?(V)d4sn><1iHo+&EraWJi@sQ+P3t{lQ}!k86X+vv}+Z9>31xN_qTR%j-Y5?4>JjSofov3#l$A)@*N+ z0vD*3w|@G@iyyq7{_h{N_lP1vG-cv!1d~GFX}O^DqV}#`>wmk&GG%h2489ckb_?GO z+|^)mgC|z1lJTvaI)}DkvlRH|9vhGf8pL(oQs(rJY``WdvP$it1sf#i2dVEH*Q=PFk0e<{8T( zRnw%7;d~4)Y_%lEpM)GmBc+Y7pq&fQ)80QNM*NDl$OWygsC{VYg#Ok-SQcp zB2jsYkpXc|U-o5>Rf??fRt9lnb}8{Xv$^30beyGZ;q@xzKo){XIGBmaK5>t=UjSR= z3)`-4&%RBZ<4wQHjZ@e9-1xQGh*XsV5Os;#S3jsOVip<#7iUYqFu(`wzR;U(SeG!= z=?KK+=z!rc+{wCNkTXLp{2CU*?Hgvqj}(DCo*x+z%W{sCMdJ8WIC>_5wbuA>cqQ(P z4~v>n!*f&T-NBv07`k9zT!K>LJrhL1KZ<@1{*g@Vmps(aQXiMDhx~cE*Oy_CDoN&B+;go-$e}Zs&lWy>(FvhBqYce<774Xc&NMdD z)LFRpcH#{FI|Rpt>prCgSS6& zanzkE&QFGpkvmAXDI64d?ho=Z4#0X>J8!pKea;|Xv?GtzwL~}APY(7)EK|?Hq*~)IX{M6 ztM))u<`u7 zEVZ3Wm_+5zTu_EiAqN-ttSawOQ_cr8NB;atQ}G665Xh7hst!Csv&0iLRj7Zeam${y z%$+6HPT^DX3COHJH|ohX-L%xZG2WV-2-vLa65(q6mFqa-(`eZu*^G=$Bz|I~BLa4+ zo}Ds#JlNkMjA%z`@<7jARtRR;DtCz2lcD|lm^hQXM=UcHjMt~cYrr})uwIar|6U*= zz`=6=!8!IpL(X#rNN_;{IACv-9NmIDhZBm*ElxVJ6-^$_NI;tV4%*aglW>6Y`yi^|3@&>*k_ z>)9Xt7$1XZkptG9y?^%2e7AmvMq~wgRmef~^TA8U_UhhBQR! z|HIz9z}a<`b^qrwNza)xXL9D+xh8v`Oq$7Ma%t1tTV|)EX)L``Dt8JL)JZ{FYG38H zr!?S{TL&mkfWCi6DKu*PnhNNs_`Xv_4Ole-YN~zx2dOYfk)XwYP?Z1o_pG(|*=I5V zis;Ap^LYuKz4tn6uf5i@p66N5Z9S_l-?ys<%Gq*Tw*!&wO(xWUn#&P5d;}!`PCO@lN8kgF0>L2`;Jq0?;8&^n48BD%DF&IxRg)oYHwHeDd*0!wYW#K%H$R z{BYBMk9ON1O>&WTtDj~5Pl2{WK=a@Vb_iMtoOWnUOCYpEDDNQzR4$QLdx?RFuMtaZ zIS0=A7zPa@VT3kP$ulg6{bAN^m|fm%`j=Vn&hU`XWh ztQ%S6zy!C`ZOkkthIj~~n^D85m8)O8^iH?!oZNUz7`G$W-TnAI`GZl>ZFN&v=$PB; zHWpm0O4{o-Mqkb6PI4p34P&zNu-9!2%SxFi+v_&Q=fsD&YOmY*!FF39#n~*YC(WAM zB(v8|TQoO=`D0)yJD4@B@Diik7O<0#3vXVeP|WKICqW?>3{C)c(GApRq^NSyWpav^ zp}e=$i~?oUNZwGTEl7GEnq|e`tdG;D@k7C7>TX%byf9i&CP9{R^eFg)b49;`CKicH zS9Y6uvK&0eY?Ec30jXVhs&$InxA;A$LOxqC%Zl&_cDS#Ae}vsBM7SKyai1VANdtIF z(6TJIcZ!r{h3MLNif%lAq{q$RolG_5=udbY^axsxPU}a~;!h+vN7E$B-s{HkFt^}e zn4%x_jac_WEdb@{&64oOas#d|f}B2sXI{U(T~DLAuI*Oy9z8|pCo8|>e9|qh4sH;= z2bGYYSSx*3yLw6u$5S#Bm?um*W3j!OrnT*6TE1_{^GD63~Sf)*usH@ z#+lD4RS+`knP(>Sbeg0FWwGLf$r(YwLX~r{WVo$eVelgLQxQGR;I&VA1;IjWZ(ya9 zlj-`L^h#78jz^VSNLOOc`yrb0o}U`;o}U`;p072@Q1AK9(D!^Z^qFo)r`-nqOObc5nS0q5c$!#y zsSJ5t!KGNdx-jI8Gbm@svn8j?0uZm|WWXWihwKW*w->Gmx`et|>$-%rGTd$A_NirZ ztagc)VdLwHj*#5p$GFaM-OLqz0F8+9R@wf#qFD@m=c}COtGS-!dIQ(F<<{gj{e+U+ z^1|D~UEF^N7I~5Efnz)z;fU8Cfv&^7nA6z$b}^UHiEz6!{#}B~_)3Wu$v+l3lc(`Y zNUYCnIG^cwMscjaBf@`ko}^WlV5{IV+t+4(^clr)^I!h$d@dz6*!X98_9|={z(=3S zK7aCWXC8P)aU*SFcw8P-Wl(f&E_0BddGZ;>Rs38&h?0j;oDXyMNo^yccQYg7#gV7| z$^SN9+!%KSHR7$?fe(4MSq{5|65r;0b&=y4uG|4#a->%6{zyCbhu!_W;>RYr7tr;$ zZKC!;xy-u8az_Z5KssZij)fX@Dyw#9(1kgWG{A&$h}(h+u}Q&hl~$}aWwR6|9IjLH z$&Yz9tSD`MpXT>OKUb7CMN|3nIY=68C zvuG~3@P>!pKon*EM(hIl>joLX2%;&Cf{FIw`&Qtd9AOU3q@kPzBS7Cj^zo1y=e(Q{%|nO zeB`pTb^L(2XaF2p-yiRXhURd4D$g--q-AcdMt3Kx;d=4SPQ+tE>QXH45SAxrz;_6} z6U6Rd!r~OO*)!Ex$2)}62{w0#NhhL9m&9jAXo$RCtZcn04hc5NbmMUZwBS`3;k5IhZ-M zpNzt1zFwP+6IQS*v0!#dG=o2GV=&HcZLnGoj9h*L1P9T)tbw@) zz(|r@ff*0BI&IUY<-t~_#iFtP{_rEYXhU=yq%^hiNA|@gl6FNWS|^}#IAm9Jym8Ax z=2pu4op{fV{xQf5ZSPUV8?>Ap^CcEDegha3&G{1By>SB*5Y77%YyRrMUcOp$rI%uq zDe%r}UoCXBTBIA&CoXoE?e6j;;BIo)m)_OVyOO`tm_@E7VxR%Vu~z!d^=NB2F57sN z;~>Eb7XO3N;_?S)e&GOHuu0xvfE{cv=JLz)3al6h#^Z=-yoL~W4DErLA~Gf%1RUA~ zJC4a(KL4N{%NH-Gmcr~t9R=a%0ur;uIjjbpkwClD_CW*C-JhC zpy^r+f^8|d0J*|!RdC+iv+Es<6kcpnt|Z;b_$`O8trqv8#=kMVK#P{Rr&=IhLD34e zyTCGH6jxkp$j1UKi$-E*?~)dC!9H00!+-z2U;Xswzclw{K2v*4cnUAlFCRH`&zJB1 z>yMuN)AR?XUO#;Jb056(!S6iuwXdWT91~n7ikF|I(7QgcP7Y?4(gU+m&tg zJdU!MP+}`jfMxjtEid*u_y}0-6?+N>R zvgYc!n)>w!Y4j!3=zewuht$mahod^B*54oF{#d(o)Y_}~fL3dMiI8v@5n`Vf+!)x? z#)-E2i|ngy^;hdU-&X%gUFYDNhYuenmC0cJbyUsmQ=*pF-1yKN>#qrZ@{RR9`~K}W zzV)47jVHhI*PqM1p?)Dg!gU1WN}WW$I20=N zo01Q`o)6vB_|OZ3+um5ez%87oi-Vi=p_5|!7Y2K7cqpKEfx7tyQiIoT z3SOMu0fzbT+m zUq5(J{KkF9|M{Db{@UMt<)8jB_XcbGM04A>CUviGuBv8FS2(1Vi9yK3;Nh12^$Ct& zB?g9W(V9qaR|#B4?W8t}L7K~g-7yTUp6K=_@YSCa?1taf6UF*`9%b_NLS$F4+wP8V z0DU9`*LQ`1`)mEdWhkVymn`Ma;gOaSk@S2XX(?I2yMoPlwqbDYKbk2yX*hq(mk_#+NQ`~9Gx#xjmGocYERx8N(KCw7vt>dp z0Q@n>acqt85(sksWvT7sIjOY)^KH^z10L)7p7>13hR}&+J^gA&*34ebnBn37-so;$ zYCx_5v?h2Z;1Wr??*Iu2ej)@9D#?YSj|QlYTggJE*?^L)5&H!ZW`Wg%qr1()4IUQ;sT*-@FXnmSp9baSyjMpIL zH#r!PTv!fsSr-G(eGu%1fjbCZyz^f}X4(dVSEM#S?6&q?rB(f&A+l1I(C z)&}xbu{VX4I~f}Dowybn$}1`bLpswY*8(?Bwlv1Mz~LYmpp6vBz48kt?0vfpB~PV# zN$4{M#92oMaHPnG&#ZX+lMbJr{8F=P!4x?Lkie=f3oAC9dOZM;v`JD+;L|4L^^gG? zh$0pr0chl#3aio(aAYk08XJXE*FyB*|4Uc4M2~aP(F+Xg{uL8u4dV7eZp}2;6+QaD zmCs1n;xpI_k@TZuZdCj{HEQ`>dV0fse=Z64x<6 z)eI)hDHCUY{9A=}$x(gdYGmBGzQFz5+C#31HNioMsCvx3-IZoP4 z%Lu?$RA=Jx*E|1-X}=_mU?=bJdR?) zdxhzjY}F*ElsOeRe962DuMo~*lGuq?VNe{9SeV<@Be+nN{CRlQEPq~y?1YHY9noX& z{ihalDwF_tAG~mlb1JMMkb^pMDy#wIS*OAp;TcYaHP-AJP^1?BOt6N}Np$Bb$$L;- z&C)4)uLA9wSE1C%pa&`L1jpAvUN{w?)oa67=<=*^sG`^`XITz*SX6{U)`o&ts8fQE zsYkLW0I|tJJdwbs6#NY0l6C;VjXtGF*LmDw1Xqh=E2`Mks8V!{TT{_suY6c=dxf%; zUm6@dfZ^}?nvg4G4aY*Pm!B_dI7(s8OE+z7f0afo5pvM(jM1noP*HY4Vvj7PrJiv=s5~8Wk@NY=N3zS;1DORBb=) zBV*D)7Qo|yQUQXj69AGDKe6LBu>!J9dO3In_=96diXxOb9*rClSjXdx^I#k_Vl zfehYKD?$82^;ei$M_X4&OcEdh{6tx0X_d|3Y>~PM3 z9S)8=Qb)p$)RAD$ZOxJk3X!{@j`s^`$jL(#IN4ouRSz0v3)6?*&akOQZwI^yYhXX2^$RH0LRIaeT{^ z&SgkFT&9T`iay`v6Uq)nk8^8uG2|1Qe>4YU#XY9P&gF*9;NM+@+og3X1oZB?day1sjxHU3A&&Bj6~KAIDH0K!AcW^ zDY4?1urCyS5B==m3~4Xi4n@=I_?|e=k7EB*IMbcgY`wdTxJ%$J;Aq!Ga(gnl-Q`Pk z(bVXAlznOYzVz@X|Luw6PyFsPh(Ffzckchqso#6|U%vT+sCq|K@DE@5?Js`lfq#7< z{xBueD*1y)KXmfXPki;GzfHju1<~X@BJ(lbF_qE#zl02kY}U*3RQp6!sr52E=8$1r z1V*ka*c!&{TLF5dD=70TCM^FeTa$IiEDW);q`O{@qepVrz{hdP)0p&MAqgKH%l#Tj z&THojx7cJ)#-US&a|-W9K==1Yxj*6WXSqM_?=?5`+^g&S1vhK2F?LE@3*BPG0y}Pq z_^70ALUWf>ZK^}|F*jZxifVmgWiQu$f*-(aDwOOV-1p@rqL&r_t7+Bjl}f9sg42*rnZfYtHGES1xDUW7GK|-e5kyKU&koVD3A{FB+5FB5FHF% zTsYcEL=M*nJ5ed^Cz`uBI^x8%wqR&9cTc;V?Q0Cr+off}C(DWO_M?QzN)z3XYLgor zGY%}^kTFd6BCpA9qy~<6Ar0~w6z^S0WWZ4LEsWZGlSmZvik4>H{o>;J*|WmG525|= zA}c(zB;HGAbBntY@3f~3wjb4W}7+^|F_bctoXmxi~n1#*{u@)e6L330o&cSq zLQ&Eck0)xaRVGq;8?e!wqQuIgMZgY!0=b0*X zu1e8dgZ(0IT$bE2B@4Ev+ZLVc*>T7Og2bihYx;FFQ{p}nUKy)mitiWg(i}xx#f54GZ9^q ztQXN)Q_h!u=)`k)0Xy@M#Sa9-mZxl$=?ig*Zm$K{>jW=nGN^WDzK< zD`o4-O1TSHv4uEM6~I>i33h?nH!2p#6HhVU3X>G;yseM?W{C1l#;$t88UWG)PB=E<*vp7pG{HFoB7BqikspCGEUf! zoDG5oR{@8w@Ky;RydomH5*4fx98j_CdLY^2;qX9(d{9z$w*&{s3q_voX9Me|^ic=w zck)I(0JsnMq)`ER+Hzzew6tGQDOSeEXFp`R<8y^j^7Nw|o_80CzWIzgQ@Ea%H!4Bb zOI3kogl($tG3tSh1Rt~gt)Gx2AkKFJv!JOHY(S|ab1VE z7arA>9nL3o{ZZt2U9a6XE)c{YWlf29gP#B&8VN!x?hFD;AfL@6`VwkQ z1TVpRY$tzM$3+a_a@yQZw$iQ^98}G{K040Gr;ot@X<0^n-EmhpkQ;y zV%XfV7&dn_^cDx3J6QGI_;WNO;Jn9xf=%>~B@2YAsu$n(U;RFHQSpoI#o5Rz!@gwF4`{nFUE&w~ zOh`mJ#7fR2Y!ag^PBMp`cPjve=fCNe;?U+_`tp2E;;~5zSm1wwXD=~Y2-W+u&+q>7 z%%^WDZr+3pkD2$J31cfleQ*J)h9R~2W9%$(?Kt|HOjexqM29iR6vC<&f%yl1)?_zz zyqf8F)dtF_%F#!@`mcZTrpxQ?Xi%hB$J{|L0-E{5Y!5L-elGjWi2ll+@y6N^dpOXt zQGqGL`&N!%oJ@x#$S0XsJuZ}-xH~YyIlU2Qv9RL4MQMxgU*H-2v$q59SGw7CdqcdOSa0{Rid&aTizU?eTlYQCEE78vtjyE(RRS$ z7GhX>HVj#fNyxIULL+9|mNbMm0d74>hpRXUAN=$ATo9A~EYV1eWF@OdgMc>&$fZ|^ zu$-!3xx%!OG<{~Tcsmi@p8Fe_FA&l>o$NzI5eSgc9re?qnSvw_(R^fE72{;O&Xr{t zI6TCeVG;{Kg55cSG+H?oG54@t_idTloBIuS8%$l2`?xGVeNztz)4(L6)_|Tcy(6Uz zT%m^lLbNKOP9RY=98=>_4|=g9jGhF)I>NpD#MyIglq@=l@jb?gIBl^*U~L@chBcCB zcz|fXad2Z=y<)}*du6|3K3(Nd58d9ig+x$dNi57`~s zu82S%LjD;PmUEsVUjJ(eU5X~jQzWaU4QSAuQSH$wZF1BnML%2Iw>GL#@vlMoq4;a( zS_pFokqP*d1|#r!(_nYf4jeQeb{83t#u;-#MtvWnbR7_xF#wKoRNU?$yejN7@PZd%RR}UW`X`lem7xcucBvzmU zin2N`IUUW##Q{*Xt9*FqxXMJY4ssPA=yiOcH{}CLMyMWT`N00FytsXi3ox#YE+amq>sepl0TGBtV30LIO2M0=PXq3Fyv|fbPyHPf;IMEBjJS(|d0|s3jz@ zEJFe{PXcD7)F&MY*ta&SQISB+NTAjrftpC5)*t~s>PcWWPbNS|0<|a>t=B{X_lg9p z1C9h3ppif=LjuE3n*?TY@`?oh==~pE-b4b-nE=52_h1V(5>STdED6|T;^iGS5*T(Q zFziWy>yjiOTv(a}h8+noE>8j)VNA>v)9OiJl>lHQAU`yRu&HZ~1W4N^1T_*sUrAh| zAV7lzRDFWPtT|LjXlRcr4y5_qmw zfm4{7(p8`qox&E{9vwN)tpY{@951PFZR1r)U<((A%cGiqF@pw8c!h`WuPgTLD2~&R zrvbt?lJAcn;`@#Uo_lRd16#BV)KEN=W#H-4z!A1jiU!{GiFY+DomvLKRrJq35WI&T z{pZraHn$AKOyU+t1AtM>fT3GW%RoW{bnP58VDllLLP7&OA(RdEno$@qboqD=7P-@Ts2P>Oc_8gDmb}TCfjZi^p#5ZaexSBmfRa71v4XY3(Qh-q_qyV$Y;cD8 zy)#?YOz-^~H^gzk^)8%zp>Xz<V(2*lK zHA;(7luedG>zb^yF5WRe^FLo-9C+GYJns#~jaL&yDJjdClvon15-z0ah5>QY2mD|m z+;wxuvdSH%IJdOIe0U5dYHu@Pn1G5q-@u{YFcVCBGPz@xszuX?g-TKDim+VOWJFCi zo>41M5$3;oPg;wW1X%#ymNXHN z{AgXLewfv-o*SCEd8H1sbLklDHs0)n;Bj zSsV@)vYf3sbgdNq)!)v~Y`L)5Gc!L~yx?hbpq@L4U?Z;xCxAI8SFf{?^(KDoeZ^2h zT{0oP=7`804)U54O#*VIZ7)ZvWYt-~xTC8xF^uI{b!46*IwSg(FXLVc$#tsY&2NEUG!Opa? zk~x;RMm=)I4&=(VXhHpGtC1DZ`diJ%>LA&)o+(m;&*LXSJySwQip-2%w*;)Mmb~qm zV2G`GLe$panR){PK5AB1hFuEoWOfx@bf@g9cwO8rsARbmC3!(8dHb(33q zY@O$}smG_QNw&}h+VEy1chc%u!z?zj$H&ozm$nI=8*^oZK3}$$gq*0Oj{02TMl4KAem4skhUB!1D^Hp zg432MWDV5;Ej8TOSm5*Fs^YjS6kHjjaj3k2`1VUW>f^hNeXiW!y&j( znQ}|?4h$gVSbK|+G8IBmi~Xcx9+xz;GnwR)6P{S@-K3YaY{=%2KHG8`G@ii$vuh;v zK>;b4X?-w2HW&XKIH<2e(b|GAGJXhIJ!kqkQ(1;K8pBau?{qk_AamfOmd`$|5~~C> zJM2STg@6QyHpZB}g%rA5(0LA?5PLl#uG00%xP+XLrQ?fII!3{y2KK3y{*57fqZDpy zdSQFxg@S*f=3m(1U)aSNLww$}j1e>pavX+%dWWG)Y`JdIzf&aKo^DN-+tZz@5^hho zcz`w}c}T*nMe+m&WwOPJU=$)Jx?wg{M-iEJXJ0bKqZF+osFm}KIl@S$zL6*|WpE^o z9v;CoA{9$zEWFCUOPq@)0CmCl&YdrgqLcIvEK<;>TSo#Rk>q8!hKTq~zR;RMtD4m~ zSHECKPKgW%hcgB=f>QuCY@a1niyd)fd_Y5}+Zh0kPk0z4pOQ@NfslMo1`H1c|H&xS zNqW}yI%^wgbtKxFesGuXM4)X10Y9N?YH=rGv4q^*8hwdHe@ijgDJEIu|ToR^UiS;YqF0Nh@*0Wd;A+3dRy@X${^auV4NNt&q>Osg%RTtub^@ z4CX-`I{R43?w3CivY7Q(k~_4@EOoE0ZFo!kbxPM^=TVVGyz1#W#kYR3k~X&z6jF5X)?1doZFFO_1AkLMXOec9r) zSu*-a9B|zku3`%&JdEr;qlKX`(4`GKFqd4oCe@#LERoWSOG|y?5TANAh|kjq!Y-Gf ze%T?q^J;LPr)~8B`hFmt&f{WCq**1zB~pl`7TVm^~D|0TcKK1~!PU%+ptTBufk39sR3 z;-bd^ZbUmgx{4!OA&EX!s}p30z0oE5hdypp|MrW$Iko;q^>4rWmps!yrLgqR`hxn` zp7if3LFj5h2>KH*x=J9rS|CcE0nyb0QSuCgt`>xnXL_I<4CymHPQ0q@7e^`))6K;yW)(aH;GfakQ87X+94RyYzqX$8y#X8|C8m5E8pS$HYa z!u45zxyH6(L`T zbe;lhb#l(Y@h{BhEJcQ~5%CwX5#ujpmlAs+>T{)9h8u{>DCyL<#UZv~Xg79))5+M) z4MeJA+fO(M#m0TuX=2<5&LNI+Zy9bfk41b0ZUW;S!Wtc9-^(2Kt#r7y zk{@r0oe8^vY&y*wOmR2}Y-A{I!nTv=#x~Nw7`E*|(xi<9+bA|{+l6W@tRvl@A=`6E zofu<9Pq2-0!?sO6o@6#WRNuM?!~9~TDNL6l@axEVDSoYN!mni6X-2PK|N1v`S5`+^ zVB(V{k6(KF4a&b8eg(m+9DcpZ;n%A&_*Ju&r&sv3H9Erzv5M;gdszp$9%lvV<2pB_ zrDv=586w6~9#es1xS}}pMGU;C0q`vb5GvbFH-qeonVI;Jf;i*f5&6AfFAZT`jd1!I!VE+8U^n{ zk+Oj>rgxc@#m5>k!uVwt!gmU@NboAVCF@9Qa1WMl_l5TW8y+|35q}lvLm>N#&~&)t zE0`0WKeZyH+~PCy7M$o!FGSxYRv7lyCAWBr{cW%d%{b=dBzDm_h9kw82F;Mw5n^5Q zP9$?W;rNkF*gj~>B6n1GrKLf4ClVb+xZ#93Wg#wK-T8{qxAVs1M|gIb5{ zy`k3OS%Nc4a7kolyePGk`MMnpyYIhGJv{kyZ~N+}FVYCwwXJ&J!=L@~yZ_}k@Bhbt zvxTUgtr`>YzMnt&t;as;x5cRF{?C7M@6Z0JuZWFn-#5PY)R_;nTTYwh zR1d$-zF#^0xu<^h??3SydtDKJoqfOk$G`lMlYR@GsvW1yw?crw>2m>jJ=vNDH`{kK ze$W5+zVAHxt;2su$s{GJ@Ht&)3wv^Zs+$6-+;lxjP&Ju|$d*X*%G#STFS&o(#c`4| zbIht6z3q?h`}n!uNNUP))9wN!k$;wKyirw|PMNjuQL9KT=6K0ktP4$Da;7kTUD`Xu%3nZjGJ7npa0j z51y~b#6;x}0ubmZADupoB&qEv0Gd29Als>Y^zh+FNW|tL|KML}*UDc}4RaCIB-Aq_ zsy;8MGTnSYjej;D~%6wT={a#?4LJDb zmaKNfSAz?6K(NOk*othQ`lsk#_?4U#oQwB0-|dP+JR{0}ndb7?^mfD=MI%n<}W zz|%Zr^^RZu_^Epn3a@C7m80z$%z3a@D2j-&93Xw*@7MJ30H*4%eAL*TG)r1agnztU9%{+Mp2@}Fa$yC2e1XnYZm4LtYK6!q-N^^Y7?#)wJM~vm)WpJ zuWJ5nCWyC}HMbrU_uSpoJOKf+g#&m+lOC#fupvnwKba@06%ay*@@Cp+>4Wf(f|PcF zofV|C3*@XIrCo+?1*QfVM`eesm88T=NIPRNAFLy9>?HO<#@FGgwxfw^f6$I$Icac} zrk;tfKb?+A3Dd!%$d_cv;6@Co!k$@835a6(i3CVA7I6pe9%BPKHQDi82w$!1Y=JYc zxE?8luhDh75Z=q4;Z7UnZ=bPzBw|-@J%m?mqfU zjU_QdFe@MX@V|WZ+!Ql&=Ld>i{~j7C{X6LcouNQjgLchnd{78Vff$FKr|a<_A5%&b z4(ti()R|q6-KWXZv?@ zH^Qyq_87FXl0Z)LxQ%5pg*(em9L;6j$j8zL@0=$fzLn`h$Pr&R?}9xgyPWZ%%Gjt$v(z6t3%Up)=%TYIyQA*g3@^$*uPC`xGSqh${g|Q;(?!_eCy@;fB>hvO- z?p{necDA!8SR-jW`8g>NNz3w(a1I|A07-WmNf)_JW)@-t_frYzVgL4Zo8^W0T+=+4PP3pO zY+#x6f}1U*kzvE}rfSGhOC!xmUo9tgE|KK~yQA6=Sq_=ZQjox+I6Pcx)w_&ZD&g+ zZA(ZdZ4Jq!ts$8Zn_-ek+hWOt@J5qN_@a|c+MHy9U55czqtDx3Pi;-+;YoYomfDJD zZZHlyX9cM&K-a7wNy<0~EH)Ohuv`Yy zs=V=9^f>kmM-C^eAfyx(4Vt{4D^p#K?eFqtsn#aSu02dF{acCGQj^)7qd0J zOwebBHJU^?r&G%v$lupM`gw{LgPa_vi$OjUeFRaXO`BBD0y1u_#Xx4a>sdfPNtvH2 z{8$+vbLveqkjZRrK>nhxg7Fu0v#|W4roqgXY%mL%!5pDz(O_mCK4u-C;-_Zo0n<*| zVvHfmeeqyUQ?wXkj`6A=%$yiYiZMsQ^d^j1I4@|!AGkaB+oA!D<~L>G#5jbh6ytnx z0?D13!nJ3jlM|mPInJ77Gl)MrCW)b)>mn^+JXXiKqr#W!736!n8m+YZb}JY7=sq-oKTJv&47Z{qB!aAFF>4Xp78ftrjH9rmHtUNC>>_$Fe8VSuxDbARfV5nA*WsX*m0sm1e$bf&=3Nqm5KpK1|9n zB7x%^$>dY;3%Lb;8z|BmfQTS*5B}f6go#ebD90dPfqxc)G8AXbNBsRf_arm0a^(pk zk%8SGE0b%6EC%*)lpin$*!?W`$k%p%g!=`5?*N$r|GaSm2Y#6k67bI_tbp@wml+p1 z$Ep+mM=v|cs$=^9HPtSyJ?gJVZNx9sVlAC;3lG${AxtJfy?dE}ihbE&aeNtx(fs{9 z_ecEw9QV`y{uuXD{{ATU*rFXwxjzH(=z~YNKW+C&E7LqIEcZZFw;VVPvNro3rRR!b zn4AsdMJXhgs$%&i#PlF_3QL%S?5cXCII-`YzxdExYSadgDDn%@m#`p1|GYLu;UZa5 z-qEv`r89-Im!;E`H5s*G^b{>#mX6Y#UzRwrDP5MJ|5>irs5jdf50r zdqUY5+>l|k-PV|vMj#nxu%UVaL@UUWsTE|&R0Yc6o;D&OMtg|U0tqqNc$^ey3n_z` z20_TAfVajbD+_Unj_>@`TfUGQ8Jk2$LEHeIijHyWMu4mG?qWgUKU#FW+1v<;?AR(_k_pS~8gbnj)H9ytiPi1GNad5E4p6>lQL1>~~@a zwzW|1YK6=qh83J()!^2+L_Ak?AeOV7AI>s%*=ywiuX`8ek6m z_WqU$0z#UqNFzaLGvRQagGMAAx?VL4ajsVqHKrABO`Eyk}-c;?J zUp;xQYOzVEK+S!1;av5atDR}8cFxXCHH~%2&c*Uy9cijJ$9@HvU@Gv6QNO)T09vg^GSWLdNoWLdNdnpw0}$+BoG$gpUae$Yu$Q>@-5NeNA~tY##s@44tF zf^2*o2`G_dB}yfdtVF3ql07CV$qT8Hq!K|^l2jtdN|H(hSqEvq=u4(^R3I73C?q3E zkuh3Z;v6!b3(4lv`7yv}%(65Mh%#nbAYlcI0H3kSqD)&2w=_BtK{9N{EQ^9~1sTA* z?OG1-sRm*62{*_9{`=M-Jltb9a)9@r40wrK2)?mFw6YkLD^1b{z^FuvAX;hIxgA6+ zO~Qt~vIvu9?oEXa%#so|Vqlk13Ix%os}v-t`~zV!@rE`7KLk`3L|JOc;ag0Vg6}ED zDX~fxvL=&dnS>SmKw_L%3fS>vqI04Yh@DF+)tRDF(bVH#D>jsZhl~HQN6XuXqjt6AHV~PK2Rt2eOha+5XHYG9-S=43(JuT99OwG_Z61e zt+}r+-?!zhLEA;SuPtW}=i$S6hRlZhDH7|b#q$1OaQ|(RzS&AX9&*}axLdw7D(%*x zYOQvtnvNRNnWnP4jAjp2Bk^WPs;H37?e0*u>Qyb{NGhsjhpJWdp0n4GD3I7GZ5gpc z)nb3DnocQ{A&Oi?e5LHR4inO__)s-vDPn^fN2HLgj9uv#3c#Ui^opQ*x-9 zRh6q+PVw_d<3ttvO}%&j&CFg=?7^2w@NUH_X27as40U&CS}HGj;35v`*OuFnJ0Ro` zEP~y8@;2oU;zsX7egT0SQ|tWhOzZSAo1|(%U?aGJcr43kqrnZjj(G1nT9R4AGy2dE z)1odm3Aj%%jx%mN}GScdc=fbBYfOsraxME#m|) zD+b7phY0p2t)?K@5JSLrZ+A=E`R;hReZ2#Qd>2Y(gTD`q@#wylyQOdRJtxwckmYmZC>{%M^(O~?K*w8ts`glCMZ zll}?s7*A)GyJlEkc;dKtHLBYMZZn=TI#yN$5FTJmq#6IIW!as43x+X;HTiG}*~#>H zL|e=saq)gLc|3l1C8-)05U|_n{wM86aJENYFifu{McP58I04<8kRJg~cPR-M?m7ha z)DM8)4IOyq{@L@PW*+&Z#TQ6DZdRGeTSi&4?Yv^Hl_nzv zp$t@OHc}8hLYZmk1aKtY7K_U{O)9G<8^tjS%yxd40%K&PxKlUBP{67MScq5@T|u0) zT`gM3<(x$4p&*rkEueyeba|r%tC22m_@J#IUEcVZ6(Bfx+pR?%(MT#e;M{VOLmp*^ z&a_7y3wFqrO7NL@;o&mghnMn>dm^;m!IAt**Vh>yf#+FaGOac)XmWtl0p_xF}mpTSqBNX{+BiI7?wOam{?=5hKr& z{NlfW4Z<1yjGj&u7SR*tX^V6FqORsxD@e95`2rZ8jkG|UQD-qrqNQ^T0A6r#w{Y94 zNh)#*6-_M&zpF*j1z79o6bfK-O9O8ILF`4$t;w_RnVWc9>e)}Z5OQY|8_GI+#eM`? zLp4mL&0Gj$XuWswmoSLdI}_8dk0M}_DeY*U`Y15Qd^F~#p0h9j8)?1TzyXf}7)tBy zi;zHaO})IB$2M`%sb%Pt8YaH&eYt(PDDOxm3b47ygsB9*#>o)IIR@KqkLcD;sVh$F z*4g)`bnCd@q;8!z>kP=01OVz5a+#8`y&xB~V^cb+*R4u~h>qos7CjrQlhDw*mEw?? zn$Qizq2kmsCv(#Z+|yzec{3Hjm{^K4expis(h4%1{)81|IQ?-eNI5;W7SdA8$ECvuF;lk0=q zNvJllu3*foH9A4r2xZJfQY3p~KoQfG6rG}I4Mj|8QiKPd-M??wc88=axZUpNwJYWA zdAmPW3T}oMxn2ABS6ssE*|@^O*UG8&73i^*(=jW!cv{g7D~yry6BT99-kf`XC1m%0 zbd*o63f`tqjrz?vvJbO)@THlrM}!Qp%lM_)p%7RAy1z6#lt~(kyD7_zWs0(1ia_CH zc+(VhP=r8}6k$b2b9<>s!57gdyPegDUuw6f*q6bU7s>JrWI>sZxIR&_6yQhES#_4H zu@;w&JlP27l9eY%?y~)as_Ts{(2ynGM$Ywfb{efpho7a}l4Jv&(#{fKc#M1H;MV;t z_m)l^-;L)XYHcCr(J>l5B87Tpq^FZSCFzMk51v3Z|6%0MG+R-tBv0=(;yr!t)OqZV z(s5p7)ljnT2>&Pt@EJgq^NkZ|wn=pzii2&)i+)>SpNh!UnC@yFRzaKX?DADeHQnY8 zIM}4$o@2Y$ZgHCx?HIHh*#9<5gO-SuGqYg4m{!>R?n?3x_zZMG-V$$N_u=eHbZA6H z5&$cP)>Rx;>4spmQBu)CMo31b+c{0c;!6*QMd^Zo5sj!7oT+^bq*YD=R$N=vnedG; zks@}0h+Nh#QI#T)lT$lm7YqA?odCo(i*ug<#NJosO1DJOxKRH=$B(usPd2y`6~h57 z53TFs+*eA3Nw?CHQqFDE^pUlMJ}Z+AMiAu~A4Tx(Wk~2d19sL#jhB%SgI@Hc$vkJ# zm7`@!V$!OFdn;58aPYm;%8S4UpVXCI_zTtIcotkA&9l$VayXEpBYHD?msX#Z#Q{QV z%H!}m?1*TmsM^>I=aMj3nxaKX7(hozuNn+sIzm$mI5!Pm$V{+k@2`?fT>I>_XQ~4t zI#*`m0p*tolwW3mVTL6ZN9Fw@v|0IOIzfqLmS0Brlg%%~A*q&MMkfmE=pN;l;oVL? zC5enH$FLAV6mW_Y7-;d$nibAIo=|t1(-IG`cy+?6=s#mRT@{Q;Qc?}sM5o;6%VdMm zw!q1%qN~ed|4IzUioP1YyW7gd71*b!wTfdUkR@C~ci|)AM9zX?4FgWg5LKsROd+Z7 zlx~f;czvO246T9kAIiHe>EUWNs)uVmiCY5Ig%bPb3KCh@>X_Np!CHqVoyH1mL*D`0 ze@EUW8_hyh(;`jUBNY4H2ObKl3g#BEDK)X3p76}*6SkCrod98Dy?rKsrqI3RR}VkZ zh%P7E`~jZ*s4Ut%a}aJR;^4oT&)ibnR0C%rizE;K!avNc_`_m<>k$}a|o!#J^2jOm*jK8PY&fWIEb5A%FTYGCn0eq=oh>?cjs0lU;!|l4lvx2=^=L| zxUVK2VgHPU^jCv^9snm3ul@Q4m|bEe%NbBJWNZ^b1~5xZ>&)X%6}G#iAK6(o{->>A z{TzbGnyfB-GRJgk82vKbr~`CA!meA1-UHoajz6nJAEKb&pRNduD$%c4!BE16D$z%+ zV8jaG>s0w|E7{nn^1D{Btx^QANMW#wtOpW zV)qh9d)f$FuYB2}Mr^?FxfzWlenM%;3?&swl-y2zU1)F<6}a&Ih~!@PQJ*`;zP(^( zh-gN))|>+Y`HE_p<2ngrQok|qf-s^?-Uggb4D-}D^Gmg2dz`Gg@H4t1~ys6T)CXNYg&D9fBvfwM)O9A=?8hdNO#0FTn8olV^2l7MJyjLR4fi;XLeX;z5LJ z)+Ek#V2Cg=1I>RPWGWfZ%=~00AV=BAQ91PH{6{2dZ(2m7+_D&n0%J#XJWu{9sQ$&9Hb8G6ilCu&EQB_eZf?C#*zvnEuEuC2n@Dz%4dbAmrC_ zWEr3h1Kc{{%o+ckN$!u*lYh;64&Ef$=G>YhJk!iazeDXZsz450uyyO>hI!uvL!C%i zi5{|&uQf`Tw%p&cBm2YFfF+{wFVFGNPpi-Z$n22w7-dz;pog^VC}r)GK@L`Cra;WB zfXnD1t$2j8jts)6+gc3Y0$%$uS+6H~=Cq&mJtd0f?4R^m9Dr#|pT&F7#ggaPRyMY$ zCI!Mw1$EmxHJ1A=PD^I@8RU?kLt4TfZRF6C!U$4umMW1Wq|oTeVp3@I0ZK?ZK*~Qp z$ZAMR%5Ht-+C90v?v#W-*CrVa@)*lq??kk2(TX#yG5YRkNYBplX3ryf#%943*z-m` zJKLQ-Z`1Smq4f7%cp7X(emvb;FrLZx5$0%Lf{6sg-!%@r`x*(&-(DRoi&%Q5t!oh^rQXX$$}8)7cv7c(U(r|o7m zb#>sYh`^XsUh5UD_vWPm-B%9#rFf96h9h*XXC8~mG(%a3ej|H~qsN!=M9z=eqDQvNvWS3-l%GWN2h1Ch*T&KM4$%odiA5*@`BMx+H;Bp}k&xfKmt zm<&>hE$n#J*}^cF63{3(eyYT3&iHYcvIacptfVhO>BI>6S`0ox^)@7_7x_c?_<@w| z<>0-}gtVKR2e4um9|+qqtaJqZ96yz0w$Z}HK?p9hl|L|^aJp8Jf|6fJ4ke_nm4oBs z!hc8^fsJ54>I|vC?_uyanq<3j14Z zwx=*dE<##bvdpb^SR(0s%=D{t-00CAv6k5;TiVk7{NCMEh9NVXKCN!X4dOa?KgshT zcJ_3-`LIzaHo+_;UH<9QoL(k-he0Jyz=1W5Gcs(Ct{(Kd|7KLM>B~Q z8gxM!`I(f`GA+|m%8^Z)p0rF$sq*SFhiMsXvQ8H4D4xs|A|+x6H=;_XwH43Oa$9?i zyw2cdVqcBjFL2LZnpPGRmRKXCgpBwYNgh{bti?xo=Crf41G796ypil_Wn>hP1~IKL z$up_Il4s$(*5xS@zv>>QUp#}sDLRctLnjU5aju$PpCSUsW^4l87j5+|0CXe|2Ry}w#?`Qbp> zHBc3a&}17NA>SBvlzf`3x=qHx5pNtEv1XM>?Esjp%E!-Rs$%8C~ zN+_yKsk%d`;BZ^rk+n>gV(t(uH1jQ5S*ysT!Aj%R_JmMj`;F}ZiDH%J$R753kAOXV z3VDDe4tig?f9;&eYV8<-vH*xQI~`f(xlzar8FN~P%9 zeoDV;rjvQDMhKbCyjqQrI&_SrIss_}YjxY|W_-KO0||pSDji~QZ(hf1$az!T;*aOJ z5z}UcRInh-Ml5%oQS43mguel)=*h#W-O(&{Pw?!;sB-jp`p~e{wXn=sRtvKX_@^YR z1saLk?NP4za`cY79$|Qjv$Od)1a91g+5C{p#CVusCr}u)yH2_^Qdz{gH94N#R&<2{ z=m|gxla1S47Xc`q=!0bxy)HUla88fpXp68A+!$=HeL5e!U_X$!F?fN{N_8xeTj5PH zhSz0eAzkFz&<3w&d)*J_*pS`toQBK+?1C`{Z#pQjuGd>{>(V~9J zn=#kYaAyq20!y@}xijW-ycGRefdfVdxZuwyMQyhbNZKl1aGvUcSIoi@s7yKB?nPEN z$HsJ}$P((;R-}j8ufqgw)itJ0a_S`a_-Y{!iSnwKx5QY6@kG?2U9++?-xz6VMv3Mu z5jY0br4rp&=@oKEHTsp9M5wug1cjP6q92^7qwlpDr>QGxe;)-sO$?{*8BSf86x2am z<2QBBZ@B7!xE?VY;(tNST1yxQny+sF|ltgXHvuT9g z>V6l05sH9{ZT>2vL2T! zNLptye8$mnWqp7HH7CN+Aoj3jHhO?0Fyw$B&_u2Aol%W#RBRk%Ex1$g5Z}5XPbMtZ zI)a=oVl8UdgU%j-%sPYa+W%5Gn+oJL@s1!ADsu?&Y^en6q^Irnx1GEdUO8C%3*w96 zV4bL-!d}v3i-vLs{tpoa#_VHN~U0Kf%XQ2s`Ia18(?Ku@kq0z}G(PRj1A{f;CHiGAI6 zJ6gDRdj$ysgnP0CFbwge_KcDu5-{M>br8X59Fz_P?Stf8<;R86OjK>@k3&&H4uOhb zXHFFrUu=)wbSq`iu19fL>_;ooIXIG;%=q-Tl5{u9UQkVa|aj8JO#Y4Z&PB zQvN1$ZTH<;xWXeEwI+-jTx%}gFoSu$P9 z=>8B5rW|ZwOD1hb@DaccCn3%8_h20v&JmBoo4zaSXwe9tKI@Qzb}rVD;T#+O%dn2N ze;?~W7Wu!dgadGt0Zd9Ekvo|4v78q0WDSTvir-;zlZ@k^#QmyH1MlO#cS2oe(#4Wcz- z9^8#h1F)>_V8cP{f$jRDD+lRF>%m*;5I^xA3@G~4osZ~!-(JIq zuQ@o#KsM+=&K`ROIZ0;%FpjGRYrhY_T^nrD#vxktA`L-(QkgDp$RX~Ixs9uqJ zF%=1lRV(~FFmYwIthI#?C~5GbCc*(@;>@yYuvu+`0Uz+M4K^qoWP`?33f9s(?;=bw z>TPs8;1A{({WeS`74M?Be)%=AR_@>${DA}zvL8jJ&&y3r?FguntG}Fw`hYBEt7%ZE zq%f?@2{{Q~PNt;b<<~G{b=ExzxAg&K$M0br1ZloJ$~_AOf-IrWsxAlhINtA2eGL3k zv+EIOc-+hR2=+XJVQ2l7^_5-{-s_jMS}^ACkYmEC<>O410#;NIaV2o#-2*NXuNrV# zRPUJ64>XzCe<1WK)?R*%q%Y|G=b`ROYe22Cm9E?nl6w;&xl+Gb3mh^s>!Fl0ykT6C zkwMR(KhYZcgYqG?2B*O#GO`6o$d84nby0AUhJNuP8M&V`K@hV*FO!j-)j}14g=FN5 zB`sALrFJpG1tcTWdB!d*lbwXh*k9b0Y0xuW?B(u!-7e9~nbXyl?pF@9!9p%}LbBf? z*$~*u$Vd>28ZdcHc2^sKlh=%64AL8-LJ^Xkyr$YA@|rRzJ9&*U&;0X+EhB?A3dCZ4 zu30Ru%tW=8uK1!RsjMFo)nGN;v!pVECZpnOsZ?fxAAx*yBBN9E>;Ja9HtEF zRb`v9oH3D$^oycBZQkhFMNNPvGDz#&f;0!_aThiq-D{Gus>@h; zG2?QOrn?T(_F*8c?=?gEW(VmPH$ghlIc{DG(guigK>B7=R~)2YEJ!2%_6Gxqzi?Qm zDxV-kaRVd=^ zLC=+}I8uY*SfDAN^V?~*LYR*mn=PCQ(Vxxi4G`yGL1P&fWX%4YET{uUf?UFq54(dvg9Q!#g@?9Lt0?R{ei{jT#AduDmJdYvQgi+4vgN^YKr0D4Llb*+IK#;cwcwDlz z{9~yg&5x6BrY1k?HS5zbdpe2|l{?{vGVXTDHfg)i;|Q$beb;hX}xv-!A2K9|#gXN)8&H~ov!)0u#Tqn!g;4_MMUwIr%@-yGT8TJjhe?h-i z*LtuTL#sZ(q$v=ZudJ4EOHPM^?sCi|7o|feg{vi9x5pF>s{(_`g~M2q)e4POS##>D za&~jW`WV0Kg`lK5)^@vV8y;REa=%qhGCQLS-`phQkYmo`6NpnF6k&M3r2GT%Z4!)W zk1(WMeG~x{d2Lwttmc=$7K1X<9Unpk3QJBgk-~MWy3lJl+>dP>Tt+QSV1d3uc(}L? zN&+E5xM@*QxRRm$FY@S07IEo*lI1-6TR|ZMdX4YfTBmp~DL8%+lo6dx9?Edj>&*02 z@CJMRCQ%bWQ{=Aw_C=j5pS0?PQoJH+wLfO(8H_JZvfMk7|ppz;nHik z_0>g`S|~*-s24K&ku@@4?D3BVNhg1loFH9cY?fDKfM~U_7a^c(0%3LLy5W|d4GO_` zW0X^+k3=1uibXU~KjqHh2M9FGuzzOrik99O1QyEE^Fwt`Syr{aMVvY`QE-(O*R^UM z>5lX40hB7E$lO|W!eXC(F?8;6sY=jEWxDoH{py67u6s->S6g+yp9qWkwm5aaTN6hm zAAwc32K^>1s=7w9mcBY+(Ra7xA7Q(bi^Dk zt=isz9qL79ouAL<E4+NB+(y;SCm14ju;;= zVthxmUivT8%)4Be5x=KmV9|_p*o??rT33LB2cb7?hm9E-&@bMw-6L2GxEUeXMo;{+ z8F_BvO;BTXdx;6o+kOts2z!nNd|ih^&V)g0kO|PonJc^v-{@Rt*`zFMVXlbkZc)U?+^a@ zOX?+as3%73s?pa>{LNxp?42xY7fqx29l=Ycaa8RxSq&|0xG{}u^-DFQg<&@lVR?1e zpl*cav@o@W;VGf3zR?S=gUqkdn6DLa1GJj=;H45+zfl5Ac ziAq#u+aU3nzy+6o<I!AC%Hbwap+^mpzd1xac&f%>bDwVJ;#d!h#lXfX6CL zBRFO)+>R(h{GC=yNw2Ab;Gl44r>d@YM^g3#b+v*Fr{zJ7u-GevQ0gqqN*1M(K}4M| zV3>5>XKsmZUvHs6V=oi%hoB!e@D28%=-CJqOxw znDS;E^bQflUl6k#lZL&poWt#$qykImWYS}ulv7cVfcatBJY0aYkBYZqU}SD>`gN=M z;x8Z)nB~qhsh>N{v9e6V$zV!qH1AquVO4N}YZ#Y&fevi2b(QUoCc2jP$B-@3D<^yB zY01g3T=fYPX@SB94}L73GE3G6!_ids?bnGq2msbgF9`5i_IDbGB+L|l?smpX9MwJM z3ny*{SuNTOx+=DcC~-ykMTOdw+_*kq2I>ew6b*G_ehn}y*qM`7+Hxm-5A-8Htn650@I=*&8)uWRZA44bV zCCJtptWF73k_zA9efwUg9xXJb6m9^+m}*H}Zmk&&`-RA(k^uUJn;ttou|sNMZ2f^1 z8f%7esgJIyC0`K#Hw4sIO3;VbDO^v0Z3OtKVGH1cnfd@e>I(K20bl!LsMjA+CH=-aOhk)#@iAg-Q9Ld2 z;dy7p*u~KA4=2b@jQ*TKcEr6YHqv73deb3b)a78(+`b|y9KaK`pfI~%xFgpft7uSO4*RvrD8t*AsKNXJsZD}m62*Ad~y1cS&n6+yq-y#;4tTYUtj}i1+)U{lbC z?+p1zOJAM0f;z!!(&hONkVgfGkl)3*!{D_jtTZG|7izCI)eOo&k9}Gis*fgtfK7~> z2>ZY*H3eI;#&U}rga0>LlOn#jHF!n8xEczZLeBbR{pt%gNm2tFJx!z3^TpWE5eYHF;PDHCC+w6o$lx7Hcr6c`cpz zzF^>>4b%MC{kX98WPWUZI)@*7fSvtNgxt`ZaAT_tpj7A00m-^ro`4l@>_5m0q8-6{ zf>*g_JlQ&O5>NJen&{@X;mO9iFX5ZnAR^8Eq;J7+h@^8PmcUhP(60* zs9vebk$s+6n;HTZi&{V!l>9MZo@`6ITKDn|z^O}YRsuOTFI(aQ592#)Reud*#O7CEaS5 z+}IK`SQChymFH+Qko^ZJ?!tpjdG1-a0WgfZ6J|Sd{$f!7P<>6qX$+-ul$-Y)<0&>% zrlKhiiR}_`S*efd`s9EjE`{YRXXZf;SMn)-!EOfB$nc;>M!EklC_2xTII zMTUYN`kXT%gfq3RE!~$53*03KW5AWfG!jQF$ZX&R7#lVNg`JXUOob4O#b+3}=!vr# z_%GQ9n!)AWCe4wHl9a^O#+Cw?jqx1d8nYNi2QCX-#G=!NbAp3muBew0C_Iamt(Ugl zK1{kEE69KXw`YxleqJgMvS4Y83@<{`if{2O7#p=*8Bi6W3g)xj)bQEvrJdAgJ6vf4 ztHhxDMS0klWT-H@gFcP!dVs>v0zLy+Or5VLcvCzt{a~6%+@XvuXZ5a#g7#$P_!v_XKxKM`1u3O_Exh6wn6Y;w)?mDuFgECnhX-#I`9CGm+YePJYlLNF{`Ew^LY)7OCLOFMh~ zbF5fA3R3z)c$5tsb-`9L=mJ}sHMB}1sbu9*`TE3_*g7HtqE683+>V2RywAZ1so-{* zmLK2--GXCkl~I?-HMxRB35CfgD0e;Btd#O0w;j8ufV<$2hDoqg}^atHy zwZ`!JH!~4t;q7ORq=sZIy!@24@b=PKv+$;MLVMi|ypnf%IqThK)(pJNFir){Cs*Q! z`V!-=iL^Zp<1ULOw2N!c?7B#3itSO7wCD^v#%ge$BMhMjQyH8c7bu(2SH@iG7|j{z zJ4J0+YH85i5NuX53JmkaFKLe8A30cCzo*KwQ#5Q3s3K?A(wGQ$wcn#@R$$vc570AK z;89tD&x5oCwRVQNgcTTZQp1u>hgu}tGAl6ty6FNRfd<~8gbZx4ga+0|_gW$be#kHS zf$hkeenNj2{Xnt^_XCFDrl#1r>3(R*{1EFSZ0~fxU*NqCf3 zM`dArnCGrzVw5{6$~_Yp3vlhOUYE-QXjMDk85gh#BKqygV?lQtTGzS^J*~_D<{bQy z-mU{Z^=VRJT$%)XwA>3ucJ)qAbFIPX?wqrn0>vssWSTVAuBs>&XQ&k4)mmj?8SKWz zWJFsc)U;udFgddv5w}Lq_*OJeu5gT+$aX7`({5tM&a|T8yUIz?znpbI}X4l0)Eb2%aW17LX zj2wVSBlC)DsfY)cjoUM)FKIa#YkSB4-`cfCS5;i$^Vs($Hwol~1VVzwb8iqSffz_Y zz>1uJP(>kHtRQbmfRHq4Aq7PACOj&l&>+fy4~%GwSVX$&>IF;NP$~Ao!q}>{wYad= zmZi4UwyV#q+HcR}<|JZQe>5w5&FnLK_RN{b-g_Q*A9G~D$v847tw!vr|aA)g>19PUgF9~kiRqxTQ~Lk9-sQl$4iF~G#YfdPLP z2Mgcufq^(Z1upSEc02(81>Q@2j|WgYAC=;MfVT^_4hKuBEXaHuCZIhx9AL@yWjHT@ z*s}Z$?4(8+R(FG$vZ)%-Y@2MqL1EH&0M65q`TyZOz<3Nm86k%OEGFAM_^GJ2a?^(P zljZ_o*6<{E0<7Ax+&NOjVpkn=2V8JQ%eI_f45g=x$KtT@$cqlC@%dsB|GEbV4{yE_ zpcf#aL4%Gwf=@RyE&2dMoo#| z9;E4bevQ#=ofu+O~uT;GC zpp%aujAJvNMRxxT+i@up%>bu_{R6d%WP5PpFZ@QzZ&S{+M4?fq%EpsTh8IVxrTS zkdXqeizdV-QJN6HDpE|CkP%6k)ReGm66}6Nz6S&ZSi0zifcBKB zx%OoUuP)N#mdpjOE@s7$2-vGj55v>IG)Feo(A2~&z-wX5Q+aH{eV~MYA~07yctR!u zeIbOW70k09pX+EVb;ARknC?l5^`=nHOn7SJ?80=v;nW(DZcDI4Oa$F}_{e65vq05< zt*)W~!B$HP(2LPQmI!u9OCq(qm@;y(vIroH0dEwbEJ0*;ahV_)EkvKi3tg- zg}>RvS@`~wygvFX7v>>mE}!Re0_-)p8xKht*Pi!rn+G4_YH%lP_F1iu1t3@>-C=jG(_ z0=`uMKkNKiSBe4%3Ow%3J9u5?l!J}4qH-UQekEL?P`Atmrrn-Y$cnIWuD#=A z`>u2-^My@u%-gWGg~J$E>j>Z49Cv(HzTk)1{{PUuSw5@+XcAt{@L4%)6JC#WBg%4~ zo!fPP#I6>lV&NM&N`G*w^go9yl)+zGaTVWY34cKz{|k$^H4F2p6seAau8osVSi$>z z(;wy*+N%@&utj}s&9kiR;DLAX#1GEBr>|>$fJiVr*z>-^&w7R8<$x{~{$4vK{uWSf zF1EbU=xE3sRx&wK$xO3+Y+1PIxUiCOjgoO_vhNxtgQXVFPq&YfaiL_yx@sDcB$hai zQ6Os2fc2a>+Sm!{4_E6ChxLc6^@rrn`h!y;%2{RTv;+tlQTjs>LVu(QNfAO|G9_bS zO6U*Q=nwTiYTpA@>d_xu5(+i?gOz^5Q3`^s*94h3^hZR;RltQQDb51hWd-7BE#b2M zpfm#g5z&Zdt-1DP2>oFcjT=CJ^zdW-(Zi7ShvvxoBZ;;qZUG0?kcJ*!F|9vbqd!2v z_0;+UcYA!tryl)*wo+4C(`o&I{xjeCaCHxJQ8qYfM_I)8OqrEYAV|{sLp8hB9}e7b zYXRXJ{hC(13xuv?bZR6&x?H!%1$xVFMGwwmg#cGg0Kkgvvp>7TMY!0_L(j6WKNbzHR z@+M+6EY490&yTG?5V&iCnnwJ<9qwZrFpQMxogIwi`l)@}>|l69!r|KMSt9pz(OPm& z#j5Q)v4NcnMG?;h*X*D`z8W@Zv2CUg?D-2C1u*C1PGH3|P+KRCeE?LFxXc74vB?Dz zxG3UtcrHtB@L&T!gV(9t5SU)kbRD8yd!kB>PNsgj`eB8FXR)Ss66puYJRhRd45!%pP{~D^fW3E2i|i$<)?>w z{PYlak@V9O!Hc7dxwaTd$oaiyulrE611mHxsEDvLvbg(RM?LE4DHm%c^G zOnTLB6@QsY-3UIMMfnIKHB^CMTn)`g-MGz}=%m_e=sE|FPTxe`1Nm04a@qv#XZhG< zfR`t6_Onz`B$IfYKu&Q_n+0^cc=i@5N?kph1~QwQKWEsmLrXSPxM?JtWw#hM85dxa z0-LvNaM(GQx-XZdWho9Q5rYwNbz;f;gCDEI=;!P~;-|OLM*A6Y^E?`L(X(4%i4cm; zSs4uQro_1x*zvl=w}O3Q-#m(!?A?ZqNy{2%S$GDkXeV&Jp=$;Ah_j%v_lu9_(e?I= z;`;eiWS~kHv6;~v4BeLy<+A98kM(Cl_xpndsBk_;=Tnm6**sCKtqu8 z_Y3GU=e;dr&_WtHpfB-zG)+0uzb<){L?Uxeq&veKgqAKEURj)XU#)cXIoYbp%}J; zJ`-DPc~%~;P4Z`WdMV}hWG@)bg`(Bc1^yIJZl-yVSot0;MfVZE)L-V;#nbl7UDy!;Di|S zdAi|7lPiU%Lqk-5L_D19uYK?ssvRz+1o#NfFS~TZ8K=*h;_|K+?v|cO!*X_p6yvH!I3*-2*BX{ z&X+QtI=z~{7v-Y1uctNklY;J`UG|k?&mDBfz$5vdRSzo5u`F(ZPnDNjqr~tHG$Zzz z0?+dCXoc9Yfu`KF185P@0|>c;k0H!K_ydG4!o`43qB##5ytp;d2fJxlI^Zv7pvb$E zX4*HT7T-y^cHaC!o~-{<7?&kTpD$YPqDuQA@!VZBC3YIAT?BRks|CtK>vn`?2uF%TNxCQc?<1HVl>#ft!J?*>qGE0<6=od&2A)1U zMyze6k@j)%WGhu>eK6Rwijgs^NW9fb<$0rvJS!Xcrx9{F=Y`)!WkroZd1$XkNC@vj z$hA5zYTM|_xXE<{k9qLDhA)R5_cn!%MRm{LioRsUJ=4e zF%0^J@R7i;3E`uF^B^$%PT{?|vIai`d>W4m^WY^s;C?gsFM+e*G5DLn`HcB~8ogEUOD?T-|G%)+}kPul4JjzJPSd&*d#E z-WT8f3KdSs9PY^)8j@9YOPkl!t-9U6cBS76ChwynF?}cX z6Xp9TR%{Y}jE3PJH26Ov;>4q10m_5}{tY{6T$(tBaTq3^5LLuK+esUl_?G)=VxHkN zQ#bk1)c*S^+fE*b253M@;{iOz41OKrEL;sf192W>2EQKhF#QtX<3i~y$i{^5TExeO zaC8&P`(&CS%dc<&T&c=XhBm~xlBNP1`{0{^hbz1VI1ba&^mij3=D!vAc$A+exCakL zhceuU_~jvddmns9AN&CDaD@*7=Sg8I{PjNgA>boQQ+YU9RnV2(iefV z zn$h|;1olUt62UI26=(NSRo1#Oih(so40(v=LqxG0tc}mDR07v;YHqU(xd@q*gpb(ENSq3$)hwbVV+-4kJ7YJ=J{DDN6vpV@T^4$=Yk(MUDtTk8XnRM#J+=crTF7M8tfez=UFq6elz&9 zQ6^=2HX^3lV%%e}J*_IS@G&a$YOnC5LBa=F{3k|tke}Rxr)+i<0?^tLctnPAt^_~p z1T7C19w0mlIbH<_)AIZa<2+yaS+~x61>!u#blzc7DdgY*8bumo+~4q8FPR{a6&fO5 zs;BEjM+a>2#p3x6TAODYg)ItJ;a#HoajG0OXTpVt2&lC z9bS+gJowf4kDF?U*!DP;U8QVizoi+&rm@jqyP~-amjGPBaetPiau3X)`s-=9@YS2zrqlgtWOL&9- z4EW#>{t9qb=qCMD;6p=r5rqEa5Pmgq9-SoiJw;dMnQX`K82@9>5q|0slo2loYrawDL`x?*saH?mW1H26QKHv!8F~BasHo$s7GoTJI z2QU*50LB7_0tNsG@P{d$^=rV-0WSeg0lp150@w@K4rl{31C|5kPVq{u8xWZUCoDDV)kT>o2oM7}fPZFq)}I0I0^R_y%wzC$_`#)~^(Np9;P~CY NJxrg1W}@($ciFb>)l>Ys~&r?Cva?o)Yewr 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