From 4e8cb4d79e995f62e5a6c8ce2dc9f37945d026b1 Mon Sep 17 00:00:00 2001 From: rllola Date: Mon, 26 Jun 2023 18:05:28 +0200 Subject: [PATCH 001/113] remove deprecated --mode argument in scripts --- scripts/generator.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/generator.sh b/scripts/generator.sh index 9c8a2de352..3c641381ea 100755 --- a/scripts/generator.sh +++ b/scripts/generator.sh @@ -39,7 +39,7 @@ elif [ "$1" = "server" ]; then sed -i 's/^epochs_per_year = 31_536_000$/epochs_per_year = 262_800/' genesis/test-vectors-single-node.toml - NAMADA_GENESIS_FILE=$(cargo run --bin namadac -- --mode validator utils init-network --genesis-path genesis/test-vectors-single-node.toml --wasm-checksums-path wasm/checksums.json --chain-prefix e2e-test --unsafe-dont-encrypt --localhost --allow-duplicate-ip | grep 'Genesis file generated at ' | sed 's/^Genesis file generated at //') + NAMADA_GENESIS_FILE=$(cargo run --bin namadac -- utils init-network --genesis-path genesis/test-vectors-single-node.toml --wasm-checksums-path wasm/checksums.json --chain-prefix e2e-test --unsafe-dont-encrypt --localhost --allow-duplicate-ip | grep 'Genesis file generated at ' | sed 's/^Genesis file generated at //') rm genesis/test-vectors-single-node.toml @@ -51,7 +51,7 @@ elif [ "$1" = "server" ]; then cp $NAMADA_BASE_DIR/setup/other/wallet.toml $NAMADA_BASE_DIR/wallet.toml - cargo run --bin namadan -- --mode validator --base-dir $NAMADA_BASE_DIR/setup/validator-0/.namada/ ledger + cargo run --bin namadan -- --base-dir $NAMADA_BASE_DIR/setup/validator-0/.namada/ ledger elif [ "$1" = "client" ]; then echo > $NAMADA_TX_LOG_PATH From 11a762cca441767903f6bb8354320044a243cf60 Mon Sep 17 00:00:00 2001 From: rllola Date: Mon, 26 Jun 2023 18:10:31 +0200 Subject: [PATCH 002/113] remove more --mode argument in the generator script --- scripts/generator.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/generator.sh b/scripts/generator.sh index 3c641381ea..e8748ded39 100755 --- a/scripts/generator.sh +++ b/scripts/generator.sh @@ -89,17 +89,17 @@ elif [ "$1" = "client" ]; then cargo run --bin namadaw -- masp add --alias bb_payment_address --value patest1vqe0vyxh6wmhahwa52gthgd6edgqxfmgyv8e94jtwn55mdvpvylcyqnp59595272qrz3zxn0ysg - cargo run --bin namadac --features std -- --mode full transfer --source albert --target aa_payment_address --token btc --amount 20 --force --ledger-address 127.0.0.1:27657 + cargo run --bin namadac --features std -- transfer --source albert --target aa_payment_address --token btc --amount 20 --force --ledger-address 127.0.0.1:27657 - cargo run --bin namadac --features std -- --mode full transfer --source a_spending_key --target ab_payment_address --token btc --amount 7 --force --ledger-address 127.0.0.1:27657 + cargo run --bin namadac --features std -- transfer --source a_spending_key --target ab_payment_address --token btc --amount 7 --force --ledger-address 127.0.0.1:27657 - until cargo run --bin namadac -- --mode full epoch --ledger-address 127.0.0.1:27657 | grep -m1 "Last committed epoch: 2" ; do sleep 10 ; done; + until cargo run --bin namadac -- epoch --ledger-address 127.0.0.1:27657 | grep -m1 "Last committed epoch: 2" ; do sleep 10 ; done; - cargo run --bin namadac --features std -- --mode full transfer --source a_spending_key --target bb_payment_address --token btc --amount 7 --force --ledger-address 127.0.0.1:27657 + cargo run --bin namadac --features std -- transfer --source a_spending_key --target bb_payment_address --token btc --amount 7 --force --ledger-address 127.0.0.1:27657 - cargo run --bin namadac --features std -- --mode full transfer --source a_spending_key --target bb_payment_address --token btc --amount 6 --force --ledger-address 127.0.0.1:27657 + cargo run --bin namadac --features std -- transfer --source a_spending_key --target bb_payment_address --token btc --amount 6 --force --ledger-address 127.0.0.1:27657 - cargo run --bin namadac --features std -- --mode full transfer --source b_spending_key --target bb_payment_address --token btc --amount 6 --force --ledger-address 127.0.0.1:27657 + cargo run --bin namadac --features std -- transfer --source b_spending_key --target bb_payment_address --token btc --amount 6 --force --ledger-address 127.0.0.1:27657 perl -0777 -i.original -pe 's/,\s*$//igs' $NAMADA_LEDGER_LOG_PATH From 89fe2606d3de8f279b926643940a3e90732de8bc Mon Sep 17 00:00:00 2001 From: "Raymond E. Pasco" Date: Tue, 27 Jun 2023 14:19:55 -0400 Subject: [PATCH 003/113] 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 004/113] 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 005/113] 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 006/113] [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 007/113] [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 951a407c545247d25d4b697262b64b01728a34ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 7 Jul 2023 10:09:37 +0100 Subject: [PATCH 008/113] github: make the PR template the default --- .../new_topic.md => pull_request_template.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/{PULL_REQUEST_TEMPLATE/new_topic.md => pull_request_template.md} (100%) diff --git a/.github/PULL_REQUEST_TEMPLATE/new_topic.md b/.github/pull_request_template.md similarity index 100% rename from .github/PULL_REQUEST_TEMPLATE/new_topic.md rename to .github/pull_request_template.md From 67c9b44460a74bcea2c7be87eebd87602d40a12a Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Sun, 9 Jul 2023 22:09:19 +0100 Subject: [PATCH 009/113] 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 010/113] 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 011/113] 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 012/113] 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 013/113] 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 014/113] 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 015/113] 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 016/113] 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 017/113] 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 018/113] 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 019/113] 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 020/113] 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 021/113] 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 022/113] 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 023/113] 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 024/113] 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 025/113] 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 026/113] 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 027/113] 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 028/113] 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 029/113] 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 030/113] 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 031/113] 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 032/113] 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 033/113] 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 034/113] 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 035/113] 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 036/113] 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 037/113] 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 038/113] 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 83d6ad03f6845226fc5f4285b37cba4e65659303 Mon Sep 17 00:00:00 2001 From: brentstone Date: Mon, 10 Jul 2023 12:52:37 -0400 Subject: [PATCH 039/113] wait for wasm-precompile before expecting block hash --- tests/src/e2e/ledger_tests.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index ca7bd974c7..3f22402c2c 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -4269,15 +4269,15 @@ fn test_genesis_validators() -> Result<()> { non_validator.exp_string("Namada ledger node started")?; non_validator.exp_string("This node is not a validator")?; + wait_for_wasm_pre_compile(&mut validator_0)?; + wait_for_wasm_pre_compile(&mut validator_1)?; + wait_for_wasm_pre_compile(&mut non_validator)?; + // Wait for a first block validator_0.exp_string("Committed block hash")?; validator_1.exp_string("Committed block hash")?; non_validator.exp_string("Committed block hash")?; - wait_for_wasm_pre_compile(&mut validator_0)?; - wait_for_wasm_pre_compile(&mut validator_1)?; - wait_for_wasm_pre_compile(&mut non_validator)?; - // Wait for a first block validator_0.exp_string("Committed block hash")?; validator_1.exp_string("Committed block hash")?; From ce0e751df42fe0f07ff39d2370e5986c63f49115 Mon Sep 17 00:00:00 2001 From: bengtlofgren Date: Tue, 4 Jul 2023 11:31:47 +0200 Subject: [PATCH 040/113] 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 c2cfd34de6aa08a6fe4ec4066128e0707906e220 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Wed, 5 Jul 2023 14:12:51 +0200 Subject: [PATCH 041/113] 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 042/113] 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 043/113] 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 044/113] 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 045/113] 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 046/113] 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 047/113] 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 048/113] 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 6de8993bf27583e41a3b995a52de394526e339cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 11 Jul 2023 08:04:53 +0100 Subject: [PATCH 049/113] test/e2e/double_signing: path for validator copy that keeps logs in CI --- tests/src/e2e/ledger_tests.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 3f22402c2c..3c16d724e2 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -4413,8 +4413,13 @@ fn double_signing_gets_slashed() -> Result<()> { // 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 = - test.test_dir.path().join("validator-0-copy"); + let validator_0_base_dir_copy = test + .test_dir + .path() + .join(test.net.chain_id.as_str()) + .join(client::utils::NET_ACCOUNTS_DIR) + .join("validator-0-copy") + .join(namada_apps::config::DEFAULT_BASE_DIR); fs_extra::dir::copy( validator_0_base_dir, &validator_0_base_dir_copy, From f1874eaa55672adff48060249bf4d179b428d68f 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:57:08 +0100 Subject: [PATCH 050/113] test/e2e/proposal_submission: wait for proposal to be committed --- tests/src/e2e/ledger_tests.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 3c16d724e2..dbae6a6630 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -2893,7 +2893,7 @@ fn proposal_submission() -> Result<()> { run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; wait_for_wasm_pre_compile(&mut ledger)?; - let _bg_ledger = ledger.background(); + let bg_ledger = ledger.background(); let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); @@ -2944,6 +2944,11 @@ fn proposal_submission() -> Result<()> { client.exp_string("Transaction is valid.")?; client.assert_success(); + // Wait for the proposal to be committed + let mut ledger = bg_ledger.foreground(); + ledger.exp_string("Committed block hash")?; + let _bg_ledger = ledger.background(); + // 3. Query the proposal let proposal_query_args = vec![ "query-proposal", From b7a112fa30823512e906649cd5c3be7b6353a7e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 11 Jul 2023 10:38:30 +0100 Subject: [PATCH 051/113] update pr template --- .github/pull_request_template.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 8364cf2c64..dd74355249 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,10 +1,7 @@ ## Describe your changes -## Indicate on which other PRs this topic is based on, if any +## Indicate on which release or other PRs this topic is based on ## Checklist before merging to `draft` -- [ ] I have checked that the following e2e test are working locally - - `masp_incentives` - - `masp_txs_and_queries` - - `proposal_submission` -- [ ] I have added a changelog \ No newline at end of file +- [ ] I have added a changelog +- [ ] Git history is in acceptable state From b06b60374fa13dcc28455d27fd0831c0bc525006 Mon Sep 17 00:00:00 2001 From: brentstone Date: Tue, 4 Jul 2023 14:18:33 +0200 Subject: [PATCH 052/113] 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 053/113] 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 054/113] 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 055/113] 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 056/113] 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 057/113] 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 058/113] 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 e0287ea3e1685c97262dc22d8688dd6298197234 Mon Sep 17 00:00:00 2001 From: bengtlofgren Date: Tue, 4 Jul 2023 11:52:28 +0200 Subject: [PATCH 059/113] 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 060/113] 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 061/113] 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 062/113] 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 85256e83bc26c7247331571eb86b2af5a0c9bc25 Mon Sep 17 00:00:00 2001 From: bengtlofgren Date: Tue, 4 Jul 2023 11:31:47 +0200 Subject: [PATCH 063/113] 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 064/113] 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 065/113] 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 066/113] 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 067/113] 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 068/113] 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 069/113] [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 070/113] 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 071/113] 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 072/113] [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 073/113] [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 074/113] 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 075/113] 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 076/113] 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 077/113] 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 078/113] 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 079/113] 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 080/113] 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 081/113] 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 082/113] 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 2cfa8697d46d2b6bde82d0d209a880ce651b9248 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Thu, 13 Jul 2023 11:59:15 +0100 Subject: [PATCH 083/113] test/e2e/slashing: wait for wasm on original validator --- tests/src/e2e/ledger_tests.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index dbae6a6630..650962183f 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -4409,11 +4409,13 @@ fn double_signing_gets_slashed() -> Result<()> { run_as!(test, Who::Validator(0), Bin::Node, args, Some(40))?; validator_0.exp_string("Namada ledger node started")?; validator_0.exp_string("This node is a validator")?; + wait_for_wasm_pre_compile(&mut validator_0)?; 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")?; + wait_for_wasm_pre_compile(&mut validator_1)?; let bg_validator_1 = validator_1.background(); // 2. Copy the first genesis validator base-dir @@ -4506,7 +4508,6 @@ fn double_signing_gets_slashed() -> Result<()> { )?; 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)?; let _bg_validator_0_copy = validator_0_copy.background(); // 5. Submit a valid token transfer tx to validator 0 From 49fd38d4b1a7ad2bd23cd5f9d50969cb0e10f686 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Thu, 13 Jul 2023 14:43:42 +0200 Subject: [PATCH 084/113] 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 085/113] 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 086/113] 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 e78de0b4078a4daef13050fcba23a486af6c7791 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 14 Jul 2023 00:22:02 +0200 Subject: [PATCH 087/113] make Who clonable --- tests/src/e2e/setup.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/src/e2e/setup.rs b/tests/src/e2e/setup.rs index 7bd9752768..6d1a2496b0 100644 --- a/tests/src/e2e/setup.rs +++ b/tests/src/e2e/setup.rs @@ -401,6 +401,7 @@ mod macros { } } +#[derive(Clone)] pub enum Who { // A non-validator NonValidator, From 1faed30615c31a38640589fa424e75b22dd61608 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 14 Jul 2023 00:26:34 +0200 Subject: [PATCH 088/113] refactor creation of namada nodes --- tests/src/e2e/ledger_tests.rs | 299 +++++++++++++--------------------- 1 file changed, 113 insertions(+), 186 deletions(-) diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 650962183f..5b8d881785 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -34,17 +34,48 @@ use namada_core::types::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; use namada_test_utils::TestWasms; use serde_json::json; use setup::constants::*; +use setup::Test; use super::helpers::{ get_height, is_debug_mode, wait_for_block_height, wait_for_wasm_pre_compile, }; -use super::setup::{get_all_wasms_hashes, set_ethereum_bridge_mode}; +use super::setup::{get_all_wasms_hashes, set_ethereum_bridge_mode, NamadaCmd}; use crate::e2e::helpers::{ epoch_sleep, find_address, find_bonded_stake, get_actor_rpc, get_epoch, }; use crate::e2e::setup::{self, default_port_offset, sleep, Bin, Who}; use crate::{run, run_as}; +fn start_namada_ledger_node( + test: &Test, + idx: Option, + timeout_sec: Option, +) -> Result { + let who = match idx { + Some(idx) => Who::Validator(idx), + _ => Who::NonValidator, + }; + let mut node = + run_as!(test, who.clone(), Bin::Node, &["ledger"], timeout_sec)?; + node.exp_string("Namada ledger node started")?; + if let Who::Validator(_) = who { + node.exp_string("This node is a validator")?; + } else { + node.exp_string("This node is not a validator")?; + } + Ok(node) +} + +fn start_namada_ledger_node_wait_wasm( + test: &Test, + idx: Option, + timeout_sec: Option, +) -> Result { + let mut node = start_namada_ledger_node(test, idx, timeout_sec)?; + wait_for_wasm_pre_compile(&mut node)?; + Ok(node) +} + /// Test that when we "run-ledger" with all the possible command /// combinations from fresh state, the node starts-up successfully for both a /// validator and non-validator user. @@ -110,28 +141,14 @@ fn test_node_connectivity_and_consensus() -> Result<()> { ); // 1. Run 2 genesis validator ledger nodes and 1 non-validator node - let args = ["ledger"]; - let mut validator_0 = - run_as!(test, Who::Validator(0), Bin::Node, args, Some(40))?; - validator_0.exp_string("Namada ledger node started")?; - validator_0.exp_string("This node is a validator")?; - 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 mut non_validator = - run_as!(test, Who::NonValidator, Bin::Node, args, Some(40))?; - non_validator.exp_string("Namada ledger node started")?; - non_validator.exp_string("This node is not a validator")?; - - wait_for_wasm_pre_compile(&mut validator_0)?; - let bg_validator_0 = validator_0.background(); - - wait_for_wasm_pre_compile(&mut validator_1)?; - let bg_validator_1 = validator_1.background(); - - wait_for_wasm_pre_compile(&mut non_validator)?; - let _bg_non_validator = non_validator.background(); + let bg_validator_0 = + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? + .background(); + let bg_validator_1 = + start_namada_ledger_node_wait_wasm(&test, Some(1), Some(40))? + .background(); + let _bg_non_validator = + start_namada_ledger_node_wait_wasm(&test, None, Some(40))?.background(); // 2. Cross over epoch to check for consensus with multiple nodes let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); @@ -217,9 +234,7 @@ fn test_namada_shuts_down_if_tendermint_dies() -> Result<()> { // 1. Run the ledger node let mut ledger = - run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; - - ledger.exp_string("Namada ledger node started")?; + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))?; // 2. Kill the tendermint node sleep(1); @@ -260,10 +275,8 @@ fn run_ledger_load_state_and_reset() -> Result<()> { ); // 1. Run the ledger node - let mut ledger = - run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; + let mut ledger = start_namada_ledger_node(&test, Some(0), Some(40))?; - ledger.exp_string("Namada ledger node started")?; // There should be no previous state ledger.exp_string("No state could be found")?; // Wait to commit a block @@ -283,10 +296,7 @@ fn run_ledger_load_state_and_reset() -> Result<()> { drop(ledger); // 3. Run the ledger again, it should load its previous state - let mut ledger = - run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; - - ledger.exp_string("Namada ledger node started")?; + let mut ledger = start_namada_ledger_node(&test, Some(0), Some(40))?; // There should be previous state now ledger.exp_string("Last state root hash:")?; @@ -308,10 +318,7 @@ fn run_ledger_load_state_and_reset() -> Result<()> { session.exp_eof()?; // 6. Run the ledger again, it should start from fresh state - let mut session = - run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; - - session.exp_string("Namada ledger node started")?; + let mut session = start_namada_ledger_node(&test, Some(0), Some(40))?; // There should be no previous state session.exp_string("No state could be found")?; @@ -411,11 +418,9 @@ fn ledger_txs_and_queries() -> Result<()> { ); // 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 _bg_ledger = + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? + .background(); // for a custom tx let transfer = token::Transfer { @@ -693,12 +698,9 @@ fn masp_txs_and_queries() -> Result<()> { ); // 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 _bg_ledger = + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? + .background(); let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); @@ -953,12 +955,9 @@ fn masp_pinned_txs() -> Result<()> { ); // 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 _bg_ledger = + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? + .background(); let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); @@ -1136,12 +1135,9 @@ fn masp_incentives() -> Result<()> { ); // 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 _bg_ledger = + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? + .background(); let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); @@ -1883,12 +1879,9 @@ fn invalid_transactions() -> Result<()> { ); // 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 bg_ledger = + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? + .background(); // 2. Submit a an invalid transaction (trying to transfer tokens should fail // in the user's VP due to the wrong signer) @@ -1938,10 +1931,7 @@ fn invalid_transactions() -> Result<()> { drop(ledger); // 4. Restart the ledger - let mut ledger = - run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; - - ledger.exp_string("Namada ledger node started")?; + let mut ledger = start_namada_ledger_node(&test, Some(0), Some(40))?; // There should be previous state now ledger.exp_string("Last state root hash:")?; @@ -2033,11 +2023,9 @@ fn pos_bonds() -> Result<()> { ); // 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 _bg_ledger = + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? + .background(); let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); @@ -2236,27 +2224,15 @@ fn pos_rewards() -> Result<()> { } // 1. Run 3 genesis validator ledger nodes - let mut validator_0 = - run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; - validator_0.exp_string("Namada ledger node started")?; - validator_0.exp_string("This node is a validator")?; - - let mut validator_1 = - run_as!(test, Who::Validator(1), Bin::Node, &["ledger"], Some(40))?; - validator_1.exp_string("Namada ledger node started")?; - validator_1.exp_string("This node is a validator")?; - - 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")?; - - wait_for_wasm_pre_compile(&mut validator_0)?; - let bg_validator_0 = validator_0.background(); - wait_for_wasm_pre_compile(&mut validator_1)?; - let bg_validator_1 = validator_1.background(); - wait_for_wasm_pre_compile(&mut validator_2)?; - let bg_validator_2 = validator_2.background(); + let bg_validator_0 = + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? + .background(); + let bg_validator_1 = + start_namada_ledger_node_wait_wasm(&test, Some(1), Some(40))? + .background(); + let bg_validator_2 = + start_namada_ledger_node_wait_wasm(&test, Some(2), Some(40))? + .background(); let validator_zero_rpc = get_actor_rpc(&test, &Who::Validator(0)); let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(1)); @@ -2401,11 +2377,9 @@ fn test_bond_queries() -> Result<()> { )?; // 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 _bg_ledger = + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? + .background(); let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); let validator_alias = "validator-0"; @@ -2562,17 +2536,10 @@ fn pos_init_validator() -> Result<()> { )?; // 1. Run a validator and non-validator ledger node - let args = ["ledger"]; let mut validator_0 = - run_as!(test, Who::Validator(0), Bin::Node, args, Some(60))?; + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(60))?; let mut non_validator = - run_as!(test, Who::NonValidator, Bin::Node, args, Some(60))?; - - wait_for_wasm_pre_compile(&mut validator_0)?; - // let _bg_ledger = validator_0.background(); - - wait_for_wasm_pre_compile(&mut non_validator)?; - // let _bg_ledger = non_validator.background(); + start_namada_ledger_node_wait_wasm(&test, None, Some(60))?; // Wait for a first block validator_0.exp_string("Committed block hash")?; @@ -2713,7 +2680,7 @@ fn pos_init_validator() -> Result<()> { let validator_1_base_dir = test.get_base_dir(&Who::NonValidator); let mut validator_1 = setup::run_cmd( Bin::Node, - args, + &["ledger"], Some(60), &test.working_dir, validator_1_base_dir, @@ -2777,11 +2744,9 @@ fn ledger_many_txs_in_a_block() -> Result<()> { ); // 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 bg_ledger = + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? + .background(); let validator_one_rpc = Arc::new(get_actor_rpc(&test, &Who::Validator(0))); @@ -2889,13 +2854,11 @@ fn proposal_submission() -> Result<()> { client.assert_success(); // 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 bg_ledger = + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? + .background(); - let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); + let validator_0_rpc = get_actor_rpc(&test, &Who::Validator(0)); // 1.1 Delegate some token let tx_args = vec![ @@ -2913,7 +2876,7 @@ fn proposal_submission() -> Result<()> { "--gas-token", NAM, "--node", - &validator_one_rpc, + &validator_0_rpc, ]; let mut client = run!(test, Bin::Client, tx_args, Some(40))?; client.exp_string("Transaction is valid.")?; @@ -3247,12 +3210,9 @@ fn eth_governance_proposal() -> Result<()> { client.assert_success(); // Run the ledger node - let mut ledger = - run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; - - ledger.exp_string("Namada ledger node started")?; - wait_for_wasm_pre_compile(&mut ledger)?; - let _bg_ledger = ledger.background(); + let _bg_ledger = + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? + .background(); let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); @@ -3481,12 +3441,9 @@ fn pgf_governance_proposal() -> Result<()> { client.assert_success(); // Run the ledger node - let mut ledger = - run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; - - ledger.exp_string("Namada ledger node started")?; - wait_for_wasm_pre_compile(&mut ledger)?; - let _bg_ledger = ledger.background(); + let _bg_ledger = + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? + .background(); let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); @@ -3801,11 +3758,9 @@ fn proposal_offline() -> Result<()> { ); // 1. Run the ledger node - let mut ledger = - run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(20))?; - - wait_for_wasm_pre_compile(&mut ledger)?; - let _bg_ledger = ledger.background(); + let _bg_ledger = + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? + .background(); let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); @@ -4258,30 +4213,12 @@ fn test_genesis_validators() -> Result<()> { test.genesis.validator.keys(), ); - let args = ["ledger"]; let mut validator_0 = - run_as!(test, Who::Validator(0), Bin::Node, args, Some(40))?; - validator_0.exp_string("Namada ledger node started")?; - validator_0.exp_string("This node is a validator")?; - + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))?; 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")?; - + start_namada_ledger_node_wait_wasm(&test, Some(1), Some(40))?; let mut non_validator = - run_as!(test, Who::NonValidator, Bin::Node, args, Some(40))?; - non_validator.exp_string("Namada ledger node started")?; - non_validator.exp_string("This node is not a validator")?; - - wait_for_wasm_pre_compile(&mut validator_0)?; - wait_for_wasm_pre_compile(&mut validator_1)?; - wait_for_wasm_pre_compile(&mut non_validator)?; - - // Wait for a first block - validator_0.exp_string("Committed block hash")?; - validator_1.exp_string("Committed block hash")?; - non_validator.exp_string("Committed block hash")?; + start_namada_ledger_node_wait_wasm(&test, None, Some(40))?; // Wait for a first block validator_0.exp_string("Committed block hash")?; @@ -4404,19 +4341,12 @@ fn double_signing_gets_slashed() -> Result<()> { ); // 1. Run 2 genesis validator ledger nodes - let args = ["ledger"]; - let mut validator_0 = - run_as!(test, Who::Validator(0), Bin::Node, args, Some(40))?; - validator_0.exp_string("Namada ledger node started")?; - validator_0.exp_string("This node is a validator")?; - wait_for_wasm_pre_compile(&mut validator_0)?; - 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")?; - wait_for_wasm_pre_compile(&mut validator_1)?; - let bg_validator_1 = validator_1.background(); + let _bg_validator_0 = + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? + .background(); + let bg_validator_1 = + start_namada_ledger_node_wait_wasm(&test, Some(1), Some(40))? + .background(); // 2. Copy the first genesis validator base-dir let validator_0_base_dir = test.get_base_dir(&Who::Validator(0)); @@ -4500,7 +4430,7 @@ fn double_signing_gets_slashed() -> Result<()> { // `validator_0` and `validator_0_copy` should start double signing let mut validator_0_copy = setup::run_cmd( Bin::Node, - args, + &["ledger"], Some(40), &test.working_dir, validator_0_base_dir_copy, @@ -4558,15 +4488,12 @@ fn implicit_account_reveal_pk() -> Result<()> { let test = setup::network(|genesis| 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)); + let _bg_ledger = + start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? + .background(); // 2. Some transactions that need signature authorization: + let validator_0_rpc = get_actor_rpc(&test, &Who::Validator(0)); let txs_args: Vec Vec>> = vec![ // A token transfer tx Box::new(|source| { @@ -4581,7 +4508,7 @@ fn implicit_account_reveal_pk() -> Result<()> { "--amount", "10.1", "--node", - &validator_one_rpc, + &validator_0_rpc, ] .into_iter() .map(|x| x.to_owned()) @@ -4598,7 +4525,7 @@ fn implicit_account_reveal_pk() -> Result<()> { "--amount", "10.1", "--node", - &validator_one_rpc, + &validator_0_rpc, ] .into_iter() .map(|x| x.to_owned()) @@ -4618,7 +4545,7 @@ fn implicit_account_reveal_pk() -> Result<()> { "--data-path", valid_proposal_json_path.to_str().unwrap(), "--node", - &validator_one_rpc, + &validator_0_rpc, ] .into_iter() .map(|x| x.to_owned()) @@ -4653,7 +4580,7 @@ fn implicit_account_reveal_pk() -> Result<()> { "--amount", "1000", "--node", - &validator_one_rpc, + &validator_0_rpc, ]; let mut client = run!(test, Bin::Client, credit_args, Some(40))?; client.assert_success(); From 0043c904d22011bc850819ee60fb1161b1c51fb3 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 14 Jul 2023 00:27:46 +0200 Subject: [PATCH 089/113] don't use flaky sleep --- tests/src/e2e/ledger_tests.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 5b8d881785..6e884a948d 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -2672,9 +2672,7 @@ fn pos_init_validator() -> Result<()> { // Stop the non-validator node and run it as the new validator let mut non_validator = bg_non_validator.foreground(); non_validator.interrupt()?; - - // it takes a bit before the node is shutdown. We dont want flasky test. - sleep(6); + non_validator.exp_eof()?; let loc = format!("{}:{}", std::file!(), std::line!()); let validator_1_base_dir = test.get_base_dir(&Who::NonValidator); From 84879a27cfb3964b53532ea6710a9e7fb439b4c7 Mon Sep 17 00:00:00 2001 From: Aleksandr Karbyshev Date: Fri, 14 Jul 2023 01:22:53 +0200 Subject: [PATCH 090/113] fix formatting --- tests/src/e2e/ledger_tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 6e884a948d..9b579bef92 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -2678,7 +2678,7 @@ fn pos_init_validator() -> Result<()> { let validator_1_base_dir = test.get_base_dir(&Who::NonValidator); let mut validator_1 = setup::run_cmd( Bin::Node, - &["ledger"], + ["ledger"], Some(60), &test.working_dir, validator_1_base_dir, @@ -4428,7 +4428,7 @@ fn double_signing_gets_slashed() -> Result<()> { // `validator_0` and `validator_0_copy` should start double signing let mut validator_0_copy = setup::run_cmd( Bin::Node, - &["ledger"], + ["ledger"], Some(40), &test.working_dir, validator_0_base_dir_copy, From 4a92c107cd2b3e18c663884aac0aa9abb5da415c Mon Sep 17 00:00:00 2001 From: Gianmarco Fraccaroli Date: Fri, 14 Jul 2023 10:46:41 +0200 Subject: [PATCH 091/113] ci: clean up --- .github/workflows/automation.yml | 13 - .github/workflows/build-and-test-bridge.yml | 517 -------------------- .github/workflows/build-and-test.yml | 17 +- .github/workflows/build-tendermint.yml | 37 -- 4 files changed, 1 insertion(+), 583 deletions(-) delete mode 100644 .github/workflows/build-and-test-bridge.yml delete mode 100644 .github/workflows/build-tendermint.yml diff --git a/.github/workflows/automation.yml b/.github/workflows/automation.yml index 48accef46c..a594d2aa88 100644 --- a/.github/workflows/automation.yml +++ b/.github/workflows/automation.yml @@ -38,12 +38,6 @@ jobs: command: spawn-devnet.py logs: 'false' timeout: 25 - - name: Load tester - comment: pls load test - command: load-test.py - logs: 'true' - logs_path: /tmp/namada-load-tester/logs/ - timeout: 360 steps: - name: Configure AWS Credentials @@ -79,13 +73,6 @@ jobs: GITHUB_DISPATCH_TOKEN: ${{ secrets.GT_DISPATCH }} SLACK_DEVNET_SECRET: ${{ secrets.SLACK_DEVNET_SECRET }} BINARIES_COMMIT_SHA: ${{ steps.comment-branch.outputs.head_sha }} - - name: Upload load tester logs - if: ${{ matrix.make.logs == 'true' && steps.check.outputs.triggered == 'true' }} - uses: actions/upload-artifact@v3 - with: - name: logs-load-tester-${{ github.event.pull_request.head.sha || github.sha }} - path: ${{ matrix.make.logs_path }} - retention-days: 5 - name: Comment not found if: steps.check.outputs.triggered != 'true' run: echo "Comment $COMMENT not found" diff --git a/.github/workflows/build-and-test-bridge.yml b/.github/workflows/build-and-test-bridge.yml deleted file mode 100644 index c6bf2ead2f..0000000000 --- a/.github/workflows/build-and-test-bridge.yml +++ /dev/null @@ -1,517 +0,0 @@ -name: Build and Test Ethereum Bridge - -on: - push: - branches: - - eth-bridge-integration - # Run in PRs with conflicts (https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request) - pull_request_target: - branches: - - eth-bridge-integration - - "**/ethbridge/**" - types: [opened, synchronize, reopened] - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number }} - cancel-in-progress: true - -permissions: - id-token: write - contents: read - packages: read - -env: - GIT_LFS_SKIP_SMUDGE: 1 - - -jobs: - build-wasm: - timeout-minutes: 30 - runs-on: ${{ matrix.os }} - container: - image: ghcr.io/anoma/namada:wasm-main - strategy: - fail-fast: false - matrix: - os: [ubuntu-20.04] - wasm_cache_version: ["v2"] - mold_version: [1.11.0] - - steps: - - name: Checkout repo - uses: actions/checkout@v3 - if: ${{ github.event_name != 'pull_request_target' }} - - name: Checkout PR - uses: actions/checkout@v3 - if: ${{ github.event_name == 'pull_request_target' }} - # From https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target: - # "This event runs in the context of the base of the pull request, - # rather than in the context of the merge commit, as the pull_request - # event does." - # We set the ref to the head commit of the PR instead. - # For this, we have to make sure that we're not running CI on untrusted - # code (more info in the link above), so the repo MUST be configured - # to disallow that. - with: - ref: ${{ github.event.pull_request.head.sha }} - - name: Build WASM - run: | - make build-wasm-scripts - - name: Upload wasm artifacts - uses: actions/upload-artifact@v3 - with: - name: wasm-${{ github.event.pull_request.head.sha || github.sha }} - path: | - wasm/tx_*.wasm - wasm/vp_*.wasm - wasm/checksums.json - - test-wasm: - timeout-minutes: 30 - runs-on: ${{ matrix.os }} - needs: [build-wasm] - container: - image: ghcr.io/anoma/namada:wasm-main - strategy: - fail-fast: false - matrix: - os: [ubuntu-20.04] - wasm_cache_version: ["v2"] - mold_version: [1.11.0] - - steps: - - name: Checkout repo - uses: actions/checkout@v3 - if: ${{ github.event_name != 'pull_request_target' }} - - name: Checkout PR - uses: actions/checkout@v3 - if: ${{ github.event_name == 'pull_request_target' }} - with: - ref: ${{ github.event.pull_request.head.sha }} - - name: Duplicate checksums file - run: cp wasm/checksums.json wasm/original-checksums.json - - name: Install mold linker - run: | - wget -q -O- https://github.com/rui314/mold/releases/download/v${{ matrix.mold_version }}/mold-${{ matrix.mold_version }}-x86_64-linux.tar.gz | tar -xz - mv mold-${{ matrix.mold_version }}-x86_64-linux/bin/mold /usr/local/bin - - name: Download wasm artifacts - uses: actions/download-artifact@v3 - with: - name: wasm-${{ github.event.pull_request.head.sha|| github.sha }} - path: ./wasm - env: - RUSTFLAGS: "-C linker=clang -C link-arg=-fuse-ld=/usr/local/bin/mold" - - name: Test Wasm - run: make test-wasm - - name: Check wasm up-to-date - run: cmp -- wasm/checksums.json wasm/original-checksums.json || true - - name: Print diff - run: diff -y -W 150 wasm/checksums.json wasm/original-checksums.json --suppress-common-lines || true - - update-wasm: - runs-on: ${{ matrix.os }} - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && needs.build-wasm.result == 'success' }} - timeout-minutes: 30 - needs: [build-wasm] - strategy: - fail-fast: false - matrix: - os: [ubuntu-20.04] - - steps: - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v1 - with: - role-to-assume: arn:aws:iam::375643557360:role/anoma-github-action-ci-master - aws-region: eu-west-1 - - name: Download wasm artifacts - uses: actions/download-artifact@v3 - with: - name: wasm-${{ github.event.pull_request.head.sha || github.sha }} - path: ./wasm - - name: Update WASM - run: aws s3 sync wasm s3://$BUCKET_NAME --acl public-read --exclude "*" --include "*.wasm" --exclude "*/*" --region $AWS_REGION - env: - BUCKET_NAME: namada-wasm-master - AWS_REGION: eu-west-1 - - namada-eth: - runs-on: ${{ matrix.os }} - timeout-minutes: 80 - needs: [build-wasm] - strategy: - fail-fast: false - matrix: - os: [ubuntu-20.04] - nightly_version: [nightly-2023-06-01] - mold_version: [1.11.0] - make: - - name: ABCI - suffix: '' - cache_key: namada - cache_version: v2 - - env: - CARGO_INCREMENTAL: 0 - RUST_BACKTRACE: full - RUSTC_WRAPPER: sccache - SCCACHE_CACHE_SIZE: 100G - SCCACHE_BUCKET: namada-sccache-master - - steps: - - name: Checkout repo - uses: actions/checkout@v3 - if: ${{ github.event_name != 'pull_request_target' }} - - name: Checkout PR - uses: actions/checkout@v3 - if: ${{ github.event_name == 'pull_request_target' }} - # See comment in build-and-test.yml - with: - ref: ${{ github.event.pull_request.head.sha }} - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v1 - with: - role-to-assume: arn:aws:iam::375643557360:role/anoma-github-action-ci-master - aws-region: eu-west-1 - - name: Install Protoc - uses: arduino/setup-protoc@v1 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - - name: Install sccache (ubuntu-20.04) - if: matrix.os == 'ubuntu-20.04' - env: - LINK: https://github.com/mozilla/sccache/releases/download - SCCACHE_VERSION: v0.3.0 - run: | - SCCACHE_FILE=sccache-$SCCACHE_VERSION-x86_64-unknown-linux-musl - mkdir -p $HOME/.local/bin - curl -L "$LINK/$SCCACHE_VERSION/$SCCACHE_FILE.tar.gz" | tar xz - mv -f $SCCACHE_FILE/sccache $HOME/.local/bin/sccache - chmod +x $HOME/.local/bin/sccache - echo "$HOME/.local/bin" >> $GITHUB_PATH - - name: Install sccache (macos-latest) - if: matrix.os == 'macos-latest' - run: | - brew update - brew install sccache - - name: Setup rust toolchain - uses: oxidecomputer/actions-rs_toolchain@ad3f86084a8a5acf2c09cb691421b31cf8af7a36 - with: - profile: default - override: true - - name: Setup rust nightly - uses: oxidecomputer/actions-rs_toolchain@ad3f86084a8a5acf2c09cb691421b31cf8af7a36 - with: - toolchain: ${{ matrix.nightly_version }} - profile: default - - name: Cache cargo registry - uses: actions/cache@v3 - continue-on-error: false - with: - path: | - ~/.cargo/registry - ~/.cargo/git - key: ${{ runner.os }}-${{ matrix.make.cache_key }}-${{ matrix.make.cache_version }}-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: ${{ runner.os }}-${{ matrix.make.cache_key }}-${{ matrix.make.cache_version }}-cargo- - - name: Start sccache server - run: sccache --start-server - - name: Install mold linker - run: | - wget -q -O- https://github.com/rui314/mold/releases/download/v${{ matrix.mold_version }}/mold-${{ matrix.mold_version }}-x86_64-linux.tar.gz | tar -xz - mv mold-${{ matrix.mold_version }}-x86_64-linux/bin/mold /usr/local/bin - - name: Download wasm artifacts - uses: actions/download-artifact@v3 - with: - name: wasm-${{ github.event.pull_request.head.sha|| github.sha }} - path: ./wasm - - uses: taiki-e/install-action@cargo-llvm-cov - - name: Run unit test - run: make test-unit-coverage${{ matrix.make.suffix }} - env: - RUSTFLAGS: "-C linker=clang -C link-arg=-fuse-ld=/usr/local/bin/mold" - - name: Upload coverage - uses: actions/upload-artifact@v3 - with: - name: coverage${{ matrix.make.suffix }}-${{ github.event.pull_request.head.sha || github.sha }} - path: target/html - retention-days: 3 - - name: Print sccache stats - if: always() - run: sccache --show-stats - - name: Stop sccache server - if: always() - run: sccache --stop-server || true - - namada-release-eth: - runs-on: ${{ matrix.os }} - timeout-minutes: 25 - strategy: - fail-fast: false - matrix: - os: [ubuntu-20.04] - mold_version: [1.11.0] - make: - - name: ABCI Release build - suffix: '' - cache_key: namada-e2e-release - cache_version: "v2" - - env: - CARGO_INCREMENTAL: 0 - RUST_BACKTRACE: full - RUSTC_WRAPPER: sccache - SCCACHE_CACHE_SIZE: 100G - SCCACHE_BUCKET: namada-sccache-master - - steps: - - name: Checkout repo - uses: actions/checkout@v3 - if: ${{ github.event_name != 'pull_request_target' }} - - name: Checkout PR - uses: actions/checkout@v3 - if: ${{ github.event_name == 'pull_request_target' }} - # See comment in build-and-test.yml - with: - ref: ${{ github.event.pull_request.head.sha }} - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v1 - with: - role-to-assume: arn:aws:iam::375643557360:role/anoma-github-action-ci-master - aws-region: eu-west-1 - - name: Install Protoc - uses: arduino/setup-protoc@v1 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - - name: Install sccache (ubuntu-20.04) - if: matrix.os == 'ubuntu-20.04' - env: - LINK: https://github.com/mozilla/sccache/releases/download - SCCACHE_VERSION: v0.3.0 - run: | - SCCACHE_FILE=sccache-$SCCACHE_VERSION-x86_64-unknown-linux-musl - mkdir -p $HOME/.local/bin - curl -L "$LINK/$SCCACHE_VERSION/$SCCACHE_FILE.tar.gz" | tar xz - mv -f $SCCACHE_FILE/sccache $HOME/.local/bin/sccache - chmod +x $HOME/.local/bin/sccache - echo "$HOME/.local/bin" >> $GITHUB_PATH - - name: Install sccache (macos-latest) - if: matrix.os == 'macos-latest' - run: | - brew update - brew install sccache - - name: Setup rust toolchain - uses: oxidecomputer/actions-rs_toolchain@ad3f86084a8a5acf2c09cb691421b31cf8af7a36 - with: - profile: default - override: true - - name: Cache cargo registry - uses: actions/cache@v3 - continue-on-error: false - with: - path: | - ~/.cargo/registry - ~/.cargo/git - key: ${{ runner.os }}-${{ matrix.make.cache_key }}-${{ matrix.make.cache_version }}-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: ${{ runner.os }}-${{ matrix.make.cache_key }}-${{ matrix.make.cache_version }}-cargo- - - name: Install mold linker - run: | - wget -q -O- https://github.com/rui314/mold/releases/download/v${{ matrix.mold_version }}/mold-${{ matrix.mold_version }}-x86_64-linux.tar.gz | tar -xz - mv mold-${{ matrix.mold_version }}-x86_64-linux/bin/mold /usr/local/bin - - name: Start sccache server - run: sccache --start-server - - name: Build - run: make build-release${{ matrix.make.suffix }} - env: - RUSTFLAGS: "-C linker=clang -C debug_assertions=true -C link-arg=-fuse-ld=/usr/local/bin/mold" - - name: Upload target binaries - uses: actions/upload-artifact@v3 - with: - name: binaries${{ matrix.make.suffix }}-${{ github.event.pull_request.head.sha || github.sha }} - path: | - target/release/namada - target/release/namadac - target/release/namadaw - target/release/namadan - - name: Upload build timing report - if: success() || failure() - uses: actions/upload-artifact@v3 - with: - name: build-timings-${{ github.event.pull_request.head.sha || github.sha }} - path: | - target/cargo-timings/cargo-timing.html - retention-days: 5 - - name: Print sccache stats - if: always() - run: sccache --show-stats - - name: Stop sccache server - if: always() - run: sccache --stop-server || true - - - namada-e2e-eth: - runs-on: ${{ matrix.os }} - timeout-minutes: 80 - strategy: - fail-fast: false - matrix: - os: [ubuntu-20.04] - nightly_version: [nightly-2023-06-01] - mold_version: [1.11.0] - make: - - name: e2e - suffix: '' - index: 0 - cache_key: namada - cache_version: v2 - wait_for: namada-release-eth (ubuntu-20.04, 1.7.0, ABCI Release build, namada-e2e-release, v2) - - name: e2e - suffix: '' - index: 1 - cache_key: namada - cache_version: v2 - wait_for: namada-release-eth (ubuntu-20.04, 1.7.0, ABCI Release build, namada-e2e-release, v2) - - env: - CARGO_INCREMENTAL: 0 - RUST_BACKTRACE: full - RUSTC_WRAPPER: sccache - SCCACHE_CACHE_SIZE: 100G - SCCACHE_BUCKET: namada-sccache-master - - steps: - - name: Checkout repo - uses: actions/checkout@v3 - if: ${{ github.event_name != 'pull_request_target' }} - - name: Checkout PR - uses: actions/checkout@v3 - if: ${{ github.event_name == 'pull_request_target' }} - # See comment in build-and-test.yml - with: - ref: ${{ github.event.pull_request.head.sha }} - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v1 - with: - role-to-assume: arn:aws:iam::375643557360:role/anoma-github-action-ci-master - aws-region: eu-west-1 - - name: Install Protoc - uses: arduino/setup-protoc@v1 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - - name: Install sccache (ubuntu-20.04) - if: matrix.os == 'ubuntu-20.04' - env: - LINK: https://github.com/mozilla/sccache/releases/download - SCCACHE_VERSION: v0.3.0 - run: | - SCCACHE_FILE=sccache-$SCCACHE_VERSION-x86_64-unknown-linux-musl - mkdir -p $HOME/.local/bin - curl -L "$LINK/$SCCACHE_VERSION/$SCCACHE_FILE.tar.gz" | tar xz - mv -f $SCCACHE_FILE/sccache $HOME/.local/bin/sccache - chmod +x $HOME/.local/bin/sccache - echo "$HOME/.local/bin" >> $GITHUB_PATH - - name: Install sccache (macos-latest) - if: matrix.os == 'macos-latest' - run: | - brew update - brew install sccache - - name: Setup rust toolchain - uses: oxidecomputer/actions-rs_toolchain@ad3f86084a8a5acf2c09cb691421b31cf8af7a36 - with: - profile: default - override: true - - name: Setup rust nightly - uses: oxidecomputer/actions-rs_toolchain@ad3f86084a8a5acf2c09cb691421b31cf8af7a36 - with: - toolchain: ${{ matrix.nightly_version }} - profile: default - - name: Cache cargo registry - uses: actions/cache@v3 - continue-on-error: false - with: - path: | - ~/.cargo/registry - ~/.cargo/git - key: ${{ runner.os }}-${{ matrix.make.cache_key }}-${{ matrix.make.cache_version }}-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: ${{ runner.os }}-${{ matrix.make.cache_key }}-${{ matrix.make.cache_version }}-cargo- - - name: Start sccache server - run: sccache --start-server - - name: Install mold linker - run: | - wget -q -O- https://github.com/rui314/mold/releases/download/v${{ matrix.mold_version }}/mold-${{ matrix.mold_version }}-x86_64-linux.tar.gz | tar -xz - mv mold-${{ matrix.mold_version }}-x86_64-linux/bin/mold /usr/local/bin - - name: Build - run: make build - env: - RUSTFLAGS: "-C linker=clang -C link-arg=-fuse-ld=/usr/local/bin/mold" - - name: Build test - run: make build-test - env: - RUSTFLAGS: "-C linker=clang -C link-arg=-fuse-ld=/usr/local/bin/mold" - - name: Wait for release binaries - uses: lewagon/wait-on-check-action@v1.2.0 - with: - ref: ${{ github.event.pull_request.head.sha || github.ref }} - check-name: ${{ matrix.make.wait_for }} - repo-token: ${{ secrets.GITHUB_TOKEN }} - wait-interval: 30 - allowed-conclusions: success - - name: Download wasm artifacts - uses: actions/download-artifact@v3 - with: - name: wasm-${{ github.event.pull_request.head.sha|| github.sha }} - path: ./wasm - - name: Download namada binaries - uses: actions/download-artifact@v3 - with: - name: binaries${{ matrix.make.suffix }}-${{ github.event.pull_request.head.sha || github.sha }} - path: ./target/release/ - - name: Download tendermint & cometbft - run: | - curl -o cometbft.tar.gz -LO https://github.com/cometbft/cometbft/releases/download/v0.37.2/cometbft_0.37.2_linux_amd64.tar.gz - tar -xvzf cometbft.tar.gz - mv cometbft /usr/local/bin - curl -o tendermint.tar.gz -LO https://github.com/heliaxdev/tendermint/releases/download/v0.1.4-abciplus/tendermint_0.1.4-abciplus_linux_amd64.tar.gz - tar -xvzf tendermint.tar.gz - mv tendermint /usr/local/bin - - name: Change permissions - run: | - chmod +x target/release/namada - chmod +x target/release/namadaw - chmod +x target/release/namadan - chmod +x target/release/namadac - chmod +x /usr/local/bin/tendermint - chmod +x /usr/local/bin/cometbft - - name: Run e2e test - run: | - mkdir -p /home/runner/work/masp-params - curl -o /home/runner/work/masp-params/masp-spend.params -LO https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-spend.params?raw=true - curl -o /home/runner/work/masp-params/masp-output.params -LO https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-output.params?raw=true - curl -o /home/runner/work/masp-params/masp-convert.params -LO https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-convert.params?raw=true - ls -l /home/runner/work/masp-params - shasum /home/runner/work/masp-params/*.params - python3 .github/workflows/scripts/schedule-e2e.py - env: - NAMADA_E2E_USE_PREBUILT_BINARIES: "true" - NAMADA_E2E_KEEP_TEMP: "true" - NAMADA_TM_STDOUT: "false" - NAMADA_LOG_COLOR: "false" - # NAMADA_MASP_PARAMS_DIR: "/home/runner/work/masp-params" - NAMADA_LOG: "info" - RUSTFLAGS: "-C linker=clang -C link-arg=-fuse-ld=/usr/local/bin/mold" - INDEX: ${{ matrix.make.index }} - - name: Upload e2e logs - if: success() || failure() - uses: actions/upload-artifact@v3 - with: - name: logs-e2e-${{ matrix.make.index }}-${{ github.event.pull_request.head.sha || github.sha }} - path: | - /tmp/.*/logs/ - /tmp/.*/e2e-test.*/setup/validator-*/.namada/logs/*.log - retention-days: 5 - - name: Print sccache stats - if: always() - run: sccache --show-stats - - name: Stop sccache server - if: always() - run: sccache --stop-server || true diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index b9929e95da..e274106660 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -392,21 +392,18 @@ jobs: index: 0 cache_key: namada cache_version: v2 - tendermint_artifact: tendermint-unreleased-v0.1.4-abciplus wait_for: namada-release (ubuntu-20.04, 1.11.0, ABCI Release build, namada-e2e-release, v2) - name: e2e suffix: '' index: 1 cache_key: namada cache_version: v2 - tendermint_artifact: tendermint-unreleased-v0.1.4-abciplus wait_for: namada-release (ubuntu-20.04, 1.11.0, ABCI Release build, namada-e2e-release, v2) - name: e2e suffix: '' index: 2 cache_key: namada cache_version: v2 - tendermint_artifact: tendermint-unreleased-v0.1.4-abciplus wait_for: namada-release (ubuntu-20.04, 1.11.0, ABCI Release build, namada-e2e-release, v2) env: @@ -509,38 +506,26 @@ jobs: with: name: binaries${{ matrix.make.suffix }}-${{ github.event.pull_request.head.sha || github.sha }} path: ./target/release/ - - name: Download tendermint & cometbft + - name: Download CometBFT run: | curl -o cometbft.tar.gz -LO https://github.com/cometbft/cometbft/releases/download/v0.37.2/cometbft_0.37.2_linux_amd64.tar.gz tar -xvzf cometbft.tar.gz mv cometbft /usr/local/bin - curl -o tendermint.tar.gz -LO https://github.com/heliaxdev/tendermint/releases/download/v0.1.4-abciplus/tendermint_0.1.4-abciplus_linux_amd64.tar.gz - tar -xvzf tendermint.tar.gz - mv tendermint /usr/local/bin - name: Change permissions run: | chmod +x target/release/namada chmod +x target/release/namadaw chmod +x target/release/namadan chmod +x target/release/namadac - chmod +x /usr/local/bin/tendermint chmod +x /usr/local/bin/cometbft - name: Run e2e test run: | - mkdir -p /home/runner/work/masp-params - curl -o /home/runner/work/masp-params/masp-spend.params -LO https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-spend.params?raw=true - curl -o /home/runner/work/masp-params/masp-output.params -LO https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-output.params?raw=true - curl -o /home/runner/work/masp-params/masp-convert.params -LO https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-convert.params?raw=true - ls -l /home/runner/work/masp-params - shasum /home/runner/work/masp-params/*.params python3 .github/workflows/scripts/schedule-e2e.py env: - NAMADA_TENDERMINT_WEBSOCKET_TIMEOUT: 20 NAMADA_E2E_USE_PREBUILT_BINARIES: "true" NAMADA_E2E_KEEP_TEMP: "true" NAMADA_TM_STDOUT: "false" NAMADA_LOG_COLOR: "false" - # NAMADA_MASP_PARAMS_DIR: "/home/runner/work/masp-params" NAMADA_LOG: "info" RUSTFLAGS: "-C linker=clang -C link-arg=-fuse-ld=/usr/local/bin/mold" INDEX: ${{ matrix.make.index }} diff --git a/.github/workflows/build-tendermint.yml b/.github/workflows/build-tendermint.yml deleted file mode 100644 index d7977a4b3b..0000000000 --- a/.github/workflows/build-tendermint.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Build tendermint binaries - -on: - schedule: - - cron: "0 0 * * *" - workflow_dispatch: - -permissions: - id-token: write - contents: read - -env: - GIT_LFS_SKIP_SMUDGE: 1 - - -jobs: - tendermint: - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - os: [ubuntu-20.04] - make: - - name: tendermint-unreleased - repository: heliaxdev/tendermint - tendermint_version: v0.1.4-abciplus - - steps: - - name: Build ${{ matrix.make.name }} - run: | - git clone https://github.com/${{ matrix.make.repository }}.git && cd tendermint - git checkout ${{ matrix.make.tendermint_version }} && make build - - name: Upload ${{ matrix.make.name }} binary - uses: actions/upload-artifact@v3 - with: - name: ${{ matrix.make.name }}-${{ matrix.make.tendermint_version }} - path: tendermint/build/tendermint From 749a8ec5134ea3efcb98893cd17bd92c0cfabf9b Mon Sep 17 00:00:00 2001 From: yito88 Date: Fri, 14 Jul 2023 11:42:05 +0200 Subject: [PATCH 092/113] 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/113] 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/113] 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/113] 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/113] 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/113] 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 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 098/113] 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 099/113] 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 100/113] 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 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 101/113] 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 102/113] 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 103/113] 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 104/113] 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 105/113] 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 67e88c8871a3d9fc48af765e6a8080635f2d2b0a Mon Sep 17 00:00:00 2001 From: Gianmarco Fraccaroli Date: Wed, 19 Jul 2023 09:36:45 +0200 Subject: [PATCH 106/113] ci: pre-download masp parameters --- .github/workflows/build-and-test.yml | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index e274106660..c34db58a34 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -200,7 +200,7 @@ jobs: if: matrix.os == 'ubuntu-20.04' env: LINK: https://github.com/mozilla/sccache/releases/download - SCCACHE_VERSION: v0.3.0 + SCCACHE_VERSION: v0.5.4 run: | SCCACHE_FILE=sccache-$SCCACHE_VERSION-x86_64-unknown-linux-musl mkdir -p $HOME/.local/bin @@ -314,7 +314,7 @@ jobs: if: matrix.os == 'ubuntu-20.04' env: LINK: https://github.com/mozilla/sccache/releases/download - SCCACHE_VERSION: v0.3.0 + SCCACHE_VERSION: v0.5.4 run: | SCCACHE_FILE=sccache-$SCCACHE_VERSION-x86_64-unknown-linux-musl mkdir -p $HOME/.local/bin @@ -386,6 +386,7 @@ jobs: os: [ubuntu-20.04] nightly_version: [nightly-2023-06-01] mold_version: [1.7.0] + comet_bft: [https://github.com/cometbft/cometbft/releases/download/v0.37.2/cometbft_0.37.2_linux_amd64.tar.gz] make: - name: e2e suffix: '' @@ -442,7 +443,7 @@ jobs: if: matrix.os == 'ubuntu-20.04' env: LINK: https://github.com/mozilla/sccache/releases/download - SCCACHE_VERSION: v0.3.0 + SCCACHE_VERSION: v0.5.4 run: | SCCACHE_FILE=sccache-$SCCACHE_VERSION-x86_64-unknown-linux-musl mkdir -p $HOME/.local/bin @@ -488,6 +489,12 @@ jobs: run: make build-test env: RUSTFLAGS: "-C linker=clang -C link-arg=-fuse-ld=/usr/local/bin/mold" + - name: Download MASP parameters + run: | + mkdir -p /home/runner/.masp-params + curl -o /home/runner/.masp-params/masp-spend.params -L https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-spend.params\?raw\=true + curl -o /home/runner/.masp-params/masp-output.params -L https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-output.params?raw=true + curl -o /home/runner/.masp-params/masp-convert.params -L https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-convert.params?raw=true - name: Wait for release binaries uses: lewagon/wait-on-check-action@v1.2.0 with: @@ -508,7 +515,7 @@ jobs: path: ./target/release/ - name: Download CometBFT run: | - curl -o cometbft.tar.gz -LO https://github.com/cometbft/cometbft/releases/download/v0.37.2/cometbft_0.37.2_linux_amd64.tar.gz + curl -o cometbft.tar.gz -LO ${{ matrix.comet_bft }} tar -xvzf cometbft.tar.gz mv cometbft /usr/local/bin - name: Change permissions @@ -519,9 +526,9 @@ jobs: chmod +x target/release/namadac chmod +x /usr/local/bin/cometbft - name: Run e2e test - run: | - python3 .github/workflows/scripts/schedule-e2e.py + run: python3 .github/workflows/scripts/schedule-e2e.py env: + NAMADA_MASP_PARAMS_DIR: /home/runner/.masp-params NAMADA_E2E_USE_PREBUILT_BINARIES: "true" NAMADA_E2E_KEEP_TEMP: "true" NAMADA_TM_STDOUT: "false" From ca154cd0543c6d3748e05a3136eb70bda5e57ff6 Mon Sep 17 00:00:00 2001 From: satan Date: Wed, 19 Jul 2023 13:52:34 +0200 Subject: [PATCH 107/113] [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 721323581adcb6faa1a097014f2446b0f48c7aea 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:55:25 +0100 Subject: [PATCH 108/113] channgelog: add #1738 --- .changelog/unreleased/miscellaneous/1738-refactor-cli.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/miscellaneous/1738-refactor-cli.md diff --git a/.changelog/unreleased/miscellaneous/1738-refactor-cli.md b/.changelog/unreleased/miscellaneous/1738-refactor-cli.md new file mode 100644 index 0000000000..ef201074db --- /dev/null +++ b/.changelog/unreleased/miscellaneous/1738-refactor-cli.md @@ -0,0 +1,2 @@ +- Refactored CLI into libraries for future re-use in integration tests and + to enable generic IO. ([\#1738](https://github.com/anoma/namada/pull/1738)) \ No newline at end of file From 331cde0ec024008601a9be288989cb613b659e22 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:07:25 +0100 Subject: [PATCH 109/113] changelog: add #1693 --- .changelog/unreleased/miscellaneous/1693-ibc-multitoken.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/miscellaneous/1693-ibc-multitoken.md diff --git a/.changelog/unreleased/miscellaneous/1693-ibc-multitoken.md b/.changelog/unreleased/miscellaneous/1693-ibc-multitoken.md new file mode 100644 index 0000000000..1c90f3a4b8 --- /dev/null +++ b/.changelog/unreleased/miscellaneous/1693-ibc-multitoken.md @@ -0,0 +1,2 @@ +- Replaced token sub-prefix with a multitoken address and native VP for IBC and + ETH bridge. ([\#1693](https://github.com/anoma/namada/pull/1693)) \ No newline at end of file From 6ea54aff44a28ab01232c2e309485699aa10f0c1 Mon Sep 17 00:00:00 2001 From: Gianmarco Fraccaroli Date: Thu, 20 Jul 2023 12:27:20 +0200 Subject: [PATCH 110/113] ci: fix github colon parsing --- .github/workflows/build-and-test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index c34db58a34..7ddf3aaf99 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -386,7 +386,7 @@ jobs: os: [ubuntu-20.04] nightly_version: [nightly-2023-06-01] mold_version: [1.7.0] - comet_bft: [https://github.com/cometbft/cometbft/releases/download/v0.37.2/cometbft_0.37.2_linux_amd64.tar.gz] + comet_bft: [0.37.2] make: - name: e2e suffix: '' @@ -515,7 +515,7 @@ jobs: path: ./target/release/ - name: Download CometBFT run: | - curl -o cometbft.tar.gz -LO ${{ matrix.comet_bft }} + curl -o cometbft.tar.gz -LO https://github.com/cometbft/cometbft/releases/download/v${{ matrix.comet_bft }}/cometbft_${{ matrix.comet_bft }}_linux_amd64.tar.gz tar -xvzf cometbft.tar.gz mv cometbft /usr/local/bin - name: Change permissions From ef2678cc4ccb0097c6a8dc208c0638592418da08 Mon Sep 17 00:00:00 2001 From: yito88 Date: Fri, 21 Jul 2023 20:49:42 +0200 Subject: [PATCH 111/113] fix tx_transfer --- tx_prelude/src/token.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tx_prelude/src/token.rs b/tx_prelude/src/token.rs index 3cf1d8ead3..e950668d2b 100644 --- a/tx_prelude/src/token.rs +++ b/tx_prelude/src/token.rs @@ -19,7 +19,7 @@ pub fn transfer( shielded_hash: &Option, shielded: &Option, ) -> TxResult { - if amount.amount != Amount::default() { + if amount.amount != Amount::default() && src != 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)?; @@ -30,10 +30,8 @@ pub fn transfer( 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 { - ctx.write(&src_key, src_bal)?; - ctx.write(&dest_key, dest_bal)?; - } + ctx.write(&src_key, src_bal)?; + ctx.write(&dest_key, dest_bal)?; } // If this transaction has a shielded component, then handle it From f258a9b6908b01a4356b721118fe3d7460db8b8b Mon Sep 17 00:00:00 2001 From: Gianmarco Fraccaroli Date: Sat, 22 Jul 2023 11:26:07 +0200 Subject: [PATCH 112/113] refator e2e masp tests --- .../testing/1714-refactor-e2e-tests.md | 3 + .github/workflows/build-and-test.yml | 19 +- Cargo.lock | 2224 ++++++++--------- Cargo.toml | 1 + Makefile | 17 +- apps/Cargo.toml | 4 +- 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 | 6 +- apps/src/lib/cli/api.rs | 29 + apps/src/lib/cli/client.rs | 525 ++++ apps/src/lib/cli/relayer.rs | 166 ++ apps/src/lib/cli/utils.rs | 62 + .../cli.rs => lib/cli/wallet.rs} | 146 +- apps/src/lib/client/rpc.rs | 14 +- apps/src/lib/client/tx.rs | 8 +- apps/src/lib/client/utils.rs | 39 +- apps/src/lib/config/genesis.rs | 13 +- apps/src/lib/node/ledger/mod.rs | 4 +- apps/src/lib/node/ledger/shell/init_chain.rs | 2 +- apps/src/lib/node/ledger/shell/mod.rs | 5 +- .../lib/node/ledger/shell/prepare_proposal.rs | 6 +- .../lib/node/ledger/shell/process_proposal.rs | 2 +- .../lib/node/ledger/shell/testing/client.rs | 82 + apps/src/lib/node/ledger/shell/testing/mod.rs | 3 + .../src/lib/node/ledger/shell/testing/node.rs | 543 ++++ .../lib/node/ledger/shell/testing/utils.rs | 48 + .../shell/vote_extensions/bridge_pool_vext.rs | 2 +- .../shell/vote_extensions/eth_events.rs | 2 +- .../shell/vote_extensions/val_set_update.rs | 2 +- .../node/ledger/shims/abcipp_shim_types.rs | 2 +- apps/src/lib/wallet/cli_utils.rs | 515 ++++ apps/src/lib/wallet/mod.rs | 1 + core/src/ledger/testnet_pow.rs | 8 +- core/src/types/uint.rs | 70 +- encoding_spec/Cargo.toml | 2 +- genesis/e2e-tests-single-node.toml | 2 +- shared/src/ledger/events/log.rs | 6 + shared/src/ledger/masp.rs | 77 +- shared/src/ledger/queries/types.rs | 4 +- shared/src/ledger/tx.rs | 46 +- tests/Cargo.toml | 5 + tests/src/e2e/ledger_tests.rs | 1215 +-------- tests/src/e2e/setup.rs | 4 +- tests/src/integration.rs | 3 + tests/src/integration/masp.rs | 1206 +++++++++ tests/src/integration/setup.rs | 163 ++ tests/src/integration/utils.rs | 83 + tests/src/lib.rs | 4 + tests/src/storage_api/testnet_pow.rs | 2 +- wasm/Cargo.lock | 164 +- wasm/checksums.json | 40 +- wasm/wasm_source/src/vp_testnet_faucet.rs | 25 +- wasm_for_tests/wasm_source/Cargo.lock | 164 +- 57 files changed, 5158 insertions(+), 3262 deletions(-) create mode 100644 .changelog/unreleased/testing/1714-refactor-e2e-tests.md 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%) create mode 100644 apps/src/lib/node/ledger/shell/testing/client.rs create mode 100644 apps/src/lib/node/ledger/shell/testing/mod.rs create mode 100644 apps/src/lib/node/ledger/shell/testing/node.rs create mode 100644 apps/src/lib/node/ledger/shell/testing/utils.rs create mode 100644 apps/src/lib/wallet/cli_utils.rs create mode 100644 tests/src/integration.rs create mode 100644 tests/src/integration/masp.rs create mode 100644 tests/src/integration/setup.rs create mode 100644 tests/src/integration/utils.rs diff --git a/.changelog/unreleased/testing/1714-refactor-e2e-tests.md b/.changelog/unreleased/testing/1714-refactor-e2e-tests.md new file mode 100644 index 0000000000..7bf4e78be3 --- /dev/null +++ b/.changelog/unreleased/testing/1714-refactor-e2e-tests.md @@ -0,0 +1,3 @@ +- Added integration testing infrastructure for node, client and + the wallet and replaced MASP E2E tests with integration tests. + ([\#1714](https://github.com/anoma/namada/pull/1714)) \ No newline at end of file diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 7ddf3aaf99..e274106660 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -200,7 +200,7 @@ jobs: if: matrix.os == 'ubuntu-20.04' env: LINK: https://github.com/mozilla/sccache/releases/download - SCCACHE_VERSION: v0.5.4 + SCCACHE_VERSION: v0.3.0 run: | SCCACHE_FILE=sccache-$SCCACHE_VERSION-x86_64-unknown-linux-musl mkdir -p $HOME/.local/bin @@ -314,7 +314,7 @@ jobs: if: matrix.os == 'ubuntu-20.04' env: LINK: https://github.com/mozilla/sccache/releases/download - SCCACHE_VERSION: v0.5.4 + SCCACHE_VERSION: v0.3.0 run: | SCCACHE_FILE=sccache-$SCCACHE_VERSION-x86_64-unknown-linux-musl mkdir -p $HOME/.local/bin @@ -386,7 +386,6 @@ jobs: os: [ubuntu-20.04] nightly_version: [nightly-2023-06-01] mold_version: [1.7.0] - comet_bft: [0.37.2] make: - name: e2e suffix: '' @@ -443,7 +442,7 @@ jobs: if: matrix.os == 'ubuntu-20.04' env: LINK: https://github.com/mozilla/sccache/releases/download - SCCACHE_VERSION: v0.5.4 + SCCACHE_VERSION: v0.3.0 run: | SCCACHE_FILE=sccache-$SCCACHE_VERSION-x86_64-unknown-linux-musl mkdir -p $HOME/.local/bin @@ -489,12 +488,6 @@ jobs: run: make build-test env: RUSTFLAGS: "-C linker=clang -C link-arg=-fuse-ld=/usr/local/bin/mold" - - name: Download MASP parameters - run: | - mkdir -p /home/runner/.masp-params - curl -o /home/runner/.masp-params/masp-spend.params -L https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-spend.params\?raw\=true - curl -o /home/runner/.masp-params/masp-output.params -L https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-output.params?raw=true - curl -o /home/runner/.masp-params/masp-convert.params -L https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-convert.params?raw=true - name: Wait for release binaries uses: lewagon/wait-on-check-action@v1.2.0 with: @@ -515,7 +508,7 @@ jobs: path: ./target/release/ - name: Download CometBFT run: | - curl -o cometbft.tar.gz -LO https://github.com/cometbft/cometbft/releases/download/v${{ matrix.comet_bft }}/cometbft_${{ matrix.comet_bft }}_linux_amd64.tar.gz + curl -o cometbft.tar.gz -LO https://github.com/cometbft/cometbft/releases/download/v0.37.2/cometbft_0.37.2_linux_amd64.tar.gz tar -xvzf cometbft.tar.gz mv cometbft /usr/local/bin - name: Change permissions @@ -526,9 +519,9 @@ jobs: chmod +x target/release/namadac chmod +x /usr/local/bin/cometbft - name: Run e2e test - run: python3 .github/workflows/scripts/schedule-e2e.py + run: | + python3 .github/workflows/scripts/schedule-e2e.py env: - NAMADA_MASP_PARAMS_DIR: /home/runner/.masp-params NAMADA_E2E_USE_PREBUILT_BINARIES: "true" NAMADA_E2E_KEEP_TEMP: "true" NAMADA_TM_STDOUT: "false" diff --git a/Cargo.lock b/Cargo.lock index 568de9a745..d00f8d60c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,11 +14,11 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.17.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" dependencies = [ - "gimli", + "gimli 0.27.2", ] [[package]] @@ -34,7 +34,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ "crypto-common", - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -51,9 +51,9 @@ dependencies = [ [[package]] name = "aes" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" dependencies = [ "cfg-if 1.0.0", "cipher 0.4.4", @@ -66,7 +66,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.8", + "getrandom 0.2.9", "once_cell", "version_check", ] @@ -149,9 +149,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.65" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" [[package]] name = "ark-bls12-381" @@ -277,9 +277,9 @@ dependencies = [ [[package]] name = "arrayref" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" [[package]] name = "arrayvec" @@ -321,34 +321,35 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-stream" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" dependencies = [ "async-stream-impl", "futures-core", + "pin-project-lite", ] [[package]] name = "async-stream-impl" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] name = "async-trait" -version = "0.1.58" +version = "0.1.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] @@ -380,9 +381,9 @@ dependencies = [ [[package]] name = "auto_impl" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a8c1df849285fbacd587de7818cc7d13be6cd2cbcd47a04fb1801b0e2706e33" +checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" dependencies = [ "proc-macro-error", "proc-macro2", @@ -398,13 +399,13 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" -version = "0.6.7" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fb79c228270dcf2426e74864cabc94babb5dbab01a4314e702d2f16540e1591" +checksum = "f8175979259124331c1d7bf6586ee7e0da434155e4b2d48ec2c8386281d8df39" dependencies = [ "async-trait", "axum-core", - "bitflags 1.3.2", + "bitflags", "bytes", "futures-util", "http", @@ -417,19 +418,18 @@ dependencies = [ "percent-encoding", "pin-project-lite", "rustversion", - "serde 1.0.147", + "serde 1.0.163", "sync_wrapper", "tower", - "tower-http", "tower-layer", "tower-service", ] [[package]] name = "axum-core" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2f958c80c248b34b9a877a643811be8dbca03ca5ba827f2b63baf3a81e5fc4e" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" dependencies = [ "async-trait", "bytes", @@ -444,16 +444,16 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.66" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" dependencies = [ "addr2line", "cc", "cfg-if 1.0.0", "libc", - "miniz_oxide", - "object 0.29.0", + "miniz_oxide 0.6.2", + "object 0.30.3", "rustc-demangle", ] @@ -463,6 +463,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base58" version = "0.1.0" @@ -475,22 +481,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" -[[package]] -name = "base58check" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee2fe4c9a0c84515f136aaae2466744a721af6d63339c18689d9e995d74d99b" -dependencies = [ - "base58 0.1.0", - "sha2 0.8.2", -] - -[[package]] -name = "base64" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" - [[package]] name = "base64" version = "0.13.1" @@ -536,9 +526,9 @@ dependencies = [ "bitvec 1.0.1", "blake2s_simd", "byteorder", - "crossbeam-channel 0.5.6", - "ff", - "group", + "crossbeam-channel 0.5.8", + "ff 0.12.1", + "group 0.12.1", "lazy_static", "log", "num_cpus", @@ -550,11 +540,11 @@ dependencies = [ [[package]] name = "bimap" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0455254eb5c6964c4545d8bac815e1a1be4f3afe0ae695ea539c12d728d44b" +checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" dependencies = [ - "serde 1.0.147", + "serde 1.0.163", ] [[package]] @@ -563,7 +553,7 @@ version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" dependencies = [ - "serde 1.0.147", + "serde 1.0.163", ] [[package]] @@ -572,19 +562,19 @@ version = "0.65.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" dependencies = [ - "bitflags 1.3.2", + "bitflags", "cexpr", "clang-sys", "lazy_static", "lazycell", "peeking_take_while", - "prettyplease 0.2.6", + "prettyplease 0.2.4", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", - "syn 2.0.16", + "syn 2.0.15", ] [[package]] @@ -625,7 +615,7 @@ dependencies = [ "bech32 0.9.1", "bitcoin_hashes", "secp256k1", - "serde 1.0.147", + "serde 1.0.163", ] [[package]] @@ -634,20 +624,14 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" dependencies = [ - "serde 1.0.147", + "serde 1.0.163", ] [[package]] name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.3.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "bitvec" @@ -673,11 +657,11 @@ dependencies = [ [[package]] name = "blake2" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b12e5fd123190ce1c2e559308a94c9bacad77907d4c6005d9e58fe1a0689e55e" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" dependencies = [ - "digest 0.10.7", + "digest 0.10.6", ] [[package]] @@ -692,9 +676,9 @@ dependencies = [ [[package]] name = "blake2b_simd" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72936ee4afc7f8f736d1c38383b56480b5497b4617b4a77bdbf1d2ababc76127" +checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" dependencies = [ "arrayref", "arrayvec 0.7.2", @@ -703,9 +687,9 @@ dependencies = [ [[package]] name = "blake2s_simd" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db539cc2b5f6003621f1cd9ef92d7ded8ea5232c7de0f9faa2de251cd98730d4" +checksum = "6637f448b9e61dfadbdcbae9a885fadee1f3eaffb1f8d3c1965d3ade8bdfd44f" dependencies = [ "arrayref", "arrayvec 0.7.2", @@ -714,16 +698,16 @@ dependencies = [ [[package]] name = "blake3" -version = "1.3.1" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08e53fc5a564bb15bfe6fae56bd71522205f1f91893f9c0116edad6496c183f" +checksum = "42ae2468a89544a466886840aa467a25b766499f4f04bf7d9fcd10ecee9fccef" dependencies = [ "arrayref", "arrayvec 0.7.2", "cc", "cfg-if 1.0.0", "constant_time_eq", - "digest 0.10.7", + "digest 0.10.6", ] [[package]] @@ -744,16 +728,16 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] name = "block-buffer" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -787,8 +771,8 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3c196a77437e7cc2fb515ce413a6401291578b5afc8ecb29a3c7ab957f05941" dependencies = [ - "ff", - "group", + "ff 0.12.1", + "group 0.12.1", "pairing", "rand_core 0.6.4", "subtle 2.4.1", @@ -840,6 +824,9 @@ name = "bs58" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" +dependencies = [ + "sha2 0.9.9", +] [[package]] name = "bstr" @@ -852,21 +839,11 @@ dependencies = [ "regex-automata", ] -[[package]] -name = "buf_redux" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f" -dependencies = [ - "memchr", - "safemem", -] - [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b" [[package]] name = "byte-slice-cast" @@ -882,29 +859,30 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "byte-unit" -version = "4.0.17" +version = "4.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581ad4b3d627b0c09a0ccb2912148f839acaca0b93cf54cbe42b6c674e86079c" +checksum = "da78b32057b8fdfc352504708feeba7216dcd65a2c9ab02978cbd288d1279b6c" dependencies = [ - "serde 1.0.147", + "serde 1.0.163", "utf8-width", ] [[package]] name = "bytecheck" -version = "0.6.9" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d11cac2c12b5adc6570dad2ee1b87eff4955dac476fe12d81e5fdd352e52406f" +checksum = "8b6372023ac861f6e6dc89c8344a8f398fb42aaba2b5dbc649ca0c0e9dbcb627" dependencies = [ "bytecheck_derive", "ptr_meta", + "simdutf8", ] [[package]] name = "bytecheck_derive" -version = "0.6.9" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e576ebe98e605500b3c8041bb888e966653577172df6dd97398714eb30b9bf" +checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61" dependencies = [ "proc-macro2", "quote", @@ -929,7 +907,7 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" dependencies = [ - "serde 1.0.147", + "serde 1.0.163", ] [[package]] @@ -945,11 +923,11 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.1" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ad0e1e3e88dd237a156ab9f571021b8a158caa0ae44b1968a241efb5144c1e" +checksum = "c530edf18f37068ac2d977409ed5cd50d53d73bc653c7647b48eb78976ac9ae2" dependencies = [ - "serde 1.0.147", + "serde 1.0.163", ] [[package]] @@ -958,7 +936,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" dependencies = [ - "serde 1.0.147", + "serde 1.0.163", ] [[package]] @@ -970,29 +948,29 @@ dependencies = [ "camino", "cargo-platform", "semver 1.0.17", - "serde 1.0.147", + "serde 1.0.163", "serde_json", ] [[package]] name = "cargo_metadata" -version = "0.15.3" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a1ec454bc3eead8719cb56e15dbbfecdbc14e4b3a3ae4936cc6e31f5fc0d07" +checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" dependencies = [ "camino", "cargo-platform", "semver 1.0.17", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "thiserror", ] [[package]] name = "cc" -version = "1.0.76" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" dependencies = [ "jobserver", ] @@ -1003,7 +981,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "nom 7.1.1", + "nom 7.1.3", ] [[package]] @@ -1055,9 +1033,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" dependencies = [ "iana-time-zone", "num-integer", @@ -1067,9 +1045,9 @@ dependencies = [ [[package]] name = "chunked_transfer" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e" +checksum = "cca491388666e04d7248af3f60f0c40cfb0991c72205595d7c396e3510207d1a" [[package]] name = "cipher" @@ -1077,7 +1055,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -1102,9 +1080,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.4.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" dependencies = [ "glob", "libc", @@ -1113,21 +1091,22 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.10" +version = "4.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384e169cc618c613d5e3ca6404dda77a8685a63e08660dcc64abaf7da7cb0c7a" +checksum = "2686c4115cb0810d9a984776e197823d08ec94f176549a89a9efded477c456dc" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.3.10" +version = "4.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef137bbe35aab78bdb468ccfba75a5f4d8321ae011d34063770780545176af2d" +checksum = "2e53afce1efce6ed1f633cf0e57612fe51db54a1ee4fd8f8503d078fe02d69ae" dependencies = [ "anstream", "anstyle", + "bitflags", "clap_lex", "strsim", ] @@ -1143,47 +1122,37 @@ name = "clru" version = "0.5.0" source = "git+https://github.com/marmeladema/clru-rs.git?rev=71ca566#71ca566915f21f3c308091ca7756a91b0f8b5afc" -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - [[package]] name = "coins-bip32" -version = "0.7.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634c509653de24b439672164bbf56f5f582a2ab0e313d3b0f6af0b7345cf2560" +checksum = "b30a84aab436fcb256a2ab3c80663d8aec686e6bae12827bb05fef3e1e439c9f" dependencies = [ "bincode", "bs58", "coins-core", - "digest 0.10.7", - "getrandom 0.2.8", + "digest 0.10.6", + "getrandom 0.2.9", "hmac 0.12.1", - "k256", + "k256 0.13.1", "lazy_static", - "serde 1.0.147", + "serde 1.0.163", "sha2 0.10.6", "thiserror", ] [[package]] name = "coins-bip39" -version = "0.7.0" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a11892bcac83b4c6e95ab84b5b06c76d9d70ad73548dd07418269c5c7977171" +checksum = "84f4d04ee18e58356accd644896aeb2094ddeafb6a713e056cef0c0a8e468c15" dependencies = [ "bitvec 0.17.4", "coins-bip32", - "getrandom 0.2.8", - "hex", + "getrandom 0.2.9", "hmac 0.12.1", - "pbkdf2 0.11.0", + "once_cell", + "pbkdf2 0.12.1", "rand 0.8.5", "sha2 0.10.6", "thiserror", @@ -1191,19 +1160,18 @@ dependencies = [ [[package]] name = "coins-core" -version = "0.7.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c94090a6663f224feae66ab01e41a2555a8296ee07b5f20dab8888bdefc9f617" +checksum = "9b949a1c63fb7eb591eb7ba438746326aedf0ae843e51ec92ba6bec5bb382c4f" dependencies = [ - "base58check", - "base64 0.12.3", + "base64 0.21.0", "bech32 0.7.3", - "blake2", - "digest 0.10.7", - "generic-array 0.14.6", + "bs58", + "digest 0.10.6", + "generic-array 0.14.7", "hex", "ripemd", - "serde 1.0.147", + "serde 1.0.163", "serde_derive", "sha2 0.10.6", "sha3", @@ -1233,7 +1201,7 @@ checksum = "b6eee477a4a8a72f4addd4de416eb56d54bc307b284d6601bafdee1f4ea462d1" dependencies = [ "once_cell", "owo-colors 1.3.0", - "tracing-core 0.1.30", + "tracing-core 0.1.31", "tracing-error", ] @@ -1260,12 +1228,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b1b9d958c2b1368a663f05538fc1b5975adce1e19f435acceae987aceeeb369" dependencies = [ "lazy_static", - "nom 5.1.2", + "nom 5.1.3", "rust-ini", - "serde 1.0.147", + "serde 1.0.163", "serde-hjson", "serde_json", - "toml", + "toml 0.5.9", "yaml-rust", ] @@ -1275,20 +1243,20 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b72b06487a0d4683349ad74d62e87ad639b09667082b3c495c5b6bab7d84b3da" dependencies = [ - "windows", + "windows 0.44.0", ] [[package]] name = "const-oid" -version = "0.9.1" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b" +checksum = "795bc6e66a8e340f075fcf6227e417a2dc976b92b91f3cdc778bb858778b6747" [[package]] name = "constant_time_eq" -version = "0.1.5" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b" [[package]] name = "contracts" @@ -1301,15 +1269,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "core-foundation" version = "0.9.3" @@ -1322,9 +1281,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "corosensei" @@ -1341,9 +1300,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" dependencies = [ "libc", ] @@ -1367,7 +1326,7 @@ dependencies = [ "cranelift-codegen-meta", "cranelift-codegen-shared", "cranelift-entity", - "gimli", + "gimli 0.26.2", "log", "regalloc", "smallvec", @@ -1428,35 +1387,35 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.6" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ "cfg-if 1.0.0", - "crossbeam-utils 0.8.12", + "crossbeam-utils 0.8.15", ] [[package]] name = "crossbeam-deque" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ "cfg-if 1.0.0", "crossbeam-epoch", - "crossbeam-utils 0.8.12", + "crossbeam-utils 0.8.15", ] [[package]] name = "crossbeam-epoch" -version = "0.9.11" +version = "0.9.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" dependencies = [ "autocfg", "cfg-if 1.0.0", - "crossbeam-utils 0.8.12", - "memoffset 0.6.5", + "crossbeam-utils 0.8.15", + "memoffset 0.8.0", "scopeguard", ] @@ -1473,9 +1432,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.12" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" dependencies = [ "cfg-if 1.0.0", ] @@ -1492,7 +1451,19 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle 2.4.1", + "zeroize", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" +dependencies = [ + "generic-array 0.14.7", "rand_core 0.6.4", "subtle 2.4.1", "zeroize", @@ -1504,7 +1475,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "typenum", ] @@ -1524,7 +1495,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "subtle 2.4.1", ] @@ -1534,7 +1505,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "subtle 2.4.1", ] @@ -1604,55 +1575,11 @@ dependencies = [ "zeroize", ] -[[package]] -name = "cxx" -version = "1.0.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97abf9f0eca9e52b7f81b945524e76710e6cb2366aead23b7d4fbf72e281f888" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cc32cc5fea1d894b77d269ddb9f192110069a8a9c1f1d441195fba90553dea3" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn 1.0.109", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca220e4794c934dc6b1207c3b42856ad4c302f2df1712e9f8d2eec5afaacf1f" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b846f081361125bfc8dc9d3940c84e1fd83ba54bbca7b17cd29483c828be0704" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "darling" -version = "0.14.2" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa" +checksum = "0558d22a7b463ed0241e993f76f09f30b126687447751a8638587b864e4b3944" dependencies = [ "darling_core", "darling_macro", @@ -1660,39 +1587,48 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.14.2" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f" +checksum = "ab8bfa2e259f8ee1ce5e97824a3c55ec4404a0d772ca7fa96bf19f0752a046eb" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] name = "darling_macro" -version = "0.14.2" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e" +checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a" dependencies = [ "darling_core", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] name = "data-encoding" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" +checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" [[package]] name = "der" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", +] + +[[package]] +name = "der" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7ed52955ce76b1554f509074bb357d3fb8ac9b51288a65a3fd480d1dfba946" dependencies = [ "const-oid", "zeroize", @@ -1753,16 +1689,17 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] name = "digest" -version = "0.10.7" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ - "block-buffer 0.10.3", + "block-buffer 0.10.4", + "const-oid", "crypto-common", "subtle 2.4.1", ] @@ -1816,7 +1753,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.15", ] [[package]] @@ -1833,9 +1770,9 @@ checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" [[package]] name = "dunce" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" [[package]] name = "dyn-clone" @@ -1849,7 +1786,7 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "add9a102807b524ec050363f09e06f1504214b0e1c7797f64261c891022dce8b" dependencies = [ - "bitflags 1.3.2", + "bitflags", "byteorder", "lazy_static", "proc-macro-error", @@ -1875,10 +1812,24 @@ version = "0.14.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" dependencies = [ - "der", - "elliptic-curve", - "rfc6979", - "signature", + "der 0.6.1", + "elliptic-curve 0.12.3", + "rfc6979 0.3.1", + "signature 1.6.4", +] + +[[package]] +name = "ecdsa" +version = "0.16.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0997c976637b606099b9985693efa3581e84e41f5c11ba5255f88711058ad428" +dependencies = [ + "der 0.7.7", + "digest 0.10.6", + "elliptic-curve 0.13.5", + "rfc6979 0.4.0", + "signature 2.1.0", + "spki", ] [[package]] @@ -1887,8 +1838,8 @@ version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" dependencies = [ - "serde 1.0.147", - "signature", + "serde 1.0.163", + "signature 1.6.4", ] [[package]] @@ -1900,7 +1851,7 @@ dependencies = [ "curve25519-dalek-ng", "hex", "rand_core 0.6.4", - "serde 1.0.147", + "serde 1.0.163", "sha2 0.9.9", "thiserror", "zeroize", @@ -1916,7 +1867,7 @@ dependencies = [ "ed25519", "merlin", "rand 0.7.3", - "serde 1.0.147", + "serde 1.0.163", "serde_bytes", "sha2 0.9.9", "zeroize", @@ -1936,9 +1887,9 @@ dependencies = [ [[package]] name = "either" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "elliptic-curve" @@ -1946,44 +1897,61 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" dependencies = [ - "base16ct", - "crypto-bigint", - "der", - "digest 0.10.7", - "ff", - "generic-array 0.14.6", - "group", + "base16ct 0.1.1", + "crypto-bigint 0.4.9", + "der 0.6.1", + "digest 0.10.6", + "ff 0.12.1", + "generic-array 0.14.7", + "group 0.12.1", + "rand_core 0.6.4", + "sec1 0.3.0", + "subtle 2.4.1", + "zeroize", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" +dependencies = [ + "base16ct 0.2.0", + "crypto-bigint 0.5.2", + "digest 0.10.6", + "ff 0.13.0", + "generic-array 0.14.7", + "group 0.13.0", "pkcs8", "rand_core 0.6.4", - "sec1", + "sec1 0.7.2", "subtle 2.4.1", "zeroize", ] [[package]] name = "encoding_rs" -version = "0.8.31" +version = "0.8.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" dependencies = [ "cfg-if 1.0.0", ] [[package]] name = "enr" -version = "0.7.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "492a7e5fc2504d5fdce8e124d3e263b244a68b283cac67a69eda0cd43e0aebad" +checksum = "cf56acd72bb22d2824e66ae8e9e5ada4d0de17a69c7fd35569dde2ada8ec9116" dependencies = [ "base64 0.13.1", - "bs58", "bytes", "hex", - "k256", + "k256 0.13.1", "log", "rand 0.8.5", "rlp", - "serde 1.0.147", + "serde 1.0.163", "sha3", "zeroize", ] @@ -2010,43 +1978,38 @@ dependencies = [ [[package]] name = "enumset" -version = "1.0.12" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19be8061a06ab6f3a6cf21106c873578bf01bd42ad15e0311a9c76161cb1c753" +checksum = "e875f1719c16de097dee81ed675e2d9bb63096823ed3f0ca827b7dea3028bbbb" dependencies = [ "enumset_derive", ] [[package]] name = "enumset_derive" -version = "0.6.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03e7b551eba279bf0fa88b83a46330168c1560a52a94f5126f892f0b364ab3e0" +checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" dependencies = [ "darling", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] -name = "erased-serde" -version = "0.3.25" +name = "equivalent" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2b0c2380453a92ea8b6c8e5f64ecaafccddde8ceab55ff7a8ac1029f894569" -dependencies = [ - "serde 1.0.147", -] +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] -name = "errno" -version = "0.2.8" +name = "erased-serde" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +checksum = "4f2b0c2380453a92ea8b6c8e5f64ecaafccddde8ceab55ff7a8ac1029f894569" dependencies = [ - "errno-dragonfly", - "libc", - "winapi", + "serde 1.0.163", ] [[package]] @@ -2087,7 +2050,7 @@ checksum = "f5584ba17d7ab26a8a7284f13e5bd196294dd2f2d79773cff29b9e9edef601a6" dependencies = [ "log", "once_cell", - "serde 1.0.147", + "serde 1.0.163", "serde_json", ] @@ -2097,15 +2060,15 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fda3bf123be441da5260717e0661c25a2fd9cb2b2c1d20bf2e05580047158ab" dependencies = [ - "aes 0.8.2", + "aes 0.8.3", "ctr", - "digest 0.10.7", + "digest 0.10.6", "hex", "hmac 0.12.1", "pbkdf2 0.11.0", "rand 0.8.5", "scrypt", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "sha2 0.10.6", "sha3", @@ -2123,7 +2086,7 @@ dependencies = [ "hex", "once_cell", "regex", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "sha3", "thiserror", @@ -2243,21 +2206,21 @@ dependencies = [ [[package]] name = "ethers-addressbook" -version = "2.0.0" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1e010165c08a2a3fa43c0bb8bc9d596f079a021aaa2cc4e8d921df09709c95" +checksum = "7b856b7b8ff5c961093cb8efe151fbcce724b451941ce20781de11a531ccd578" dependencies = [ "ethers-core", "once_cell", - "serde 1.0.147", + "serde 1.0.163", "serde_json", ] [[package]] name = "ethers-contract" -version = "2.0.0" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be33fd47a06cc8f97caf614cf7cf91af9dd6dcd767511578895fa884b430c4b8" +checksum = "e066a0d9cfc70c454672bf16bb433b0243427420076dc5b2f49c448fb5a10628" dependencies = [ "ethers-contract-abigen", "ethers-contract-derive", @@ -2267,83 +2230,73 @@ dependencies = [ "hex", "once_cell", "pin-project", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "thiserror", ] [[package]] name = "ethers-contract-abigen" -version = "2.0.0" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60d9f9ecb4a18c1693de954404b66e0c9df31dac055b411645e38c4efebf3dbc" +checksum = "c113e3e86b6bc16d98484b2c3bb2d01d6fed9f489fe2e592e5cc87c3024d616b" dependencies = [ "Inflector", - "cfg-if 1.0.0", "dunce", "ethers-core", - "ethers-etherscan", "eyre", - "getrandom 0.2.8", "hex", - "prettyplease 0.1.24", + "prettyplease 0.2.4", "proc-macro2", "quote", "regex", - "reqwest", - "serde 1.0.147", + "serde 1.0.163", "serde_json", - "syn 1.0.109", - "tokio", - "toml", - "url", + "syn 2.0.15", + "toml 0.7.6", "walkdir", ] [[package]] name = "ethers-contract-derive" -version = "2.0.0" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "001b33443a67e273120923df18bab907a0744ad4b5fef681a8b0691f2ee0f3de" +checksum = "8c3fb5adee25701c79ec58fcf2c63594cd8829bc9ad6037ff862d5a111101ed2" dependencies = [ + "Inflector", "ethers-contract-abigen", "ethers-core", - "eyre", "hex", "proc-macro2", "quote", "serde_json", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] name = "ethers-core" -version = "2.0.0" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5925cba515ac18eb5c798ddf6069cc33ae00916cb08ae64194364a1b35c100b" +checksum = "6da5fa198af0d3be20c19192df2bd9590b92ce09a8421e793bec8851270f1b05" dependencies = [ "arrayvec 0.7.2", "bytes", - "cargo_metadata 0.15.3", + "cargo_metadata 0.15.4", "chrono", - "convert_case", - "elliptic-curve", + "elliptic-curve 0.13.5", "ethabi", - "generic-array 0.14.6", - "getrandom 0.2.8", + "generic-array 0.14.7", "hex", - "k256", + "k256 0.13.1", "num_enum", "once_cell", "open-fastrlp", - "proc-macro2", "rand 0.8.5", "rlp", - "rlp-derive", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "strum", - "syn 1.0.109", + "syn 2.0.15", "tempfile", "thiserror", "tiny-keccak", @@ -2352,16 +2305,14 @@ dependencies = [ [[package]] name = "ethers-etherscan" -version = "2.0.0" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d769437fafd0b47ea8b95e774e343c5195c77423f0f54b48d11c0d9ed2148ad" +checksum = "84ebb401ba97c6f5af278c2c9936c4546cad75dec464b439ae6df249906f4caa" dependencies = [ "ethers-core", - "getrandom 0.2.8", "reqwest", "semver 1.0.17", - "serde 1.0.147", - "serde-aux", + "serde 1.0.163", "serde_json", "thiserror", "tracing 0.1.37", @@ -2369,9 +2320,9 @@ dependencies = [ [[package]] name = "ethers-middleware" -version = "2.0.0" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7dd311b76eab9d15209e4fd16bb419e25543709cbdf33079e8923dfa597517c" +checksum = "740f4a773c19dd6d6a68c8c2e0996c096488d38997d524e21dc612c55da3bd24" dependencies = [ "async-trait", "auto_impl", @@ -2380,11 +2331,12 @@ dependencies = [ "ethers-etherscan", "ethers-providers", "ethers-signers", + "futures-channel", "futures-locks", "futures-util", "instant", "reqwest", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "thiserror", "tokio", @@ -2395,27 +2347,27 @@ dependencies = [ [[package]] name = "ethers-providers" -version = "2.0.0" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed7174af93619e81844d3d49887106a3721e5caecdf306e0b824bfb4316db3be" +checksum = "56b498fd2a6c019d023e43e83488cd1fb0721f299055975aa6bac8dbf1e95f2c" dependencies = [ "async-trait", "auto_impl", "base64 0.21.0", + "bytes", "enr", "ethers-core", "futures-core", "futures-timer", "futures-util", - "getrandom 0.2.8", "hashers", "hex", "http", + "instant", "once_cell", - "parking_lot 0.11.2", "pin-project", "reqwest", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "thiserror", "tokio", @@ -2424,21 +2376,20 @@ dependencies = [ "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-timer", "web-sys", "ws_stream_wasm", ] [[package]] name = "ethers-signers" -version = "2.0.0" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d45ff294473124fd5bb96be56516ace179eef0eaec5b281f68c953ddea1a8bf" +checksum = "02c4b7e15f212fa7cc2e1251868320221d4ff77a3d48068e69f47ce1c491df2d" dependencies = [ "async-trait", "coins-bip32", "coins-bip39", - "elliptic-curve", + "elliptic-curve 0.13.5", "eth-keystore", "ethers-core", "hex", @@ -2455,7 +2406,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b36f34b0325008d05b0e9be8361bfa8a0fb905f10de0d951c2621c59e811cb91" dependencies = [ "conpty", - "nix 0.26.2", + "nix", "ptyprocess", "regex", ] @@ -2484,9 +2435,9 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" [[package]] name = "fastrand" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] @@ -2508,7 +2459,7 @@ dependencies = [ "blake2", "blake2b_simd", "borsh", - "digest 0.10.7", + "digest 0.10.6", "ed25519-dalek", "either", "ferveo-common", @@ -2520,7 +2471,7 @@ dependencies = [ "num 0.4.0", "rand 0.7.3", "rand 0.8.5", - "serde 1.0.147", + "serde 1.0.163", "serde_bytes", "serde_json", "subproductdomain", @@ -2537,7 +2488,7 @@ dependencies = [ "ark-ec", "ark-serialize", "ark-std", - "serde 1.0.147", + "serde 1.0.163", "serde_bytes", ] @@ -2552,23 +2503,31 @@ dependencies = [ "subtle 2.4.1", ] +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle 2.4.1", +] + [[package]] name = "file-lock" -version = "2.1.6" +version = "2.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0815fc2a1924e651b71ae6d13df07b356a671a09ecaf857dbd344a2ba937a496" +checksum = "f59be9010c5418713a48aac4c1b897d85dafd958055683dc31bdae553536647b" dependencies = [ "cc", "libc", - "mktemp", - "nix 0.24.2", ] [[package]] name = "file-serve" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e43addbb09a5dcb5609cb44a01a79e67716fe40b50c109f50112ef201a8c7c59" +checksum = "547ebf393d987692a02b5d2be1c0b398b16a5b185c23a047c1d3fc3050d6d803" dependencies = [ "log", "mime_guess", @@ -2577,14 +2536,14 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.18" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b9663d381d07ae25dc88dbdf27df458faa83a9b25336bcac83d5e452b5fc9d3" +checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", - "windows-sys 0.42.0", + "redox_syscall 0.2.16", + "windows-sys 0.48.0", ] [[package]] @@ -2607,12 +2566,12 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.24" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.7.1", ] [[package]] @@ -2671,9 +2630,9 @@ dependencies = [ [[package]] name = "fs_extra" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "funty" @@ -2747,7 +2706,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.15", ] [[package]] @@ -2767,6 +2726,10 @@ name = "futures-timer" version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" +dependencies = [ + "gloo-timers", + "send_wrapper 0.4.0", +] [[package]] name = "futures-util" @@ -2806,12 +2769,13 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -2829,9 +2793,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -2847,17 +2811,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" dependencies = [ "fallible-iterator", - "indexmap", + "indexmap 1.9.3", "stable_deref_trait", ] +[[package]] +name = "gimli" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" + [[package]] name = "git2" version = "0.13.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f29229cc1b24c0e6062f6e742aa3e256492a5323365e5ed3413599f8a5eff7d6" dependencies = [ - "bitflags 1.3.2", + "bitflags", "libc", "libgit2-sys", "log", @@ -2868,9 +2838,21 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.0" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "gloo-timers" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] [[package]] name = "group" @@ -2878,17 +2860,28 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" dependencies = [ - "ff", + "ff 0.12.1", "memuse", "rand_core 0.6.4", "subtle 2.4.1", ] [[package]] -name = "group-threshold-cryptography" -version = "0.1.0" -source = "git+https://github.com/anoma/ferveo?rev=e5abd0acc938da90140351a65a26472eb495ce4d#e5abd0acc938da90140351a65a26472eb495ce4d" -dependencies = [ +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff 0.13.0", + "rand_core 0.6.4", + "subtle 2.4.1", +] + +[[package]] +name = "group-threshold-cryptography" +version = "0.1.0" +source = "git+https://github.com/anoma/ferveo?rev=e5abd0acc938da90140351a65a26472eb495ce4d#e5abd0acc938da90140351a65a26472eb495ce4d" +dependencies = [ "anyhow", "ark-bls12-381", "ark-ec", @@ -2930,9 +2923,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.15" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" dependencies = [ "bytes", "fnv", @@ -2940,10 +2933,10 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", - "tokio-util 0.7.4", + "tokio-util 0.7.8", "tracing 0.1.37", ] @@ -2971,6 +2964,12 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + [[package]] name = "hashers" version = "1.0.1" @@ -2982,9 +2981,9 @@ dependencies = [ [[package]] name = "hdpath" -version = "0.6.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dafb09e5d85df264339ad786a147d9de1da13687a3697c52244297e5e7c32d9c" +checksum = "dfa5bc9db2c17d2660f53ce217b778d06d68de13d1cd01c0f4e5de4b7918935f" dependencies = [ "byteorder", ] @@ -3006,7 +3005,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" dependencies = [ "base64 0.13.1", - "bitflags 1.3.2", + "bitflags", "bytes", "headers-core", "http", @@ -3026,24 +3025,24 @@ dependencies = [ [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.1.19" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" dependencies = [ "libc", ] [[package]] name = "hermit-abi" -version = "0.3.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" [[package]] name = "hex" @@ -3087,7 +3086,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.7", + "digest 0.10.6", ] [[package]] @@ -3108,7 +3107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" dependencies = [ "digest 0.9.0", - "generic-array 0.14.6", + "generic-array 0.14.7", "hmac 0.8.1", ] @@ -3120,9 +3119,9 @@ checksum = "77e806677ce663d0a199541030c816847b36e8dc095f70dae4a4f4ad63da5383" [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -3140,12 +3139,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "http-range-header" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" - [[package]] name = "httparse" version = "1.8.0" @@ -3171,14 +3164,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" dependencies = [ "humantime", - "serde 1.0.147", + "serde 1.0.163", ] [[package]] name = "hyper" -version = "0.14.23" +version = "0.14.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" dependencies = [ "bytes", "futures-channel", @@ -3209,7 +3202,7 @@ dependencies = [ "headers", "http", "hyper", - "hyper-rustls 0.22.1", + "hyper-rustls", "rustls-native-certs 0.5.0", "tokio", "tokio-rustls 0.22.0", @@ -3235,19 +3228,6 @@ dependencies = [ "webpki-roots 0.21.1", ] -[[package]] -name = "hyper-rustls" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" -dependencies = [ - "http", - "hyper", - "rustls 0.20.7", - "tokio", - "tokio-rustls 0.23.4", -] - [[package]] name = "hyper-timeout" version = "0.4.1" @@ -3275,26 +3255,25 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.53" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "winapi", + "windows 0.48.0", ] [[package]] name = "iana-time-zone-haiku" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "cxx", - "cxx-build", + "cc", ] [[package]] @@ -3311,11 +3290,11 @@ dependencies = [ "ibc-proto", "ics23", "num-traits 0.2.15", - "parking_lot 0.12.1", + "parking_lot", "primitive-types", "prost", "safe-regex", - "serde 1.0.147", + "serde 1.0.163", "serde_derive", "serde_json", "sha2 0.10.6", @@ -3338,7 +3317,7 @@ dependencies = [ "bytes", "flex-error", "prost", - "serde 1.0.147", + "serde 1.0.163", "subtle-encoding", "tendermint-proto", "tonic", @@ -3355,15 +3334,15 @@ dependencies = [ "bitcoin", "bs58", "bytes", - "crossbeam-channel 0.5.6", - "digest 0.10.7", + "crossbeam-channel 0.5.8", + "digest 0.10.6", "dirs-next", "ed25519", "ed25519-dalek", "ed25519-dalek-bip32", "flex-error", "futures", - "generic-array 0.14.6", + "generic-array 0.14.7", "hdpath", "hex", "http", @@ -3381,11 +3360,11 @@ dependencies = [ "ripemd", "secp256k1", "semver 1.0.17", - "serde 1.0.147", + "serde 1.0.163", "serde_derive", "serde_json", "sha2 0.10.6", - "signature", + "signature 1.6.4", "strum", "subtle-encoding", "tendermint", @@ -3397,10 +3376,10 @@ dependencies = [ "tiny-bip39 1.0.0", "tiny-keccak", "tokio", - "toml", + "toml 0.5.9", "tonic", "tracing 0.1.37", - "uuid 1.2.1", + "uuid 1.3.2", ] [[package]] @@ -3420,7 +3399,7 @@ dependencies = [ "primitive-types", "prost", "safe-regex", - "serde 1.0.147", + "serde 1.0.163", "serde_derive", "serde_json", "subtle-encoding", @@ -3499,7 +3478,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" dependencies = [ - "serde 1.0.147", + "serde 1.0.163", ] [[package]] @@ -3519,7 +3498,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5ad43a3f5795945459d577f6589cf62a476e92c79b75e70cd954364e14ce17b" dependencies = [ - "serde 1.0.147", + "serde 1.0.163", ] [[package]] @@ -3534,18 +3513,28 @@ version = "0.7.1" source = "git+https://github.com/heliaxdev/index-set?tag=v0.7.1#dc24cdbbe3664514d59f1a4c4031863fc565f1c2" dependencies = [ "borsh", - "serde 1.0.147", + "serde 1.0.163", ] [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", - "serde 1.0.147", + "serde 1.0.163", +] + +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", ] [[package]] @@ -3554,7 +3543,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -3589,28 +3578,30 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.4" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" dependencies = [ + "hermit-abi 0.3.1", "libc", - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] name = "ipnet" -version = "2.5.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f88c5561171189e69df9d98bcf18fd5f9558300f7ea7b801eb8a0fd748bd8745" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" [[package]] name = "is-terminal" -version = "0.4.8" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24fddda5af7e54bf7da53067d6e802dbcc381d0a8eef629df528e3ebf68755cb" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ - "hermit-abi 0.3.2", - "rustix 0.38.3", + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", "windows-sys 0.48.0", ] @@ -3625,24 +3616,24 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "jobserver" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "68c16e1bfd491478ab155fd8b4896b86f9ede344949b641e61501e07c2b8b4d5" dependencies = [ "wasm-bindgen", ] @@ -3655,8 +3646,8 @@ checksum = "a575df5f985fe1cd5b2b05664ff6accfc46559032b954529fd225a2168d27b0f" dependencies = [ "bitvec 1.0.1", "bls12_381", - "ff", - "group", + "ff 0.12.1", + "group 0.12.1", "rand_core 0.6.4", "subtle 2.4.1", ] @@ -3668,17 +3659,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" dependencies = [ "cfg-if 1.0.0", - "ecdsa", - "elliptic-curve", + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", "sha2 0.10.6", - "sha3", +] + +[[package]] +name = "k256" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +dependencies = [ + "cfg-if 1.0.0", + "ecdsa 0.16.7", + "elliptic-curve 0.13.5", + "once_cell", + "sha2 0.10.6", + "signature 2.1.0", ] [[package]] name = "keccak" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9b7d56ba4a8344d6be9729995e6b06f928af29998cdf79fe390cbf6b1fee838" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] [[package]] name = "lazy_static" @@ -3705,7 +3712,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" dependencies = [ "arrayvec 0.5.2", - "bitflags 1.3.2", + "bitflags", "cfg-if 1.0.0", "ryu", "static_assertions", @@ -3713,9 +3720,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.147" +version = "0.2.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" [[package]] name = "libgit2-sys" @@ -3792,7 +3799,7 @@ dependencies = [ "libsecp256k1-gen-ecmult", "libsecp256k1-gen-genmult", "rand 0.8.5", - "serde 1.0.147", + "serde 1.0.163", "sha2 0.9.9", "typenum", ] @@ -3839,9 +3846,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" +checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db" dependencies = [ "cc", "libc", @@ -3849,35 +3856,20 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "link-cplusplus" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" -dependencies = [ - "cc", -] - [[package]] name = "linked-hash-map" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" dependencies = [ - "serde 1.0.147", + "serde 1.0.163", ] [[package]] name = "linux-raw-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" - -[[package]] -name = "linux-raw-sys" -version = "0.4.3" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" +checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" [[package]] name = "lock_api" @@ -3891,12 +3883,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if 1.0.0", -] +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "loupe" @@ -3904,7 +3893,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b6a72dfa44fe15b5e76b94307eeb2ff995a8c5b283b55008940c02e0c5b634d" dependencies = [ - "indexmap", + "indexmap 1.9.3", "loupe-derive", "rustversion", ] @@ -3928,16 +3917,25 @@ dependencies = [ "libc", ] +[[package]] +name = "mach2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d0d1830bcd151a6fc4aea1369af235b36c1528fe976b8ff678683c9995eade8" +dependencies = [ + "libc", +] + [[package]] name = "madato" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c36112680b23ee7a5d2a10e56b59a74e4028963e9080b8abec4e69d0d773dc06" dependencies = [ - "indexmap", + "indexmap 1.9.3", "linked-hash-map", "regex", - "serde 1.0.147", + "serde 1.0.163", "serde_derive", "serde_yaml", ] @@ -3968,9 +3966,9 @@ dependencies = [ "bls12_381", "borsh", "byteorder", - "ff", + "ff 0.12.1", "fpe", - "group", + "group 0.12.1", "hex", "incrementalmerkletree", "jubjub", @@ -3994,8 +3992,8 @@ dependencies = [ "blake2b_simd", "bls12_381", "directories", - "getrandom 0.2.8", - "group", + "getrandom 0.2.9", + "group 0.12.1", "itertools", "jubjub", "lazy_static", @@ -4046,9 +4044,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memmap2" -version = "0.5.8" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" dependencies = [ "libc", ] @@ -4071,6 +4069,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + [[package]] name = "memuse" version = "0.2.1" @@ -4097,9 +4104,9 @@ dependencies = [ [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mime_guess" @@ -4119,36 +4126,45 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.5.4" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", ] [[package]] name = "minreq" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c785bc6027fd359756e538541c8624012ba3776d3d3fe123885643092ed4132" +checksum = "cb6c6973f78ef55d0e5fc04fdb8f9ad67c87c9e86bca0ff77b6a3102b0eb36b7" dependencies = [ - "lazy_static", "log", - "rustls 0.20.7", + "once_cell", + "rustls 0.20.8", "webpki 0.22.0", - "webpki-roots 0.22.5", + "webpki-roots 0.22.6", ] [[package]] name = "mio" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -4157,27 +4173,18 @@ 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" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b9268097a2cf211ac9955b1cc95e80fa84fff5c2d13ba292916445dc8a311f" +checksum = "19ca9b167ed904bc89a2f64c4be5014615c26fd9c4ddd2042c6094744c7df11a" dependencies = [ - "crossbeam-channel 0.5.6", + "crossbeam-channel 0.5.8", "crossbeam-epoch", - "crossbeam-utils 0.8.12", + "crossbeam-utils 0.8.15", "num_cpus", "once_cell", - "parking_lot 0.12.1", + "parking_lot", "quanta", "rustc_version 0.4.0", "scheduled-thread-pool", @@ -4186,7 +4193,7 @@ dependencies = [ "tagptr", "thiserror", "triomphe", - "uuid 1.2.1", + "uuid 1.3.2", ] [[package]] @@ -4196,29 +4203,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" [[package]] -name = "multimap" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" - -[[package]] -name = "multipart" -version = "0.18.0" +name = "multer" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182" +checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2" dependencies = [ - "buf_redux", + "bytes", + "encoding_rs", + "futures-util", + "http", "httparse", "log", + "memchr", "mime", - "mime_guess", - "quick-error", - "rand 0.8.5", - "safemem", - "tempfile", - "twoway", + "spin 0.9.8", + "version_check", ] +[[package]] +name = "multimap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + [[package]] name = "namada" version = "0.19.0" @@ -4261,7 +4268,7 @@ dependencies = [ "rand_core 0.6.4", "rayon", "ripemd", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "sha2 0.9.9", "slip10_ed25519", @@ -4272,9 +4279,9 @@ dependencies = [ "tiny-bip39 0.8.2", "tiny-hderive", "tokio", - "toml", + "toml 0.5.9", "tracing 0.1.37", - "tracing-subscriber 0.3.16", + "tracing-subscriber 0.3.17", "wasm-instrument", "wasmer", "wasmer-cache", @@ -4323,6 +4330,7 @@ dependencies = [ "futures", "git2", "itertools", + "lazy_static", "libc", "libloading", "masp_primitives", @@ -4348,7 +4356,7 @@ dependencies = [ "rlimit", "rocksdb", "rpassword", - "serde 1.0.147", + "serde 1.0.163", "serde_bytes", "serde_json", "sha2 0.9.9", @@ -4362,14 +4370,14 @@ dependencies = [ "thiserror", "tokio", "tokio-test", - "toml", + "toml 0.5.9", "tonic", "tower", "tower-abci", "tracing 0.1.37", "tracing-appender", "tracing-log", - "tracing-subscriber 0.3.16", + "tracing-subscriber 0.3.17", "warp", "winapi", "zeroize", @@ -4415,7 +4423,7 @@ dependencies = [ "rand 0.8.5", "rand_core 0.6.4", "rayon", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "sha2 0.9.9", "sparse-merkle-tree", @@ -4424,10 +4432,10 @@ dependencies = [ "test-log", "thiserror", "tiny-keccak", - "toml", + "toml 0.5.9", "tonic-build", "tracing 0.1.37", - "tracing-subscriber 0.3.16", + "tracing-subscriber 0.3.17", "uint", "zeroize", ] @@ -4458,12 +4466,12 @@ dependencies = [ "namada_macros", "namada_proof_of_stake", "rand 0.8.5", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "tendermint", "tendermint-proto", "tendermint-rpc", - "toml", + "toml 0.5.9", "tracing 0.1.37", ] @@ -4491,7 +4499,7 @@ dependencies = [ "test-log", "thiserror", "tracing 0.1.37", - "tracing-subscriber 0.3.16", + "tracing-subscriber 0.3.17", ] [[package]] @@ -4508,8 +4516,10 @@ name = "namada_tests" version = "0.19.0" dependencies = [ "assert_cmd", + "async-trait", "borsh", "chrono", + "clap", "color-eyre", "concat-idents", "data-encoding", @@ -4523,12 +4533,14 @@ dependencies = [ "ibc-relayer", "ibc-relayer-types", "itertools", + "lazy_static", "namada", "namada_apps", "namada_core", "namada_test_utils", "namada_tx_prelude", "namada_vp_prelude", + "num-traits 0.2.15", "once_cell", "pretty_assertions", "proptest", @@ -4541,9 +4553,9 @@ dependencies = [ "tempfile", "test-log", "tokio", - "toml", + "toml 0.5.9", "tracing 0.1.37", - "tracing-subscriber 0.3.16", + "tracing-subscriber 0.3.17", ] [[package]] @@ -4600,24 +4612,13 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" dependencies = [ - "bitflags 1.3.2", + "bitflags", "cfg-if 1.0.0", "libc", "memoffset 0.7.1", @@ -4627,9 +4628,9 @@ dependencies = [ [[package]] name = "nom" -version = "5.1.2" +version = "5.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" +checksum = "08959a387a676302eebf4ddbcbc611da04285579f76f88ee0506c63b1a61dd4b" dependencies = [ "lexical-core", "memchr", @@ -4638,9 +4639,9 @@ dependencies = [ [[package]] name = "nom" -version = "7.1.1" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", @@ -4692,7 +4693,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" dependencies = [ "num-bigint 0.4.3", - "num-complex 0.4.2", + "num-complex 0.4.3", "num-integer", "num-iter", "num-rational 0.4.1", @@ -4719,7 +4720,7 @@ dependencies = [ "autocfg", "num-integer", "num-traits 0.2.15", - "serde 1.0.147", + "serde 1.0.163", ] [[package]] @@ -4734,9 +4735,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19" +checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" dependencies = [ "num-traits 0.2.15", ] @@ -4795,7 +4796,7 @@ dependencies = [ "num-bigint 0.4.3", "num-integer", "num-traits 0.2.15", - "serde 1.0.147", + "serde 1.0.163", ] [[package]] @@ -4827,39 +4828,39 @@ dependencies = [ "num 0.4.0", "num-derive", "num-traits 0.2.15", - "serde 1.0.147", + "serde 1.0.163", "serde_derive", ] [[package]] name = "num_cpus" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi 0.1.19", + "hermit-abi 0.2.6", "libc", ] [[package]] name = "num_enum" -version = "0.5.11" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.11" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ - "proc-macro-crate 1.2.1", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] @@ -4870,15 +4871,15 @@ checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" dependencies = [ "crc32fast", "hashbrown 0.11.2", - "indexmap", + "indexmap 1.9.3", "memchr", ] [[package]] name = "object" -version = "0.29.0" +version = "0.30.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" dependencies = [ "memchr", ] @@ -4928,11 +4929,11 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.42" +version = "0.10.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" +checksum = "01b8574602df80f7b85fdfc5392fa884a4e3b3f4f35402c070ab34c3d3f78d56" dependencies = [ - "bitflags 1.3.2", + "bitflags", "cfg-if 1.0.0", "foreign-types", "libc", @@ -4943,13 +4944,13 @@ dependencies = [ [[package]] name = "openssl-macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] @@ -4960,11 +4961,10 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.77" +version = "0.9.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03b84c3b2d099b81f0953422b4d4ad58761589d0229b5506356afca05a3670a" +checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e" dependencies = [ - "autocfg", "cc", "libc", "pkg-config", @@ -4978,7 +4978,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6624905ddd92e460ff0685567539ed1ac985b2dee4c92c7edcd64fce905b00c" dependencies = [ "ct-codecs", - "getrandom 0.2.8", + "getrandom 0.2.9", "subtle 2.4.1", "zeroize", ] @@ -5016,30 +5016,30 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "135590d8bdba2b31346f9cd1fb2a912329f5135e832a4f422942eb6ead8b6b3b" dependencies = [ - "group", + "group 0.12.1", ] [[package]] name = "parity-scale-codec" -version = "3.2.1" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "366e44391a8af4cfd6002ef6ba072bae071a96aafca98d7d448a34c5dca38b6a" +checksum = "5ddb756ca205bd108aee3c62c6d3c994e1df84a59b9d6d4a5ea42ee1fd5a9a28" dependencies = [ "arrayvec 0.7.2", "bitvec 1.0.1", "byte-slice-cast", "impl-trait-for-tuples", "parity-scale-codec-derive", - "serde 1.0.147", + "serde 1.0.163", ] [[package]] name = "parity-scale-codec-derive" -version = "3.1.3" +version = "3.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9299338969a3d2f491d65f140b00ddec470858402f888af98e8642fb5e8965cd" +checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" dependencies = [ - "proc-macro-crate 1.2.1", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 1.0.109", @@ -5051,17 +5051,6 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - [[package]] name = "parking_lot" version = "0.12.1" @@ -5069,34 +5058,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.4", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if 1.0.0", - "instant", - "libc", - "redox_syscall", - "smallvec", - "winapi", + "parking_lot_core", ] [[package]] name = "parking_lot_core" -version = "0.9.4" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -5121,22 +5096,11 @@ dependencies = [ "subtle 2.4.1", ] -[[package]] -name = "password-hash" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" -dependencies = [ - "base64ct", - "rand_core 0.6.4", - "subtle 2.4.1", -] - [[package]] name = "paste" -version = "1.0.9" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" [[package]] name = "pbkdf2" @@ -5154,7 +5118,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f05894bce6a1ba4be299d0c5f29563e08af2bc18bb7d48313113bed71e904739" dependencies = [ "crypto-mac 0.11.1", - "password-hash 0.3.2", + "password-hash", ] [[package]] @@ -5163,10 +5127,17 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ - "digest 0.10.7", + "digest 0.10.6", +] + +[[package]] +name = "pbkdf2" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0ca0b5a68607598bf3bad68f32227a8164f6254833f84eafaac409cd6746c31" +dependencies = [ + "digest 0.10.6", "hmac 0.12.1", - "password-hash 0.4.2", - "sha2 0.10.6", ] [[package]] @@ -5210,21 +5181,22 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.1.3" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +checksum = "e68e84bfb01f0507134eac1e9b410a12ba379d064eab48c50ba4ce329a527b70" dependencies = [ + "thiserror", "ucd-trie", ] [[package]] name = "petgraph" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" +checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" dependencies = [ "fixedbitset", - "indexmap", + "indexmap 1.9.3", ] [[package]] @@ -5239,22 +5211,22 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.12" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.12" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] @@ -5271,19 +5243,19 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkcs8" -version = "0.9.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der", + "der 0.7.7", "spki", ] [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "poly1305" @@ -5304,9 +5276,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "predicates" -version = "2.1.3" +version = "2.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6bd09a7f7e68f3f0bf710fb7ab9c4615a488b58b5f653382a687701e458c92" +checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" dependencies = [ "difflib", "itertools", @@ -5315,15 +5287,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f883590242d3c6fc5bf50299011695fa6590c2c70eac95ee1bdb9a733ad1a2" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" [[package]] name = "predicates-tree" -version = "1.0.7" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54ff541861505aabf6ea722d2131ee980b8276e10a1297b94e896dd8b621850d" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" dependencies = [ "predicates-core", "termtree", @@ -5343,9 +5315,9 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebcd279d20a4a0a2404a33056388e950504d891c855c7975b9a8fef75f3bf04" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" dependencies = [ "proc-macro2", "syn 1.0.109", @@ -5353,12 +5325,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.6" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b69d39aab54d069e7f2fe8cb970493e7834601ca2d8c65fd7bbd183578080d1" +checksum = "1ceca8aaf45b5c46ec7ed39fff75f57290368c1846d33d24a122ca81416ab058" dependencies = [ "proc-macro2", - "syn 2.0.16", + "syn 2.0.15", ] [[package]] @@ -5381,18 +5353,17 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" dependencies = [ - "toml", + "toml 0.5.9", ] [[package]] name = "proc-macro-crate" -version = "1.2.1" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "thiserror", - "toml", + "toml_edit", ] [[package]] @@ -5421,9 +5392,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.58" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] @@ -5435,14 +5406,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" dependencies = [ "bit-set", - "bitflags 1.3.2", + "bitflags", "byteorder", "lazy_static", "num-traits 0.2.15", "rand 0.8.5", "rand_chacha 0.3.1", "rand_xorshift", - "regex-syntax 0.6.28", + "regex-syntax 0.6.29", "rusty-fork", "tempfile", "unarray", @@ -5480,7 +5451,7 @@ dependencies = [ "log", "multimap", "petgraph", - "prettyplease 0.1.24", + "prettyplease 0.1.25", "prost", "prost-types", "regex", @@ -5537,7 +5508,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e05aef7befb11a210468a2d77d978dde2c6381a0381e33beb575e91f57fe8cf" dependencies = [ - "nix 0.26.2", + "nix", ] [[package]] @@ -5546,23 +5517,23 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34f197a544b0c9ab3ae46c359a7ec9cbbb5c7bf97054266fecb7ead794a181d6" dependencies = [ - "bitflags 1.3.2", + "bitflags", "memchr", "unicase", ] [[package]] name = "quanta" -version = "0.10.1" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e31331286705f455e56cca62e0e717158474ff02b7936c1fa596d983f4ae27" +checksum = "a17e662a7a8291a865152364c20c7abc5e60486ab2001e8ec10b24862de0b9ab" dependencies = [ - "crossbeam-utils 0.8.12", + "crossbeam-utils 0.8.15", "libc", - "mach", + "mach2", "once_cell", "raw-cpuid", - "wasi 0.10.2+wasi-snapshot-preview1", + "wasi 0.11.0+wasi-snapshot-preview1", "web-sys", "winapi", ] @@ -5653,7 +5624,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.8", + "getrandom 0.2.9", ] [[package]] @@ -5676,11 +5647,11 @@ dependencies = [ [[package]] name = "raw-cpuid" -version = "10.6.0" +version = "10.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6823ea29436221176fe662da99998ad3b4db2c7f31e7b6f5fe43adccd6320bb" +checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" dependencies = [ - "bitflags 1.3.2", + "bitflags", ] [[package]] @@ -5697,13 +5668,13 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.9.3" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" dependencies = [ - "crossbeam-channel 0.5.6", + "crossbeam-channel 0.5.8", "crossbeam-deque", - "crossbeam-utils 0.8.12", + "crossbeam-utils 0.8.15", "num_cpus", ] @@ -5718,7 +5689,7 @@ dependencies = [ "digest 0.9.0", "jubjub", "rand_core 0.6.4", - "serde 1.0.147", + "serde 1.0.163", "thiserror", "zeroize", ] @@ -5729,7 +5700,16 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags 1.3.2", + "bitflags", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", ] [[package]] @@ -5738,8 +5718,8 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.8", - "redox_syscall", + "getrandom 0.2.9", + "redox_syscall 0.2.16", "thiserror", ] @@ -5771,14 +5751,14 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "regex-syntax 0.6.28", + "regex-syntax 0.6.29", ] [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" @@ -5792,7 +5772,7 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" dependencies = [ - "bitflags 1.3.2", + "bitflags", "libc", "mach", "winapi", @@ -5800,18 +5780,18 @@ dependencies = [ [[package]] name = "rend" -version = "0.3.6" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79af64b4b6362ffba04eef3a4e10829718a4896dac19daa741851c86781edf95" +checksum = "581008d2099240d37fb08d77ad713bcaec2c4d89d50b5b21a8bb1996bbab68ab" dependencies = [ "bytecheck", ] [[package]] name = "reqwest" -version = "0.11.14" +version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" dependencies = [ "base64 0.21.0", "bytes", @@ -5822,7 +5802,6 @@ dependencies = [ "http", "http-body", "hyper", - "hyper-rustls 0.23.2", "hyper-tls", "ipnet", "js-sys", @@ -5832,20 +5811,16 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.20.7", - "rustls-pemfile 1.0.2", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "serde_urlencoded", "tokio", "tokio-native-tls", - "tokio-rustls 0.23.4", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 0.22.5", "winreg", ] @@ -5861,11 +5836,21 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" dependencies = [ - "crypto-bigint", + "crypto-bigint 0.4.9", "hmac 0.12.1", "zeroize", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac 0.12.1", + "subtle 2.4.1", +] + [[package]] name = "ring" version = "0.16.20" @@ -5875,7 +5860,7 @@ dependencies = [ "cc", "libc", "once_cell", - "spin", + "spin 0.5.2", "untrusted", "web-sys", "winapi", @@ -5887,7 +5872,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" dependencies = [ - "digest 0.10.7", + "digest 0.10.6", ] [[package]] @@ -5903,23 +5888,26 @@ dependencies = [ [[package]] name = "rkyv" -version = "0.7.39" +version = "0.7.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cec2b3485b07d96ddfd3134767b8a447b45ea4eb91448d0a35180ec0ffd5ed15" +checksum = "0200c8230b013893c0b2d6213d6ec64ed2b9be2e0e016682b7224ff82cff5c58" dependencies = [ + "bitvec 1.0.1", "bytecheck", "hashbrown 0.12.3", "ptr_meta", "rend", "rkyv_derive", "seahash", + "tinyvec", + "uuid 1.3.2", ] [[package]] name = "rkyv_derive" -version = "0.7.39" +version = "0.7.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eaedadc88b53e36dd32d940ed21ae4d850d5916f2581526921f553a72ac34c4" +checksum = "b2e06b915b5c230a17d7a736d1e2e63ee753c256a8614ef3f5147b13a4f5541d" dependencies = [ "proc-macro2", "quote", @@ -5942,6 +5930,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" dependencies = [ "bytes", + "rlp-derive", "rustc-hex", ] @@ -5984,9 +5973,9 @@ checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" [[package]] name = "rustc-demangle" -version = "0.1.21" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc-hash" @@ -6020,28 +6009,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.5" +version = "0.37.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588" +checksum = "f79bef90eb6d984c72722595b5b1348ab39275a5e5123faca6863bf07d75a4e0" dependencies = [ - "bitflags 1.3.2", - "errno 0.2.8", + "bitflags", + "errno", "io-lifetimes", "libc", - "linux-raw-sys 0.1.4", - "windows-sys 0.42.0", -] - -[[package]] -name = "rustix" -version = "0.38.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac5ffa1efe7548069688cd7028f32591853cd7b5b756d41bcffd2353e4fc75b4" -dependencies = [ - "bitflags 2.3.3", - "errno 0.3.1", - "libc", - "linux-raw-sys 0.4.3", + "linux-raw-sys", "windows-sys 0.48.0", ] @@ -6060,9 +6036,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.20.7" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" dependencies = [ "log", "ring", @@ -6084,39 +6060,30 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", - "rustls-pemfile 1.0.2", + "rustls-pemfile", "schannel", "security-framework", ] [[package]] name = "rustls-pemfile" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" -dependencies = [ - "base64 0.13.1", -] - -[[package]] -name = "rustls-pemfile" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ "base64 0.21.0", ] [[package]] name = "rustversion" -version = "1.0.9" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" +checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" [[package]] name = "rusty-fork" @@ -6132,9 +6099,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "safe-proc-macro2" @@ -6183,12 +6150,6 @@ dependencies = [ "safe-regex-compiler", ] -[[package]] -name = "safemem" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" - [[package]] name = "salsa20" version = "0.10.2" @@ -6209,9 +6170,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.3.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "001cf62ece89779fd16105b5f515ad0e5cedcd5440d3dd806bb067978e7c3608" +checksum = "35c0a159d0c45c12b20c5a844feb1fe4bea86e28f17b92a5f0c42193634d3782" dependencies = [ "cfg-if 1.0.0", "derive_more", @@ -6221,11 +6182,11 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.3.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "303959cf613a6f6efd19ed4b4ad5bf79966a13352716299ad532cfb115f4205c" +checksum = "912e55f6d20e0e80d63733872b40e1227c0bce1e1ab81ba67d696339bfd7fd29" dependencies = [ - "proc-macro-crate 1.2.1", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 1.0.109", @@ -6233,21 +6194,20 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" dependencies = [ - "lazy_static", - "windows-sys 0.36.1", + "windows-sys 0.42.0", ] [[package]] name = "scheduled-thread-pool" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "977a7519bff143a44f842fd07e80ad1329295bd71686457f18e496736f4bf9bf" +checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" dependencies = [ - "parking_lot 0.12.1", + "parking_lot", ] [[package]] @@ -6262,12 +6222,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -[[package]] -name = "scratch" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" - [[package]] name = "scrypt" version = "0.10.0" @@ -6312,9 +6266,22 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ - "base16ct", - "der", - "generic-array 0.14.6", + "base16ct 0.1.1", + "der 0.6.1", + "generic-array 0.14.7", + "subtle 2.4.1", + "zeroize", +] + +[[package]] +name = "sec1" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0aec48e813d6b90b15f0b8948af3c63483992dee44c03e9930b3eebdabe046e" +dependencies = [ + "base16ct 0.2.0", + "der 0.7.7", + "generic-array 0.14.7", "pkcs8", "subtle 2.4.1", "zeroize", @@ -6329,7 +6296,7 @@ dependencies = [ "bitcoin_hashes", "rand 0.8.5", "secp256k1-sys", - "serde 1.0.147", + "serde 1.0.163", ] [[package]] @@ -6347,7 +6314,7 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23a2ac85147a3a11d77ecf1bc7166ec0b92febfa4461c37944e180f319ece467" dependencies = [ - "bitflags 1.3.2", + "bitflags", "core-foundation", "core-foundation-sys", "libc", @@ -6356,9 +6323,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.6.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" dependencies = [ "core-foundation-sys", "libc", @@ -6379,7 +6346,7 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" dependencies = [ - "serde 1.0.147", + "serde 1.0.163", ] [[package]] @@ -6391,6 +6358,12 @@ dependencies = [ "pest", ] +[[package]] +name = "send_wrapper" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" + [[package]] name = "send_wrapper" version = "0.6.0" @@ -6405,23 +6378,13 @@ checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" [[package]] name = "serde" -version = "1.0.147" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" +checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" dependencies = [ "serde_derive", ] -[[package]] -name = "serde-aux" -version = "4.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c599b3fd89a75e0c18d6d2be693ddb12cccaf771db4ff9e39097104808a014c0" -dependencies = [ - "serde 1.0.147", - "serde_json", -] - [[package]] name = "serde-hjson" version = "0.9.1" @@ -6436,11 +6399,11 @@ dependencies = [ [[package]] name = "serde_bytes" -version = "0.11.7" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfc50e8183eeeb6178dcb167ae34a8051d63535023ae38b5d8d12beae193d37b" +checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294" dependencies = [ - "serde 1.0.147", + "serde 1.0.163", ] [[package]] @@ -6450,40 +6413,49 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" dependencies = [ "half", - "serde 1.0.147", + "serde 1.0.163", ] [[package]] name = "serde_derive" -version = "1.0.147" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" +checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] name = "serde_json" -version = "1.0.87" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "itoa", "ryu", - "serde 1.0.147", + "serde 1.0.163", ] [[package]] name = "serde_repr" -version = "0.1.9" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fe39d9fbb0ebf5eb2c7cb7e2a47e4f462fad1379f1166b8ae49ad9eae89a7ca" +checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", +] + +[[package]] +name = "serde_spanned" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +dependencies = [ + "serde 1.0.163", ] [[package]] @@ -6495,7 +6467,7 @@ dependencies = [ "form_urlencoded", "itoa", "ryu", - "serde 1.0.147", + "serde 1.0.163", ] [[package]] @@ -6506,7 +6478,7 @@ checksum = "ef8099d3df28273c99a1728190c7a9f19d444c941044f64adf986bee7ec53051" dependencies = [ "dtoa", "linked-hash-map", - "serde 1.0.147", + "serde 1.0.163", "yaml-rust", ] @@ -6523,17 +6495,6 @@ dependencies = [ "opaque-debug 0.3.0", ] -[[package]] -name = "sha-1" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" -dependencies = [ - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.10.7", -] - [[package]] name = "sha1" version = "0.10.5" @@ -6542,7 +6503,7 @@ checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.7", + "digest 0.10.6", ] [[package]] @@ -6578,16 +6539,16 @@ checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.7", + "digest 0.10.6", ] [[package]] name = "sha3" -version = "0.10.6" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ - "digest 0.10.7", + "digest 0.10.6", "keccak", ] @@ -6608,9 +6569,9 @@ checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" [[package]] name = "signal-hook" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" +checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" dependencies = [ "libc", "signal-hook-registry", @@ -6618,9 +6579,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] @@ -6631,10 +6592,26 @@ version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" dependencies = [ - "digest 0.10.7", + "digest 0.10.6", "rand_core 0.6.4", ] +[[package]] +name = "signature" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +dependencies = [ + "digest 0.10.6", + "rand_core 0.6.4", +] + +[[package]] +name = "simdutf8" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" + [[package]] name = "simple-error" version = "0.2.3" @@ -6658,9 +6635,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] @@ -6682,9 +6659,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -6708,14 +6685,20 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "spki" -version = "0.6.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" dependencies = [ "base64ct", - "der", + "der 0.7.7", ] [[package]] @@ -6811,9 +6794,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.16" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" dependencies = [ "proc-macro2", "quote", @@ -6826,18 +6809,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "unicode-xid", -] - [[package]] name = "sysinfo" version = "0.21.1" @@ -6877,21 +6848,21 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.5" +version = "0.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9410d0f6853b1d94f0e519fb95df60f29d2c1eff2d921ffdf01a4c8a3b54f12d" +checksum = "fd1ba337640d60c3e96bc6f0638a939b9c9a7f2c316a1598c279828b3d1dc8c5" [[package]] name = "tempfile" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if 1.0.0", "fastrand", - "redox_syscall", - "rustix 0.36.5", - "windows-sys 0.42.0", + "redox_syscall 0.3.5", + "rustix", + "windows-sys 0.45.0", ] [[package]] @@ -6905,18 +6876,18 @@ dependencies = [ "ed25519-dalek", "flex-error", "futures", - "k256", + "k256 0.11.6", "num-traits 0.2.15", "once_cell", "prost", "prost-types", "ripemd160", - "serde 1.0.147", + "serde 1.0.163", "serde_bytes", "serde_json", "serde_repr", "sha2 0.9.9", - "signature", + "signature 1.6.4", "subtle 2.4.1", "subtle-encoding", "tendermint-proto", @@ -6930,10 +6901,10 @@ version = "0.23.6" source = "git+https://github.com/heliaxdev/tendermint-rs.git?rev=b7d1e5afc6f2ccb3fd1545c2174bab1cc48d7fa7#b7d1e5afc6f2ccb3fd1545c2174bab1cc48d7fa7" dependencies = [ "flex-error", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "tendermint", - "toml", + "toml 0.5.9", "url", ] @@ -6947,7 +6918,7 @@ dependencies = [ "derive_more", "flex-error", "futures", - "serde 1.0.147", + "serde 1.0.163", "serde_cbor", "serde_derive", "static_assertions", @@ -6965,7 +6936,7 @@ source = "git+https://github.com/heliaxdev/tendermint-rs.git?rev=b7d1e5afc6f2ccb dependencies = [ "derive_more", "flex-error", - "serde 1.0.147", + "serde 1.0.163", "tendermint", "time", ] @@ -6981,7 +6952,7 @@ dependencies = [ "num-traits 0.2.15", "prost", "prost-types", - "serde 1.0.147", + "serde 1.0.163", "serde_bytes", "subtle-encoding", "time", @@ -6997,14 +6968,14 @@ dependencies = [ "bytes", "flex-error", "futures", - "getrandom 0.2.8", + "getrandom 0.2.9", "http", "hyper", "hyper-proxy", - "hyper-rustls 0.22.1", + "hyper-rustls", "peg", "pin-project", - "serde 1.0.147", + "serde 1.0.163", "serde_bytes", "serde_json", "subtle-encoding", @@ -7027,7 +6998,7 @@ source = "git+https://github.com/heliaxdev/tendermint-rs.git?rev=b7d1e5afc6f2ccb dependencies = [ "ed25519-dalek", "gumdrop", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "simple-error", "tempfile", @@ -7035,20 +7006,11 @@ dependencies = [ "time", ] -[[package]] -name = "termcolor" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" -dependencies = [ - "winapi-util", -] - [[package]] name = "termtree" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "test-log" @@ -7063,41 +7025,41 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ + "cfg-if 1.0.0", "once_cell", ] [[package]] name = "tikv-jemalloc-sys" -version = "0.5.2+5.3.0-patched" +version = "0.5.3+5.3.0-patched" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec45c14da997d0925c7835883e4d5c181f196fa142f8c19d7643d1e9af2592c3" +checksum = "a678df20055b43e57ef8cddde41cdfda9a3c1a060b67f4c5836dfb1d78543ba8" dependencies = [ "cc", - "fs_extra", "libc", ] @@ -7108,7 +7070,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" dependencies = [ "itoa", - "serde 1.0.147", + "serde 1.0.163", "time-core", "time-macros", ] @@ -7189,15 +7151,14 @@ dependencies = [ [[package]] name = "tiny_http" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0d6ef4e10d23c1efb862eecad25c5054429a71958b4eeef85eb5e7170b477ca" +checksum = "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82" dependencies = [ "ascii", "chunked_transfer", + "httpdate", "log", - "time", - "url", ] [[package]] @@ -7211,28 +7172,27 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.21.2" +version = "1.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105" dependencies = [ "autocfg", "bytes", "libc", - "memchr", "mio", "num_cpus", - "parking_lot 0.12.1", + "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -7247,20 +7207,20 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.8.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] name = "tokio-native-tls" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", "tokio", @@ -7283,16 +7243,16 @@ version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ - "rustls 0.20.7", + "rustls 0.20.8", "tokio", "webpki 0.22.0", ] [[package]] name = "tokio-stream" -version = "0.1.11" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", "pin-project-lite", @@ -7314,14 +7274,14 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.17.2" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181" +checksum = "54319c93411147bced34cb5609a80e0a8e44c5999c93903a81cd866630ec0bfd" dependencies = [ "futures-util", "log", "tokio", - "tungstenite 0.17.3", + "tungstenite 0.18.0", ] [[package]] @@ -7340,9 +7300,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", @@ -7358,7 +7318,41 @@ version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" dependencies = [ - "serde 1.0.147", + "serde 1.0.163", +] + +[[package]] +name = "toml" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" +dependencies = [ + "serde 1.0.163", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde 1.0.163", +] + +[[package]] +name = "toml_edit" +version = "0.19.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c500344a19072298cd05a7224b3c0c629348b78692bf48466c5238656e315a78" +dependencies = [ + "indexmap 2.0.0", + "serde 1.0.163", + "serde_spanned", + "toml_datetime", + "winnow", ] [[package]] @@ -7383,12 +7377,12 @@ dependencies = [ "pin-project", "prost", "prost-derive", - "rustls-native-certs 0.6.2", - "rustls-pemfile 1.0.2", + "rustls-native-certs 0.6.3", + "rustls-pemfile", "tokio", "tokio-rustls 0.23.4", "tokio-stream", - "tokio-util 0.7.4", + "tokio-util 0.7.8", "tower", "tower-layer", "tower-service", @@ -7402,7 +7396,7 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bf5e9b9c0f7e0a7c027dcfaba7b2c60816c7049171f679d99ee2ff65d0de8c4" dependencies = [ - "prettyplease 0.1.24", + "prettyplease 0.1.25", "proc-macro2", "prost-build", "quote", @@ -7418,13 +7412,13 @@ dependencies = [ "futures-core", "futures-util", "hdrhistogram", - "indexmap", + "indexmap 1.9.3", "pin-project", "pin-project-lite", "rand 0.8.5", "slab", "tokio", - "tokio-util 0.7.4", + "tokio-util 0.7.8", "tower-layer", "tower-service", "tracing 0.1.37", @@ -7448,25 +7442,6 @@ dependencies = [ "tracing-tower", ] -[[package]] -name = "tower-http" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" -dependencies = [ - "bitflags 1.3.2", - "bytes", - "futures-core", - "futures-util", - "http", - "http-body", - "http-range-header", - "pin-project-lite", - "tower", - "tower-layer", - "tower-service", -] - [[package]] name = "tower-layer" version = "0.3.2" @@ -7508,8 +7483,8 @@ dependencies = [ "cfg-if 1.0.0", "log", "pin-project-lite", - "tracing-attributes 0.1.23", - "tracing-core 0.1.30", + "tracing-attributes 0.1.24", + "tracing-core 0.1.31", ] [[package]] @@ -7518,9 +7493,9 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09d48f71a791638519505cefafe162606f706c25592e4bde4d97600c0195312e" dependencies = [ - "crossbeam-channel 0.5.6", + "crossbeam-channel 0.5.8", "time", - "tracing-subscriber 0.3.16", + "tracing-subscriber 0.3.17", ] [[package]] @@ -7535,13 +7510,13 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] @@ -7554,9 +7529,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", "valuable", @@ -7599,7 +7574,7 @@ checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" dependencies = [ "lazy_static", "log", - "tracing-core 0.1.30", + "tracing-core 0.1.31", ] [[package]] @@ -7608,8 +7583,8 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" dependencies = [ - "serde 1.0.147", - "tracing-core 0.1.30", + "serde 1.0.163", + "tracing-core 0.1.31", ] [[package]] @@ -7620,25 +7595,25 @@ checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" dependencies = [ "sharded-slab", "thread_local", - "tracing-core 0.1.30", + "tracing-core 0.1.31", ] [[package]] name = "tracing-subscriber" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" dependencies = [ "matchers", "nu-ansi-term", "once_cell", "regex", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "sharded-slab", "thread_local", "tracing 0.1.37", - "tracing-core 0.1.30", + "tracing-core 0.1.31", "tracing-log", "tracing-serde", ] @@ -7659,15 +7634,15 @@ dependencies = [ [[package]] name = "triomphe" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1ee9bd9239c339d714d657fac840c6d2a4f9c45f4f9ec7b0975113458be78db" +checksum = "0eee8098afad3fb0c54a9007aab6804558410503ad676d4633f9c2559a00ac0f" [[package]] name = "try-lock" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "tungstenite" @@ -7683,16 +7658,16 @@ dependencies = [ "input_buffer", "log", "rand 0.8.5", - "sha-1 0.9.8", + "sha-1", "url", "utf-8", ] [[package]] name = "tungstenite" -version = "0.17.3" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" +checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788" dependencies = [ "base64 0.13.1", "byteorder", @@ -7701,26 +7676,17 @@ dependencies = [ "httparse", "log", "rand 0.8.5", - "sha-1 0.10.0", + "sha1", "thiserror", "url", "utf-8", ] -[[package]] -name = "twoway" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1" -dependencies = [ - "memchr", -] - [[package]] name = "typenum" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "ucd-trie" @@ -7757,15 +7723,15 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.5" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unicode-normalization" @@ -7776,12 +7742,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-segmentation" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" - [[package]] name = "unicode-width" version = "0.1.10" @@ -7845,17 +7805,17 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ - "getrandom 0.2.8", - "serde 1.0.147", + "getrandom 0.2.9", + "serde 1.0.163", ] [[package]] name = "uuid" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83" +checksum = "4dad5567ad0cf5b760e5665964bec1b47dfd077ba8a2544b513f3556d3d239a2" dependencies = [ - "getrandom 0.2.8", + "getrandom 0.2.9", ] [[package]] @@ -7937,12 +7897,11 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", - "winapi", "winapi-util", ] @@ -7958,9 +7917,9 @@ dependencies = [ [[package]] name = "warp" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed7b8be92646fc3d18b06147664ebc5f48d222686cb11a8755e561a735aacc6d" +checksum = "ba431ef570df1287f7f8b07e376491ad54f84d26ac473489427231e1718e1f69" dependencies = [ "bytes", "futures-channel", @@ -7971,18 +7930,18 @@ dependencies = [ "log", "mime", "mime_guess", - "multipart", + "multer", "percent-encoding", "pin-project", - "rustls-pemfile 0.2.1", + "rustls-pemfile", "scoped-tls", - "serde 1.0.147", + "serde 1.0.163", "serde_json", "serde_urlencoded", "tokio", "tokio-stream", "tokio-tungstenite", - "tokio-util 0.7.4", + "tokio-util 0.7.8", "tower-service", "tracing 0.1.37", ] @@ -7993,12 +7952,6 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" -[[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -8007,9 +7960,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "5b6cb788c4e39112fbe1822277ef6fb3c55cd86b95cb3d3c4c1c9597e4ac74b4" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -8017,24 +7970,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "35e522ed4105a9d626d885b35d62501b30d9666283a5c8be12c14a8bdafe7822" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.33" +version = "0.4.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +checksum = "083abe15c5d88556b77bdf7aef403625be9e327ad37c62c4e4129af740168163" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -8044,9 +7997,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "358a79a0cb89d21db8120cbfb91392335913e4890665b1a7981d9e956903b434" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -8054,28 +8007,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "4783ce29f09b9d93134d41297aded3a712b7b979e9c6f28c32cb88c973a94869" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "a901d592cafaa4d711bc324edfaff879ac700b19c3dfd60058d2b445be2691eb" [[package]] name = "wasm-encoder" -version = "0.19.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9424cdab516a16d4ea03c8f4a01b14e7b2d04a129dcc2bcdde5bcc5f68f06c41" +checksum = "d05d0b6fcd0aeb98adf16e7975331b3c17222aa815148f5b976370ce589d80ef" dependencies = [ "leb128", ] @@ -8089,21 +8042,6 @@ dependencies = [ "parity-wasm", ] -[[package]] -name = "wasm-timer" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" -dependencies = [ - "futures", - "js-sys", - "parking_lot 0.11.2", - "pin-utils", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - [[package]] name = "wasmer" version = "2.3.0" @@ -8111,7 +8049,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea8d8361c9d006ea3d7797de7bd6b1492ffd0f91a22430cfda6c1658ad57bedf" dependencies = [ "cfg-if 1.0.0", - "indexmap", + "indexmap 1.9.3", "js-sys", "loupe", "more-asserts", @@ -8165,7 +8103,7 @@ dependencies = [ "enumset", "loupe", "rkyv", - "serde 1.0.147", + "serde 1.0.163", "serde_bytes", "smallvec", "target-lexicon", @@ -8183,7 +8121,7 @@ dependencies = [ "cranelift-codegen", "cranelift-entity", "cranelift-frontend", - "gimli", + "gimli 0.26.2", "loupe", "more-asserts", "rayon", @@ -8203,7 +8141,7 @@ dependencies = [ "byteorder", "dynasm", "dynasmrt", - "gimli", + "gimli 0.26.2", "lazy_static", "loupe", "more-asserts", @@ -8238,7 +8176,7 @@ dependencies = [ "memmap2", "more-asserts", "rustc-demangle", - "serde 1.0.147", + "serde 1.0.163", "serde_bytes", "target-lexicon", "thiserror", @@ -8262,7 +8200,7 @@ dependencies = [ "loupe", "object 0.28.4", "rkyv", - "serde 1.0.147", + "serde 1.0.163", "tempfile", "tracing 0.1.37", "wasmer-artifact", @@ -8330,11 +8268,11 @@ checksum = "39df01ea05dc0a9bab67e054c7cb01521e53b35a7bb90bd02eca564ed0b2667f" dependencies = [ "backtrace", "enum-iterator", - "indexmap", + "indexmap 1.9.3", "loupe", "more-asserts", "rkyv", - "serde 1.0.147", + "serde 1.0.163", "thiserror", ] @@ -8349,7 +8287,7 @@ dependencies = [ "cfg-if 1.0.0", "corosensei", "enum-iterator", - "indexmap", + "indexmap 1.9.3", "lazy_static", "libc", "loupe", @@ -8359,7 +8297,7 @@ dependencies = [ "region", "rkyv", "scopeguard", - "serde 1.0.147", + "serde 1.0.163", "thiserror", "wasmer-artifact", "wasmer-types", @@ -8378,7 +8316,7 @@ version = "0.107.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29e3ac9b780c7dda0cac7a52a5d6d2d6707cc6e3451c9db209b6c758f40d7acb" dependencies = [ - "indexmap", + "indexmap 1.9.3", "semver 1.0.17", ] @@ -8390,7 +8328,7 @@ checksum = "5f656cd8858a5164932d8a90f936700860976ec21eb00e0fe2aa8cab13f6b4cf" dependencies = [ "futures", "js-sys", - "parking_lot 0.12.1", + "parking_lot", "pin-utils", "slab", "wasm-bindgen", @@ -8398,9 +8336,9 @@ dependencies = [ [[package]] name = "wast" -version = "49.0.0" +version = "57.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ef81fcd60d244cafffeafac3d17615fdb2fddda6aca18f34a8ae233353587c" +checksum = "6eb0f5ed17ac4421193c7477da05892c2edafd67f9639e3c11a82086416662dc" dependencies = [ "leb128", "memchr", @@ -8410,18 +8348,18 @@ dependencies = [ [[package]] name = "wat" -version = "1.0.51" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c347c4460ffb311e95aafccd8c29e4888f241b9e4b3bb0e0ccbd998de2c8c0d" +checksum = "ab9ab0d87337c3be2bb6fc5cd331c4ba9fd6bcb4ee85048a0dd59ed9ecf92e53" dependencies = [ "wast", ] [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "16b5f940c7edfdc6d12126d98c9ef4d1b3d470011c47c76a6581df47ad9ba721" dependencies = [ "js-sys", "wasm-bindgen", @@ -8458,18 +8396,18 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.22.5" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" dependencies = [ "webpki 0.22.0", ] [[package]] name = "which" -version = "4.3.0" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" dependencies = [ "either", "libc", @@ -8516,6 +8454,15 @@ dependencies = [ "windows-targets 0.42.2", ] +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.0", +] + [[package]] name = "windows-sys" version = "0.33.0" @@ -8529,19 +8476,6 @@ dependencies = [ "windows_x86_64_msvc 0.33.0", ] -[[package]] -name = "windows-sys" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" -dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", -] - [[package]] name = "windows-sys" version = "0.42.0" @@ -8557,13 +8491,22 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.1", + "windows-targets 0.48.0", ] [[package]] @@ -8583,9 +8526,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" dependencies = [ "windows_aarch64_gnullvm 0.48.0", "windows_aarch64_msvc 0.48.0", @@ -8614,12 +8557,6 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd761fd3eb9ab8cc1ed81e56e567f02dd82c4c837e48ac3b2181b9ffc5060807" -[[package]] -name = "windows_aarch64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" - [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -8638,12 +8575,6 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cab0cf703a96bab2dc0c02c0fa748491294bf9b7feb27e1f4f96340f208ada0e" -[[package]] -name = "windows_i686_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" - [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -8662,12 +8593,6 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8cfdbe89cc9ad7ce618ba34abc34bbb6c36d99e96cae2245b7943cd75ee773d0" -[[package]] -name = "windows_i686_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" - [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -8686,12 +8611,6 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4dd9b0c0e9ece7bb22e84d70d01b71c6d6248b81a3c60d11869451b4cb24784" -[[package]] -name = "windows_x86_64_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" - [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -8722,12 +8641,6 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff1e4aa646495048ec7f3ffddc411e1d829c026a2ec62b39da15c1055e406eaa" -[[package]] -name = "windows_x86_64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" - [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -8740,6 +8653,15 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "winnow" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.10.1" @@ -8761,7 +8683,7 @@ dependencies = [ "log", "pharos", "rustc_version 0.4.0", - "send_wrapper", + "send_wrapper 0.6.0", "thiserror", "wasm-bindgen", "wasm-bindgen-futures", @@ -8806,31 +8728,31 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.3.2" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", - "synstructure", + "syn 2.0.15", ] [[package]] name = "zstd-sys" -version = "2.0.1+zstd.1.5.2" +version = "2.0.8+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fd07cbbc53846d9145dbffdf6dd09a7a0aa52be46741825f5c97bdd4f73f12b" +checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" dependencies = [ "cc", "libc", + "pkg-config", ] diff --git a/Cargo.toml b/Cargo.toml index a7bec0a2c4..50b7101bc0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,6 +81,7 @@ ibc-relayer-types = {git = "https://github.com/heliaxdev/hermes.git", rev = "fea ics23 = "0.9.0" index-set = {git = "https://github.com/heliaxdev/index-set", tag = "v0.7.1", features = ["serialize-borsh", "serialize-serde"]} itertools = "0.10.0" +lazy_static = "1.4.0" libc = "0.2.97" libloading = "0.7.2" libsecp256k1 = {git = "https://github.com/heliaxdev/libsecp256k1", rev = "bbb3bd44a49db361f21d9db80f9a087c194c0ae9", default-features = false, features = ["std", "static-context"]} diff --git a/Makefile b/Makefile index a0d9f4a600..a347ff8e27 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,7 @@ build: $(cargo) build $(jobs) build-test: - $(cargo) build --tests $(jobs) + $(cargo) +$(nightly) build --tests $(jobs) build-release: NAMADA_DEV=false $(cargo) build $(jobs) --release --package namada_apps --manifest-path Cargo.toml @@ -119,7 +119,7 @@ test-unit-coverage: $(cargo) +$(nightly) llvm-cov --output-dir target \ --features namada/testing \ --html \ - -- --skip e2e -Z unstable-options --report-time + -- --skip e2e --skip integration -Z unstable-options --report-time # NOTE: `TEST_FILTER` is prepended with `e2e::`. Since filters in `cargo test` # work with a substring search, TEST_FILTER only works if it contains a string @@ -135,11 +135,18 @@ test-e2e: --test-threads=1 \ -Z unstable-options --report-time +test-integration: + RUST_BACKTRACE=$(RUST_BACKTRACE) \ + $(cargo) +$(nightly) test integration::$(TEST_FILTER) \ + -Z unstable-options \ + -- \ + -Z unstable-options --report-time + test-unit: $(cargo) +$(nightly) test \ $(TEST_FILTER) \ $(jobs) \ - -- --skip e2e \ + -- --skip e2e --skip integration \ -Z unstable-options --report-time test-unit-mainnet: @@ -147,14 +154,14 @@ test-unit-mainnet: --features "mainnet" \ $(TEST_FILTER) \ $(jobs) \ - -- --skip e2e \ + -- --skip e2e --skip integration \ -Z unstable-options --report-time test-unit-debug: $(debug-cargo) +$(nightly) test \ $(jobs) \ $(TEST_FILTER) \ - -- --skip e2e \ + -- --skip e2e --skip integration \ --nocapture \ -Z unstable-options --report-time diff --git a/apps/Cargo.toml b/apps/Cargo.toml index b82a4966b7..a367bf945e 100644 --- a/apps/Cargo.toml +++ b/apps/Cargo.toml @@ -64,6 +64,7 @@ abciplus = [ "namada/tendermint-rpc", ] + [dependencies] namada = {path = "../shared", features = ["ferveo-tpke", "masp-tx-gen", "multicore", "http-client"]} ark-serialize.workspace = true @@ -96,6 +97,7 @@ file-lock.workspace = true flate2.workspace = true futures.workspace = true itertools.workspace = true +lazy_static.workspace= true libc.workspace = true libloading.workspace = true masp_primitives = { workspace = true, features = ["transparent-inputs"] } @@ -125,6 +127,7 @@ sha2.workspace = true signal-hook.workspace = true sysinfo.workspace = true tar.workspace = true +tempfile.workspace = true tendermint-config.workspace = true thiserror.workspace = true tokio = {workspace = true, features = ["full"]} @@ -148,7 +151,6 @@ namada = {path = "../shared", default-features = false, features = ["testing", " namada_test_utils = {path = "../test_utils"} bit-set.workspace = true proptest.workspace = true -tempfile.workspace = true test-log.workspace = true tokio-test.workspace = true 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..86b31da3aa 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -6,13 +6,17 @@ //! 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; -pub use utils::safe_exit; use utils::*; +pub use utils::{dispatch_prompt, safe_exit, Cmd, TESTIN}; pub use self::context::Context; 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/lib/cli/utils.rs b/apps/src/lib/cli/utils.rs index eac233ed28..7bcd9c5bd9 100644 --- a/apps/src/lib/cli/utils.rs +++ b/apps/src/lib/cli/utils.rs @@ -1,10 +1,12 @@ //! Command line interface utilities use std::fmt::Debug; +use std::io::Write; use std::marker::PhantomData; use std::str::FromStr; use clap::{ArgAction, ArgMatches}; use color_eyre::eyre::Result; +use lazy_static::lazy_static; use super::args; use super::context::{Context, FromContext}; @@ -322,6 +324,7 @@ where }) } +#[cfg(not(feature = "testing"))] /// A helper to exit after flushing output, borrowed from `clap::util` module. pub fn safe_exit(code: i32) -> ! { use std::io::Write; @@ -331,3 +334,62 @@ pub fn safe_exit(code: i32) -> ! { std::process::exit(code) } + +#[cfg(feature = "testing")] +/// A helper to exit after flushing output, borrowed from `clap::util` module. +pub fn safe_exit(_: i32) -> ! { + let _ = std::io::stdout().lock().flush(); + let _ = std::io::stderr().lock().flush(); + + panic!("Test failed because the client exited unexpectedly.") +} + +lazy_static! { + /// A replacement for stdin in testing. + pub static ref TESTIN: std::sync::Arc>> = + std::sync::Arc::new(std::sync::Mutex::new(vec![])); +} + +/// A generic function for displaying a prompt to users and reading +/// in their response. +fn prompt_aux(mut reader: R, mut writer: W, question: &str) -> String +where + R: std::io::Read, + W: Write, +{ + write!(&mut writer, "{}", question).expect("Unable to write"); + writer.flush().unwrap(); + let mut s = String::new(); + reader.read_to_string(&mut s).expect("Unable to read"); + s +} + +/// A function that chooses how to dispatch prompts +/// to users. There is a hierarchy of feature flags +/// that determines this. If no flags are set, +/// the question is printed to stdout and response +/// read from stdin. +pub fn dispatch_prompt(question: impl AsRef) -> String { + if cfg!(feature = "testing") { + prompt_aux( + TESTIN.lock().unwrap().as_slice(), + std::io::stdout(), + question.as_ref(), + ) + } else { + prompt_aux( + std::io::stdin().lock(), + std::io::stdout(), + question.as_ref(), + ) + } +} + +#[macro_export] +/// A convenience macro for formatting the user prompt before +/// forwarding it to the `[dispatch_prompt]` method. +macro_rules! prompt { + ($($arg:tt)*) => {{ + $crate::cli::dispatch_prompt(format!("{}", format_args!($($arg)*))) + }} +} 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/rpc.rs b/apps/src/lib/client/rpc.rs index 44955967d0..b849d21916 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -53,6 +53,7 @@ use tokio::time::Instant; use crate::cli::{self, args}; use crate::facade::tendermint::merkle::proof::Proof; use crate::facade::tendermint_rpc::error::Error as TError; +use crate::prompt; use crate::wallet::CliWalletUtils; /// Query the status of a given transaction. @@ -111,7 +112,7 @@ 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, + U: ShieldedUtils, >( client: &C, wallet: &mut Wallet, @@ -263,7 +264,7 @@ pub async fn query_raw_bytes( /// Query token balance(s) pub async fn query_balance< C: namada::ledger::queries::Client + Sync, - U: ShieldedUtils, + U: ShieldedUtils, >( client: &C, wallet: &mut Wallet, @@ -397,7 +398,7 @@ 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, + U: ShieldedUtils, >( client: &C, wallet: &mut Wallet, @@ -439,10 +440,7 @@ pub async fn query_pinned_balance< } // If a suitable viewing key was not found, then demand it from the user if balance == pinned_error { - print!("Enter the viewing key for {}: ", owner); - io::stdout().flush().unwrap(); - let mut vk_str = String::new(); - io::stdin().read_line(&mut vk_str).unwrap(); + let vk_str = prompt!("Enter the viewing key for {}: ", owner); let fvk = match ExtendedViewingKey::from_str(vk_str.trim()) { Ok(fvk) => fvk, _ => { @@ -761,7 +759,7 @@ pub async fn query_proposal( /// Query token shielded balance(s) pub async fn query_shielded_balance< C: namada::ledger::queries::Client + Sync, - U: ShieldedUtils, + U: ShieldedUtils, >( client: &C, wallet: &mut Wallet, diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index d70f8c7e6f..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}; @@ -463,8 +463,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) { let params_dir = PathBuf::from(params_dir); @@ -525,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> { diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index 272d74b6eb..6f20fe165a 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -47,41 +47,6 @@ const DEFAULT_NETWORK_CONFIGS_SERVER: &str = /// We do pre-genesis validator set up in this directory pub const PRE_GENESIS_DIR: &str = "pre-genesis"; -/// Environment variable set to reduce the amount of printing the CLI -/// tools perform. Extra prints, while good for UI, clog up test tooling. -pub const REDUCED_CLI_PRINTING: &str = "REDUCED_CLI_PRINTING"; - -macro_rules! cli_print { - ($($arg:tt)*) => {{ - if std::env::var(REDUCED_CLI_PRINTING) - .map(|v| if v.to_lowercase().trim() == "true" { - false - } else { - true - }).unwrap_or(true) { - let mut stdout = std::io::stdout().lock(); - _ = stdout.write_all(format!("{}", std::format_args!($($arg)*)).as_bytes()); - _ = stdout.flush(); - } - }}; -} - -#[allow(unused)] -macro_rules! cli_println { - ($($arg:tt)*) => {{ - if std::env::var(REDUCED_CLI_PRINTING) - .map(|v| if v.to_lowercase().trim() == "true" { - false - } else { - true - }).unwrap_or(true) { - let mut stdout = std::io::stdout().lock(); - _ = stdout.write_all(format!("{}\n", std::format_args!($($arg)*)).as_bytes()); - _ = stdout.flush(); - } - }}; -} - /// Configure Namada to join an existing network. The chain must be released in /// the repository. pub async fn join_network( @@ -1204,9 +1169,9 @@ where print!("{}", msg); _ = std::io::stdout().flush(); for c in spinny_wheel.chars().cycle() { - cli_print!("{}", c); + print!("{}", c); std::thread::sleep(std::time::Duration::from_secs(1)); - cli_print!("{}", (8u8 as char)); + print!("{}", (8u8 as char)); if task.is_finished() { break; } diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index fcd57be745..9a90e03de3 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 @@ -1035,9 +1036,9 @@ pub fn genesis(num_validators: u64) -> Genesis { public_key: wallet::defaults::ester_keypair().ref_to(), }, ]; - let default_user_tokens = token::Amount::native_whole(1_000_000); - let default_key_tokens = token::Amount::native_whole(1_000); - let mut balances: HashMap = HashMap::from_iter([ + let default_user_tokens = Uint::from(1_000_000); + let default_key_tokens = Uint::from(1_000_000); + let mut balances: HashMap = HashMap::from_iter([ // established accounts' balances (wallet::defaults::albert_address(), default_user_tokens), (wallet::defaults::bertha_address(), default_user_tokens), @@ -1083,7 +1084,11 @@ pub fn genesis(num_validators: u64) -> Genesis { denom, vp_code_path: vp_token_path.into(), vp_sha256: Default::default(), - balances: balances.clone(), + balances: balances + .clone() + .into_iter() + .map(|(k, v)| (k, token::Amount::from_uint(v, denom).unwrap())) + .collect(), }) .collect(); Genesis { diff --git a/apps/src/lib/node/ledger/mod.rs b/apps/src/lib/node/ledger/mod.rs index ebc86468c4..37b447b4da 100644 --- a/apps/src/lib/node/ledger/mod.rs +++ b/apps/src/lib/node/ledger/mod.rs @@ -1,8 +1,8 @@ mod abortable; mod broadcaster; pub mod ethereum_oracle; -mod shell; -mod shims; +pub mod shell; +pub mod shims; pub mod storage; pub mod tendermint_node; diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index 91f48e3e05..82aafa59b5 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -317,7 +317,7 @@ where &mut self.wl_storage, &address, difficulty, - withdrawal_limit, + withdrawal_limit.into(), ) .expect("Couldn't init faucet storage") } diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index e1b7f08883..de5665bce9 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -13,6 +13,9 @@ mod prepare_proposal; mod process_proposal; pub(super) mod queries; mod stats; +#[cfg(any(test, feature = "testing"))] +#[allow(dead_code)] +pub mod testing; mod vote_extensions; use std::collections::{BTreeSet, HashSet}; @@ -137,7 +140,7 @@ impl From for TxResult { /// The different error codes that the ledger may /// send back to a client indicating the status /// of their submitted tx -#[derive(Debug, Copy, Clone, FromPrimitive, ToPrimitive, PartialEq)] +#[derive(Debug, Copy, Clone, FromPrimitive, ToPrimitive, PartialEq, Eq)] pub enum ErrorCodes { Ok = 0, InvalidDecryptedChainId = 1, diff --git a/apps/src/lib/node/ledger/shell/prepare_proposal.rs b/apps/src/lib/node/ledger/shell/prepare_proposal.rs index 3fc5cd8983..e1e94be61e 100644 --- a/apps/src/lib/node/ledger/shell/prepare_proposal.rs +++ b/apps/src/lib/node/ledger/shell/prepare_proposal.rs @@ -16,8 +16,6 @@ use namada::types::transaction::{ use namada::types::vote_extensions::VoteExtensionDigest; use super::super::*; -#[allow(unused_imports)] -use super::block_space_alloc; use super::block_space_alloc::states::{ BuildingDecryptedTxBatch, BuildingProtocolTxBatch, EncryptedTxBatchAllocator, NextState, TryAlloc, @@ -41,8 +39,8 @@ where { /// Begin a new block. /// - /// Block construction is documented in [`block_space_alloc`] - /// and [`block_space_alloc::states`]. + /// Block construction is documented in `block_space_alloc` + /// and `block_space_alloc::states` (private modules). /// /// INVARIANT: Any changes applied in this method must be reverted if /// the proposal is rejected (unless we can simply overwrite diff --git a/apps/src/lib/node/ledger/shell/process_proposal.rs b/apps/src/lib/node/ledger/shell/process_proposal.rs index ff3bd60236..ce4f71a6f5 100644 --- a/apps/src/lib/node/ledger/shell/process_proposal.rs +++ b/apps/src/lib/node/ledger/shell/process_proposal.rs @@ -934,7 +934,7 @@ where /// Checks if it is not possible to include encrypted txs at the current /// block height. - fn encrypted_txs_not_allowed(&self) -> bool { + pub(super) fn encrypted_txs_not_allowed(&self) -> bool { let pos_queries = self.wl_storage.pos_queries(); let is_2nd_height_off = pos_queries.is_deciding_offset_within_epoch(1); let is_3rd_height_off = pos_queries.is_deciding_offset_within_epoch(2); diff --git a/apps/src/lib/node/ledger/shell/testing/client.rs b/apps/src/lib/node/ledger/shell/testing/client.rs new file mode 100644 index 0000000000..164e4a03ea --- /dev/null +++ b/apps/src/lib/node/ledger/shell/testing/client.rs @@ -0,0 +1,82 @@ +use std::ops::ControlFlow; + +use clap::Command as App; +use eyre::Report; +use namada::types::control_flow::Halt; +use tendermint_config::net::Address as TendermintAddress; + +use super::node::MockNode; +use crate::cli::api::{CliApi, CliClient}; +use crate::cli::args::Global; +use crate::cli::{args, cmds, Cmd, Context, NamadaClient}; +use crate::node::ledger::shell::testing::utils::Bin; + +pub fn run( + node: &MockNode, + who: Bin, + mut args: Vec<&str>, +) -> Result<(), Report> { + let global = { + let locked = node.shell.lock().unwrap(); + Global { + chain_id: Some(locked.chain_id.clone()), + base_dir: locked.base_dir.clone(), + wasm_dir: Some(locked.wasm_dir.clone()), + } + }; + let ctx = Context::new(global.clone())?; + + let rt = tokio::runtime::Runtime::new().unwrap(); + match who { + Bin::Node => { + unreachable!("Node commands aren't supported by integration tests") + } + Bin::Client => { + args.insert(0, "client"); + let app = App::new("test"); + let app = cmds::NamadaClient::add_sub(args::Global::def(app)); + let matches = app.get_matches_from(args.clone()); + let cmd = match cmds::NamadaClient::parse(&matches) + .expect("Could not parse client command") + { + cmds::NamadaClient::WithContext(sub_cmd) => { + NamadaClient::WithContext(Box::new((sub_cmd, ctx))) + } + cmds::NamadaClient::WithoutContext(sub_cmd) => { + NamadaClient::WithoutContext(sub_cmd, global) + } + }; + rt.block_on(CliApi::<()>::handle_client_command(Some(node), cmd)) + } + Bin::Wallet => { + args.insert(0, "wallet"); + let app = App::new("test"); + let app = cmds::NamadaWallet::add_sub(args::Global::def(app)); + let matches = app.get_matches_from(args.clone()); + + let cmd = cmds::NamadaWallet::parse(&matches) + .expect("Could not parse wallet command"); + CliApi::<()>::handle_wallet_command(cmd, ctx) + } + Bin::Relayer => { + args.insert(0, "relayer"); + let app = App::new("test"); + let app = cmds::NamadaRelayer::add_sub(args::Global::def(app)); + let matches = app.get_matches_from(args.clone()); + let cmd = cmds::NamadaRelayer::parse(&matches) + .expect("Could not parse wallet command"); + rt.block_on(CliApi::<()>::handle_relayer_command(Some(node), cmd)) + } + } +} + +#[async_trait::async_trait(?Send)] +impl<'a> CliClient for &'a MockNode { + fn from_tendermint_address(_: &mut TendermintAddress) -> Self { + unreachable!("MockNode should always be instantiated at test start.") + } + + async fn wait_until_node_is_synced(&self) -> Halt<()> { + ControlFlow::Continue(()) + } +} diff --git a/apps/src/lib/node/ledger/shell/testing/mod.rs b/apps/src/lib/node/ledger/shell/testing/mod.rs new file mode 100644 index 0000000000..fff1df00ba --- /dev/null +++ b/apps/src/lib/node/ledger/shell/testing/mod.rs @@ -0,0 +1,3 @@ +pub mod client; +pub mod node; +pub mod utils; diff --git a/apps/src/lib/node/ledger/shell/testing/node.rs b/apps/src/lib/node/ledger/shell/testing/node.rs new file mode 100644 index 0000000000..a66d33cb78 --- /dev/null +++ b/apps/src/lib/node/ledger/shell/testing/node.rs @@ -0,0 +1,543 @@ +use std::mem::ManuallyDrop; +use std::path::PathBuf; +use std::str::FromStr; +use std::sync::{Arc, Mutex}; + +use color_eyre::eyre::{Report, Result}; +use lazy_static::lazy_static; +use namada::ledger::events::log::dumb_queries; +use namada::ledger::queries::{ + Client, EncodedResponseQuery, RequestCtx, RequestQuery, Router, RPC, +}; +use namada::ledger::storage::{ + LastBlock, Sha256Hasher, EPOCH_SWITCH_BLOCKS_DELAY, +}; +use namada::tendermint_rpc::endpoint::abci_info; +use namada::tendermint_rpc::SimpleRequest; +use namada::types::hash::Hash; +use namada::types::storage::{BlockHash, BlockHeight, Epoch, Header}; +use namada::types::time::DateTimeUtc; +use num_traits::cast::FromPrimitive; +use regex::Regex; +use tokio::sync::mpsc::UnboundedReceiver; + +use crate::facade::tendermint_proto::abci::response_process_proposal::ProposalStatus; +use crate::facade::tendermint_proto::abci::RequestProcessProposal; +use crate::facade::tendermint_rpc::endpoint::abci_info::AbciInfo; +use crate::facade::tendermint_rpc::error::Error as RpcError; +use crate::facade::{tendermint, tendermint_rpc}; +use crate::node::ledger::shell::testing::utils::TestDir; +use crate::node::ledger::shell::{ErrorCodes, Shell}; +use crate::node::ledger::shims::abcipp_shim_types::shim::request::{ + FinalizeBlock, ProcessedTx, +}; +use crate::node::ledger::shims::abcipp_shim_types::shim::response::TxResult; +use crate::node::ledger::storage; + +/// Status of tx +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum NodeResults { + /// Success + Ok, + /// Rejected by Process Proposal + Rejected(TxResult), + /// Failure in application in Finalize Block + Failed(ErrorCodes), +} + +pub struct MockNode { + pub shell: Arc>>, + pub test_dir: ManuallyDrop, + pub keep_temp: bool, + pub _broadcast_recv: UnboundedReceiver>, + pub results: Arc>>, +} + +impl Drop for MockNode { + fn drop(&mut self) { + unsafe { + if !self.keep_temp { + ManuallyDrop::take(&mut self.test_dir).clean_up() + } else { + println!( + "Keeping tempfile at {}", + self.test_dir.path().to_string_lossy() + ); + ManuallyDrop::drop(&mut self.test_dir) + } + } + } +} + +impl MockNode { + pub fn genesis_dir(&self) -> PathBuf { + self.test_dir + .path() + .join(self.shell.lock().unwrap().chain_id.to_string()) + } + + pub fn genesis_path(&self) -> PathBuf { + self.test_dir + .path() + .join(format!("{}.toml", self.shell.lock().unwrap().chain_id)) + } + + pub fn wasm_dir(&self) -> PathBuf { + self.genesis_path().join("wasm") + } + + pub fn wallet_path(&self) -> PathBuf { + self.genesis_dir().join("wallet.toml") + } + + pub fn current_epoch(&self) -> Epoch { + self.shell.lock().unwrap().wl_storage.storage.last_epoch + } + + pub fn next_epoch(&mut self) -> Epoch { + { + let mut locked = self.shell.lock().unwrap(); + + let next_epoch_height = + locked.wl_storage.storage.get_last_block_height() + 1; + locked.wl_storage.storage.next_epoch_min_start_height = + next_epoch_height; + locked.wl_storage.storage.next_epoch_min_start_time = + DateTimeUtc::now(); + let next_epoch_min_start_height = + locked.wl_storage.storage.next_epoch_min_start_height; + if let Some(LastBlock { height, .. }) = + locked.wl_storage.storage.last_block.as_mut() + { + *height = next_epoch_min_start_height; + } + } + self.finalize_and_commit(); + + for _ in 0..EPOCH_SWITCH_BLOCKS_DELAY { + self.finalize_and_commit(); + } + self.shell + .lock() + .unwrap() + .wl_storage + .storage + .get_current_epoch() + .0 + } + + /// Simultaneously call the `FinalizeBlock` and + /// `Commit` handlers. + pub fn finalize_and_commit(&self) { + let mut req = FinalizeBlock { + hash: BlockHash([0u8; 32]), + header: Header { + hash: Hash([0; 32]), + time: DateTimeUtc::now(), + next_validators_hash: Hash([0; 32]), + }, + byzantine_validators: vec![], + txs: vec![], + proposer_address: vec![], + votes: vec![], + }; + req.header.time = DateTimeUtc::now(); + let mut locked = self.shell.lock().unwrap(); + locked.finalize_block(req).expect("Test failed"); + locked.commit(); + } + + /// Advance to a block height that allows + /// txs + fn advance_to_allowed_block(&self) { + loop { + let not_allowed = + { self.shell.lock().unwrap().encrypted_txs_not_allowed() }; + if not_allowed { + self.finalize_and_commit(); + } else { + break; + } + } + } + + /// Send a tx through Process Proposal and Finalize Block + /// and register the results. + fn submit_tx(&self, tx_bytes: Vec) { + let req = RequestProcessProposal { + txs: vec![tx_bytes.clone()], + ..Default::default() + }; + // The block space allocator disallows txs in certain blocks. + // Advance to block height that allows txs. + self.advance_to_allowed_block(); + let mut locked = self.shell.lock().unwrap(); + let mut result = locked.process_proposal(req); + let mut errors: Vec<_> = result + .tx_results + .iter() + .map(|e| { + if e.code == 0 { + NodeResults::Ok + } else { + NodeResults::Rejected(e.clone()) + } + }) + .collect(); + if result.status != i32::from(ProposalStatus::Accept) { + self.results.lock().unwrap().append(&mut errors); + return; + } + + // process proposal succeeded, now run finalize block + let req = FinalizeBlock { + hash: BlockHash([0u8; 32]), + header: Header { + hash: Hash([0; 32]), + time: DateTimeUtc::now(), + next_validators_hash: Hash([0; 32]), + }, + byzantine_validators: vec![], + txs: vec![ProcessedTx { + tx: tx_bytes, + result: result.tx_results.remove(0), + }], + proposer_address: vec![], + votes: vec![], + }; + + // process the results + let resp = locked.finalize_block(req).unwrap(); + let mut error_codes = resp + .events + .into_iter() + .map(|e| { + let code = ErrorCodes::from_u32( + e.attributes + .get("code") + .map(|e| u32::from_str(e).unwrap()) + .unwrap_or_default(), + ) + .unwrap(); + if code == ErrorCodes::Ok { + NodeResults::Ok + } else { + NodeResults::Failed(code) + } + }) + .collect::>(); + self.results.lock().unwrap().append(&mut error_codes); + locked.commit(); + } + + /// Check that applying a tx succeeded. + pub fn success(&self) -> bool { + self.results + .lock() + .unwrap() + .iter() + .all(|r| *r == NodeResults::Ok) + } + + pub fn clear_results(&self) { + self.results.lock().unwrap().clear(); + } + + pub fn assert_success(&self) { + if !self.success() { + panic!( + "Assert failed: The node did not execute \ + successfully:\nErrors:\n {:?}", + self.results.lock().unwrap() + ); + } else { + self.clear_results(); + } + } +} + +#[async_trait::async_trait(?Send)] +impl<'a> Client for &'a MockNode { + type Error = Report; + + async fn request( + &self, + path: String, + data: Option>, + height: Option, + prove: bool, + ) -> std::result::Result { + let rpc = RPC; + let data = data.unwrap_or_default(); + let latest_height = { + self.shell + .lock() + .unwrap() + .wl_storage + .storage + .last_block + .as_ref() + .map(|b| b.height) + .unwrap_or_default() + }; + let height = height.unwrap_or(latest_height); + // Handle a path by invoking the `RPC.handle` directly with the + // borrowed storage + let request = RequestQuery { + data, + path, + height, + prove, + }; + let borrowed = self.shell.lock().unwrap(); + let ctx = RequestCtx { + wl_storage: &borrowed.wl_storage, + event_log: borrowed.event_log(), + vp_wasm_cache: borrowed.vp_wasm_cache.read_only(), + tx_wasm_cache: borrowed.tx_wasm_cache.read_only(), + storage_read_past_height_limit: None, + }; + rpc.handle(ctx, &request).map_err(Report::new) + } + + async fn perform( + &self, + _request: R, + ) -> std::result::Result + where + R: SimpleRequest, + { + unreachable!() + } + + /// `/abci_info`: get information about the ABCI application. + async fn abci_info(&self) -> Result { + let locked = self.shell.lock().unwrap(); + Ok(AbciInfo { + data: "Namada".to_string(), + version: "test".to_string(), + app_version: 0, + last_block_height: locked + .wl_storage + .storage + .last_block + .as_ref() + .map(|b| b.height.0 as u32) + .unwrap_or_default() + .into(), + last_block_app_hash: locked + .wl_storage + .storage + .last_block + .as_ref() + .map(|b| b.hash.0) + .unwrap_or_default() + .to_vec(), + }) + } + + /// `/broadcast_tx_sync`: broadcast a transaction, returning the response + /// from `CheckTx`. + async fn broadcast_tx_sync( + &self, + tx: namada::tendermint::abci::Transaction, + ) -> Result + { + let resp = tendermint_rpc::endpoint::broadcast::tx_sync::Response { + code: Default::default(), + data: Default::default(), + log: Default::default(), + hash: tendermint::abci::transaction::Hash::new([0; 32]), + }; + let tx_bytes: Vec = tx.into(); + self.submit_tx(tx_bytes); + if !self.success() { + return Ok(resp); + } else { + self.clear_results(); + } + let tx_bytes = { + let locked = self.shell.lock().unwrap(); + locked.prepare_proposal(Default::default()).txs.remove(0) + }; + self.submit_tx(tx_bytes); + Ok(resp) + } + + /// `/block_search`: search for blocks by BeginBlock and EndBlock events. + async fn block_search( + &self, + query: namada::tendermint_rpc::query::Query, + _page: u32, + _per_page: u8, + _order: namada::tendermint_rpc::Order, + ) -> Result + { + let matcher = parse_tm_query(query); + let borrowed = self.shell.lock().unwrap(); + // we store an index into the event log as a block + // height in the response of the query... VERY NAISSSE + let matching_events = borrowed.event_log().iter().enumerate().flat_map( + |(index, event)| { + if matcher.matches(event) { + Some(EncodedEvent(index as u64)) + } else { + None + } + }, + ); + let blocks = matching_events + .map(|encoded_event| namada::tendermint_rpc::endpoint::block::Response { + block_id: Default::default(), + block: namada::tendermint_proto::types::Block { + header: Some(namada::tendermint_proto::types::Header { + version: Some(namada::tendermint_proto::version::Consensus { + block: 0, + app: 0, + }), + chain_id: "Namada".try_into().unwrap(), + height: encoded_event.0 as i64, + time: None, + last_block_id: None, + last_commit_hash: vec![], + data_hash: vec![], + validators_hash: vec![], + next_validators_hash: vec![], + consensus_hash: vec![], + app_hash: vec![], + last_results_hash: vec![], + evidence_hash: vec![], + proposer_address: vec![] + + }), + data: Default::default(), + evidence: Default::default(), + last_commit: Some(namada::tendermint_proto::types::Commit { + height: encoded_event.0 as i64, + round: 0, + block_id: Some(namada::tendermint_proto::types::BlockId { + hash: vec![0u8; 32], + part_set_header: Some(namada::tendermint_proto::types::PartSetHeader { + total: 1, + hash: vec![1; 32], + }), + }), + signatures: vec![], + }), + }.try_into().unwrap(), + }) + .collect::>(); + + Ok(namada::tendermint_rpc::endpoint::block_search::Response { + total_count: blocks.len() as u32, + blocks, + }) + } + + /// `/block_results`: get ABCI results for a block at a particular height. + async fn block_results( + &self, + height: H, + ) -> Result + where + H: Into + Send, + { + let height = height.into(); + let encoded_event = EncodedEvent(height.value()); + let locked = self.shell.lock().unwrap(); + let events: Vec<_> = locked + .event_log() + .iter() + .enumerate() + .flat_map(|(index, event)| { + if index == encoded_event.log_index() { + Some(event) + } else { + None + } + }) + .map(|event| namada::tendermint::abci::responses::Event { + type_str: event.event_type.to_string(), + attributes: event + .attributes + .iter() + .map(|(k, v)| namada::tendermint::abci::tag::Tag { + key: k.parse().unwrap(), + value: v.parse().unwrap(), + }) + .collect(), + }) + .collect(); + let has_events = !events.is_empty(); + + Ok(tendermint_rpc::endpoint::block_results::Response { + height, + txs_results: None, + begin_block_events: None, + end_block_events: has_events.then_some(events), + validator_updates: vec![], + consensus_param_updates: None, + }) + } + + /// `/tx_search`: search for transactions with their results. + async fn tx_search( + &self, + _query: namada::tendermint_rpc::query::Query, + _prove: bool, + _page: u32, + _per_page: u8, + _order: namada::tendermint_rpc::Order, + ) -> Result { + // In the past, some cli commands for masp called this. However, these + // commands are not currently supported, so we do not need to fill + // in this function for now. + unreachable!() + } + + /// `/health`: get node health. + /// + /// Returns empty result (200 OK) on success, no response in case of an + /// error. + async fn health(&self) -> Result<(), RpcError> { + Ok(()) + } +} + +/// Parse a Tendermint query. +fn parse_tm_query( + query: namada::tendermint_rpc::query::Query, +) -> dumb_queries::QueryMatcher { + const QUERY_PARSING_REGEX_STR: &str = + r"^tm\.event='NewBlock' AND (accepted|applied)\.hash='([^']+)'$"; + + lazy_static! { + /// Compiled regular expression used to parse Tendermint queries. + static ref QUERY_PARSING_REGEX: Regex = Regex::new(QUERY_PARSING_REGEX_STR).unwrap(); + } + + let query = query.to_string(); + let captures = QUERY_PARSING_REGEX.captures(&query).unwrap(); + + match captures.get(0).unwrap().as_str() { + "accepted" => dumb_queries::QueryMatcher::accepted( + captures.get(1).unwrap().as_str().try_into().unwrap(), + ), + "applied" => dumb_queries::QueryMatcher::applied( + captures.get(1).unwrap().as_str().try_into().unwrap(), + ), + _ => unreachable!("We only query accepted or applied txs"), + } +} + +/// A Namada event log index and event type encoded as +/// a Tendermint block height. +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +struct EncodedEvent(u64); + +impl EncodedEvent { + /// Get the encoded event log index. + const fn log_index(self) -> usize { + self.0 as usize + } +} diff --git a/apps/src/lib/node/ledger/shell/testing/utils.rs b/apps/src/lib/node/ledger/shell/testing/utils.rs new file mode 100644 index 0000000000..e66ead21e7 --- /dev/null +++ b/apps/src/lib/node/ledger/shell/testing/utils.rs @@ -0,0 +1,48 @@ +use std::path::{Path, PathBuf}; + +use tempfile::tempdir; + +/// Namada binaries +#[derive(Debug)] +#[allow(dead_code)] +pub enum Bin { + Node, + Client, + Wallet, + Relayer, +} + +/// A temporary directory for testing +#[derive(Debug)] +pub struct TestDir(PathBuf); + +impl TestDir { + /// Creat a new temp directory. This will have to be manually + /// cleaned up. + pub fn new() -> Self { + let temp = tempdir().unwrap(); + Self(temp.into_path()) + } + + /// Get the path of the directory + pub fn path(&self) -> &Path { + &self.0 + } + + /// Manually remove the test directory from the + /// file system. + pub fn clean_up(self) { + if let Err(e) = std::fs::remove_dir_all(&self.0) { + println!( + "Failed to clean up test dir at {}: {e:?}", + self.0.to_string_lossy() + ); + } + } +} + +impl Default for TestDir { + fn default() -> Self { + Self::new() + } +} diff --git a/apps/src/lib/node/ledger/shell/vote_extensions/bridge_pool_vext.rs b/apps/src/lib/node/ledger/shell/vote_extensions/bridge_pool_vext.rs index 3de6e03c0c..edd83cdb3a 100644 --- a/apps/src/lib/node/ledger/shell/vote_extensions/bridge_pool_vext.rs +++ b/apps/src/lib/node/ledger/shell/vote_extensions/bridge_pool_vext.rs @@ -176,7 +176,7 @@ where /// Takes an iterator over Bridge pool root vote extension instances, /// and returns another iterator. The latter yields /// valid Brige pool root vote extensions, or the reason why these - /// are invalid, in the form of a [`VoteExtensionError`]. + /// are invalid, in the form of a `VoteExtensionError`. #[inline] pub fn validate_bp_roots_vext_list<'iter>( &'iter self, diff --git a/apps/src/lib/node/ledger/shell/vote_extensions/eth_events.rs b/apps/src/lib/node/ledger/shell/vote_extensions/eth_events.rs index c836ecbbc5..891a403f90 100644 --- a/apps/src/lib/node/ledger/shell/vote_extensions/eth_events.rs +++ b/apps/src/lib/node/ledger/shell/vote_extensions/eth_events.rs @@ -308,7 +308,7 @@ where /// Takes an iterator over Ethereum events vote extension instances, /// and returns another iterator. The latter yields /// valid Ethereum events vote extensions, or the reason why these - /// are invalid, in the form of a [`VoteExtensionError`]. + /// are invalid, in the form of a `VoteExtensionError`. #[inline] pub fn validate_eth_events_vext_list<'iter>( &'iter self, diff --git a/apps/src/lib/node/ledger/shell/vote_extensions/val_set_update.rs b/apps/src/lib/node/ledger/shell/vote_extensions/val_set_update.rs index c491c5d6d2..aa8183d9aa 100644 --- a/apps/src/lib/node/ledger/shell/vote_extensions/val_set_update.rs +++ b/apps/src/lib/node/ledger/shell/vote_extensions/val_set_update.rs @@ -161,7 +161,7 @@ where /// Takes an iterator over validator set update vote extension instances, /// and returns another iterator. The latter yields /// valid validator set update vote extensions, or the reason why these - /// are invalid, in the form of a [`VoteExtensionError`]. + /// are invalid, in the form of a `VoteExtensionError`. #[inline] pub fn validate_valset_upd_vext_list( &self, diff --git a/apps/src/lib/node/ledger/shims/abcipp_shim_types.rs b/apps/src/lib/node/ledger/shims/abcipp_shim_types.rs index 331aaeeeba..ca7be9a9e5 100644 --- a/apps/src/lib/node/ledger/shims/abcipp_shim_types.rs +++ b/apps/src/lib/node/ledger/shims/abcipp_shim_types.rs @@ -306,7 +306,7 @@ pub mod shim { #[derive(Debug, Default)] pub struct VerifyHeader; - #[derive(Debug, Default, Clone)] + #[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct TxResult { pub code: u32, pub info: String, diff --git a/apps/src/lib/wallet/cli_utils.rs b/apps/src/lib/wallet/cli_utils.rs new file mode 100644 index 0000000000..ffaf080841 --- /dev/null +++ b/apps/src/lib/wallet/cli_utils.rs @@ -0,0 +1,515 @@ +use std::fs::File; +use std::io::{self, Write}; + +use borsh::BorshSerialize; +use itertools::sorted; +use masp_primitives::zip32::ExtendedFullViewingKey; +use namada::ledger::masp::find_valid_diversifier; +use namada::ledger::wallet::{DecryptionError, FindKeyError}; +use namada::types::key::{PublicKeyHash, RefTo}; +use namada::types::masp::{MaspValue, PaymentAddress}; +use rand_core::OsRng; + +use crate::cli; +use crate::cli::{args, Context}; +use crate::wallet::{read_and_confirm_encryption_password, CliWalletUtils}; + +/// Find shielded address or key +pub fn address_key_find( + ctx: Context, + args::AddrKeyFind { + alias, + unsafe_show_secret, + }: args::AddrKeyFind, +) { + let mut wallet = ctx.wallet; + let alias = alias.to_lowercase(); + if let Ok(viewing_key) = wallet.find_viewing_key(&alias) { + // Check if alias is a viewing key + println!("Viewing key: {}", viewing_key); + if unsafe_show_secret { + // Check if alias is also a spending key + match wallet.find_spending_key(&alias, None) { + Ok(spending_key) => println!("Spending key: {}", spending_key), + Err(FindKeyError::KeyNotFound) => {} + Err(err) => eprintln!("{}", err), + } + } + } else if let Some(payment_addr) = wallet.find_payment_addr(&alias) { + // Failing that, check if alias is a payment address + println!("Payment address: {}", payment_addr); + } else { + // Otherwise alias cannot be referring to any shielded value + println!( + "No shielded address or key with alias {} found. Use the commands \ + `masp list-addrs` and `masp list-keys` to see all the known \ + addresses and keys.", + alias.to_lowercase() + ); + } +} + +/// List spending keys. +pub fn spending_keys_list( + ctx: Context, + args::MaspKeysList { + decrypt, + unsafe_show_secret, + }: args::MaspKeysList, +) { + let wallet = ctx.wallet; + let known_view_keys = wallet.get_viewing_keys(); + let known_spend_keys = wallet.get_spending_keys(); + if known_view_keys.is_empty() { + println!( + "No known keys. Try `masp add --alias my-addr --value ...` to add \ + a new key to the wallet." + ); + } else { + let stdout = std::io::stdout(); + let mut w = stdout.lock(); + writeln!(w, "Known keys:").unwrap(); + for (alias, key) in known_view_keys { + write!(w, " Alias \"{}\"", alias).unwrap(); + let spending_key_opt = known_spend_keys.get(&alias); + // If this alias is associated with a spending key, indicate whether + // or not the spending key is encrypted + // TODO: consider turning if let into match + if let Some(spending_key) = spending_key_opt { + if spending_key.is_encrypted() { + writeln!(w, " (encrypted):") + } else { + writeln!(w, " (not encrypted):") + } + .unwrap(); + } else { + writeln!(w, ":").unwrap(); + } + // Always print the corresponding viewing key + writeln!(w, " Viewing Key: {}", key).unwrap(); + // A subset of viewing keys will have corresponding spending keys. + // Print those too if they are available and requested. + if unsafe_show_secret { + if let Some(spending_key) = spending_key_opt { + match spending_key.get::(decrypt, None) { + // Here the spending key is unencrypted or successfully + // decrypted + Ok(spending_key) => { + writeln!(w, " Spending key: {}", spending_key) + .unwrap(); + } + // Here the key is encrypted but decryption has not been + // requested + Err(DecryptionError::NotDecrypting) if !decrypt => { + continue; + } + // Here the key is encrypted but incorrect password has + // been provided + Err(err) => { + writeln!( + w, + " Couldn't decrypt the spending key: {}", + err + ) + .unwrap(); + } + } + } + } + } + } +} + +/// List payment addresses. +pub fn payment_addresses_list(ctx: Context) { + let wallet = ctx.wallet; + let known_addresses = wallet.get_payment_addrs(); + if known_addresses.is_empty() { + println!( + "No known payment addresses. Try `masp gen-addr --alias my-addr` \ + to generate a new payment address." + ); + } else { + let stdout = std::io::stdout(); + let mut w = stdout.lock(); + writeln!(w, "Known payment addresses:").unwrap(); + for (alias, address) in sorted(known_addresses) { + writeln!(w, " \"{}\": {}", alias, address).unwrap(); + } + } +} + +/// Generate a spending key. +pub fn spending_key_gen( + ctx: Context, + args::MaspSpendKeyGen { + alias, + alias_force, + unsafe_dont_encrypt, + }: args::MaspSpendKeyGen, +) { + let mut wallet = ctx.wallet; + 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); + crate::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); + println!( + "Successfully added a spending key with alias: \"{}\"", + alias + ); +} + +/// Generate a shielded payment address from the given key. +pub fn payment_address_gen( + ctx: Context, + args::MaspPayAddrGen { + alias, + alias_force, + viewing_key, + pin, + }: args::MaspPayAddrGen, +) { + let alias = alias.to_lowercase(); + let viewing_key = ExtendedFullViewingKey::from(viewing_key).fvk.vk; + let (div, _g_d) = find_valid_diversifier(&mut OsRng); + let payment_addr = viewing_key + .to_payment_address(div) + .expect("a PaymentAddress"); + let mut wallet = ctx.wallet; + let alias = wallet + .insert_payment_addr( + alias, + PaymentAddress::from(payment_addr).pinned(pin), + alias_force, + ) + .unwrap_or_else(|| { + eprintln!("Payment address not added"); + cli::safe_exit(1); + }); + crate::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); + println!( + "Successfully generated a payment address with the following alias: {}", + alias, + ); +} + +/// Add a viewing key, spending key, or payment address to wallet. +pub fn address_key_add( + mut ctx: Context, + args::MaspAddrKeyAdd { + alias, + alias_force, + value, + unsafe_dont_encrypt, + }: args::MaspAddrKeyAdd, +) { + let alias = alias.to_lowercase(); + let (alias, typ) = match value { + MaspValue::FullViewingKey(viewing_key) => { + let alias = ctx + .wallet + .insert_viewing_key(alias, viewing_key, alias_force) + .unwrap_or_else(|| { + eprintln!("Viewing key not added"); + cli::safe_exit(1); + }); + (alias, "viewing key") + } + MaspValue::ExtendedSpendingKey(spending_key) => { + let password = + read_and_confirm_encryption_password(unsafe_dont_encrypt); + let alias = ctx + .wallet + .encrypt_insert_spending_key( + alias, + spending_key, + password, + alias_force, + ) + .unwrap_or_else(|| { + eprintln!("Spending key not added"); + cli::safe_exit(1); + }); + (alias, "spending key") + } + MaspValue::PaymentAddress(payment_addr) => { + let alias = ctx + .wallet + .insert_payment_addr(alias, payment_addr, alias_force) + .unwrap_or_else(|| { + eprintln!("Payment address not added"); + cli::safe_exit(1); + }); + (alias, "payment address") + } + }; + crate::wallet::save(&ctx.wallet).unwrap_or_else(|err| eprintln!("{}", err)); + println!( + "Successfully added a {} with the following alias to wallet: {}", + typ, alias, + ); +} + +/// Restore a keypair and an implicit address from the mnemonic code in the +/// wallet. +pub fn key_and_address_restore( + ctx: Context, + args::KeyAndAddressRestore { + scheme, + alias, + alias_force, + unsafe_dont_encrypt, + derivation_path, + }: args::KeyAndAddressRestore, +) { + let mut wallet = ctx.wallet; + let encryption_password = + read_and_confirm_encryption_password(unsafe_dont_encrypt); + let (alias, _key) = wallet + .derive_key_from_user_mnemonic_code( + scheme, + alias, + alias_force, + derivation_path, + encryption_password, + ) + .unwrap_or_else(|err| { + eprintln!("{}", err); + cli::safe_exit(1) + }) + .unwrap_or_else(|| { + println!("No changes are persisted. Exiting."); + cli::safe_exit(0); + }); + crate::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); + println!( + "Successfully added a key and an address with alias: \"{}\"", + alias + ); +} + +/// Generate a new keypair and derive implicit address from it and store them in +/// the wallet. +pub fn key_and_address_gen( + ctx: Context, + args::KeyAndAddressGen { + scheme, + alias, + alias_force, + unsafe_dont_encrypt, + derivation_path, + }: args::KeyAndAddressGen, +) { + let mut wallet = ctx.wallet; + let encryption_password = + read_and_confirm_encryption_password(unsafe_dont_encrypt); + let mut rng = OsRng; + let derivation_path_and_mnemonic_rng = + derivation_path.map(|p| (p, &mut rng)); + let (alias, _key) = wallet + .gen_key( + scheme, + alias, + alias_force, + encryption_password, + derivation_path_and_mnemonic_rng, + ) + .unwrap_or_else(|err| { + eprintln!("{}", err); + cli::safe_exit(1); + }) + .unwrap_or_else(|| { + println!("No changes are persisted. Exiting."); + cli::safe_exit(0); + }); + crate::wallet::save(&wallet).unwrap_or_else(|err| eprintln!("{}", err)); + println!( + "Successfully added a key and an address with alias: \"{}\"", + alias + ); +} + +/// Find a keypair in the wallet store. +pub fn key_find( + ctx: Context, + args::KeyFind { + public_key, + alias, + value, + unsafe_show_secret, + }: args::KeyFind, +) { + let mut wallet = ctx.wallet; + let found_keypair = match public_key { + Some(pk) => wallet.find_key_by_pk(&pk, None), + None => { + let alias = alias.or(value); + match alias { + None => { + eprintln!( + "An alias, public key or public key hash needs to be \ + supplied" + ); + cli::safe_exit(1) + } + Some(alias) => wallet.find_key(alias.to_lowercase(), None), + } + } + }; + match found_keypair { + Ok(keypair) => { + let pkh: PublicKeyHash = (&keypair.ref_to()).into(); + println!("Public key hash: {}", pkh); + println!("Public key: {}", keypair.ref_to()); + if unsafe_show_secret { + println!("Secret key: {}", keypair); + } + } + Err(err) => { + eprintln!("{}", err); + } + } +} + +/// List all known keys. +pub fn key_list( + ctx: Context, + args::KeyList { + decrypt, + unsafe_show_secret, + }: args::KeyList, +) { + let wallet = ctx.wallet; + let known_keys = wallet.get_keys(); + if known_keys.is_empty() { + println!( + "No known keys. Try `key gen --alias my-key` to generate a new \ + key." + ); + } else { + let stdout = io::stdout(); + let mut w = stdout.lock(); + writeln!(w, "Known keys:").unwrap(); + for (alias, (stored_keypair, pkh)) in known_keys { + let encrypted = if stored_keypair.is_encrypted() { + "encrypted" + } else { + "not encrypted" + }; + writeln!(w, " Alias \"{}\" ({}):", alias, encrypted).unwrap(); + if let Some(pkh) = pkh { + writeln!(w, " Public key hash: {}", pkh).unwrap(); + } + match stored_keypair.get::(decrypt, None) { + Ok(keypair) => { + writeln!(w, " Public key: {}", keypair.ref_to()) + .unwrap(); + if unsafe_show_secret { + writeln!(w, " Secret key: {}", keypair).unwrap(); + } + } + Err(DecryptionError::NotDecrypting) if !decrypt => { + continue; + } + Err(err) => { + writeln!(w, " Couldn't decrypt the keypair: {}", err) + .unwrap(); + } + } + } + } +} + +/// Export a keypair to a file. +pub fn key_export(ctx: Context, args::KeyExport { alias }: args::KeyExport) { + let mut wallet = ctx.wallet; + wallet + .find_key(alias.to_lowercase(), None) + .map(|keypair| { + let file_data = keypair + .try_to_vec() + .expect("Encoding keypair shouldn't fail"); + let file_name = format!("key_{}", alias.to_lowercase()); + let mut file = File::create(&file_name).unwrap(); + + file.write_all(file_data.as_ref()).unwrap(); + println!("Exported to file {}", file_name); + }) + .unwrap_or_else(|err| { + eprintln!("{}", err); + cli::safe_exit(1) + }) +} + +/// List all known addresses. +pub fn address_list(ctx: Context) { + let wallet = ctx.wallet; + let known_addresses = wallet.get_addresses(); + if known_addresses.is_empty() { + println!( + "No known addresses. Try `address gen --alias my-addr` to \ + generate a new implicit address." + ); + } else { + let stdout = io::stdout(); + let mut w = stdout.lock(); + writeln!(w, "Known addresses:").unwrap(); + for (alias, address) in sorted(known_addresses) { + writeln!(w, " \"{}\": {}", alias, address.to_pretty_string()) + .unwrap(); + } + } +} + +/// Find address (alias) by its alias (address). +pub fn address_or_alias_find(ctx: Context, args: args::AddressOrAliasFind) { + let wallet = ctx.wallet; + if args.address.is_some() && args.alias.is_some() { + panic!( + "This should not be happening: clap should emit its own error \ + message." + ); + } else if args.alias.is_some() { + if let Some(address) = wallet.find_address(args.alias.as_ref().unwrap()) + { + println!("Found address {}", address.to_pretty_string()); + } else { + println!( + "No address with alias {} found. Use the command `address \ + list` to see all the known addresses.", + args.alias.unwrap().to_lowercase() + ); + } + } else if args.address.is_some() { + if let Some(alias) = wallet.find_alias(args.address.as_ref().unwrap()) { + println!("Found alias {}", alias); + } else { + println!( + "No alias with address {} found. Use the command `address \ + list` to see all the known addresses.", + args.address.unwrap() + ); + } + } +} + +/// Add an address to the wallet. +pub fn address_add(ctx: Context, args: args::AddressAdd) { + let mut wallet = ctx.wallet; + if wallet + .add_address( + args.alias.clone().to_lowercase(), + args.address, + args.alias_force, + ) + .is_none() + { + eprintln!("Address not added"); + cli::safe_exit(1); + } + 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/wallet/mod.rs b/apps/src/lib/wallet/mod.rs index 32d2ab6eac..464d276ecc 100644 --- a/apps/src/lib/wallet/mod.rs +++ b/apps/src/lib/wallet/mod.rs @@ -1,3 +1,4 @@ +pub mod cli_utils; pub mod defaults; pub mod pre_genesis; mod store; 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/encoding_spec/Cargo.toml b/encoding_spec/Cargo.toml index 8ac5dc567d..61fe37995d 100644 --- a/encoding_spec/Cargo.toml +++ b/encoding_spec/Cargo.toml @@ -22,5 +22,5 @@ abciplus = [ namada = {path = "../shared"} borsh.workspace = true itertools.workspace = true -lazy_static = "1.4.0" +lazy_static.workspace = true madato = "0.5.3" 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/shared/src/ledger/events/log.rs b/shared/src/ledger/events/log.rs index 931f0088c4..a2dc3978d0 100644 --- a/shared/src/ledger/events/log.rs +++ b/shared/src/ledger/events/log.rs @@ -67,6 +67,12 @@ impl EventLog { /// Returns a new iterator over this [`EventLog`]. #[inline] + pub fn iter(&self) -> impl Iterator { + self.queue.iter() + } + + /// Returns a filtering iterator over this [`EventLog`]. + #[inline] pub fn iter_with_matcher( &self, matcher: dumb_queries::QueryMatcher, diff --git a/shared/src/ledger/masp.rs b/shared/src/ledger/masp.rs index b6ccd8672b..28e1fd8e13 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 @@ -744,10 +741,10 @@ impl ShieldedContext { /// associated to notes, memos, and diversifiers. And the set of notes that /// we have spent are updated. The witness map is maintained to make it /// easier to construct note merkle paths in other code. See - /// . - pub async fn scan_tx( + /// + pub async fn scan_tx( &mut self, - client: &U::C, + client: &C, height: BlockHeight, index: TxIndex, epoch: Epoch, @@ -883,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 @@ -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 + 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 { @@ -993,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, @@ -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/queries/types.rs b/shared/src/ledger/queries/types.rs index 720b1ce7d6..78f136cad2 100644 --- a/shared/src/ledger/queries/types.rs +++ b/shared/src/ledger/queries/types.rs @@ -108,7 +108,7 @@ pub trait Client { ) -> Result; /// `/abci_info`: get information about the ABCI application. - async fn abci_info(&self) -> Result { + async fn abci_info(&self) -> Result { Ok(self.perform(abci_info::Request).await?.response) } @@ -271,7 +271,7 @@ pub trait Client { /// /// Returns empty result (200 OK) on success, no response in case of an /// error. - async fn health(&self) -> Result<(), Error> { + async fn health(&self) -> Result<(), RpcError> { self.perform(health::Request).await?; Ok(()) } diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index 36644a3e14..0c333a9eb9 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -18,7 +18,7 @@ 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_core::types::token::{MaspDenom, TokenAddress}; use namada_proof_of_stake::parameters::PosParams; use namada_proof_of_stake::types::CommissionPair; use prost::EncodeError; @@ -35,7 +35,10 @@ use crate::ibc_proto::cosmos::base::v1beta1::Coin; use crate::ledger::args::{self, InputAmount}; use crate::ledger::governance::storage as gov_storage; use crate::ledger::masp::{ShieldedContext, ShieldedUtils}; -use crate::ledger::rpc::{self, validate_amount, TxBroadcastData, TxResponse}; +use crate::ledger::rpc::{ + self, format_denominated_amount, validate_amount, TxBroadcastData, + TxResponse, +}; use crate::ledger::signing::{tx_signer, wrap_tx, TxSigningKey}; use crate::ledger::wallet::{Wallet, WalletUtils}; use crate::proto::{Code, Data, MaspBuilder, Section, Tx}; @@ -1056,7 +1059,10 @@ pub async fn build_bond< // TODO Should we state the same error message for the native token? check_balance_too_low_err( - &args.native_token, + &TokenAddress { + address: args.native_token, + sub_prefix: None, + }, bond_source, args.amount, balance_key, @@ -1157,7 +1163,10 @@ pub async fn build_ibc_transfer< }; check_balance_too_low_err( - &token, + &TokenAddress { + address: token.clone(), + sub_prefix: sub_prefix.clone(), + }, &source, args.amount, balance_key, @@ -1241,7 +1250,7 @@ pub async fn build_ibc_transfer< /// 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, + U: ShieldedUtils, >( asset_types: &mut HashSet<(Address, Option, MaspDenom, Epoch)>, shielded: &mut ShieldedContext, @@ -1262,7 +1271,7 @@ async fn add_asset_type< /// type information. async fn used_asset_types< C: crate::ledger::queries::Client + Sync, - U: ShieldedUtils, + U: ShieldedUtils, P, R, K, @@ -1314,7 +1323,7 @@ async fn used_asset_types< pub async fn build_transfer< C: crate::ledger::queries::Client + Sync, V: WalletUtils, - U: ShieldedUtils, + U: ShieldedUtils, >( client: &C, wallet: &mut Wallet, @@ -1368,9 +1377,15 @@ pub async fn build_transfer< args.amount = InputAmount::Validated(validated_amount); args.tx.fee_amount = InputAmount::Validated(validate_fee); - + let sub_prefix = args + .sub_prefix + .as_ref() + .map(|k| k.parse().expect("Could not parse multi-token sub-prefix")); check_balance_too_low_err::( - &token, + &TokenAddress { + address: token.clone(), + sub_prefix: sub_prefix.clone(), + }, &source, validated_amount.amount, balance_key, @@ -1799,7 +1814,7 @@ async fn target_exists_or_err( /// given amount, along with the balance even existing. force /// overrides this async fn check_balance_too_low_err( - token: &Address, + token: &TokenAddress, source: &Address, amount: token::Amount, balance_key: storage::Key, @@ -1818,14 +1833,14 @@ async fn check_balance_too_low_err( transfer is {} and the balance is {}.", source, token, - amount.to_string_native(), - balance.to_string_native() + format_denominated_amount(client, token, amount).await, + format_denominated_amount(client, token, balance).await, ); Ok(()) } else { Err(Error::BalanceTooLow( source.clone(), - token.clone(), + token.address.clone(), amount.to_string_native(), balance.to_string_native(), )) @@ -1842,7 +1857,10 @@ async fn check_balance_too_low_err( ); Ok(()) } else { - Err(Error::NoBalanceForToken(source.clone(), token.clone())) + Err(Error::NoBalanceForToken( + source.clone(), + token.address.clone(), + )) } } } diff --git a/tests/Cargo.toml b/tests/Cargo.toml index b36bfcabb6..65c0d306f3 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -25,6 +25,7 @@ abciplus = [ "namada_tx_prelude/abciplus", "namada_apps/abciplus" ] + wasm-runtime = ["namada/wasm-runtime"] [dependencies] @@ -33,12 +34,16 @@ namada_core = {path = "../core", features = ["testing"]} namada_test_utils = {path = "../test_utils"} namada_vp_prelude = {path = "../vp_prelude"} namada_tx_prelude = {path = "../tx_prelude"} +async-trait.workspace = true chrono.workspace = true +clap.workspace = true concat-idents.workspace = true derivative.workspace = true hyper = {version = "0.14.20", features = ["full"]} ibc-relayer-types.workspace = true ibc-relayer.workspace = true +lazy_static.workspace = true +num-traits.workspace = true prost.workspace = true regex.workspace = true serde_json.workspace = true diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 9b579bef92..2abb7749ac 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -19,25 +19,23 @@ use std::time::{Duration, Instant}; use borsh::BorshSerialize; use color_eyre::eyre::Result; use data_encoding::HEXLOWER; -use namada::types::address::{btc, eth, masp_rewards, Address}; +use namada::types::address::Address; use namada::types::governance::ProposalType; use namada::types::storage::Epoch; use namada::types::token; -use namada_apps::client::tx::CLIShieldedUtils; use namada_apps::config::ethereum_bridge; use namada_apps::config::genesis::genesis_config::{ GenesisConfig, ParametersConfig, PosParamsConfig, }; use namada_apps::config::utils::convert_tm_addr_to_socket_addr; use namada_apps::facade::tendermint_config::net::Address as TendermintAddress; -use namada_core::types::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; use namada_test_utils::TestWasms; use serde_json::json; use setup::constants::*; use setup::Test; use super::helpers::{ - get_height, is_debug_mode, wait_for_block_height, wait_for_wasm_pre_compile, + get_height, wait_for_block_height, wait_for_wasm_pre_compile, }; use super::setup::{get_all_wasms_hashes, set_ethereum_bridge_mode, NamadaCmd}; use crate::e2e::helpers::{ @@ -651,1215 +649,6 @@ fn ledger_txs_and_queries() -> Result<()> { Ok(()) } -/// In this test we: -/// 1. Run the ledger node -/// 2. Attempt to spend 10 BTC at SK(A) to PA(B) -/// 3. Attempt to spend 15 BTC at SK(A) to Bertha -/// 4. Send 20 BTC from Albert to PA(A) -/// 5. Attempt to spend 10 ETH at SK(A) to PA(B) -/// 6. Spend 7 BTC at SK(A) to PA(B) -/// 7. Spend 7 BTC at SK(A) to PA(B) -/// 8. Attempt to spend 7 BTC at SK(A) to PA(B) -/// 9. Spend 6 BTC at SK(A) to PA(B) -/// 10. Assert BTC balance at VK(A) is 0 -/// 11. Assert ETH balance at VK(A) is 0 -/// 12. Assert balance at VK(B) is 10 BTC -/// 13. Send 10 BTC from SK(B) to Bertha - -#[test] -fn masp_txs_and_queries() -> Result<()> { - // Download the shielded pool parameters before starting node - let _ = CLIShieldedUtils::new(PathBuf::new()); - // Lengthen epoch to ensure that a transaction can be constructed and - // submitted within the same block. Necessary to ensure that conversion is - // not invalidated. - let test = setup::network( - |genesis| { - let parameters = ParametersConfig { - epochs_per_year: epochs_per_year_from_min_duration( - if is_debug_mode() { 3600 } else { 360 }, - ), - min_num_of_blocks: 1, - ..genesis.parameters - }; - GenesisConfig { - parameters, - ..genesis - } - }, - None, - )?; - set_ethereum_bridge_mode( - &test, - &test.net.chain_id, - &Who::Validator(0), - ethereum_bridge::ledger::Mode::Off, - None, - ); - - // 1. Run the ledger node - let _bg_ledger = - start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? - .background(); - - let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); - - let _ep1 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - let txs_args = vec![ - // 2. Attempt to spend 10 BTC at SK(A) to PA(B) - ( - vec![ - "transfer", - "--source", - A_SPENDING_KEY, - "--target", - AB_PAYMENT_ADDRESS, - "--token", - BTC, - "--amount", - "10", - "--node", - &validator_one_rpc, - ], - "No balance found", - ), - // 3. Attempt to spend 15 BTC at SK(A) to Bertha - ( - vec![ - "transfer", - "--source", - A_SPENDING_KEY, - "--target", - BERTHA, - "--token", - BTC, - "--amount", - "15", - "--node", - &validator_one_rpc, - ], - "No balance found", - ), - // 4. Send 20 BTC from Albert to PA(A) - ( - vec![ - "transfer", - "--source", - ALBERT, - "--target", - AA_PAYMENT_ADDRESS, - "--token", - BTC, - "--amount", - "20", - "--node", - &validator_one_rpc, - ], - "Transaction is valid", - ), - // 5. Attempt to spend 10 ETH at SK(A) to PA(B) - ( - vec![ - "transfer", - "--source", - A_SPENDING_KEY, - "--target", - AB_PAYMENT_ADDRESS, - "--token", - ETH, - "--amount", - "10", - "--node", - &validator_one_rpc, - ], - "No balance found", - ), - // 6. Spend 7 BTC at SK(A) to PA(B) - ( - vec![ - "transfer", - "--source", - A_SPENDING_KEY, - "--target", - AB_PAYMENT_ADDRESS, - "--token", - BTC, - "--amount", - "7", - "--node", - &validator_one_rpc, - ], - "Transaction is valid", - ), - // 7. Spend 7 BTC at SK(A) to PA(B) - ( - vec![ - "transfer", - "--source", - A_SPENDING_KEY, - "--target", - BB_PAYMENT_ADDRESS, - "--token", - BTC, - "--amount", - "7", - "--node", - &validator_one_rpc, - ], - "Transaction is valid", - ), - // 8. Attempt to spend 7 BTC at SK(A) to PA(B) - ( - vec![ - "transfer", - "--source", - A_SPENDING_KEY, - "--target", - BB_PAYMENT_ADDRESS, - "--token", - BTC, - "--amount", - "7", - "--node", - &validator_one_rpc, - ], - "is lower than the amount to be transferred and fees", - ), - // 9. Spend 6 BTC at SK(A) to PA(B) - ( - vec![ - "transfer", - "--source", - A_SPENDING_KEY, - "--target", - BB_PAYMENT_ADDRESS, - "--token", - BTC, - "--amount", - "6", - "--node", - &validator_one_rpc, - ], - "Transaction is valid", - ), - // 10. Assert BTC balance at VK(A) is 0 - ( - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - BTC, - "--node", - &validator_one_rpc, - ], - "No shielded btc balance found", - ), - // 11. Assert ETH balance at VK(A) is 0 - ( - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - ETH, - "--node", - &validator_one_rpc, - ], - "No shielded eth balance found", - ), - // 12. Assert balance at VK(B) is 10 BTC - ( - vec![ - "balance", - "--owner", - AB_VIEWING_KEY, - "--node", - &validator_one_rpc, - ], - "btc : 20", - ), - // 13. Send 10 BTC from SK(B) to Bertha - ( - vec![ - "transfer", - "--source", - B_SPENDING_KEY, - "--target", - BERTHA, - "--token", - BTC, - "--amount", - "20", - "--node", - &validator_one_rpc, - ], - "Transaction is valid", - ), - ]; - - for (tx_args, tx_result) 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() - } else { - tx_args.clone() - }; - let mut client = run!(test, Bin::Client, tx_args, Some(720))?; - - if *tx_result == "Transaction is valid" && !dry_run { - client.exp_string("Transaction accepted")?; - client.exp_string("Transaction applied")?; - } - client.exp_string(tx_result)?; - } - } - - Ok(()) -} - -/// In this test we: -/// 1. Run the ledger node -/// 2. Assert PPA(C) cannot be recognized by incorrect viewing key -/// 3. Assert PPA(C) has not transaction pinned to it -/// 4. Send 20 BTC from Albert to PPA(C) -/// 5. Assert PPA(C) has the 20 BTC transaction pinned to it - -#[test] -fn masp_pinned_txs() -> Result<()> { - // Download the shielded pool parameters before starting node - let _ = CLIShieldedUtils::new(PathBuf::new()); - // Lengthen epoch to ensure that a transaction can be constructed and - // submitted within the same block. Necessary to ensure that conversion is - // not invalidated. - let test = setup::network( - |genesis| { - let parameters = ParametersConfig { - epochs_per_year: epochs_per_year_from_min_duration(120), - ..genesis.parameters - }; - GenesisConfig { - parameters, - ..genesis - } - }, - None, - )?; - set_ethereum_bridge_mode( - &test, - &test.net.chain_id, - &Who::Validator(0), - ethereum_bridge::ledger::Mode::Off, - None, - ); - - // 1. Run the ledger node - let _bg_ledger = - start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? - .background(); - - let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); - - // Wait till epoch boundary - let _ep0 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Assert PPA(C) cannot be recognized by incorrect viewing key - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AC_PAYMENT_ADDRESS, - "--token", - BTC, - "--node", - &validator_one_rpc - ], - Some(300) - )?; - client.send_line(AB_VIEWING_KEY)?; - client.exp_string("Supplied viewing key cannot decode transactions to")?; - client.assert_success(); - - // Assert PPA(C) has no transaction pinned to it - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AC_PAYMENT_ADDRESS, - "--token", - BTC, - "--node", - &validator_one_rpc - ], - Some(300) - )?; - client.send_line(AC_VIEWING_KEY)?; - client.exp_string("has not yet been consumed")?; - client.assert_success(); - - // Wait till epoch boundary - let _ep1 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Send 20 BTC from Albert to PPA(C) - let mut client = run!( - test, - Bin::Client, - vec![ - "transfer", - "--source", - ALBERT, - "--target", - AC_PAYMENT_ADDRESS, - "--token", - BTC, - "--amount", - "20", - "--node", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string("Transaction accepted")?; - client.exp_string("Transaction applied")?; - client.exp_string("Transaction is valid")?; - client.assert_success(); - - // Wait till epoch boundary - // This makes it more consistent for some reason? - let _ep2 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Assert PPA(C) has the 20 BTC transaction pinned to it - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AC_PAYMENT_ADDRESS, - "--token", - BTC, - "--node", - &validator_one_rpc - ], - Some(300) - )?; - client.send_line(AC_VIEWING_KEY)?; - client.exp_string("Received 20 btc")?; - client.assert_success(); - - // Assert PPA(C) has no NAM pinned to it - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AC_PAYMENT_ADDRESS, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(300) - )?; - client.send_line(AC_VIEWING_KEY)?; - client.exp_string("Received no shielded nam")?; - client.assert_success(); - - // Wait till epoch boundary - let _ep1 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Assert PPA(C) does not NAM pinned to it on epoch boundary - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AC_PAYMENT_ADDRESS, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(300) - )?; - client.send_line(AC_VIEWING_KEY)?; - client.exp_string("Received no shielded nam")?; - client.assert_success(); - - Ok(()) -} - -/// In this test we verify that users of the MASP receive the correct rewards -/// for leaving their assets in the pool for varying periods of time. - -#[test] -fn masp_incentives() -> Result<()> { - // The number of decimal places used by BTC amounts. - const BTC_DENOMINATION: u8 = 8; - // The number of decimal places used by ETH amounts. - const ETH_DENOMINATION: u8 = 18; - // Download the shielded pool parameters before starting node - let _ = CLIShieldedUtils::new(PathBuf::new()); - // Lengthen epoch to ensure that a transaction can be constructed and - // submitted within the same block. Necessary to ensure that conversion is - // not invalidated. - let test = setup::network( - |genesis| { - let parameters = ParametersConfig { - epochs_per_year: epochs_per_year_from_min_duration( - if is_debug_mode() { 340 } else { 85 }, - ), - min_num_of_blocks: 1, - ..genesis.parameters - }; - GenesisConfig { - parameters, - ..genesis - } - }, - None, - )?; - set_ethereum_bridge_mode( - &test, - &test.net.chain_id, - &Who::Validator(0), - ethereum_bridge::ledger::Mode::Off, - None, - ); - - // 1. Run the ledger node - let _bg_ledger = - start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))? - .background(); - - let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); - - // Wait till epoch boundary - let ep0 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Send 20 BTC from Albert to PA(A) - let mut client = run!( - test, - Bin::Client, - vec![ - "transfer", - "--source", - ALBERT, - "--target", - AA_PAYMENT_ADDRESS, - "--token", - BTC, - "--amount", - "20", - "--node", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string("Transaction accepted")?; - client.exp_string("Transaction applied")?; - client.exp_string("Transaction is valid")?; - client.assert_success(); - - // Assert BTC balance at VK(A) is 20 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - BTC, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - client.exp_string("btc: 20")?; - client.assert_success(); - - // Assert NAM balance at VK(A) is 0 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - client.exp_string("No shielded nam balance found")?; - client.assert_success(); - - let masp_rewards = masp_rewards(); - - // Wait till epoch boundary - let ep1 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Assert BTC balance at VK(A) is 20 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - BTC, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - client.exp_string("btc: 20")?; - client.assert_success(); - - let amt20 = token::Amount::from_uint(20, BTC_DENOMINATION).unwrap(); - let amt10 = token::Amount::from_uint(10, ETH_DENOMINATION).unwrap(); - - // Assert NAM balance at VK(A) is 20*BTC_reward*(epoch_1-epoch_0) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep1.0 - ep0.0); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated,))?; - client.assert_success(); - - // Assert NAM balance at MASP pool is 20*BTC_reward*(epoch_1-epoch_0) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - MASP, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep1.0 - ep0.0); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated,))?; - client.assert_success(); - - // Wait till epoch boundary - let ep2 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Assert BTC balance at VK(A) is 20 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - BTC, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - client.exp_string("btc: 20")?; - client.assert_success(); - - // Assert NAM balance at VK(A) is 20*BTC_reward*(epoch_2-epoch_0) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep2.0 - ep0.0); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated,))?; - client.assert_success(); - - // Assert NAM balance at MASP pool is 20*BTC_reward*(epoch_2-epoch_0) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - MASP, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep2.0 - ep0.0); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated,))?; - client.assert_success(); - - // Wait till epoch boundary - let ep3 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Send 10 ETH from Albert to PA(B) - let mut client = run!( - test, - Bin::Client, - vec![ - "transfer", - "--source", - ALBERT, - "--target", - AB_PAYMENT_ADDRESS, - "--token", - ETH, - "--amount", - "10", - "--node", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string("Transaction accepted")?; - client.exp_string("Transaction applied")?; - client.exp_string("Transaction is valid")?; - client.assert_success(); - - // Assert ETH balance at VK(B) is 10 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AB_VIEWING_KEY, - "--token", - ETH, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - client.exp_string("eth: 10")?; - client.assert_success(); - - // Assert NAM balance at VK(B) is 0 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AB_VIEWING_KEY, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - client.exp_string("No shielded nam balance found")?; - client.assert_success(); - - // Wait till epoch boundary - let ep4 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Assert ETH balance at VK(B) is 10 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AB_VIEWING_KEY, - "--token", - ETH, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - client.exp_string("eth: 10")?; - client.assert_success(); - - // Assert NAM balance at VK(B) is 10*ETH_reward*(epoch_4-epoch_3) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AB_VIEWING_KEY, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = (amt10 * masp_rewards[&(eth(), None)]).0 * (ep4.0 - ep3.0); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated,))?; - client.assert_success(); - - // Assert NAM balance at MASP pool is - // 20*BTC_reward*(epoch_4-epoch_0)+10*ETH_reward*(epoch_4-epoch_3) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - MASP, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - 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 denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated))?; - client.assert_success(); - - // Wait till epoch boundary - let ep5 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Send 10 ETH from SK(B) to Christel - let mut client = run!( - test, - Bin::Client, - vec![ - "transfer", - "--source", - B_SPENDING_KEY, - "--target", - CHRISTEL, - "--token", - ETH, - "--amount", - "10", - "--signer", - BERTHA, - "--node", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string("Transaction accepted")?; - client.exp_string("Transaction applied")?; - client.exp_string("Transaction is valid")?; - client.assert_success(); - - // Assert ETH balance at VK(B) is 0 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AB_VIEWING_KEY, - "--token", - ETH, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - client.exp_string("No shielded eth balance found")?; - client.assert_success(); - - // let mut ep = get_epoch(&test, &validator_one_rpc)?; - - // Assert NAM balance at VK(B) is 10*ETH_reward*(ep-epoch_3) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AB_VIEWING_KEY, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = (amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated,))?; - client.assert_success(); - - // ep = get_epoch(&test, &validator_one_rpc)?; - // Assert NAM balance at MASP pool is - // 20*BTC_reward*(epoch_5-epoch_0)+10*ETH_reward*(epoch_5-epoch_3) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - MASP, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = ((amt20 * masp_rewards[&(btc(), None)]).0 * (ep5.0 - ep0.0)) - + ((amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0)); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated))?; - client.assert_success(); - - // Wait till epoch boundary - let ep6 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Send 20 BTC from SK(A) to Christel - let mut client = run!( - test, - Bin::Client, - vec![ - "transfer", - "--source", - A_SPENDING_KEY, - "--target", - CHRISTEL, - "--token", - BTC, - "--amount", - "20", - "--signer", - ALBERT, - "--node", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string("Transaction accepted")?; - client.exp_string("Transaction applied")?; - client.exp_string("Transaction is valid")?; - client.assert_success(); - - // Assert BTC balance at VK(A) is 0 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - BTC, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - client.exp_string("No shielded btc balance found")?; - client.assert_success(); - - // Assert NAM balance at VK(A) is 20*BTC_reward*(epoch_6-epoch_0) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep6.0 - ep0.0); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated,))?; - client.assert_success(); - - // Assert NAM balance at MASP pool is - // 20*BTC_reward*(epoch_6-epoch_0)+20*ETH_reward*(epoch_5-epoch_3) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - MASP, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - 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 denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated,))?; - client.assert_success(); - - // Wait till epoch boundary - let _ep7 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Assert NAM balance at VK(A) is 20*BTC_reward*(epoch_6-epoch_0) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep6.0 - ep0.0); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated))?; - client.assert_success(); - - // Assert NAM balance at VK(B) is 10*ETH_reward*(epoch_5-epoch_3) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AB_VIEWING_KEY, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - let amt = (amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0); - let denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated,))?; - client.assert_success(); - - // Assert NAM balance at MASP pool is - // 20*BTC_reward*(epoch_6-epoch_0)+10*ETH_reward*(epoch_5-epoch_3) - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - MASP, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - 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 denominated = DenominatedAmount { - amount: amt, - denom: NATIVE_MAX_DECIMAL_PLACES.into(), - }; - client.exp_string(&format!("nam: {}", denominated,))?; - client.assert_success(); - - // Wait till epoch boundary to prevent conversion expiry during transaction - // construction - let _ep8 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Send 10*ETH_reward*(epoch_5-epoch_3) NAM from SK(B) to Christel - let mut client = run!( - test, - Bin::Client, - vec![ - "transfer", - "--source", - B_SPENDING_KEY, - "--target", - CHRISTEL, - "--token", - NAM, - "--amount", - &((amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0)) - .to_string_native(), - "--signer", - BERTHA, - "--node", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string("Transaction accepted")?; - client.exp_string("Transaction applied")?; - client.exp_string("Transaction is valid")?; - client.assert_success(); - - // Wait till epoch boundary - let _ep9 = epoch_sleep(&test, &validator_one_rpc, 720)?; - - // Send 20*BTC_reward*(epoch_6-epoch_0) NAM from SK(A) to Bertha - let mut client = run!( - test, - Bin::Client, - vec![ - "transfer", - "--source", - A_SPENDING_KEY, - "--target", - BERTHA, - "--token", - NAM, - "--amount", - &((amt20 * masp_rewards[&(btc(), None)]).0 * (ep6.0 - ep0.0)) - .to_string_native(), - "--signer", - ALBERT, - "--node", - &validator_one_rpc - ], - Some(300) - )?; - client.exp_string("Transaction accepted")?; - client.exp_string("Transaction applied")?; - client.exp_string("Transaction is valid")?; - client.assert_success(); - - // Assert NAM balance at VK(A) is 0 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AA_VIEWING_KEY, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - client.exp_string("No shielded nam balance found")?; - client.assert_success(); - - // Assert NAM balance at VK(B) is 0 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - AB_VIEWING_KEY, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - client.exp_string("No shielded nam balance found")?; - client.assert_success(); - - // Assert NAM balance at MASP pool is 0 - let mut client = run!( - test, - Bin::Client, - vec![ - "balance", - "--owner", - MASP, - "--token", - NAM, - "--node", - &validator_one_rpc - ], - Some(60) - )?; - client.exp_string("nam: 0")?; - client.assert_success(); - - Ok(()) -} - /// In this test we: /// 1. Run the ledger node /// 2. Submit an invalid transaction (disallowed by state machine) diff --git a/tests/src/e2e/setup.rs b/tests/src/e2e/setup.rs index 6d1a2496b0..5f148b4fc2 100644 --- a/tests/src/e2e/setup.rs +++ b/tests/src/e2e/setup.rs @@ -21,7 +21,6 @@ use eyre::{eyre, Context}; use itertools::{Either, Itertools}; use namada::types::chain::ChainId; use namada_apps::client::utils; -use namada_apps::client::utils::REDUCED_CLI_PRINTING; use namada_apps::config::genesis::genesis_config::{self, GenesisConfig}; use namada_apps::config::{ethereum_bridge, Config}; use namada_apps::{config, wallet}; @@ -42,7 +41,7 @@ pub const APPS_PACKAGE: &str = "namada_apps"; pub const ENV_VAR_DEBUG: &str = "NAMADA_E2E_DEBUG"; /// Env. var for keeping temporary files created by the E2E tests -const ENV_VAR_KEEP_TEMP: &str = "NAMADA_E2E_KEEP_TEMP"; +pub const ENV_VAR_KEEP_TEMP: &str = "NAMADA_E2E_KEEP_TEMP"; /// Env. var for temporary path const ENV_VAR_TEMP_PATH: &str = "NAMADA_E2E_TEMP_PATH"; @@ -155,7 +154,6 @@ pub fn network( eprintln!("Failed setting up colorful error reports {}", err); } }); - env::set_var(REDUCED_CLI_PRINTING, "true"); let working_dir = working_dir(); let test_dir = TestDir::new(); diff --git a/tests/src/integration.rs b/tests/src/integration.rs new file mode 100644 index 0000000000..8642e0e03c --- /dev/null +++ b/tests/src/integration.rs @@ -0,0 +1,3 @@ +mod masp; +mod setup; +mod utils; diff --git a/tests/src/integration/masp.rs b/tests/src/integration/masp.rs new file mode 100644 index 0000000000..c48377896e --- /dev/null +++ b/tests/src/integration/masp.rs @@ -0,0 +1,1206 @@ +use std::path::PathBuf; + +use color_eyre::eyre::Result; +use color_eyre::owo_colors::OwoColorize; +use namada_apps::client::tx::CLIShieldedUtils; +use namada_apps::node::ledger::shell::testing::client::run; +use namada_apps::node::ledger::shell::testing::utils::Bin; +use namada_core::types::address::{btc, eth, masp_rewards}; +use namada_core::types::token; +use namada_core::types::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; +use test_log::test; + +use super::setup; +use crate::e2e::setup::constants::{ + AA_PAYMENT_ADDRESS, AA_VIEWING_KEY, AB_PAYMENT_ADDRESS, AB_VIEWING_KEY, + AC_PAYMENT_ADDRESS, AC_VIEWING_KEY, ALBERT, A_SPENDING_KEY, + BB_PAYMENT_ADDRESS, BERTHA, BTC, B_SPENDING_KEY, CHRISTEL, ETH, MASP, NAM, +}; +use crate::integration::utils::CapturedOutput; + +/// In this test we verify that users of the MASP receive the correct rewards +/// for leaving their assets in the pool for varying periods of time. +#[test] +fn masp_incentives() -> Result<()> { + // The number of decimal places used by BTC amounts. + const BTC_DENOMINATION: u8 = 8; + // The number of decimal places used by ETH amounts. + const ETH_DENOMINATION: u8 = 18; + // This address doesn't matter for tests. But an argument is required. + let validator_one_rpc = "127.0.0.1:26567"; + // Download the shielded pool parameters before starting node + let _ = CLIShieldedUtils::new(PathBuf::new()); + // Lengthen epoch to ensure that a transaction can be constructed and + // submitted within the same block. Necessary to ensure that conversion is + // not invalidated. + let mut node = setup::setup()?; + // Wait till epoch boundary + let ep0 = node.next_epoch(); + // Send 20 BTC from Albert to PA + run( + &node, + Bin::Client, + vec![ + "transfer", + "--source", + ALBERT, + "--target", + AA_PAYMENT_ADDRESS, + "--token", + BTC, + "--amount", + "20", + "--node", + validator_one_rpc, + ], + )?; + node.assert_success(); + + // Assert BTC balance at VK(A) is 20 + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AA_VIEWING_KEY, + "--token", + BTC, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("btc: 20")); + + // Assert NAM balance at VK(A) is 0 + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AA_VIEWING_KEY, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("No shielded nam balance found")); + + let masp_rewards = masp_rewards(); + + // Wait till epoch boundary + let ep1 = node.next_epoch(); + + // Assert BTC balance at VK(A) is 20 + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AA_VIEWING_KEY, + "--token", + BTC, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("btc: 20")); + + let amt20 = token::Amount::from_uint(20, BTC_DENOMINATION).unwrap(); + let amt10 = token::Amount::from_uint(10, ETH_DENOMINATION).unwrap(); + + // Assert NAM balance at VK(A) is 20*BTC_reward*(epoch_1-epoch_0) + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AA_VIEWING_KEY, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + + let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep1.0 - ep0.0); + let denominated = DenominatedAmount { + amount: amt, + denom: NATIVE_MAX_DECIMAL_PLACES.into(), + }; + assert!(captured.result.is_ok()); + assert!(captured.contains(&format!("nam: {}", denominated))); + + // Assert NAM balance at MASP pool is 20*BTC_reward*(epoch_1-epoch_0) + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + MASP, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep1.0 - ep0.0); + let denominated = DenominatedAmount { + amount: amt, + denom: NATIVE_MAX_DECIMAL_PLACES.into(), + }; + assert!(captured.result.is_ok()); + assert!(captured.contains(&format!("nam: {}", denominated))); + + // Wait till epoch boundary + let ep2 = node.next_epoch(); + + // Assert BTC balance at VK(A) is 20 + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AA_VIEWING_KEY, + "--token", + BTC, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("btc: 20")); + + // Assert NAM balance at VK(A) is 20*BTC_reward*(epoch_2-epoch_0) + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AA_VIEWING_KEY, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep2.0 - ep0.0); + let denominated = DenominatedAmount { + amount: amt, + denom: NATIVE_MAX_DECIMAL_PLACES.into(), + }; + assert!(captured.result.is_ok()); + assert!(captured.contains(&format!("nam: {}", denominated))); + + // Assert NAM balance at MASP pool is 20*BTC_reward*(epoch_2-epoch_0) + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + MASP, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep2.0 - ep0.0); + let denominated = DenominatedAmount { + amount: amt, + denom: NATIVE_MAX_DECIMAL_PLACES.into(), + }; + assert!(captured.result.is_ok()); + assert!(captured.contains(&format!("nam: {}", denominated))); + + // Wait till epoch boundary + let ep3 = node.next_epoch(); + + // Send 10 ETH from Albert to PA(B) + run( + &node, + Bin::Client, + vec![ + "transfer", + "--source", + ALBERT, + "--target", + AB_PAYMENT_ADDRESS, + "--token", + ETH, + "--amount", + "10", + "--node", + validator_one_rpc, + ], + )?; + node.assert_success(); + + // Assert ETH balance at VK(B) is 10 + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AB_VIEWING_KEY, + "--token", + ETH, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("eth: 10")); + + // Assert NAM balance at VK(B) is 0 + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AB_VIEWING_KEY, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("No shielded nam balance found")); + + // Wait till epoch boundary + let ep4 = node.next_epoch(); + + // Assert ETH balance at VK(B) is 10 + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AB_VIEWING_KEY, + "--token", + ETH, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("eth: 10")); + + // Assert NAM balance at VK(B) is 10*ETH_reward*(epoch_4-epoch_3) + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AB_VIEWING_KEY, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + let amt = (amt10 * masp_rewards[&(eth(), None)]).0 * (ep4.0 - ep3.0); + let denominated = DenominatedAmount { + amount: amt, + denom: NATIVE_MAX_DECIMAL_PLACES.into(), + }; + assert!(captured.result.is_ok()); + assert!(captured.contains(&format!("nam: {}", denominated))); + + // Assert NAM balance at MASP pool is + // 20*BTC_reward*(epoch_4-epoch_0)+10*ETH_reward*(epoch_4-epoch_3) + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + MASP, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + let amt = ((amt20 * masp_rewards[&(btc(), None)]).0 * (ep4.0 - ep0.0)) + + ((amt10 * masp_rewards[&(eth(), None)]).0 * (ep4.0 - ep3.0)); + let denominated = DenominatedAmount { + amount: amt, + denom: NATIVE_MAX_DECIMAL_PLACES.into(), + }; + assert!(captured.result.is_ok()); + assert!(captured.contains(&format!("nam: {}", denominated))); + + // Wait till epoch boundary + let ep5 = node.next_epoch(); + + // Send 10 ETH from SK(B) to Christel + run( + &node, + Bin::Client, + vec![ + "transfer", + "--source", + B_SPENDING_KEY, + "--target", + CHRISTEL, + "--token", + ETH, + "--amount", + "10", + "--signer", + BERTHA, + "--node", + validator_one_rpc, + ], + )?; + node.assert_success(); + + // Assert ETH balance at VK(B) is 0 + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AB_VIEWING_KEY, + "--token", + ETH, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("No shielded eth balance found")); + + let _ep = node.next_epoch(); + + // Assert NAM balance at VK(B) is 10*ETH_reward*(ep-epoch_3) + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AB_VIEWING_KEY, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + let amt = (amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0); + let denominated = DenominatedAmount { + amount: amt, + denom: NATIVE_MAX_DECIMAL_PLACES.into(), + }; + assert!(captured.result.is_ok()); + assert!(captured.contains(&format!("nam: {}", denominated))); + + let ep = node.next_epoch(); + // Assert NAM balance at MASP pool is + // 20*BTC_reward*(epoch_5-epoch_0)+10*ETH_reward*(epoch_5-epoch_3) + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + MASP, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + let amt = ((amt20 * masp_rewards[&(btc(), None)]).0 * (ep.0 - ep0.0)) + + ((amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0)); + let denominated = DenominatedAmount { + amount: amt, + denom: NATIVE_MAX_DECIMAL_PLACES.into(), + }; + assert!(captured.result.is_ok()); + assert!(captured.contains(&format!("nam: {}", denominated))); + + // Wait till epoch boundary + let ep6 = node.next_epoch(); + + // Send 20 BTC from SK(A) to Christel + run( + &node, + Bin::Client, + vec![ + "transfer", + "--source", + A_SPENDING_KEY, + "--target", + CHRISTEL, + "--token", + BTC, + "--amount", + "20", + "--signer", + ALBERT, + "--node", + validator_one_rpc, + ], + )?; + node.assert_success(); + + // Assert BTC balance at VK(A) is 0 + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AA_VIEWING_KEY, + "--token", + BTC, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("No shielded btc balance found")); + + // Assert NAM balance at VK(A) is 20*BTC_reward*(epoch_6-epoch_0) + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AA_VIEWING_KEY, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep6.0 - ep0.0); + let denominated = DenominatedAmount { + amount: amt, + denom: NATIVE_MAX_DECIMAL_PLACES.into(), + }; + assert!(captured.result.is_ok()); + assert!(captured.contains(&format!("nam: {}", denominated,))); + + // Assert NAM balance at MASP pool is + // 20*BTC_reward*(epoch_6-epoch_0)+20*ETH_reward*(epoch_5-epoch_3) + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + MASP, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + let amt = ((amt20 * masp_rewards[&(btc(), None)]).0 * (ep6.0 - ep0.0)) + + ((amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0)); + let denominated = DenominatedAmount { + amount: amt, + denom: NATIVE_MAX_DECIMAL_PLACES.into(), + }; + assert!(captured.result.is_ok()); + assert!(captured.contains(&format!("nam: {}", denominated,))); + + // Wait till epoch boundary + let _ep7 = node.next_epoch(); + + // Assert NAM balance at VK(A) is 20*BTC_reward*(epoch_6-epoch_0) + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AA_VIEWING_KEY, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + let amt = (amt20 * masp_rewards[&(btc(), None)]).0 * (ep6.0 - ep0.0); + let denominated = DenominatedAmount { + amount: amt, + denom: NATIVE_MAX_DECIMAL_PLACES.into(), + }; + assert!(captured.result.is_ok()); + assert!(captured.contains(&format!("nam: {}", denominated))); + + // Assert NAM balance at VK(B) is 10*ETH_reward*(epoch_5-epoch_3) + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AB_VIEWING_KEY, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + let amt = (amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0); + let denominated = DenominatedAmount { + amount: amt, + denom: NATIVE_MAX_DECIMAL_PLACES.into(), + }; + assert!(captured.result.is_ok()); + assert!(captured.contains(&format!("nam: {}", denominated,))); + + // Assert NAM balance at MASP pool is + // 20*BTC_reward*(epoch_6-epoch_0)+10*ETH_reward*(epoch_5-epoch_3) + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + MASP, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + let amt = ((amt20 * masp_rewards[&(btc(), None)]).0 * (ep6.0 - ep0.0)) + + ((amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0)); + let denominated = DenominatedAmount { + amount: amt, + denom: NATIVE_MAX_DECIMAL_PLACES.into(), + }; + assert!(captured.result.is_ok()); + assert!(captured.contains(&format!("nam: {}", denominated,))); + + // Wait till epoch boundary to prevent conversion expiry during transaction + // construction + let _ep8 = node.next_epoch(); + + // Send 10*ETH_reward*(epoch_5-epoch_3) NAM from SK(B) to Christel + run( + &node, + Bin::Client, + vec![ + "transfer", + "--source", + B_SPENDING_KEY, + "--target", + CHRISTEL, + "--token", + NAM, + "--amount", + &((amt10 * masp_rewards[&(eth(), None)]).0 * (ep5.0 - ep3.0)) + .to_string_native(), + "--signer", + BERTHA, + "--node", + validator_one_rpc, + ], + )?; + node.assert_success(); + + // Wait till epoch boundary + let _ep9 = node.next_epoch(); + + // Send 20*BTC_reward*(epoch_6-epoch_0) NAM from SK(A) to Bertha + run( + &node, + Bin::Client, + vec![ + "transfer", + "--source", + A_SPENDING_KEY, + "--target", + BERTHA, + "--token", + NAM, + "--amount", + &((amt20 * masp_rewards[&(btc(), None)]).0 * (ep6.0 - ep0.0)) + .to_string_native(), + "--signer", + ALBERT, + "--node", + validator_one_rpc, + ], + )?; + node.assert_success(); + + // Assert NAM balance at VK(A) is 0 + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AA_VIEWING_KEY, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("No shielded nam balance found")); + + // Assert NAM balance at VK(B) is 0 + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AB_VIEWING_KEY, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("No shielded nam balance found")); + + // Assert NAM balance at MASP pool is 0 + let captured = CapturedOutput::of(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + MASP, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("nam: 0")); + + Ok(()) +} + +/// In this test we: +/// 1. Run the ledger node +/// 2. Assert PPA(C) cannot be recognized by incorrect viewing key +/// 3. Assert PPA(C) has not transaction pinned to it +/// 4. Send 20 BTC from Albert to PPA(C) +/// 5. Assert PPA(C) has the 20 BTC transaction pinned to it +#[test] +fn masp_pinned_txs() -> Result<()> { + // This address doesn't matter for tests. But an argument is required. + let validator_one_rpc = "127.0.0.1:26567"; + // Download the shielded pool parameters before starting node + let _ = CLIShieldedUtils::new(PathBuf::new()); + + let mut node = setup::setup()?; + // Wait till epoch boundary + let _ep0 = node.next_epoch(); + + // Assert PPA(C) cannot be recognized by incorrect viewing key + let captured = + CapturedOutput::with_input(AB_VIEWING_KEY.into()).run(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AC_PAYMENT_ADDRESS, + "--token", + BTC, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!( + captured.contains("Supplied viewing key cannot decode transactions to") + ); + + // Assert PPA(C) has no transaction pinned to it + let captured = + CapturedOutput::with_input(AC_VIEWING_KEY.into()).run(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AC_PAYMENT_ADDRESS, + "--token", + BTC, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("has not yet been consumed")); + + // Wait till epoch boundary + let _ep1 = node.next_epoch(); + + // Send 20 BTC from Albert to PPA(C) + run( + &node, + Bin::Client, + vec![ + "transfer", + "--source", + ALBERT, + "--target", + AC_PAYMENT_ADDRESS, + "--token", + BTC, + "--amount", + "20", + "--node", + validator_one_rpc, + ], + )?; + node.assert_success(); + + // Wait till epoch boundary + // This makes it more consistent for some reason? + let _ep2 = node.next_epoch(); + + // Assert PPA(C) has the 20 BTC transaction pinned to it + let captured = + CapturedOutput::with_input(AC_VIEWING_KEY.into()).run(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AC_PAYMENT_ADDRESS, + "--token", + BTC, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("Received 20 btc")); + + // Assert PPA(C) has no NAM pinned to it + let captured = + CapturedOutput::with_input(AC_VIEWING_KEY.into()).run(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AC_PAYMENT_ADDRESS, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("Received no shielded nam")); + + // Wait till epoch boundary + let _ep1 = node.next_epoch(); + + // Assert PPA(C) does not NAM pinned to it on epoch boundary + let captured = + CapturedOutput::with_input(AC_VIEWING_KEY.into()).run(|| { + run( + &node, + Bin::Client, + vec![ + "balance", + "--owner", + AC_PAYMENT_ADDRESS, + "--token", + NAM, + "--node", + validator_one_rpc, + ], + ) + }); + assert!(captured.result.is_ok()); + assert!(captured.contains("Received no shielded nam")); + Ok(()) +} + +/// In this test we: +/// 1. Run the ledger node +/// 2. Attempt to spend 10 BTC at SK(A) to PA(B) +/// 3. Attempt to spend 15 BTC at SK(A) to Bertha +/// 4. Send 20 BTC from Albert to PA(A) +/// 5. Attempt to spend 10 ETH at SK(A) to PA(B) +/// 6. Spend 7 BTC at SK(A) to PA(B) +/// 7. Spend 7 BTC at SK(A) to PA(B) +/// 8. Attempt to spend 7 BTC at SK(A) to PA(B) +/// 9. Spend 6 BTC at SK(A) to PA(B) +/// 10. Assert BTC balance at VK(A) is 0 +/// 11. Assert ETH balance at VK(A) is 0 +/// 12. Assert balance at VK(B) is 10 BTC +/// 13. Send 10 BTC from SK(B) to Bertha +#[test] +fn masp_txs_and_queries() -> Result<()> { + // Uncomment for better debugging + // let _log_guard = + // namada_apps::logging::init_from_env_or(tracing::level_filters::LevelFilter::INFO)? + // ; This address doesn't matter for tests. But an argument is required. + let validator_one_rpc = "127.0.0.1:26567"; + // Download the shielded pool parameters before starting node + let _ = CLIShieldedUtils::new(PathBuf::new()); + + enum Response { + Ok(&'static str), + Err(&'static str), + } + + let mut node = setup::setup()?; + _ = node.next_epoch(); + let txs_args = vec![ + // 0. Attempt to spend 10 BTC at SK(A) to PA(B) + ( + vec![ + "transfer", + "--source", + A_SPENDING_KEY, + "--target", + AB_PAYMENT_ADDRESS, + "--token", + BTC, + "--amount", + "10", + "--node", + validator_one_rpc, + ], + Response::Err(""), + ), + // 1. Attempt to spend 15 BTC at SK(A) to Bertha + ( + vec![ + "transfer", + "--source", + A_SPENDING_KEY, + "--target", + BERTHA, + "--token", + BTC, + "--amount", + "15", + "--node", + validator_one_rpc, + ], + Response::Err(""), + ), + // 2. Send 20 BTC from Albert to PA(A) + ( + vec![ + "transfer", + "--source", + ALBERT, + "--target", + AA_PAYMENT_ADDRESS, + "--token", + BTC, + "--amount", + "20", + "--node", + validator_one_rpc, + ], + Response::Ok("Transaction is valid"), + ), + // 3. Attempt to spend 10 ETH at SK(A) to PA(B) + ( + vec![ + "transfer", + "--source", + A_SPENDING_KEY, + "--target", + AB_PAYMENT_ADDRESS, + "--token", + ETH, + "--amount", + "10", + "--node", + validator_one_rpc, + ], + Response::Err(""), + ), + // 4. Spend 7 BTC at SK(A) to PA(B) + ( + vec![ + "transfer", + "--source", + A_SPENDING_KEY, + "--target", + AB_PAYMENT_ADDRESS, + "--token", + BTC, + "--amount", + "7", + "--node", + validator_one_rpc, + ], + Response::Ok("Transaction is valid"), + ), + // 5. Spend 7 BTC at SK(A) to PA(B) + ( + vec![ + "transfer", + "--source", + A_SPENDING_KEY, + "--target", + BB_PAYMENT_ADDRESS, + "--token", + BTC, + "--amount", + "7", + "--node", + validator_one_rpc, + ], + Response::Ok("Transaction is valid"), + ), + // 6. Attempt to spend 7 BTC at SK(A) to PA(B) + ( + vec![ + "transfer", + "--source", + A_SPENDING_KEY, + "--target", + BB_PAYMENT_ADDRESS, + "--token", + BTC, + "--amount", + "7", + "--node", + validator_one_rpc, + ], + Response::Err(""), + ), + // 7. Spend 6 BTC at SK(A) to PA(B) + ( + vec![ + "transfer", + "--source", + A_SPENDING_KEY, + "--target", + BB_PAYMENT_ADDRESS, + "--token", + BTC, + "--amount", + "6", + "--node", + validator_one_rpc, + ], + Response::Ok("Transaction is valid"), + ), + // 8. Assert BTC balance at VK(A) is 0 + ( + vec![ + "balance", + "--owner", + AA_VIEWING_KEY, + "--token", + BTC, + "--node", + validator_one_rpc, + ], + Response::Ok("No shielded btc balance found"), + ), + // 9. Assert ETH balance at VK(A) is 0 + ( + vec![ + "balance", + "--owner", + AA_VIEWING_KEY, + "--token", + ETH, + "--node", + validator_one_rpc, + ], + Response::Ok("No shielded eth balance found"), + ), + // 10. Assert balance at VK(B) is 20 BTC + ( + vec![ + "balance", + "--owner", + AB_VIEWING_KEY, + "--node", + validator_one_rpc, + ], + Response::Ok("btc : 20"), + ), + // 11. Send 20 BTC from SK(B) to Bertha + ( + vec![ + "transfer", + "--source", + B_SPENDING_KEY, + "--target", + BERTHA, + "--token", + BTC, + "--amount", + "20", + "--node", + validator_one_rpc, + ], + Response::Ok("Transaction is valid"), + ), + ]; + + for (tx_args, tx_result) in &txs_args { + // We ensure transfers don't cross epoch boundaries. + if tx_args[0] == "transfer" { + node.next_epoch(); + } + for &dry_run in &[true, false] { + let tx_args = if dry_run && tx_args[0] == "transfer" { + vec![tx_args.clone(), vec!["--dry-run"]].concat() + } else { + tx_args.clone() + }; + println!( + "{}: {:?}\n\n", + "Running".green().underline(), + tx_args.join(" ").yellow().underline() + ); + let captured = + CapturedOutput::of(|| run(&node, Bin::Client, tx_args.clone())); + match tx_result { + Response::Ok("Transaction is valid") => { + assert!( + captured.result.is_ok(), + "{:?} failed with result {:?}.\n Unread output: {}", + tx_args, + captured.result, + captured.output, + ); + if !dry_run { + node.assert_success(); + } else { + assert!( + captured.contains("Transaction is valid"), + "{:?} failed to contain needle 'Transaction is \ + valid',\nGot output '{}'", + tx_args, + captured.output + ); + } + } + Response::Ok(out) => { + assert!( + captured.result.is_ok(), + "{:?} failed with result {:?}.\n Unread output: {}", + tx_args, + captured.result, + captured.output, + ); + assert!( + captured.contains(out), + "{:?} failed to contain needle '{}',\nGot output '{}'", + tx_args, + out, + captured.output + ); + } + Response::Err(msg) => { + assert!( + captured.result.is_err(), + "{:?} unexpectedly succeeded", + tx_args + ); + assert!( + captured.contains(msg), + "{:?} failed to contain needle {},\nGot output {}", + tx_args, + msg, + captured.output + ); + } + } + } + } + + Ok(()) +} diff --git a/tests/src/integration/setup.rs b/tests/src/integration/setup.rs new file mode 100644 index 0000000000..df74c5f6f1 --- /dev/null +++ b/tests/src/integration/setup.rs @@ -0,0 +1,163 @@ +use std::mem::ManuallyDrop; +use std::path::Path; +use std::str::FromStr; +use std::sync::{Arc, Mutex}; + +use color_eyre::eyre::{eyre, Result}; +use namada_apps::cli::args; +use namada_apps::config; +use namada_apps::config::genesis::genesis_config; +use namada_apps::config::genesis::genesis_config::GenesisConfig; +use namada_apps::config::TendermintMode; +use namada_apps::facade::tendermint::Timeout; +use namada_apps::facade::tendermint_proto::google::protobuf::Timestamp; +use namada_apps::node::ledger::shell::testing::node::MockNode; +use namada_apps::node::ledger::shell::testing::utils::TestDir; +use namada_apps::node::ledger::shell::Shell; +use namada_core::types::address::Address; +use namada_core::types::chain::{ChainId, ChainIdPrefix}; +use toml::value::Table; + +use crate::e2e::setup::{ + copy_wasm_to_chain_dir, get_all_wasms_hashes, SINGLE_NODE_NET_GENESIS, +}; + +/// Env. var for keeping temporary files created by the integration tests +const ENV_VAR_KEEP_TEMP: &str = "NAMADA_INT_KEEP_TEMP"; + +/// Setup a network with a single genesis validator node. +pub fn setup() -> Result { + initialize_genesis(|genesis| genesis) +} + +/// Setup folders with genesis, configs, wasm, etc. +pub fn initialize_genesis( + mut update_genesis: impl FnMut(GenesisConfig) -> GenesisConfig, +) -> Result { + let working_dir = std::fs::canonicalize("..").unwrap(); + let keep_temp = match std::env::var(ENV_VAR_KEEP_TEMP) { + Ok(val) => val.to_ascii_lowercase() != "false", + _ => false, + }; + let test_dir = TestDir::new(); + + // Open the source genesis file + let mut genesis = genesis_config::open_genesis_config( + working_dir.join(SINGLE_NODE_NET_GENESIS), + )?; + + genesis.parameters.vp_whitelist = + Some(get_all_wasms_hashes(&working_dir, Some("vp_"))); + genesis.parameters.tx_whitelist = + Some(get_all_wasms_hashes(&working_dir, Some("tx_"))); + + // Run the provided function on it + let genesis = update_genesis(genesis); + + // Run `init-network` to generate the finalized genesis config, keys and + // addresses and update WASM checksums + let genesis_path = test_dir.path().join("e2e-test-genesis-src.toml"); + genesis_config::write_genesis_config(&genesis, &genesis_path); + let wasm_checksums_path = working_dir.join("wasm/checksums.json"); + + // setup genesis file + namada_apps::client::utils::init_network( + args::Global { + chain_id: None, + base_dir: test_dir.path().to_path_buf(), + wasm_dir: None, + }, + args::InitNetwork { + genesis_path, + wasm_checksums_path, + chain_id_prefix: ChainIdPrefix::from_str("integration-test") + .unwrap(), + unsafe_dont_encrypt: true, + consensus_timeout_commit: Timeout::from_str("1s").unwrap(), + localhost: true, + allow_duplicate_ip: true, + dont_archive: true, + archive_dir: None, + }, + ); + + create_node(test_dir, &genesis, keep_temp) +} + +/// Create a mock ledger node. +fn create_node( + base_dir: TestDir, + genesis: &GenesisConfig, + keep_temp: bool, +) -> Result { + // look up the chain id from the global file. + let chain_id = if let toml::Value::String(chain_id) = + toml::from_str::( + &std::fs::read_to_string( + base_dir.path().join("global-config.toml"), + ) + .unwrap(), + ) + .unwrap() + .get("default_chain_id") + .unwrap() + { + chain_id.to_string() + } else { + return Err(eyre!("Could not read chain id from global-config.toml")); + }; + + // the directory holding compiled wasm + let wasm_dir = base_dir.path().join(Path::new(&chain_id)).join("wasm"); + // copy compiled wasms into the wasm directory + let chain_id = ChainId::from_str(&chain_id).unwrap(); + copy_wasm_to_chain_dir( + &std::fs::canonicalize("..").unwrap(), + &base_dir.path().join(Path::new(&chain_id.to_string())), + &chain_id, + genesis.validator.keys(), + ); + + // instantiate and initialize the ledger node. + let (sender, recv) = tokio::sync::mpsc::unbounded_channel(); + let node = MockNode { + shell: Arc::new(Mutex::new(Shell::new( + config::Ledger::new( + base_dir.path(), + chain_id.clone(), + TendermintMode::Validator + ), + wasm_dir, + sender, + None, + None, + 50 * 1024 * 1024, // 50 kiB + 50 * 1024 * 1024, // 50 kiB + Address::from_str("atest1v4ehgw36x3prswzxggunzv6pxqmnvdj9xvcyzvpsggeyvs3cg9qnywf589qnwvfsg5erg3fkl09rg5").unwrap(), + ))), + test_dir: ManuallyDrop::new(base_dir), + keep_temp, + _broadcast_recv: recv, + results: Arc::new(Mutex::new(vec![])), + }; + let init_req = namada_apps::facade::tower_abci::request::InitChain { + time: Some(Timestamp { + seconds: 0, + nanos: 0, + }), + chain_id: chain_id.to_string(), + consensus_params: None, + validators: vec![], + app_state_bytes: vec![], + initial_height: 0, + }; + { + let mut locked = node.shell.lock().unwrap(); + locked + .init_chain(init_req, 1) + .map_err(|e| eyre!("Failed to initialize ledger: {:?}", e))?; + locked.commit(); + } + + Ok(node) +} diff --git a/tests/src/integration/utils.rs b/tests/src/integration/utils.rs new file mode 100644 index 0000000000..f626a001ee --- /dev/null +++ b/tests/src/integration/utils.rs @@ -0,0 +1,83 @@ +use std::fs::File; +use std::path::PathBuf; +use std::sync::Arc; + +struct TempFile(PathBuf); +impl TempFile { + fn new(path: PathBuf) -> (Self, File) { + let f = File::create(&path).unwrap(); + (Self(path), f) + } +} + +impl Drop for TempFile { + fn drop(&mut self) { + _ = std::fs::remove_file(&self.0); + } +} + +/// Test helper that captures stdout of +/// a process. +pub struct CapturedOutput { + pub output: String, + pub result: T, + input: String, +} + +impl CapturedOutput { + pub fn with_input(input: String) -> Self { + Self { + output: "".to_string(), + result: (), + input, + } + } +} + +impl CapturedOutput { + /// Run a client command and capture + /// the output to the mocked stdout. + pub(crate) fn of(func: F) -> Self + where + F: FnOnce() -> T, + { + std::io::set_output_capture(Some(Default::default())); + let mut capture = Self { + output: Default::default(), + result: func(), + input: Default::default(), + }; + let captured = std::io::set_output_capture(None); + let captured = captured.unwrap(); + let captured = Arc::try_unwrap(captured).unwrap(); + let captured = captured.into_inner().unwrap(); + capture.output = String::from_utf8(captured).unwrap(); + capture + } + + /// Run a client command with input to the mocked stdin and capture + /// the output to the mocked stdout + pub fn run(&self, func: F) -> CapturedOutput + where + F: FnOnce() -> U, + { + { + // write the input to the mocked stdin + let mut buf = namada_apps::cli::TESTIN.lock().unwrap(); + buf.clear(); + buf.extend_from_slice(self.input.as_bytes()); + } + CapturedOutput::of(func) + } + + /// Check if the captured output contains the regex. + pub fn matches(&self, needle: regex::Regex) -> bool { + needle.captures(&self.output).is_some() + } + + /// Check if the captured output contains the string. + pub fn contains(&self, needle: &str) -> bool { + let needle = regex::Regex::new(needle).unwrap(); + self.matches(needle) + } +} diff --git a/tests/src/lib.rs b/tests/src/lib.rs index db37039878..7e35947477 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -1,3 +1,4 @@ +#![cfg_attr(test, feature(internal_output_capture))] //! Namada integrations and WASM tests and testing helpers. #![doc(html_favicon_url = "https://dev.namada.net/master/favicon.png")] @@ -11,6 +12,9 @@ mod vm_host_env; pub use vm_host_env::{ibc, tx, vp}; #[cfg(test)] mod e2e; +#[cfg(test)] +#[allow(dead_code)] +mod integration; pub mod native_vp; pub mod storage; #[cfg(test)] 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/Cargo.lock b/wasm/Cargo.lock index 05ea297b9d..d57798939e 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -89,6 +89,55 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + +[[package]] +name = "anstyle-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + [[package]] name = "anyhow" version = "1.0.71" @@ -315,7 +364,7 @@ checksum = "f8175979259124331c1d7bf6586ee7e0da434155e4b2d48ec2c8386281d8df39" dependencies = [ "async-trait", "axum-core", - "bitflags", + "bitflags 1.3.2", "bytes", "futures-util", "http", @@ -511,6 +560,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + [[package]] name = "bitvec" version = "0.17.4" @@ -896,6 +951,33 @@ dependencies = [ "version_check", ] +[[package]] +name = "clap" +version = "4.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1640e5cc7fb47dbb8338fd471b105e7ed6c3cb2aeb00c2e067127ffd3764a05d" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98c59138d527eeaf9b53f35a77fcc1fad9d883116070c63d5de1c7dc7b00c72b" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + [[package]] name = "clru" version = "0.5.0" @@ -957,6 +1039,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "concat-idents" version = "1.1.4" @@ -1467,7 +1555,7 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "add9a102807b524ec050363f09e06f1504214b0e1c7797f64261c891022dce8b" dependencies = [ - "bitflags", + "bitflags 1.3.2", "byteorder", "lazy_static", "proc-macro-error", @@ -2567,7 +2655,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" dependencies = [ "base64 0.13.1", - "bitflags", + "bitflags 1.3.2", "bytes", "headers-core", "http", @@ -3142,6 +3230,17 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi 0.3.1", + "rustix 0.38.4", + "windows-sys 0.48.0", +] + [[package]] name = "itertools" version = "0.10.5" @@ -3229,9 +3328,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.144" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "libloading" @@ -3315,6 +3414,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" +[[package]] +name = "linux-raw-sys" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" + [[package]] name = "lock_api" version = "0.4.9" @@ -3768,17 +3873,21 @@ dependencies = [ name = "namada_tests" version = "0.19.0" dependencies = [ + "async-trait", "chrono", + "clap", "concat-idents", "derivative", "hyper", "ibc-relayer", "ibc-relayer-types", + "lazy_static", "namada", "namada_core", "namada_test_utils", "namada_tx_prelude", "namada_vp_prelude", + "num-traits", "prost", "regex", "serde_json", @@ -4467,7 +4576,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" dependencies = [ "bit-set", - "bitflags", + "bitflags 1.3.2", "byteorder", "lazy_static", "num-traits", @@ -4560,7 +4669,7 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63" dependencies = [ - "bitflags", + "bitflags 1.3.2", "memchr", "unicase", ] @@ -4694,7 +4803,7 @@ version = "10.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -4743,7 +4852,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -4752,7 +4861,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -4815,7 +4924,7 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" dependencies = [ - "bitflags", + "bitflags 1.3.2", "libc", "mach", "winapi", @@ -5015,11 +5124,24 @@ version = "0.37.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", - "linux-raw-sys", + "linux-raw-sys 0.3.7", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" +dependencies = [ + "bitflags 2.3.3", + "errno", + "libc", + "linux-raw-sys 0.4.3", "windows-sys 0.48.0", ] @@ -5310,7 +5432,7 @@ version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -5656,6 +5778,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "strum" version = "0.24.1" @@ -5773,7 +5901,7 @@ dependencies = [ "cfg-if 1.0.0", "fastrand", "redox_syscall 0.3.5", - "rustix", + "rustix 0.37.19", "windows-sys 0.45.0", ] @@ -6474,6 +6602,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "uuid" version = "0.8.2" diff --git a/wasm/checksums.json b/wasm/checksums.json index 8397ffb0d6..67d1547d8d 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.e1528eb25894ef3c949039098f03c967f3ac54fe85b1ceaf7188ae8530f82f09.wasm", + "tx_bridge_pool.wasm": "tx_bridge_pool.7e5ef98663de40a9d57b3a41237fbd990e45fcab69963964c68691e33419984a.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.9678d081b00754a308ff2933e82ba12eb435c253255634c2f66eef0b5ed7664f.wasm", + "tx_ibc.wasm": "tx_ibc.03f5035cdaebf9791ffdc1c6680fb49e85905fded1cb6af9c6346971fff3d18d.wasm", + "tx_init_account.wasm": "tx_init_account.4f459874b97ba38e1896154b5a21aede2a903e423bb9791d7c484e5d41bbf645.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.7d853deb8f46cec9994d91a23cf54f59e087362d01bdedc1665dbd9edaeb2875.wasm", + "tx_init_validator.wasm": "tx_init_validator.374cc193df2d8f52709d75d8b6a3ee0c899e167e94213600edaf49405f74710c.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.e2223fd749dd49887a10cf48deb99b67caf65259d78b697ca9627a59d9932aab.wasm", + "tx_transfer.wasm": "tx_transfer.77b50bd69208af0b2481f709411f8531474902bd1a16d3976ca72878662015c2.wasm", + "tx_unbond.wasm": "tx_unbond.026172d96c52b6c1d24d97e0c327415cd4bd20120efc565f94a3401594596fce.wasm", + "tx_unjail_validator.wasm": "tx_unjail_validator.47b4cb5e76967bb1f1264d4e682ac7aa15ab5a59d36335bdd1ec8862e06f435e.wasm", + "tx_update_vp.wasm": "tx_update_vp.4b677b8fd176a1d73b2b7754b4e0c5de1d835f3205d0b9200089c6245c5261ef.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.3db37f2f2c4489f0903cb3d82455fffc2a21fc6c3d802483f6904686c8c9d0bc.wasm", + "tx_withdraw.wasm": "tx_withdraw.25b0da34e26726dbf9d6a34fa5995d916433cef6b1dc8de61649352b7a21011b.wasm", + "vp_implicit.wasm": "vp_implicit.d3d7ec77e15aaacbffaa68208b6660e5b98412c3738d346b5bbd43630896827f.wasm", + "vp_masp.wasm": "vp_masp.57bd2fbfd79eb4c1ff0b2380db013d99463bfec8cb998153e168611b1a53eb14.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.7dd6f780b372e0f0fead49243ffc0e2ae76f28962cf471e6d5ef20b1bbce78f7.wasm", + "vp_token.wasm": "vp_token.9aa48172e5432e32f8a99263ffdbcfee301175e59f9ad827ec8e54560a779d45.wasm", + "vp_user.wasm": "vp_user.810fb7c65cd18985f91bd87caa28e9ad93df5d442816e08b7c2f4d01bef6246e.wasm", + "vp_validator.wasm": "vp_validator.8e9855ecf63d2c27b4e2860fd2f828e4bda8a4ab798bf8eb3edb224a5a65ae02.wasm" } \ No newline at end of file diff --git a/wasm/wasm_source/src/vp_testnet_faucet.rs b/wasm/wasm_source/src/vp_testnet_faucet.rs index 8e002663ee..3278f7a111 100644 --- a/wasm/wasm_source/src/vp_testnet_faucet.rs +++ b/wasm/wasm_source/src/vp_testnet_faucet.rs @@ -43,7 +43,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 +51,17 @@ fn validate_tx( let post: token::Amount = 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)?; + 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() { @@ -60,7 +70,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 +317,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 +362,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 +427,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(); diff --git a/wasm_for_tests/wasm_source/Cargo.lock b/wasm_for_tests/wasm_source/Cargo.lock index 19cdba1a22..713a8b77e5 100644 --- a/wasm_for_tests/wasm_source/Cargo.lock +++ b/wasm_for_tests/wasm_source/Cargo.lock @@ -89,6 +89,55 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + +[[package]] +name = "anstyle-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + [[package]] name = "anyhow" version = "1.0.71" @@ -315,7 +364,7 @@ checksum = "f8175979259124331c1d7bf6586ee7e0da434155e4b2d48ec2c8386281d8df39" dependencies = [ "async-trait", "axum-core", - "bitflags", + "bitflags 1.3.2", "bytes", "futures-util", "http", @@ -511,6 +560,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + [[package]] name = "bitvec" version = "0.17.4" @@ -896,6 +951,33 @@ dependencies = [ "version_check", ] +[[package]] +name = "clap" +version = "4.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1640e5cc7fb47dbb8338fd471b105e7ed6c3cb2aeb00c2e067127ffd3764a05d" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98c59138d527eeaf9b53f35a77fcc1fad9d883116070c63d5de1c7dc7b00c72b" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + [[package]] name = "clru" version = "0.5.0" @@ -957,6 +1039,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "concat-idents" version = "1.1.4" @@ -1467,7 +1555,7 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "add9a102807b524ec050363f09e06f1504214b0e1c7797f64261c891022dce8b" dependencies = [ - "bitflags", + "bitflags 1.3.2", "byteorder", "lazy_static", "proc-macro-error", @@ -2567,7 +2655,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" dependencies = [ "base64 0.13.1", - "bitflags", + "bitflags 1.3.2", "bytes", "headers-core", "http", @@ -3142,6 +3230,17 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi 0.3.1", + "rustix 0.38.3", + "windows-sys 0.48.0", +] + [[package]] name = "itertools" version = "0.10.5" @@ -3229,9 +3328,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.144" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "libloading" @@ -3315,6 +3414,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" +[[package]] +name = "linux-raw-sys" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" + [[package]] name = "lock_api" version = "0.4.9" @@ -3768,17 +3873,21 @@ dependencies = [ name = "namada_tests" version = "0.19.0" dependencies = [ + "async-trait", "chrono", + "clap", "concat-idents", "derivative", "hyper", "ibc-relayer", "ibc-relayer-types", + "lazy_static", "namada", "namada_core", "namada_test_utils", "namada_tx_prelude", "namada_vp_prelude", + "num-traits", "prost", "regex", "serde_json", @@ -4460,7 +4569,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" dependencies = [ "bit-set", - "bitflags", + "bitflags 1.3.2", "byteorder", "lazy_static", "num-traits", @@ -4553,7 +4662,7 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63" dependencies = [ - "bitflags", + "bitflags 1.3.2", "memchr", "unicase", ] @@ -4687,7 +4796,7 @@ version = "10.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -4736,7 +4845,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -4745,7 +4854,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -4808,7 +4917,7 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" dependencies = [ - "bitflags", + "bitflags 1.3.2", "libc", "mach", "winapi", @@ -5008,11 +5117,24 @@ version = "0.37.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", - "linux-raw-sys", + "linux-raw-sys 0.3.7", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac5ffa1efe7548069688cd7028f32591853cd7b5b756d41bcffd2353e4fc75b4" +dependencies = [ + "bitflags 2.3.3", + "errno", + "libc", + "linux-raw-sys 0.4.3", "windows-sys 0.48.0", ] @@ -5303,7 +5425,7 @@ version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -5649,6 +5771,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "strum" version = "0.24.1" @@ -5766,7 +5894,7 @@ dependencies = [ "cfg-if 1.0.0", "fastrand", "redox_syscall 0.3.5", - "rustix", + "rustix 0.37.19", "windows-sys 0.45.0", ] @@ -6456,6 +6584,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "uuid" version = "0.8.2" From b6714b5eca577c520b2b42b5f7f8048d46bb9d8b Mon Sep 17 00:00:00 2001 From: Gianmarco Fraccaroli Date: Sat, 22 Jul 2023 12:03:40 +0200 Subject: [PATCH 113/113] Namada 0.20.0 --- .../bug-fixes/1667-faucet-limit-fix.md | 0 .../bug-fixes/1686-delete-prefix.md | 0 .../1709-fix_changes_before_commit.md | 0 .../1729-pos-fix-rewards-boundary.md | 0 ...clear-out-validator-sets-for-old-epochs.md | 0 .../improvements/1173-token-balance-query.md | 0 .../improvements/1605-win-build.md | 0 .../improvements/1621-utils-next-epoch.md | 0 .../improvements/1656-pos-cli-queries.md | 0 .../1670-remove-unused-assoc-ty.md | 0 .../1692-rm-from-u64-on-ethbridge-stake.md | 0 .../improvements/1695-update-sysinfo.md | 0 .../improvements/1717-storage-refactor.md | 0 .../miscellaneous/1693-ibc-multitoken.md | 0 .../miscellaneous/1733-pos-data-history.md | 0 .../miscellaneous/1738-refactor-cli.md | 0 .changelog/v0.20.0/summary.md | 2 + .../testing/1714-refactor-e2e-tests.md | 0 CHANGELOG.md | 56 ++++++++++++++++++ Cargo.lock | 24 ++++---- Cargo.toml | 2 +- wasm/Cargo.lock | 26 ++++---- wasm/checksums.json | 41 +++++++------ wasm/tx_template/Cargo.toml | 2 +- wasm/vp_template/Cargo.toml | 2 +- wasm/wasm_source/Cargo.toml | 2 +- wasm_for_tests/tx_memory_limit.wasm | Bin 440249 -> 434136 bytes wasm_for_tests/tx_mint_tokens.wasm | Bin 574210 -> 596222 bytes wasm_for_tests/tx_no_op.wasm | Bin 359578 -> 355245 bytes wasm_for_tests/tx_proposal_code.wasm | Bin 494389 -> 518639 bytes wasm_for_tests/tx_read_storage_key.wasm | Bin 443432 -> 440700 bytes wasm_for_tests/tx_write.wasm | Bin 446580 -> 443991 bytes wasm_for_tests/tx_write_storage_key.wasm | Bin 465585 -> 465585 bytes wasm_for_tests/vp_always_false.wasm | Bin 410553 -> 409988 bytes wasm_for_tests/vp_always_true.wasm | Bin 410553 -> 409988 bytes wasm_for_tests/vp_eval.wasm | Bin 488056 -> 480359 bytes wasm_for_tests/vp_memory_limit.wasm | Bin 462956 -> 458746 bytes wasm_for_tests/vp_read_storage_key.wasm | Bin 468486 -> 466346 bytes wasm_for_tests/wasm_source/Cargo.lock | 22 +++---- wasm_for_tests/wasm_source/Cargo.toml | 2 +- 40 files changed, 119 insertions(+), 62 deletions(-) rename .changelog/{unreleased => v0.20.0}/bug-fixes/1667-faucet-limit-fix.md (100%) rename .changelog/{unreleased => v0.20.0}/bug-fixes/1686-delete-prefix.md (100%) rename .changelog/{unreleased => v0.20.0}/bug-fixes/1709-fix_changes_before_commit.md (100%) rename .changelog/{unreleased => v0.20.0}/bug-fixes/1729-pos-fix-rewards-boundary.md (100%) rename .changelog/{unreleased => v0.20.0}/improvements/1129-clear-out-validator-sets-for-old-epochs.md (100%) rename .changelog/{unreleased => v0.20.0}/improvements/1173-token-balance-query.md (100%) rename .changelog/{unreleased => v0.20.0}/improvements/1605-win-build.md (100%) rename .changelog/{unreleased => v0.20.0}/improvements/1621-utils-next-epoch.md (100%) rename .changelog/{unreleased => v0.20.0}/improvements/1656-pos-cli-queries.md (100%) rename .changelog/{unreleased => v0.20.0}/improvements/1670-remove-unused-assoc-ty.md (100%) rename .changelog/{unreleased => v0.20.0}/improvements/1692-rm-from-u64-on-ethbridge-stake.md (100%) rename .changelog/{unreleased => v0.20.0}/improvements/1695-update-sysinfo.md (100%) rename .changelog/{unreleased => v0.20.0}/improvements/1717-storage-refactor.md (100%) rename .changelog/{unreleased => v0.20.0}/miscellaneous/1693-ibc-multitoken.md (100%) rename .changelog/{unreleased => v0.20.0}/miscellaneous/1733-pos-data-history.md (100%) rename .changelog/{unreleased => v0.20.0}/miscellaneous/1738-refactor-cli.md (100%) create mode 100644 .changelog/v0.20.0/summary.md rename .changelog/{unreleased => v0.20.0}/testing/1714-refactor-e2e-tests.md (100%) diff --git a/.changelog/unreleased/bug-fixes/1667-faucet-limit-fix.md b/.changelog/v0.20.0/bug-fixes/1667-faucet-limit-fix.md similarity index 100% rename from .changelog/unreleased/bug-fixes/1667-faucet-limit-fix.md rename to .changelog/v0.20.0/bug-fixes/1667-faucet-limit-fix.md diff --git a/.changelog/unreleased/bug-fixes/1686-delete-prefix.md b/.changelog/v0.20.0/bug-fixes/1686-delete-prefix.md similarity index 100% rename from .changelog/unreleased/bug-fixes/1686-delete-prefix.md rename to .changelog/v0.20.0/bug-fixes/1686-delete-prefix.md diff --git a/.changelog/unreleased/bug-fixes/1709-fix_changes_before_commit.md b/.changelog/v0.20.0/bug-fixes/1709-fix_changes_before_commit.md similarity index 100% rename from .changelog/unreleased/bug-fixes/1709-fix_changes_before_commit.md rename to .changelog/v0.20.0/bug-fixes/1709-fix_changes_before_commit.md diff --git a/.changelog/unreleased/bug-fixes/1729-pos-fix-rewards-boundary.md b/.changelog/v0.20.0/bug-fixes/1729-pos-fix-rewards-boundary.md similarity index 100% rename from .changelog/unreleased/bug-fixes/1729-pos-fix-rewards-boundary.md rename to .changelog/v0.20.0/bug-fixes/1729-pos-fix-rewards-boundary.md diff --git a/.changelog/unreleased/improvements/1129-clear-out-validator-sets-for-old-epochs.md b/.changelog/v0.20.0/improvements/1129-clear-out-validator-sets-for-old-epochs.md similarity index 100% rename from .changelog/unreleased/improvements/1129-clear-out-validator-sets-for-old-epochs.md rename to .changelog/v0.20.0/improvements/1129-clear-out-validator-sets-for-old-epochs.md diff --git a/.changelog/unreleased/improvements/1173-token-balance-query.md b/.changelog/v0.20.0/improvements/1173-token-balance-query.md similarity index 100% rename from .changelog/unreleased/improvements/1173-token-balance-query.md rename to .changelog/v0.20.0/improvements/1173-token-balance-query.md diff --git a/.changelog/unreleased/improvements/1605-win-build.md b/.changelog/v0.20.0/improvements/1605-win-build.md similarity index 100% rename from .changelog/unreleased/improvements/1605-win-build.md rename to .changelog/v0.20.0/improvements/1605-win-build.md diff --git a/.changelog/unreleased/improvements/1621-utils-next-epoch.md b/.changelog/v0.20.0/improvements/1621-utils-next-epoch.md similarity index 100% rename from .changelog/unreleased/improvements/1621-utils-next-epoch.md rename to .changelog/v0.20.0/improvements/1621-utils-next-epoch.md diff --git a/.changelog/unreleased/improvements/1656-pos-cli-queries.md b/.changelog/v0.20.0/improvements/1656-pos-cli-queries.md similarity index 100% rename from .changelog/unreleased/improvements/1656-pos-cli-queries.md rename to .changelog/v0.20.0/improvements/1656-pos-cli-queries.md diff --git a/.changelog/unreleased/improvements/1670-remove-unused-assoc-ty.md b/.changelog/v0.20.0/improvements/1670-remove-unused-assoc-ty.md similarity index 100% rename from .changelog/unreleased/improvements/1670-remove-unused-assoc-ty.md rename to .changelog/v0.20.0/improvements/1670-remove-unused-assoc-ty.md diff --git a/.changelog/unreleased/improvements/1692-rm-from-u64-on-ethbridge-stake.md b/.changelog/v0.20.0/improvements/1692-rm-from-u64-on-ethbridge-stake.md similarity index 100% rename from .changelog/unreleased/improvements/1692-rm-from-u64-on-ethbridge-stake.md rename to .changelog/v0.20.0/improvements/1692-rm-from-u64-on-ethbridge-stake.md diff --git a/.changelog/unreleased/improvements/1695-update-sysinfo.md b/.changelog/v0.20.0/improvements/1695-update-sysinfo.md similarity index 100% rename from .changelog/unreleased/improvements/1695-update-sysinfo.md rename to .changelog/v0.20.0/improvements/1695-update-sysinfo.md diff --git a/.changelog/unreleased/improvements/1717-storage-refactor.md b/.changelog/v0.20.0/improvements/1717-storage-refactor.md similarity index 100% rename from .changelog/unreleased/improvements/1717-storage-refactor.md rename to .changelog/v0.20.0/improvements/1717-storage-refactor.md diff --git a/.changelog/unreleased/miscellaneous/1693-ibc-multitoken.md b/.changelog/v0.20.0/miscellaneous/1693-ibc-multitoken.md similarity index 100% rename from .changelog/unreleased/miscellaneous/1693-ibc-multitoken.md rename to .changelog/v0.20.0/miscellaneous/1693-ibc-multitoken.md diff --git a/.changelog/unreleased/miscellaneous/1733-pos-data-history.md b/.changelog/v0.20.0/miscellaneous/1733-pos-data-history.md similarity index 100% rename from .changelog/unreleased/miscellaneous/1733-pos-data-history.md rename to .changelog/v0.20.0/miscellaneous/1733-pos-data-history.md diff --git a/.changelog/unreleased/miscellaneous/1738-refactor-cli.md b/.changelog/v0.20.0/miscellaneous/1738-refactor-cli.md similarity index 100% rename from .changelog/unreleased/miscellaneous/1738-refactor-cli.md rename to .changelog/v0.20.0/miscellaneous/1738-refactor-cli.md diff --git a/.changelog/v0.20.0/summary.md b/.changelog/v0.20.0/summary.md new file mode 100644 index 0000000000..56e6ca5932 --- /dev/null +++ b/.changelog/v0.20.0/summary.md @@ -0,0 +1,2 @@ +Namada 0.20.0 is a minor releasing addressing several improvements to the PoS system and the ledger +stability. diff --git a/.changelog/unreleased/testing/1714-refactor-e2e-tests.md b/.changelog/v0.20.0/testing/1714-refactor-e2e-tests.md similarity index 100% rename from .changelog/unreleased/testing/1714-refactor-e2e-tests.md rename to .changelog/v0.20.0/testing/1714-refactor-e2e-tests.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 33f8ff322a..fe22259365 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,61 @@ # CHANGELOG +## v0.20.0 + +Namada 0.20.0 is a minor releasing addressing several improvements to the PoS system and the ledger +stability. + +### BUG FIXES + +- Fix genesis `faucet_withdrawal_limit` parser to respect tokens' denomination. + ([\#1667](https://github.com/anoma/namada/pull/1667)) +- 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)) +- Fix inconsistency state before commit + ([\#1709](https://github.com/anoma/namada/issues/1709)) +- 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)) + +### IMPROVEMENTS + +- PoS: purge validator sets for old epochs from the storage; store total + validator stake ([\#1129](https://github.com/anoma/namada/issues/1129)) +- Added a reusable token balance query method. + ([\#1173](https://github.com/anoma/namada/pull/1173)) +- Replaced file-lock with fd-lock dependency to support Windows build. + ([\#1605](https://github.com/anoma/namada/pull/1605)) +- Added a command to wait for the next epoch: `client utils epoch-sleep`. + ([\#1621](https://github.com/anoma/namada/pull/1621)) +- Added a client query for `validator-state` and improved the slashes query to + show more info. ([\#1656](https://github.com/anoma/namada/pull/1656)) +- 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)) +- Removed `impl From for EthBridgeVotingPower` and replaced it with a + `TryFrom`. ([\#1692](https://github.com/anoma/namada/pull/1692)) +- Updated sysinfo dependency. + ([\#1695](https://github.com/anoma/namada/pull/1695)) +- Refactored storage code to only use an immutable reference when reading and + writing to a batch. ([\#1717](https://github.com/anoma/namada/pull/1717)) + +### MISCELLANEOUS + +- Replaced token sub-prefix with a multitoken address and native VP for IBC and + ETH bridge. ([\#1693](https://github.com/anoma/namada/pull/1693)) +- PoS: Keep the data for last two epochs by default. + ([\#1733](https://github.com/anoma/namada/pull/1733)) +- Refactored CLI into libraries for future re-use in integration tests and + to enable generic IO. ([\#1738](https://github.com/anoma/namada/pull/1738)) + +### TESTING + +- Added integration testing infrastructure for node, client and + the wallet and replaced MASP E2E tests with integration tests. + ([\#1714](https://github.com/anoma/namada/pull/1714)) + ## v0.19.0 Namada 0.19.0 is a minor releasing addressing the integration with the namada trustless ethereum bridge. diff --git a/Cargo.lock b/Cargo.lock index 2735ce0e72..9517b5cf7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4241,7 +4241,7 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "namada" -version = "0.19.0" +version = "0.20.0" dependencies = [ "assert_matches", "async-trait", @@ -4309,7 +4309,7 @@ dependencies = [ [[package]] name = "namada_apps" -version = "0.19.0" +version = "0.20.0" dependencies = [ "ark-serialize", "ark-std", @@ -4398,7 +4398,7 @@ dependencies = [ [[package]] name = "namada_core" -version = "0.19.0" +version = "0.20.0" dependencies = [ "ark-bls12-381", "ark-ec", @@ -4455,7 +4455,7 @@ dependencies = [ [[package]] name = "namada_encoding_spec" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "itertools", @@ -4466,7 +4466,7 @@ dependencies = [ [[package]] name = "namada_ethereum_bridge" -version = "0.19.0" +version = "0.20.0" dependencies = [ "assert_matches", "borsh", @@ -4490,7 +4490,7 @@ dependencies = [ [[package]] name = "namada_macros" -version = "0.19.0" +version = "0.20.0" dependencies = [ "proc-macro2", "quote", @@ -4499,7 +4499,7 @@ dependencies = [ [[package]] name = "namada_proof_of_stake" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "data-encoding", @@ -4517,7 +4517,7 @@ dependencies = [ [[package]] name = "namada_test_utils" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "namada_core", @@ -4526,7 +4526,7 @@ dependencies = [ [[package]] name = "namada_tests" -version = "0.19.0" +version = "0.20.0" dependencies = [ "assert_cmd", "async-trait", @@ -4573,7 +4573,7 @@ dependencies = [ [[package]] name = "namada_tx_prelude" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "masp_primitives", @@ -4587,7 +4587,7 @@ dependencies = [ [[package]] name = "namada_vm_env" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "masp_primitives", @@ -4596,7 +4596,7 @@ dependencies = [ [[package]] name = "namada_vp_prelude" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "namada_core", diff --git a/Cargo.toml b/Cargo.toml index 51bda1eb33..16442008f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ keywords = ["blockchain", "privacy", "crypto", "protocol", "network"] license = "GPL-3.0" readme = "README.md" repository = "https://github.com/anoma/namada" -version = "0.19.0" +version = "0.20.0" [workspace.dependencies] ark-bls12-381 = {version = "0.3"} diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index d57798939e..f6d14e2c2b 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -3707,7 +3707,7 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "namada" -version = "0.19.0" +version = "0.20.0" dependencies = [ "async-trait", "bimap", @@ -3767,7 +3767,7 @@ dependencies = [ [[package]] name = "namada_core" -version = "0.19.0" +version = "0.20.0" dependencies = [ "ark-bls12-381", "ark-ec", @@ -3819,7 +3819,7 @@ dependencies = [ [[package]] name = "namada_ethereum_bridge" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "ethers", @@ -3839,7 +3839,7 @@ dependencies = [ [[package]] name = "namada_macros" -version = "0.19.0" +version = "0.20.0" dependencies = [ "proc-macro2", "quote", @@ -3848,7 +3848,7 @@ dependencies = [ [[package]] name = "namada_proof_of_stake" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "data-encoding", @@ -3862,7 +3862,7 @@ dependencies = [ [[package]] name = "namada_test_utils" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "namada_core", @@ -3871,7 +3871,7 @@ dependencies = [ [[package]] name = "namada_tests" -version = "0.19.0" +version = "0.20.0" dependencies = [ "async-trait", "chrono", @@ -3901,7 +3901,7 @@ dependencies = [ [[package]] name = "namada_tx_prelude" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "masp_primitives", @@ -3915,7 +3915,7 @@ dependencies = [ [[package]] name = "namada_vm_env" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "masp_primitives", @@ -3924,7 +3924,7 @@ dependencies = [ [[package]] name = "namada_vp_prelude" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "namada_core", @@ -3937,7 +3937,7 @@ dependencies = [ [[package]] name = "namada_wasm" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "getrandom 0.2.9", @@ -6488,7 +6488,7 @@ dependencies = [ [[package]] name = "tx_template" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "getrandom 0.2.9", @@ -6635,7 +6635,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vp_template" -version = "0.19.0" +version = "0.20.0" dependencies = [ "borsh", "getrandom 0.2.9", diff --git a/wasm/checksums.json b/wasm/checksums.json index 51c1c83bb7..71bcf7c9ef 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,22 +1,21 @@ { - "tx_bond.wasm": "tx_bond.e1528eb25894ef3c949039098f03c967f3ac54fe85b1ceaf7188ae8530f82f09.wasm", - "tx_bridge_pool.wasm": "tx_bridge_pool.7e5ef98663de40a9d57b3a41237fbd990e45fcab69963964c68691e33419984a.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.9678d081b00754a308ff2933e82ba12eb435c253255634c2f66eef0b5ed7664f.wasm", - "tx_ibc.wasm": "tx_ibc.03f5035cdaebf9791ffdc1c6680fb49e85905fded1cb6af9c6346971fff3d18d.wasm", - "tx_init_account.wasm": "tx_init_account.4f459874b97ba38e1896154b5a21aede2a903e423bb9791d7c484e5d41bbf645.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.7d853deb8f46cec9994d91a23cf54f59e087362d01bdedc1665dbd9edaeb2875.wasm", - "tx_init_validator.wasm": "tx_init_validator.374cc193df2d8f52709d75d8b6a3ee0c899e167e94213600edaf49405f74710c.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.e2223fd749dd49887a10cf48deb99b67caf65259d78b697ca9627a59d9932aab.wasm", - "tx_transfer.wasm": "tx_transfer.77b50bd69208af0b2481f709411f8531474902bd1a16d3976ca72878662015c2.wasm", - "tx_unbond.wasm": "tx_unbond.026172d96c52b6c1d24d97e0c327415cd4bd20120efc565f94a3401594596fce.wasm", - "tx_unjail_validator.wasm": "tx_unjail_validator.47b4cb5e76967bb1f1264d4e682ac7aa15ab5a59d36335bdd1ec8862e06f435e.wasm", - "tx_update_vp.wasm": "tx_update_vp.4b677b8fd176a1d73b2b7754b4e0c5de1d835f3205d0b9200089c6245c5261ef.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.3db37f2f2c4489f0903cb3d82455fffc2a21fc6c3d802483f6904686c8c9d0bc.wasm", - "tx_withdraw.wasm": "tx_withdraw.25b0da34e26726dbf9d6a34fa5995d916433cef6b1dc8de61649352b7a21011b.wasm", - "vp_implicit.wasm": "vp_implicit.d3d7ec77e15aaacbffaa68208b6660e5b98412c3738d346b5bbd43630896827f.wasm", - "vp_masp.wasm": "vp_masp.57bd2fbfd79eb4c1ff0b2380db013d99463bfec8cb998153e168611b1a53eb14.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.7dd6f780b372e0f0fead49243ffc0e2ae76f28962cf471e6d5ef20b1bbce78f7.wasm", - "vp_token.wasm": "vp_token.9aa48172e5432e32f8a99263ffdbcfee301175e59f9ad827ec8e54560a779d45.wasm", - "vp_user.wasm": "vp_user.810fb7c65cd18985f91bd87caa28e9ad93df5d442816e08b7c2f4d01bef6246e.wasm", - "vp_validator.wasm": "vp_validator.8e9855ecf63d2c27b4e2860fd2f828e4bda8a4ab798bf8eb3edb224a5a65ae02.wasm" -} + "tx_bond.wasm": "tx_bond.e9201ea2342459a86343e5e6db1400b23d0843fe4407f6f759c65dc63395b5cd.wasm", + "tx_bridge_pool.wasm": "tx_bridge_pool.c1b7457cc85bfb4ca141a3f1157700be2d0f672aec21a2a7883a6264e8775cc1.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.c91a746933ecb67daf41d77d6cb7810277dfb661a72e6f4726cee7300f750cde.wasm", + "tx_ibc.wasm": "tx_ibc.917a7c8ad4138679eb467e8a18b0234d6c3ba56802a728186d7fcd047e0c0c4b.wasm", + "tx_init_account.wasm": "tx_init_account.6fc0c6ebbf933befd27f8515f0d439814ad8ea2732ec62271fe5ec4da177340e.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.0dd098e0962ba8b3fac93eefae0524959125ce334edf05e05edbdf4d38ec9069.wasm", + "tx_init_validator.wasm": "tx_init_validator.88f4d2b5cc358e8e9a53512314450161b8ab123a54ccbbbc44c6272fc3496ee2.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.4d4243a995611cb5c53bb7ad9657dd6112f8f3e309b1497c4af2763dcff5f0e7.wasm", + "tx_transfer.wasm": "tx_transfer.c6d4ac6e8311a75f199a0ca009f2b80c1ef107eeadfe7bbab94ff6453f075954.wasm", + "tx_unbond.wasm": "tx_unbond.00df2be4c4eaa94bec27f4bb89ccb96f7725cfdc4af2c965473ec1e9679f0989.wasm", + "tx_unjail_validator.wasm": "tx_unjail_validator.96081388ae0cb289df7fb48b37a5f640c867205f19c9c44a2af4f2f9598c4a27.wasm", + "tx_update_vp.wasm": "tx_update_vp.a1b0a203c05769a90c63711ef3ff922c5ba4b82f74511ade2a446ead47a2913b.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.9b4103f524a21e45d39361afb349e60a5010d94db5bf5ed5a8f678ec9c0df1b8.wasm", + "tx_withdraw.wasm": "tx_withdraw.37ffd46d73cc8cb67c14df42389b18fff03cd0c77861e2d9fc64ac486a13f65c.wasm", + "vp_implicit.wasm": "vp_implicit.e16621fd592ef6ad906b820762959dec1224e5a2e6917e8d8f0258601ed8dadd.wasm", + "vp_masp.wasm": "vp_masp.6aee2c99eba0b680198c985aabb1c6be877538ae338e36e9164c60685eec9334.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.1a78846b5aca8ce6a34c258dec573fec7d247ae07c09dd7b92196b2bd0a8d1dd.wasm", + "vp_user.wasm": "vp_user.e0200e6274839583464001363bfce2137c8a0a9191b242a9c34c32e31b525a04.wasm", + "vp_validator.wasm": "vp_validator.3a23a0a5629de85f2029543d3fc3d9d3fd58473280859b4984a9a25723c1ca88.wasm" +} \ No newline at end of file diff --git a/wasm/tx_template/Cargo.toml b/wasm/tx_template/Cargo.toml index 385b45d4de..15aacd618c 100644 --- a/wasm/tx_template/Cargo.toml +++ b/wasm/tx_template/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "GPL-3.0" name = "tx_template" resolver = "2" -version = "0.19.0" +version = "0.20.0" [lib] crate-type = ["cdylib"] diff --git a/wasm/vp_template/Cargo.toml b/wasm/vp_template/Cargo.toml index b98b74b7c6..634ad3ee1d 100644 --- a/wasm/vp_template/Cargo.toml +++ b/wasm/vp_template/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "GPL-3.0" name = "vp_template" resolver = "2" -version = "0.19.0" +version = "0.20.0" [lib] crate-type = ["cdylib"] diff --git a/wasm/wasm_source/Cargo.toml b/wasm/wasm_source/Cargo.toml index 266392752a..a60c6ce875 100644 --- a/wasm/wasm_source/Cargo.toml +++ b/wasm/wasm_source/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "GPL-3.0" name = "namada_wasm" resolver = "2" -version = "0.19.0" +version = "0.20.0" [lib] crate-type = ["cdylib"] diff --git a/wasm_for_tests/tx_memory_limit.wasm b/wasm_for_tests/tx_memory_limit.wasm index 2544e625489dff54ab2443abd6dfdaaf82153789..676b16e3ceacb98ac52833acd8f28040dd13cd74 100755 GIT binary patch delta 121259 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%0Agrbb8KnNn%RhbeB0bzR+CF{Gqj!J$ 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 delta 196019 zcmc${4V+z9Rri0+xp(f|x%bY@owv@UdE4hCX(vt6gr;QDgqEJ6DXnd7TKdO=$cwz+ z)Y_EFgV6R&X<7mz3ScAA_T#E z-hcgjzHxS?nPq!w;dC}wdG*b29J=`xKlX}Oz2arJz3FAQzWNof3fjt??9F_`Puz6d z%YO7HUh|q)-x`eQ>2UYy|G4$_x4k+Tt$Zaq7F8?JU=&7CWuQ`xBE_P57%4gsMb$8> zC^PIE8msUq2#2yDAQndTdS5*Z!f1S=5)PbIsRWZ0tw&K`G+e0$Aw|Nv{SHrsVOR;P zjb@EVI8X_LX2_diP_HlI0R{ONRVoVV1AX8vqffs2s+EcwjcPS~ zA?>Kkk%mAP(IBLNuc;Zff>sbL4VJZ4CBOj?Ps$^k0H@6Tp zvmib4GY{wCp7oWp!~Q)#P+5{aMvZCMynQU0UVZ-S{vbLM-STMo<9{E1D(XBpedTL+ z-W6VcPk8I@Fnw3}?(oy$Plx|EJQ@B|_}1uO!d=n5(Jx28AN}v>f=8pzMz1*#-532z z^tNbsG#{M^KNfu=`egK_@IBFvXU|;p6Yq+qFZj*q?a_vBhc~|H#W%h2O+WeOx7_~g zVfGv0z2QHC-2V{XAKe|kEnJ9pg})j8ukbgcUybgMz8%ho4@58j(cg+*@s8-HqW>9v zH2O$%Fgg^)_e39$9*pje?uvdR`gHWaqGvxG{dV-3=y#%rqQlYeMU4-9W%5|`aR3Lh zyI?wcRT5S&bwPSO=uAg}3)8UO2eh(v!S*z~yn1P*hmYCABvh$&L6AmQ4yx!AmYr6g zZ>bZ$P_)fb-}d!Is!#3Q5Qj}(IUJ>RYapdS5~|4{J!xgzHKKVX!6==|pB{{EvUm30 zyfNGn{>-PoyfI|H-?jHIFASg6V(MJDDGHi@+Me9|&_jJo_g*{MSKD*2+E{+)iXe46 zlKM(YR4=Vu>RKsP#SvlHiCcR&yfPf$d(*SS^TWQqA9!}SBK53GN0_x|~&@OgvvBI`nBJ!>YslD6J56Rsh+ZzhZh9+?Tx zCHU4%=rU%rlI{-G?IZ%KmGqsKpw~z19b+HT;@_obCLrrU4oB+YcgjuH)<=~{KqV_`F$ z-jPJ=Z9ZSstUF&#>JRUTr`&KF?udt}wV96F(8e{n&9rTa3A#C+Ry%Q&25~>Vjp7<* zqqM3M}0kB(do)w7<8`P7GkWO#k>4#TU;mI`Isd=Krke<{u zP1U$h^D~r2+0JgVrf$)NovwQ8rl{&vSyIzWecKp;+9na#TpzJ`vg!KVq)OK)9aw1% zZG;h3)IXPZw=YljrO{5+Q+4WC8tJW8X5FwJt2;Yy9gJLc9Xy0yM>J5?+*|qEtWYK_6)jchX$k4 zmGopuZ+klzg(J}7?H7g12|nn9PhS)|Qh!R|3G~5G^e2L%i&etIzJ`9yo}l@7g+2=z zK||6>9}k3lz#zWKGwmpy@7KT>=8mM_x(bI{2#xGj2`|t0mn470x0I!ioz|4P;G0^x zL!ElYCRNMnO+I=`lSjg`$;b1-9qVcF9F6X<$W5bDV$O3{Sg&&P;p~mKhGVPpQJkpe zRfev`;=>HqQ`cD;G{5>g)%S;c@A^!*e(#Zo`Wk!Nuj#Ap{cvBkz2~v&%Dq3|SFN+~ z-?``W)p+lQpRd;U{^s+mwY_gX8jY%Wea}n!oSyG}#qz4#+iv$Q-ut&(!@i7#nRPbcG z1Je`B=OxQI(UWt~a*p@p9H?|MiXQ99+HZx9_T=odoFhFsyDjIPBf?`*N=1D;+@7h- z6C)n&8kwog5lg=psatzSv@&f8)UA`2v%h>dquBc3JtJGg?}DMKXdf@1Aige`ABmr> zfxd4f{sDy_8i}cHIyx{C@92Wz)1lbrHPuVgvq9B?1Q{U&t)@EZXTJAPus#U3(s0BQ zn0}h{>L8f0biI>4NIEMs?k}IFEeLZ!qzAuA&)yW(VLOqSnx%sLdEU_YmY`1kyT0-e zz@VHl2^T#u>+Vg0*~+dsZdD-L>FEBE_y-jp0)wZ6Abu0$t!9wiCO_$rLT3|C8QPuh zO>kP4h-h|B|3a8fI_Tcy-johryEus?Y`_i%lC!tL=qlp!M*yCqua7FEvxf;&W-b0v z!u>;?_@{(W?;|p&2&fX@MI=ifCANtia1G`d|0L15;ZFQQ!f#Xhtt$N(k-HT+K;$OE zgM{x?#&3^w;-6OJtwi3X2m)99Zbgvb;$4ayBXW--v^1~qFmK;$k#^RJ-=^67P$#Y2 z8oz|-6N8=jm4wF#e~9og!Z#D{YIWkb5Pq=PiEmd(E$`90U{m~FMdpa?R^%~S`Z2-} zk@0qA>>~0GMZoO%X2K8f@}97ADfmAP_>-pKT_Ox9sSXEn$xiM9lv>)-SFzKJXvs`uCWssWl;OAAJqi!?-?pJ~Uu z1oLhW_A7j}oix(=8{=wPjhn4{{zjaDes%uF`E&d&=5K<(A^tYb2JvS80{%kM+xQ#i z?^@FFIYcfOTdT5Cz}EWHc_Vt*STfZf@st}~y64i#@jX93Fqrj2>5&AB{`8!VBejR2 z=rx#MfBK=QvpO=K)}Kx&}(}o4SPr~nw`2=vP9OuI(lCezb|xj7!gpcA#zi0z&Jyd z7!$)x2l=oBDj?kG;S67d!O`3o19(Ff*}+(Y+cr3$azF<5aI^}H+@|O;g@`dn2sJNh znB5xJSRMT=O|=(m2IeC~zslwCmj3j;NQN5`%c6pb_q&?7YB*07*7cRHe>(bL6psS- zOrxm6J47s1nU0PmHkn?$Uur1(-Oa)vK|M?3L_`b&aDtNRks9l6|i}=M^{%JioyU< zu>jK&jd@-=Pk*{E(n=V&l=Z$y+}^`uMGl|U29p?CTs`Y{-xp7~)%Tgi-V&5D&vgLP z@H2tw`+(B$8KEQqOi4e5jR>newlV+0Eg^-5-PW*Cp{iF7Ru~!D?+1kg?__P<6zwy4 zX;bteg{0>d_HBwD6i)T02Zg2k$s;~jA%0Leh789qnARTeuA4*PE0b{~oQZ;r-quy- zs0nU#rkNLfrU{KM1(;eI+czKpO}G_p=TI~h?L$>E$(k~gXi{$F?34T(zI(QM{jHQ( zsY#HS5V%p=&SaDjQ8L@Jvq1|j#CS<6ydeq$5*!qL?1>T##qfFZuMIyD7yv*LwZ|nM zv?n~Gc!ZhHqGp1I{zg;)ly=zgzpdM5N*Q<{o^|yHU?|lGTGcLgJ{h|HXWM)sPXTJ7 zYEoSsFLPC4tk5*B=OE0U5r#4z{0F^??X^zZK~ouuwIf@{X(;oupJWNUZ=bek4e zYFu8;fY%Yk^OiTVEL|DURW!FwGO%rMU0QW5F-jK1+W_qVvf02@@IA4~YZkMVZMX?g zHqu}D;zuT~Oe?n;K${GEX!Y0$QF3tEf*cQ}_trqE-J^UGxvu*a&gQ+Tx6=Nf9? zIdnTW2@o;{FuEzCKDTlv(lDkwZW)9@%yrxfpp66*>B^3SqCs8Xwm{ghwIGrj7opv_{tP;-Hj_n06-@xQ;igV}ae5HdRu-4w z`avWJ60;GC#cUW~lpazlBxQvxHX9lGX*oz%&$f4imY&zO2F?}#7Ot^;ZWlZP< zDmzUj*GB$IAZuT7n1UukOrfe*2Y4y(OW>?`2#&MY^uOzBZwh*phdEOS#>G`gv}deviJ zQDuyacj#znZOgubDx&_PwMO7M_LaKXS5!J?WW)5;+gI}L!vB04m^{_MV!f~?tT(c+ zM4BzLuSn~Wk+!~5%wRFWV-u4Qt+Unx@kK6} zeKNe;&Cb^Eot^vMll^z^YUWYa4N#beyWB;nbi3l!t+A$&fb5GG8EFt}g7N+76PRue zeOZeH_?{jo<)iMR85TQxi(10lrK0!C)OrjXO~0F+sT>Vm(_Jj%Uw?`P!(Hr}JJJm| zlVSmFe;6Pxx-Xt_4dF*K9uQP*Q32jy!MUAyDKqOjaUTH?nj}Ee86}X-(D4c~9?EgL zLEzIQChNR}moYo5gHT%WQou4@NY{NN;vz9gVo{OMsyR|5Y9t1VM88|N1>w=J?fPX{ ze(`^=m+g!t~*qOA{%Ve1g?t$55_s{`r zHru~27v6DquU$D_&0DKwQeNhk&4%}$@^Je$M5C4)6Yh_7;)bST^_v;pY7D?`_1(!V z%kesl%8O)Eb}d-=PO~hZ570qGFHpi0)`7!Fm6@=+?#_p+rQ_|#qQQzMnQn%01Lw|% z{>7`DXOT;Ir7cuf-Z>;ZlU$-ILYcMKY$MZi$cb&Y3UM})VO;Y05b{Q`jWz3nEJzlE z3CXydpo7?lE0@1v5LO#+XC|O7fMk4v zq}7V-X{*CfQL80Y-FW7g8ir#_jvMj-1X$aziiZ{0icV+HrBoT(8QWK#csr{tq~>h} z0$mQq2Dv39=>)u(O#v*@Vr&y`&8=iPkYS~}RuE+U3EXCP{_4u~^TBGI|; zS{huzyq&RIaRUjd8SU>`xE$WSH$_e@}*~SZJz6C1SDj66w5f2$W@rBrsX=ID55daL1X~0hT3Z z!&V$q_XC&AM%hf|Mwd7lW?CbxTy8+iRXn8P&w+g?a~W*nAvk7PdYy+ zSFGEff^!T9<2vz1XhEW{3e_d#8NtCBQfFIl$qGn7BHH&w0`;emz$p|UGX^bGPM7sY z{aQZiWxdRVz3S5od)h{lbT*-WPiO>AOu*Pb)tgX7?KV>W3uok`e3ULqPJIaJ{p2z$0IX2oNuTTAn4!rrN}-h_25-%Mkwty>e@2>o7~ z76jv5D9RH|R*$TQ*8ViYo}`1P2zv%KggpVpZ>Du+z0s%0dZ#j<6=FUsjQNZUjwYCA z+C?Q*vLH4j-9|6x%>c=!n<*u{{*+yfqydFsVt}6N1rb0pJP-r7g~X&b+c%!6>_Z=w zfr7Q%xP)3AqJ~R%*dR8sYLnXBagCgyARBb;%1mVg6ol#EGJ@4Jm1#<$`@5<3Uqf2b z!_BaQtul$8&DYN+8GlNY6_kqrIk;SL`}|l~dU)zAi6}v~^kwS{%G( z4nrfaUFT=SHXDCr+hEQ8xcd=J5gBIfFsfedc+}VX2-(Q$ z_wd;c|A{YZ4K=+`6q>XWuSiX;@!e;`m!eq5(d|o1oZtNcpRxanX@u!!goTN8GdA5udN}Y#UFaXv z8-RfS)53nRR84$PA)39gHvMAMLAd!ThlZ+evh}Aqh)BEJWT73bHtZ z(9EvaUgpRWtK86~mXnZ=BMTiN^J`ImdV;)h@?5tVdQUP$)>Jo(g*RDDR=JDlX>pjW z9%}qJO7gW>AA3yAEhDcZblkPg>MWxo5)u1`k+&3Se==&T1Uf+lag1<(#a4cx=BP5V zWy)L-P$BBn7_W3r!7?|lV38YAu-PqE5V&ClRA>w!W)_=Ug3AcvnF^CEYslIYd#13to%C-bn zNE8*5zl{~=NR`!IXvUwme6C9CO~l;^D?{S8TWKH0wBj^U(}MFKCbbpfP%AvCt?=8f zmMYBrP#W!IPbD?8l=*$vS#6wMBpxzFRT6AUI+J|>FpI6S&8pp&WFx$lQLgCOQK_T( zY)1CqX!KWyf7N?wu-?q{0)%rq5n^I2iLjm%;Y5YTa<(TDl;DNQ0bU0wI*}riW+d1W z!JkTkLu8#sf{V$@put`e947BH61dLnkiaar@wg|qm7d&|d2(Cit`*g7c5MZCOeyHn zV9hzvV9nEDEzJs2s~I^OHP(z88#y)BN*b(5U^W`8NtQMmtN~O{gEg(THqr_I?G{zMUJ0gEOMF_FS-Kcs!07A~lstA&&3L4>m2KO@ozgN(B zs+^&A#kCe!|D4{E1>V_Q4Rdss($lq`q!gEgKldxQ7?A#0qh;jYbaxp9>z z)by~GJ5q@*J^jBecfh=Tt%S+dna3avs(tF9+G6iyTbnQvjGJO43BQqJ#EWqYg;C$u(ax)c@^KbC<~!XD-`GOfORsa}v9;ezu<1#r z0u!p__+I3fRY2QuVhDbDf=<>2AB)iL8T){0uD-03EOlFo*V6q|-)H>X&+hb8FA78mBYo)b#ltBU0R@xV%fwPCf zXwAQ2Y}?=nR#+SnJ{c-xznL(vIby#cdjrdkJ@vxoS}cgb7&&*8nL_XDMN0{&YY>rG z9Scl7o{@Jxc`TxNo~v36Mp{c-*Wx~-b|}@};cCwxj2%{vIA+yb7dQmbaUNE0D>Y8h zCIE?JdP_fC14);DP*rkM`;Viu)p>2b-=?hUZS6nKa1K@_`52_cUhLKuFg4wEom*3; z@$XA!WJmOrX84XY3O^oa-E1vRS~mO$q@h{c8&KC>%dod`4Ol&zPZXpu>W1JkF(C`D zV6ItG` zR*n^|ykFgJIIKr zQ{vh|?HM3f!47w5A996u=fxuk#3LF!6pygFVGfJDerh}d{sZDo8mT!uBO&lR}W#mXLh z&5igzKmt_I~q$m|}A;;>$bvuJYM`kdS~ zZEi!EfDv5i6QW>SgUd#cg4Bxb{V#T_BzKso@7t*QHt+{g8(JjFHq0}lXhW+^*@iJM zW4~0j$}R3;UT3H;D?0tO74~*WTvlk&pw|k!Tw;|*;fe>059TEvX3U|X8Ae!Mh1`Z_ z#z4iU7c-;qKffVC#mukuSCelHvEiJdocO#R)+BV*-T1XABsMU4X<&U$9JQ%S!k-7# z60|NmCR9~7T!D#tT#+4L)frAA`0QAkIJ5e)C3z*);<+G!=}xz52OVwf`RTQjSmBVu zcUV23iq#`$4Arkk(dm!;`39rN(;n#+m!mJR%Bx}zok0nfARptgoe;J$BP22qF=NQ1 zr%dC&2lc3=MBPa$*6d9_Wfmo9Fz)<;pGEE6`)QoCX%t;xwrjH}6GWOxI%E@6r(#W% zx=X~*Vv%XHH|fSTrBF1Zy9Tn&N9N~Iel=>ln8!NvSeNexVkM{m(Jyv*Y9NEzhKy=9 ztn2aAV5%hDu~Pw5na5Gduo|%H{4}%gsMDn_n?7MeKC|pJSj8@%S;IM-KHG)JpZTdZ z1oBf`zIH0M6?HMSjy|im3BIQ`wTN!s9MfIxy6fB-R=Dh7)eRw}#qlZ2nQJ8ZvS;|~ z)dn`OSY1tPh>F#yp-lw{mmz?@sh4F5b2ySVX6mugEWDQyOQf;;l$`&JckN=Y)EAC7Y%ti>s$!BGp7f zgzB}e;&7OyURy3aqK%0ojODm7<|#`0JRmq+u>l;ewAc>Kr7aK|epHB7t$}9O8y|9m zz4rQS7Z&_1&{|8wQulh@hoh&u52IOYdY|byhZ;Jg&$d^pO{Jc#v?g*T3*WiZbu5IX z9+QYPEOMS!zcQ7|^sLxvC#IG*)1!|)6tsT;d1k0LQO70rc>W~)#;%8hbWQr;7aq!j z_PU^%9{IzEf^%FzaBOlVvL<=^ z(5~b*rG2=n`9dOMN~| zBYspGS;Xm`sieVHIeNfCAp_ylLVE+f4E>`uIeaDGuIt=<~1CHfs6ts(s9G1Rz}^5@HVWf0~h zjXT#QxO24&Z&f$kbB%om8~~25!N5A5K7Qh%AU=;-TR9uJbETl6jz1S_LA`4zqvzE= zmM-v6!}Z0cgAjd4#Rhtc4XBvW09kS@r`X$5{Az`V2IJRciYyGquT^+>Fn*oF`Jwpr3il1g zHz?dc6hBAdfuZyQAN0KV@@X*~el>=vkM~hra zbibm@EV@t8trpGZ6~ENtyA^$oMduVv-3`2VlB$Wmp6Cf5eVr=y(buYCAAJqceTu$% z_HWo33eFMEFXXEVGwDbh{fgGa5(+&25x z`JbNK|KtM?9}TQXPm;M;kvGhKk6nf3_SDDVVR~?DPm`y2;IInp>}l`}9avDA8=tWQ zhg9Te!ZY;Xpq`}e{o&mS^4dHp5o-JQLhKTF;m*Zs??~iluN#h0ZAu=Sh&9!*Z1@5oSNjozpdEjHCYol%AabS4in?`B|Ex~>jq+{MoQVC)-ZV}9X>aBnR$PcEu z<%V?g4WS$5tg}T|r)TG~x$okiAFt%3jkXB;cgCn|>GC(m*SKlh3_Q=Z&HKGY8oz!D zW9ONDE9Z0;e(L|N$16$YGk2c6pU(5*|9NTQz;m9h&+Mz^=-7l+PTNs|m26SYW#6lC zF5opXR)?kJ0ktAe40nDI4mm*n`}#Fb zMq7+?B;DPwh0Q=!ypqn5Hwphw0ce?dvgT0-sF!(vL*B)dd$)PSN&*?N_8PQTQ+$2! z(~Yg+6LQ`_e+;{Nm+0iodRL)#VIlvw=w109CK9a|PEPlnQbT9-oXR&dr=@wV6xFOW zAB|h?j;GCDr?Z32e9bogMlxTwe|4~vtmAR%$bevE`;7N@ePBVqhnSlgracXEKA+ox;|0ot&?EgjI6{vX)`j9G z?R2czu|1!Jr`OD|$vbA97z3H8gDuv-Cz?w>FqifM@MVP>gu}yH?mACWA`?iDfGf;y z2FSvyypjV#lXVVR@Q6IiJ7YVewNZhI=b(Ya+fNjrVW%Y$ z*0IAZ2Nr3W#1}9Sv5~PWv{ecVhQCv+=rXl%c^12lF@YmExSXlUDY#)o#d2A%Ig)nnW+o~?mR!LzmptTA-HishtWtA4u z0z#{V^EreV;CZrAoM#}SG0f1dpcypN>jVScW2JYcg1%y--lVGmay6v zJYa$J@^XjK5bVUjOyv*@mToKJmt``2JS+I|5v(&ydxVg~q`6?KTOa93H&v!TNqWj% zoD+?&zefEPAv1m-_zgUm$L}B98X}IlbygV7Mz^-{%ZkAK7afR|#j22g&MygQqYPmb zsks=^QPS;l-^-euN5((Gkfx)0Vnh*u2iQ4a((BCZWN3%rdmef}rGjKehn@kw7!rq` zvHzk*bL#mVUA}}CeNOuJ$KM@ZnO1JIy;6C?uk**A*WnOP8J&AxM_0{bBtQ1NPL>0X zJ+HHR*Fgq4_Pmbv#D)lw!~wuLCZ5^~F6oAP?d&w##$Cm^XI7*0K>T$eDHnfeT=98E zxK(hI)`d2gA}qPB_;O(a5Lq4p?lLw)e8#bqFR~oXA!itGK6{ZYX1sBTH&kc?`!5B` zMDpTWQoeHO4!2(2jdY0|!v$vwaVkn51%YZJ9}Jg7xdwu$+hF#|$X#Rh$tXR}BMxM% z)G?xW`lyf()~MX0x;<%?kAr{y%ywiKFPYnc^%19x5-lV1qbray(y0U+PQ?%Bvr4W9 z?_?W_($A5c$`O?7s`OBpSI8jT0aT;?%l$Y$R@39SL_eq-G4eTLYf<`ENtEk?vwcwn zM>R9GHPo}8QffLF+hir*tyDUxqf;vE`JQx{E8Nv)pnaY?jGvh7S|Cj(a~3BYN5BQp z1z1Q(D@uFR8T?p*A1W0)r|z?$o=leR1j3SG8ylN|(eHNgd&qAT2B3VC3H928;5 z8o7q(xBEKrC4}(H_*sPe20HPJ2@erImk=E%exC9|X|A|Kg4Atb#3VgxY}ygaNCllGnp}Z2F5z zU&|@nMJD($kAgD@GxrA%1vznwV`k~e&la{23NXqOz3Jm+B3E*~1eq=pNte19DZBLq z7rQIjR%y!~>wDE#_R5U5jA3pLqpj3ASbqD;!7LPNw9EohNz$Pivi6fzm`ZeJ#=W?F&XPBn&hxxjAaq)WtleZ4f{RYekTpk^Esxlrktf?^ zA-*5efLY8QCzSYpRN(==D$$GJ>2%Y5hC!`j#HDQ2Y@_}pN;#25xj2`55Ida=V81*O;-jeLK__D+h_e@x-qY8r#Yx%rb zhf#{G^1Ewd>9!KUe+pAOk>0g{B>W=)AU}s_hF3`;Q*F`ikl9(bsLxD7VOCj3!s8#4 z29}vA5)dKh#u}n7xE~tx7b=e{wWBj5+v-v2PCzm<~b{U4m#e+ zJZBlFa8yXk*dhO#DJoYud;1Dau)TGKkkwB7T|r@g0_k$!ml+?Nj2pRk!l>t<4Q84#=WZw7!q;a`jy5=>Rw@H|bFO$@#7dv_}_0GHr z)`7$d;|6M02_Cm;?*eI-kw5z$L9X%u%TF;>X`*dCNS4T;Z~LZo&g%8}3yw<(uj$}HU{obd(^W;b8zW!%_ z7~Gz$WSe-x8PHATmwVFJ3Tb4I;gBp@Z=2p7t8y;}<&b^jy-ah->wvmgDEvjlyG+Pj_Ek2mAylAqdz;oMcM3ZY2A|^T+&IS$X z-(+VX&Ib29*j`fDYD_9kt|1fQt}vCnj3gHPLJAmk&x0@}m{N(5TdNs* zexN7kD1DI|g)ak>MQ+CgUvn>}S9zaI=1yAf#Ti{I^N_p&;EO%iu|+9Y08mCYl(wvE zO2g;NkOI8@um>JXXivPRYD1{8Q&P6t>fJVJ+qOzpR$$g_qQGn`?vJE(te7sp|!jnsYh@e<2a0qUI z*&ZQTvO*yzT!tXL1{vXgzYA_+!-0xd$zlB0NGYGjO3~6L%}Nwe*!zx5a-dgdgkZ`$gj$Twxg{`_j2)u`V6{}W4=JQi`xVkpv=CDZg!LM`HZ$)hUWk1tW$YN` z8tCPN2%=i_?;mCaT2Lhb4EOFBp_pc8m6~ZF%~U!k#bw^p4Gq*Sozp~T%hGycI;WOM zsSiCmry-;57zqbaIp^r5#$h@&IjJ6^#WV29URX^ERt1zMCC(M_nH28kkO3M9C+1-TXtdQB zXKlY#bb4!?_b7LY(#H?el?~}TA3lo#lJ;|shk@`BR80#VVA zj3Z|4VK%ClajT2XThq46S_p$nv{g1~3XTJzWrOK+OHkqzk%YJiNv?$)IHeskfL`pF zp+M+@cd24-S+QlteSW(s4YocT>8n*KtvzFt^Gua;U_b@y7&9FV3oP{W(jif$6-n)^6 z=eBJmVGE?%KB%Pq`7E?AzKCr5Wcb%b9CqA>u9+(?34H$n$}OdT6Z z(DU~AMGCHtS2ghfG;8ABZoOGeaEn~!UZg$=Jf>yYp(_ks5nFUQw5sk_Hu)miFZ$ zGIZI1ba4@}kUASiaM!Kh7sx2W5>{yHFjmwUF-)&G7`rcoDrB7@EjqbqoJ-$k6N+4| zP5-5b>^DS^N+z8TSEr`&8a2He3&AiKE~c6Hp2LLPhgydB+`C|VrDX{9_U*mtyUIk@ zQq%{4%z3V~7@PAPns#ZO<_Y}qbnCR)atjx=f17n$rsbzvr+a*~D(*aQg*x3;3*K>K z6Z@jBaow5wddV{@GuJGpBcFvPGiHqS1?1#-bb+_NF{y@PRreyWwQ8Tsxd29RLCUvk z#Jut&Mm)R#d}Yj@Xq!Be>-q&?Asg^7GEI}3uFwVyTeO0X-werjr(S^eZm3f)cBcl} zG%Ee|wF@PBu?;9)y?_rr?Qzi4Z$jxDzuH^H%wISg#9Im1@OM5Gzc9HY8O!&cGD$l2 z0|g@){kOQuXXO`J&b$q^%5OArMp4wXs@#-XRc=a|D~6T{@597?(~gAATFG<_icP6} z1IljB+QRKXX$^D+2VIY+?AnJ7%wBGY zt74+n)k{!E&*y05Ki>N}8u6>%<4b4$`T>V`yeF6$Vue*b7m)EmwTjLVHQWXvAxzE# z2p3JD02ec+%ZQPo5^m{T7#4QuzgssHVf|_HqU-wqnK|b_q2F1~dFB8Osxu#m$~pVHyw@nUOWEd8)$ zl$Rp|;nhJEJ8|%KuBX67X-^nV?-+9lM-}(VE_9^4tPTywKPJk97k0mzBP-pGyGj*n|;5a7A(^?+{7#QD8OvE3t* zVf=vo>P|wU0F={yNcWt8+mwiW0`xDQL^>oOFMIJb4THmkI&_k$_lJhL?4JdD?mVM3 zwYr-R@u*!1jJ{R@lNSpSq^MBa`n<;uoiR@%M7l{z1ZI8{W6zEbjN*nyc>id;(m$eO z=cN0>%sgbL+}L#R?jdbYO&Y!rNyCES4v3m^5lyQ0ntT(bmaeh=6I5~e=Q4)*vyfS} z0jT#6k0BA$1I!Z_7alQV>347upD8@B@&zw@vd8lRruJXPj1iNy;$?)zwg?lgWz6_X zl+K;Dj#bIAF^{D`=V!dTYc%Gneh-KXtmVAC$itY0rP)dUp&}UbwrvVtB8}n_H?}_9 zH-slqMjU2T!N!8NAnq^K{}SHwRpf7zz@oPJa8O^d6gm0{+}*v?VwxLE}3WJz_SB zxrov~#*(wfJCS{k=vKeE{8^&sdzZCGi6UC4T~r@;iO8KJ0@!v$v&Zdj4l~YeH9P~0 zqx744xSlEURs1c#Wf_K0L?Sq0unrriQLugyYl51nF7rF$C&7`zu^Jd$Y!3{W+i|^WI$0|UZ zL#Eg?P;44rv1#Orjoj|wno@`h#fEdlC>jl#q$m1b4EOA)6GPktQK&R@B5#1h{MJI< zJoNm1%%Ocm=Ep5IPlT6 zcm~l}4N_lWr0-AXtoDWO`Q|j^v(^-&cV!pkKdhU;M4>2m_MC2cB=wy*#F6MX`RIP4 zFZa=TqTAh7y8kueG+AJlhRxuuYzmvTMAI)W`&->~T+!}tXwSHWTFq?kw0|j*3BniI zoZtK`ybz2}@PRJb3v|)po`_G=x&DpZP02@4c5(l+zp+s+Dl~)D%Ie??b|~9A5p_{+ zt^&oFtqL_Y`Jw^2oVd#PehNEuYGu~!AS>Kv?q}q4Df&RMMpNGTxY?d_f1@sM+?-$D z=(rD-3)G$6BPyhCx~m>AEq}WRN+*PL^6PH(R7bFwtLv#a(S@HcC&;+K^9Dj}7TXDC zXDU|^aO(U`GnLm71Ejk{LS|!5Ax~azlb)ELw@qn2SBVxmM#z+xIUb#u<1%Nzp#XF2 zb294WUf%<%+u8RT6W#a0XI>NCHM*d1v00=NU7Cc2DsjVn##3ReWA%jBqT?vshDVFS z0lTO_(T>7#rRnqbndp9^+IOdi;8c!H3HU!9T)8Rux}NfV8lFDNQ|~I2NG8x{7juqF zRqMl*;>1=dW^gbG4;9MzT`WC8P#`@~s3KMpe+S2tX+7D{g~bN=uTZZ9&Sn)TFEmKp zE%p_;#JzM=^v;Mzug4cp;j8e&Q+OSF3<^QwlN#xdZ{qXxgg?Sj422hYBu9O@l+^pU zz`?$rQeyw28C>MtkAM)}JJ0!CixAB?)pc+-e-nDs32k2Lt}oLC&A{Kv`b11dE>-VK zobKn2jdVIaOkmC?{wuxQWP^G(k_22-F3!P+QxSB3tgD7p(u0;!XepGyzm*cD9#fGn zNCrFgrpHhMPgu7bkHZ3-758KIZ2y3(@W6mPPaOMEJ6^2t(RQ5CI%{4Vn=avZ+i_hX zpVXaD$R~Eksacxfd-*<*m&y2~J1x=PP=Q5!OIGK~ArY!ikKiT#p0L=4gOcx!Ll@6Z zqtjLiW$bLEHH?u6x|Pg!Z%_S*aj2%}Mv}VZ`95kCbZ$N!IyVdFim?kiH=hojn@@+% zI}AGcJMLz^jsJnb^N!G}5_r=8-OfG#=*43>q=1+K3Wx!ybpZh5W2x(dN7}JIuq%MP zU6to#RUu^+0uW?H-B{1q`MBx_M`Av6%;m^AfZvw^;j|xMaM;+cNob__y`Z$$qe~Hk z?Y_&JTyE9N8b&YU5fweM!?rLA3|uN)D|RcuSu2Q zTnfi?6xIpP(s_7}C+n!+*{~aY_q3(b(1)8d?lg1tUe$reUUO);v%s>3*Bp4YK?Bkq z2&FsJ?Q8S|h3=qRWz>hLyP<;M$2VM7Xl86nMPK4TgaT7PLyyl&gEV{mw;l@Iz~W>e z#|2{mbiJA`2Ec(ki(0`l?+6-=v7dt3q!ljYUw~fuV0!S7g|i!WKPUJ-A9cGg z4t}TQ!r7_a_X%O3AFe|XPJt{V9$7?1BLxx}f#E0x1`T5W)w2og5IKEKl;|}Quj64Q zeY`=|{E(k+eqo<&@KUh!Tx6mAv@#kR`EV21Zq5mNOS8A8esR*XooPmW*#T#7@nPoZ zRulQ-m%D-Z`8dq|u&c)t+9dP^v6$#+fIicZOHq>WB**L2a3NU1Y$jBYtGc=cMMwEa zz6+;I7r09(9@Y**Wk)iK`oJz9(oQA)uK6MVrP;3ND@P}{gIM-WkJ0Jq2JmmxcJg=u z$7tP$2O45IgO74yFGtMC(bcA#B4;t#IF?upJA$y^H#90onKn`io9x?hs7vQKm5Xn^ zzTBy={~^+_UL2E|KuwBpHu$#Ik^(!X3E21K*FJG3bQwj|;HPwtp(Cp+AmnE1i3ifq zwt4gB(+mmu>x|NOK$Ahwr=w}PyN@E-#N(dUYi2rVJM%HFUNy)kxOB;Bkk|)FK~yaW zJ;s9ujmdb>NPqWNza5oF^E583v;qL1=c7z!)&nm5J%t6-auy(m5_bMP3jk3)10WOw zuqzY;f`S3a{HreYGJtJ^YY<&(p8OfuTGk|pLqlZk8C{nJFdr?)!-4>ZrmAkhU_YP% zc*&dZm;qO*4g(GYQa&GNGY^Hw^#(Wqni^jy4A4(#aF}foAm*_h zi=POEG*!bG3Upmk-U5!y^|%QVsb{`pDpPbUFGcr-idCcYxvK_vm9 zjmlV?2wCM3JB%)K9vJf-Xd}>glxd#hy!a5W4E4~!K)a-Yp&lAwlTI|ihp*HR?qn4W z@O@)X1G>h|Xkdtlrvb%04JdX}i{KQIBN@1>Z`-vI>G>RqrvbLcp#j~!$VU@$8Zf)C zn)EbaZLK9`G%#c|Fw~`iA<@84mj-Awr-A+8KQthpUyTz((U7#nBCQQpFU*1)LjzsmdBpZ!Fwqydc!ED!*g|8W(P z1#K8C?XjRy1Q23CW3H)pIHv)9(o{Vp6b;mL)&~||(tyzG%rr3UX@G&PwL8;oh&bvv z<~64Q?rhF!K#pR%mYcqL8Zg#lG|=WgcWA)o<80>KpaJS_u;Mt86f`g@8lZBcfl<-G zP)-AO=d)1A&QiiUMFV*YMgyLa79E47l^x4ViG+pudJP&d%(q%mF2w;o4Ge+OT^fM% z8x7o&)4N1|HCE;rOYeoY%Y8^O+`*sfv9J6&`U%y`mj0QIQwk}l5g1)C( z;VbhmD0GP;S452nrcfHwef^MSznBOv^hA&`iyK(9!8Ip*49E$5CMIA8X~X>=_LX_HSNY>TG~t|RHY zH95!hQm|(is}Ktw%ZAJ?zm0^c%ljmBrV#5w%appNltqL%oDOB2tLu}9D1+1*%Ac)> z@-+?#T}V_IdIpK1EyTlka0IZ`ZTm5?LU7!@6nagOyYu~jN^%{SZaU9jTv0p_t_a%`Uv|d*rsy0zi>z^CEab$nDjJ(i z;dUflr+o~D1WU@G&N=j8PFAMJ{NYnSRazFXTdI2)-G5tAyaLjMx+S-PMg;3`udQ1jXX(Jkx6I~y%_p9d@eh_;;0vgNFg3!i1+ zZ?UXPBJc}4R&ndWqiuxm-A$Hr&#U{?A#d}+@ZL!GVNpz5ZPtDD7GaWyDAAil?W|mE z_MoGhOUov41hUwhQq~bGHKo2}2|SiOw@J_81Ba|>1|7NEo$UnQE)0#eCwx2Au)k$7 zczgaP(B9Xa4F%nbcE9Eo-onMxy?y^u8YL z_Vq2kuWwObt%q+>U#-_~0rPtLI@c<$wLZz~#cl#3l&su`A{^b}n3;^o#Qp5v^xB z3ex_n%_Vl!QJJvaB<2BDiMhxO&@!S7a1~W>9a0fE#mY6AzMU;Z*tliDk|xu;dNQV! z@f|i0ixRM=$@E*w@E5L&ILY~XPZ6Lunf`fC27sMRzf@*SdSk()AiI_)W-OSrB}C_3 zKrP|bq>KeR>XI+fjNBUw$Yiv9cI;NvrG+4N7!Jl+uL~exH~1=8pAQ{{nQv5^wkYhQ`VR8MRELaVF+H2ErvOa_b5 zq-wWm#umdpdJP7PbA!QRnIR~ya|nE&f~KU+5z1h|x@3H(G#D&qy49OgjblJcJEq+6)FL--48kW@#u{qwj1JHfpzl0)44{^l-pAdFK?aOj?yCjuf*j z5rZ^xhM#r2?5ZtksYq*q%IHa%0TL7!F@i`EBS?E$!qT8XTlDHH;PW1WJ7$3m&N0t? za=@GDm`XX3c1I%xs~nBl2ulM4MxxxVK;>d6XP(uY3UpZ!g$hCk?F1Us&VLQRAjwEeH3w zw>DmC@-yYX+ei%Q_WbGgRoPPDldv_Fg9&!K6bL2~Mm!)b2hTVuS*6^zG#A2HwS+MI z$hCes&eKh93Nahb@-&p5yS(&gUyj+wU;D1RL`=ck1UU=5l7Rf>5yU+P`pYA@V*@Bd zi+*ufDio0<;unUcAcwvcVCw))*{RWF*-W6zP1vtA_OhG}4>+uKh0CsD#g>Z7B}OKh zU01Nw*gy`0qA4-A90)~ovd-jC(9)yP#kqgh@^p1x`SSExWrG*xomeV=Ge>dx9>Sm3 z1+0JQ-)Z)btQJ4nb#r9-L*TI$5v28tpJkMjQAv+lMmZOi^q6IMy0BM{TZX3`%lM{c ztScy?;(pScDZj1doRw5fX*bEQAoNe{W7mk|=s!2ofV~b0?_?gHPzVh?p%90S<9T>Y zA&wi56Y6~q3G00wAI9YrhmJhd)}foy)*)dg!+GsCl)Y~&IfUVJfn!X#cUWo8KVf<& z$*E)+4(4?pbT^le+EI`F>|Z?#wq$sooGaOXi6hv|HQ?v2hCgu?|8%s30}BK#sE;7< zbYQeVGOKe-B4IUo=?zFuV5!>O+q9rkeQ z>rIvDGV1HKTK6#O>${BFFXz`YG76NOnl9t;AGsAk-@K>AeCE7^EYI+GjoeF_E_?PY zo*kfvR<>T*f-JlMx@2>hA)Is^F=2E~0 z^-un&d0I`V5Bgk?HFcRFbe+~RZxFal5W0?^G7W^T159O_wd^_|R2)vfOg*@++k?y0 zgX_9IxJ*5`uG@pl+;w?X4Mw=%_u@iT@~Unn7pjt1bt|!Jj9=BQFszb+kZs)OlAQa%e@}{|JRvWJ1>KPXH-U4sJm(1FNxz;$gHV1w z8B$P9nmed8=L%B^ykI4%(g_eDNb+jB<8R>0t+d^pWg~k?{jAST=sp zh8?hzZW29QZq)sdX9+*#1&|-|dI1eCiHGSGj6+WA^5*9k)47=!fC4=Q2|bd^-JS19 z$`kj8v@X@%q(?T1V7Ya^i)`3Vx;O}IWdH6#wrowh|LL)fs~Qd4_?}-cwvlbvwkF3m zE=;w>?HbNrut88PS7aNuZ4tJCVEf}8$=cHpEbGf9My9aN1-%4@U;Fw?{962Pz^_mK z?LP!p4JIr%N9#F$De)y${vwI2i`Q=^3=c~+27lqGPB{N+VKVOZ^bQ!PqZ@=mnA@AK`q$Aw6;i$XdHtt!h zze{_No9!Zyn|r~9+^=-_SJj=2C~(p~VV2)|>LRyTb;)4Yf?!>HknTiBFxrjvFZVBk zEn>Qv~c%vCVDus-~Db8BeAA5wYt+hW(16ft<64(2>eQumm`_e>X+f=Z2Kyt12d zFM5)VgopRaC(-W8wW98aFwSsaPlH-tY0+bfzAY?7?Dt{~A-O&b_^|nq%g~1&9|ZXb z`3{lkQ;!YG$xs*LVC9krXKq-*v8agRm0v`01<9jsjgFYF0{8Mc(GCywqL<;O6l1c> z_r`25jr}Z4&1(O#L9@A8513E{MnBJF94jAvm5(;S6a6Dr2J7h)gNckQ6g)BLY2V&# zx_Q}SaI?a&@EL!Q=pKXF3GS#5+&u;}gR{P0DZuTr$H(l>A}-rETmb6%Q>>wzBCthb z{0#)_Hbw8_1$Qw{UtpbacK-*{xBI$ziEo!O6l;jiBF&%gem zzyBVStvpT_%>KgB?|kXu{D7tMFptiD{+^%v+kg7p``-5Xyd3*Vv)}sWw@-dJKX=J? zR$W^~v%hlU55N7T@tg-7v0=a2{6S6t3eZGRcFQN9S&xvqsT zoXJF6_YSOQctvmMa+yB-eGH96!TU%K;RQVUeG-zLez(oA>ST$tMq}0A>CfMww^F(p{Qtob|(>^*! z)cNR1c-@qbo*>F@uF4({vzdUMTE&l%O>3Zwm^jShnR~vk+i_lg+5sN;SBhmAVg!go z|4O;M*x|ew45$Uc9)w`c8?eOSij>P_I2m{E254?#aZ=}gMmDMI@Ko(pp7XXh`DP1KCiPo?VGp7j1P_Mk-Dy>yJTzIciC-+WFgHEkxc&uFyZP z9Y@u4{FLfdE1RM6awX*0J`#i)KN{kWThq5KSsqI5RD)4Qsh$Em%P1)n$XNyy>gkbT zTZR!R$I}IG5wScX$|9m+Z5b6AX1TNRwQ|~QokE-c-i~Px{>(l5emjP13lqxf|ScSn4MkZ>;I zD};n6L%z5~cp{9&-rHJSnXo!u4~ofQBVYCknt1zxU+%7ZHCqa8-COph5C8Kwj7|Rk zLLywP>EZbAB#%zqiZX@>d1!bzVI%X4Lsa-H(4YOGk|aFTh5VwNgvrg3`-HeU_>bFq zNhl>9ctWX{bfBWrDQfa!&z=Z31?);$;_Jl!TyZwJj)l4th+S(u>evdFkjD@u{9Y{Y(`jh03Ve1mSGDC!|i}1<}(Y3B# zFEd2ftzNor*(3N?)96~q1?GCzT!5ZU2jA|Qfp48M19V-4z={Vk6sxkIPQNNYlrPH9 zG{S-}iE}-5fVNFk6!{N~&@%_@EKgw#fS{K#7+r78>3mw3j)?GADg#EqMZ3lr7Ab^L z#0vN0=cW)wg6n$vRv!mhT>6gi6 z+e5iuAi5I0O7Y((KJBv~CTfSr$GPnTTdQw_?TJ_W=WJQL#>d#kc&(4c9rrpPW8dWU zK86;4gO6cpwXds;yGz+{Ny9!i!c;3P2?>UM9Yh$L=HpK{&Bx1W78HaH$9u2=I`^W< z$y16wZEd?B_DB@aQx&I5< zq+Wm-KW0Zrb>^GI6P6%~r3zQ($u7}JF;_;pnD*&IEo785*Qd`Hl2OuJpGpc*pcAz zVa#_Ez5T^LNJdY84_QV}e-BzlPk#?sMo)kDTSmzv0D@)obT^#X6Mpop=!?;wMIZfR z1%LO^$Ih+lOG1bwz=%BwTNM1lpnk&t$)}#yo^KvJdy0GNkcxJ+AL0HyUD3(!abQy2 zv;1R|XX)Xko%S`SJ^K9r9NT~-mGIcV=PR}M{HoH&$wuv10~&-8ETyeI@BY}5ELRP% zO%`Z(IL9Y;U$`vxFrT$JR$^yQv3;i$o9BWMsxND!d5^>gp1D|7lsi(Qj$)H zl)w`u$sxYX5GD0Ec=n~DB>Cr+B}7R*hf!cBmeDgKq9pkv7OyOmPaI5P9uCXxu2`W? zCHZfD%E;??4_WXFzy9cDRSb+bAy6{AnEtt?(x^?7bBHZ~_WwuRxxmR$Rr$Yeb@xoq z^gKGRNis=DbrUckF+m_;0x}iA5FR18fUiYieV_@t1d;W>f3*jQ8dn~{AeB|rpa@Z6 z!Ij-)6?FpWKYvBduINUM7z7m+*WjXNT~_|z-?>%Q)yaUo{=4MERNq^-?(@9vIp>~R zGO7R-M0Sa$pmCH!-cSkcxUU>6yY=(4Ty6a~K98H!`d{1M31665!e3?mf-g*-UCyy- z3~W;9rXr5k*1z@(jcGOA?}gWlBs9=&9S&xu*;s$w7YYcMqsWfCQm$_ZHwMD-MGkrp z0FOxzg6M{t`>w>-&GS{v%c%ddqK1Vn=E6mo)%sw>}tgY3YwEp+g zA0t0S(u(rih$J)lPg_M=cWix|Yd~7(_V!2%fn3s@v3~SUwCC$Te&+$Lo|r2Ll^LEu z?3@clOF6qS(oksQ0qqG9w+3|mMz3AJ!Hpa&m{Vlf&g(ZgGGKmM93rtBHKHDFl!KV5 z6dOf3TJxR6i~RNt4gnK?OLujgW;T@`y+`ceFfgrz+4URfW;?In&@o}WB)N<=;1}0# zOkwG`euKWzAZ}!!c$MI5X@wOUax%}Dx` zBT3vm-)_{%$?8L0O*WfL<*UqF-Pj?Y>jn_b=u*Vn4b|1Y_#vadunnutD)tH_QQeBK zC)8bieNh!thkXC=AY9d);X9J5iT=M!w#`2!*#c0H-F;YF+&~ueG=GB;xb;iF(roqJ zf+qoyZZzsPDMY|a<*b|(J~?;D$YfCjveq*FIq^W}}wJuGu8ROmB1!i8<#M^usc87E3QIBj*-_ zEh(~#g888ARYk#^)+{4uv4hfPQzB=vgR&7w3UuuMAYeV*iOpEFi!IL!MsP9P8Z3jR zG4*T$Y(21Ogp$1miNN|00SlJubu@_C!Gd~a*fS_Cc`93FRsh*jWRSILPwSu2S*<~_ zuiDovwcC@_4AR7V(yIH0InyvN7_C53pUGT4lc{Vy;aoznWi-N2(V@Wd)29wHH8>V9 zXMz<#$q<_UUs{P}5@OKvf=en*6pL%qDcFV{V>JCe#_zW?|EfR%%`Wh>bQ?e4=znhF z=Q;l8Mt;8B|J=aO75=9po}CEE&sD=rTDWq_h8w=O>(S)juKN2Fx5mi(>>ZbF-LmC@ z|N2*>vw$IaW<6=;Mo|Vv=HAG)iM&o@ilBcVyAwldw*q2|#>8 z%kTmU#p(RQJpMP(9+ANhu4`@GXmn6o^#U3E-p3#LbvkzdRyK_V5>+%Qi!^04OS9NX zqS%Nw`)H}7AvUAUx+rez>i;aX#g?>L>m<5y#6B3J#G5JmKnEMtW_2I3?6g2SJ$V<1u(ATug>}^EN)A=2ll;nphgakV@ zTSD6yq?X)513!lFh|J9cSyNu1*hCSB?Sd92iE7?CZZQYU>|2}!wEzqXZ7hAY4nTJt&&^ypO{ zrdek}Dj3@%d{<;DHerakbcFjkFv8KM5POZRnPjmpp%2k$N1s3%6PvkJ|}JIEW1&MFQ@ z-qu}Y6$bMVSv9f>gV{}14_QETHoiS%mB~W*$+AZ2Mo1O{QkoUDF7?A)t0A9d+1v2A zIw*X2fMsGN4-Fs<@O*H9Z54Bi1e@K8=EDNcHtd!wm{b7SE%!cb-huf$?XZ!2(-l(0 zI$iW)fkn*sjVsxFEIcN01HTmuPZPS9-=qAF*J?ACS1?K{3v7y;5-NL7WYmq_q&!Vh z3N@Qd7-_@1NCA0iY6njC3ftq8YHk}!;_cm>bwJ^-sCjXPW3vPd%{wSlVs1%};W02M6z4NLFGtl1g$AUo?rQGes^v!vq zZJ^anR~DfUA-JW}6qYPlXw^^}V{Ofkw*n>FY0E$xmm0&ev6K!9w$>XXfC#cPkrXT# z8lB#vPU|$}7KilmF4Pam$>#1IG=?n>ten0f$N2Sd{KK6#qs0dm9fa#?5*v`~sv5)0 z3{8%L-wKZK7_G^Uyhovk{G9nI;|R_?9Tn0+-Vfj>SKH^T5LV%i(?Z|C{iaG5H(Uos z{tE6Vw*29T2G5%sZ%gsKp{XCMBa(&^YTpK65CQ0I9#AR-H`aSQ*36SLgMf^d zO6wuk2Gy{mm8{(@-l1R(C}*CfieaRRwt~9>q0?oweGc+3y z%hb-~mJe>wiDtgjT$*jTrojoZwC7|q1w+K=lxa=TE@ZNWaB(Q;^8cdpK!v}Wf#)u%>8q4uM4xN=w3E@qa2`xQW=e5q0|mEriF81 zoQx=RXQH}MAw&w4n zU{}i;=w6%P!wOHRU1skRWA=EjkibJY^_RmM#_q_!(u0i+PG)2dW#!opu&GKvW-HLl zT(Ok5Ma4Iij{{~%pWiA$%=XfXE{sFeW@J(p9|O5x4^0L2#R4kAn4gPIjSMW;z$*Jq$Dd z>IXoK9zbqMi2o%BjCmeg+2&%uh_Gv`w`tecgIT2U{IdOU(=J5>sVxuX0Bgx%a{~-% zZ*6$Wvm@mItL14g&Hmq?Aoo6{+Jt&=f{IuDwTpaAqQ_rLV$QyRPuk#a-h?=E**ANr z)pu6I*dD54&R}$@PK*v+F*kp8#aEg3R-A>A&p$G_!Yy{X(()@P$-3g-=Akya{6Tn+ zzg_Iv3bVRk<@&RC6BM)n2#cBO-}?8{gx`*4&Dp^+3)CYW)Qlm%v_Tm-hwQ9jQ>2>4LT;N-syor`Qh7!F_X zjKWq%Fjm$VLgMNa7f{k_B~@9l72)V zZy{0DlLI0d&64gI5H+mG^ETPc7*^zY8_ClEz=}L?19?;0o4Dz(75N<75S|H2m4A02s1)t|Cx|bSKxGe}^E9il6 zqBn1UWL29;3m;nTt}G}Luen830Y~xBV2;JH=LIkI zQXk<{yQE?P0h#tbV1qRJl`K66-9cBXQ?vqyr~s;!tia()`|Bj^JqAS^Q97PJF_PnH z-&`~MBASuRd6$uaq3qHN>*(aggov_V^~J#n6#73_s5LPyr4U|S@pUNqQ6rX{QXSwKlt`rp>hB zqvJDeSvkEt9Sunf!S2FBWzkZ4U4`~&+EKH0w42ll@is!kQaka;_9;{KXnOk$g<;@kdv4Bj-_yp4odMc$)j(1p45K|yH5AAJZ(|g;OY#h>Av>pJgDix zxC?`@6Sr9%s2X}A6w1|=*#W^4NgKuv_062e-k;xq;T3V22@p1VmrURq0t~T(R!_I}GyaXKnofGNOl7x&ZcAl{TDK%k>OtYny-;c<2<01> zhk9l1h`*WROcYa5NZ_VuB)y}97rLV(I@g{vqH}LT)!MkJfgHwpkRjriW+yybwmsF^ zJst5*@8VQ65}|=kfeYft()1n74LKZsPEUq&gMdPvZqb|(y97dIo7HAmCsaAc1(uZM zsMi_|DFAwl3V`&qX{*k*ULm~-3Wa(&;-OeNl9H*ZPzVcQzn(_UjXp=#Q%l8(zJ7XTnE6?|BiS=rk_SRQD8YU*55R z90Xp*p1rY*mNA!LCZGpBIZ`TQ19k#Kl^iu>zC(9!>yu~7csEEv7BSD0fsR;j@^s7d zWQk+_bZtw%tbp1^P-qhk3GC!`xFP*L%I~%6zz*A{7#evbYDrbOiF4^w3p(xFNDs7? z+VK|B+>4&KK1P~wAbA=Od3p_L_as)WZ>oUU-M%Lq_`Qzb@{?yI9TcF2{SH4DUSg|( zDWsUqgl{itgsg#uG-O^&gc`tCCGufB&>k@bMDay=Ih#3Xa@=0`k?b8~4X?sbL8&Y8 zhp)M{lt*S)L=!>-EL2eHnH<%pkTjS?2k6jX65iW1m@}K99>&=u!VdY2$dOK#Ru9)bpku=NYIq+JsbO4txcbbD?)hXV*&AS;BJVNS=yTjUdj zjA{9+qlr74CvfcZLF9;Rz)y>e7REdIsfe0QCX64|TLD0}{M?v|03l@?Z6Jt5sQC30$uoMJewy}>>4FZIqGi$B?cfv* zY%8VOS;SEhZS*M_RLZZy81KeH-u9Nst)M_56S3V`ExMlkV`O_{wP+*qyobmW`k;5n z@@xj!4sVf0U-C4hI$oRUB|JLl{=H3leB-_FL?|s&)Y$sgd&EQ=WQoXE5#V| zJY9J-B>Kxx^Gt38GrG!v+g(3+t?(LNMMo?OHmb-cC{oTwBUs#CSsYxi5+^MTZ%~QbD6yVb~Nx06mcJU#Kvc_Q5~pw{k&I;N*J31lH#AgN4GpVn)KHGAUQAJB9!gYUar&Yn0{qlig!>#bPv;b4zs(+ z6F~;-^Sp=1!**@JpJ(nNvm7mDh zHT)coj=0dL){wHOsD$Q(Q}&wgj*b0Ml7>{4dYF{yAT0GDDbr9`Y9}c}I7{72$_S;U z?jdDLV5O`}l_Nt%iHdKqX##ulX}V51(BDl&Rn^3HryGsix>hbp_o|!PKM4w$XLFvWwu6c2#0@$g_fv1knsH07IwLR1_YE`;8e z*arWQokFSU@2N}G(5X^dhwl7D>a^YZ?T;NSH33t)34^`v%JWKHL>7M9R08b$Y|r8@ zF$-qRc8<&vg56Lq4={T+Yr{4r!-#o)wuneyHVZFAY~27`R8TiG)EzQ35x1*FZOq$+ zA()0MZIBXA6OSxno_Jp`c}2|2cqEQ3IpS$qhUrNG@x+F;lyyvOA)^tL?jcY3xfkXk z#qUt)B{4{_(8hfa{c9u9-K5$`bO$NpcLw^mlWL>X&7@3dv3EC+>NPRNlGl;U@l?&h z=E7^#^boNG^$ZQ$AJk7I;d^-swTxvy%E$XsPyCc93k@7)>m|PRSeo9WBPl~7n}`XJ z4b`D$Xim~lzm(BF&VzhWQCevwI0j>IY~O_&lWy&#;VyJf@1**Xy0^S zt?EdjztPA|4G4;<=B83-KXRZCyG2uYUM`K!gx6jOdo|UKzxRbG#Ga|TC9P;}BcLd| zv!&62Y0;y7$VqL`70=Lzq14sN?k1>6s$i)%v~0tJQZG>kkWf2S4|J-H`_8hQeMC*%9JI{Rr9cw!KF3+bjgj9WXT z0V&HoF$8RKJpkT90a!FV5SE=(s&Na(CM6lu5X14jj<`cz26Z%~M{RbEpcmm?CR_{G ziO_BrN?I-`XrPgsa}nSeoZts}looJOQ<3~~UdeeaAI{@X4YB}ofO)Fap)Tkmm8l>t3Ss@Y&iMK&{NIOzJCuWpCuck^%(h z^aZ@D98;)Hh<~R2F`pk$Pzo`HVB5|+t}eml2gT+ZF=bS@i*LgH6Z+G}CX4iKoQn8xR;TdPrB zi+%%;blws13;>lt{Pif}9vb~57{K{)*~jCpzQ>AkUC~kl_BMb+mjvLUCz-7AC67Hm zco|;D0k^f3F0;=%iE^t=YICX_L~ae=NXP!v$g9Y*)D&R{KHw4TIYBM;OPwbAtxGDm z)TUq_$7d3~eUVbVG>`r|u#)$FE-Phq&6b zHNNLpr{ha94kMR!bE{Z^WoI!u=tL2J6l zLqH>0L-LRZu4vi`T!UqYlNGYnWCiGO&q^+q;dt_&z!Al}>O_cf@ZlND8L|jUbq8b3 z#q95rr#;N~$#Y!t?t}&(!o&3lo=A95G_Pj+fI0G0u$?Ep&7FvRyF~6vF{I3@^(Mjj zNP>%6UJZm0`&4jakh8_lVG(zZ(X*?^ zkZz8$km`VamtBn`#xBMu@`}1lVmAVxL|gOSCgA42EV3k!^OON;EhP+|J89c2r1Sxj zQ!_mr*|3uE*cpJwUjtKxl)zpN9bZe45(v!l1b;HjOGwr_ZL9pWj+A12#&h}K@Qg$Xm02Cu2c;#?*QA> zszYp&J(kj;?uEe*R2~r&ooyU(ElwNSF+1g^0UH()rAEcYr)(-?j7vcDw<`-h&A^h! z&x3e>;G$pw?++{BsBmRl|Ki|KUZ1%rIFSdAa3efs>Z(ONwsh4Z_lV417#_~IU&m5Z zKxsbiSsW~*z|=*-IXrNwQwU-rYb%cNISa!TJh083!nY6bZ8@7gL(T@>J9 zWtX0CRLm|fS*s`bvqn!=sx_(~hy?H8fhOSXJn#*?fCm=1^LcPH${Ts$V(~iFy~_cE zQUL|)^(6+ot9izsaw1+33e8RY2~s8m`o;txvcB;uyAgWsP8A>e8F zR(n`Sr$3Ny#!!@a>7b;Y+D?`)zGrj{j=n)E5D2YlN#xw&7dpmQO5yL9tl@aaX!%I* z?!c$=gfRIs%D^>lq4|SaN&jIxg;bw{W{Wfj67Y8bgzn{}^eqzdcU0<(Pb+VsuZ4{% z#})NP`VZRp11^yR)6OtTAax=w3;WaGt2N88t?0J8Zy!?B%|LpZN9e;N z@b$vLd)KeF2FB9@a>m*Tqzi-^E+DVZNxx!C$g!p9h_rNBzG6CEWNR!Pl@>}wSPPZw ztZb9evPnL`@7hd@Qj2Sfwq!dguWc`Qc;sX7YTJExp1`J^I_I?@1u(bC#tG-V4 ze7Js~&ul;efWPhiW-vh8(+}Ah7AZym05^2r(o4zS<-b=G3eOMvAjue}&2We<1#^!k z*Fa{hk5wRrG7CY`4kGo`=SXNngIiL(0XwaP+SI6l`J4y@Wf!cx<6#$uYI51zE9L{cqTnmS}#{5eD zfJ{LzQ{|dyFpZ#2^`f)T!>ijolh1bB>1-oV*_j_^$dqP~c1>pk_M!PDmhuQNlyb%) zDzRV*#@ao*962HStbUZ8yv1krsnSlR8n{-D^_0 zREeQrdB8YESwWzwPei|%SEaWV{Hi4B3wg?C(NYB302<3U+_8!o5zv`*=qMqX z)hPjRs=MjHps`S9U(+@8c?=R@FY_2Q7RoP3FFC-aALv}csH64AYtIwH z>2nXJUVv+5aEj{k$>4MxFqL>=U<%uQ3Si32jRU5{Q8zeU^KUtvLdfFsg&f!uxxx@R&xle@5DQzu^x#bfWe=g^y*es%nK4ly^SM3%wLCbu>Ba91G zgaZ*Vj^vwU)WqP3Q$A=@1V;R#Y3=BBmJ>Rt;SIGkJ{^lCB|0Kr zRqgTVnt^-^2FoTqWDNf=dL>9hUEG4@wMoe|+!Gx`kx+BIJddP{q6md9eaJS3C+IVwqFf zD9|+uWwsrGOag>IM#HAi$FjdXDTwXHuLBTc651eU^H?_$DUa|RYC;dcTqL2kE734- zbib-0s%N`Bf~j;vDnyn3dmpwdj+(MvIE_4OyiW-6+zd~~WxI0BXbu}W)5&|ew5JT0 zCXp<5Aic}!M2lS#LTco@*#G5Z2BF4MbWnKcKO4g{KK<-&>?Qs)MC-y=_gJdqs|D#t zxByZx`|POVS4S(MBX(I^@nhco#kSL|pSe02OTZQL=@PrD7bH809YfCc^ck%~|1dtW zW18OXsV~3Y!Qcv$8L#zy1Ga2F*Kh#Y=yW;5ZT%nB%c;U zb_ml?Dx_4~NL`Snp+8bt2^Gf0>Vlf-xq!%c%jbZxf%rl8?{-t7?vdiYs?=auGmiTt z%4=KnP7CH!Hed#A1E%ciez=5K$iomI8kk~ejEl1I4~m};UWm(&(%JYr_4r_bKaHSx zZ#IHbj0VEaSoqUK+d%FKOw72a)UaLJ_L2z``|18FJkO_KSzwn2Mw-Jm z>tMLFe(T#N;YqCs6uRGdpxcF|a;2-Ax@u|oY2!b&)RQnHK-h7-pP^gY-wh$JNi#wP z&7lql#Gwuc#G%Xq(S|;hIUsrxt^1WYD(gSy-- zeJ3)hg!!|i1rhl2f|!ncB`TMq&qoM|I@6d^g=mY#A+FD)~i~|nyD$fSB|`t zCHexFXH5}L+&it-bZEam=nZ<@d4HqEh9|Yg;lFt-`zE}|8=7nJ8e3kX;8?5pXZx9G z7r>3XUBNZ`zQ<(Qe$OQP?+Y^omb43a6zQBG^rbDX+hf5@PQVK4`>d^k^da%i5~>f- z7EUNLr}b5eDXM9t@s);ju>&G86puSvOhRLY#}FFQN+`Q7yQC2_W7^WaX-g6jttoZE zH#KJ!eR|RsRmt8h-qX?I_AH=NE#8^7xNEPm5j>R!F;QwjTmx%DEmT6;9ix#SzYSiH zroJ+w-Q{%YIfScI6vD;qgvz#(jlGv*sn2pPYM5sDg3B6U?MjB6J5RTsp7*p7uiSgu zC`S$7>dbq(kvir?!J5X!VmLUqkIQh3MIIc~>IWV|k3tTn$60{6I59R zsbS*x+%Pf`^B`^(@3r~aJ^Qj|)$7ntm++~rr)&n82QK1)?ePL0y-91PYfkOu)Z)>> zRnmE0=T^p;JS6kGWXnu9&uvVOp5uz3mI!-vKa9jw>rNg4d zuv{DtMGp3E`JTrnBm248zBN^6jsw=(>iyh{>KOe;o$0S94TN1TNWSs68OmY06RTaKyjf0X@o~20x3zfu8Bzv zKpfb@B-hS%3*%{464iifOai#Fdel8Wv)TY{Ew$-C4zQ&Gk~Z z5k#h0Nk-WG_DZ*DeVdT1irmb>cERt!Lg+-~_HW^#MxjQxK#s8C#9vO^cj7CGV4ga* zuzc=0SRcISp|JjH0+>>buN!z6ipo{g$`$IX1eF}j!{;ggHa|?)6Mq7Qg!1F zOYBXZ=6XZ)E$r(X{iG{+q~v`j;5?U!A(PQdx#g7QqOKlybeK?j((6VilV8L_D3~mdQvS_1pUhu!^e7(cMlhJZkQ!s;v;_x{Zsci*rV=k3^}!w z_ivMl6Z9_?^g$M&cl>p)Jq371dbNfjEZ)y@L_P>(<|G*^5+oijVM~F{Q;Q@ll*|fW z?P}+#pWDlvk?Yp4ZDp>3&T7T)4g+4f*^gRD{)-_Bgfh%RDu;4Zy$PkCa^o9umZIcc zz#*`pDCJ%fEs-MP56SF6F4Kt7sm!54x~BGa;eo%2-x!z59166)U;?$4&=+PsnaH)a zYPF(P4L|)*o3lAr4*m1xBg%Vl5$O4_b2>H3;xI>#JANX7G|NY5O_-Ckj`9pNk!1*1 zR+1U8c|=@p51BHR6YvwkMX?_Uzt9{<6lY)Brl_9Jh@dN zPhQ?nIocq)TU=TWQjoLH!r~6Htvh^|C<5Aaqt^nEHofiv1fMNG)#+x1m^4R3DinFO z6sjmgMd1XxWe@AcO|6v3ammJn<88Uoh(c8EKgb0w~4GZVxc(fo_28gJ}MdE>;m`(#c*qJBda$ULD`d9o471@BKYlO{t_ zC@uqG6b^TlP5jU5;7`BGki5>*_&z9=3bVz@Dt< zPr2i2;R!oFJr#rY?Rj$D!LWQ{9htY1`aB_JrCsp^>#)3niBAv^20@GiFOP{()i_|d z0XS8c!)QvK1go6{xwa4aV1`o~<=NgkR8FX03WP%dFZvJX<`<~9{}Pe)B|)t*2Z-jp zGCUvV%3&+-t`9~K&j6Y&LZ9bxUfDPc)IQI;nYa^$+JkXUzjyI_7r*2C__~sx zD(BsN2E%~#PC`OgI+nP#P3LaSkR&;wA&=7L8e4BwxRx1_EouKFOLKY5IkFV-P^%nk zUBJ&-N0v_Hf$4I@zu3i#6&_f!XwL>l6Vj3B&el9eMi~z&Bzu>?+eK<}mWuip1#hI* z=twz~5w8MrsV5#}Z|A{@vx|6e4D@^+8X=BpZ=&g6YO|0oaydp@;|HA%*~p6`GwWVl z0y(hhS`?HKaUjCrqeYA8mY+0K<6?PPcO#0LSZ~J+yX7;d;u7xK@A$E$N+15*>U2q!-!S2kHIpByzd0{Y23%xET&wj7I|0 z#$;%|+t^~~eSSnJZTaW17nFZq5KUhgp2K7KNFq?8(LFL;!DE{I1Z~fFJda6!2RZY| zV1H6GjtolDY}!6>oDC*hd%wZdtQ8BPmT7N8!@QJ?+1gT$Q_&^(CMHdmkajKRaIjWS@w~OLUS0?{@s5_uk<@3i6 zb;nn;&y$m1=>EE$`o;_0dxj~bS|hVrt4dX9)HnIwYb$fSF2X$r+$8zq`zxs~Le=Hq zll;}|Dwhssn_fX5{zFOi^_A!Qwgqd?R;oxInN?2P?f~87+(*jOt7QNLZPg*kH(y_w zTl%G6oJq-Fo>8v5pCqGalwW_~&!E=xf@|3F;RpwnGrCriLISA9Q4-y;dR;rS{0jH8 z8Cm*QLq>hrQtpEc}Ljzc_ z9bRR&_1`pfxtI9Z*OG;^%ljqYx}bcmJ1u#`>T+Y^&EJwdguz}k1Zh9+N&a?qdH-B{ zbJyzf9Iw48Y{{rd6Ol~3u>87NJ9kq<0Gv5v#pM{f=^^?(+00yiT=}K*9&k~R`EIR1 z&>lsI60v7eWWLp;xC@(vrBqXNoJvvci| z|G~WEO(W&wwR2$=RyJ6DT`ycqKK%a5@P7kr&%eC#diUw%>RIJftM~sMnv)e5p++3p zuRJm7xumkP{PmByImso5hO?3jW|zy!(H|)v@5jxV+-Hwhtv6=+MOD|v>D%7uhBntf zTE6v|$)grr7&rD%Ah})7-sIaqC?Ax=WNBylKwuKc*I^Q?q$HK-;+^G#eP5pwUFhHK zG#sKCPIa`K+5DrOl_#}$kE})Zv)#T3PNlPtciCH}!@t#y zCT#lhhDmCNn|`~SWZ~P5X?VRC`frchPFgZl+Urs7MVQWfJs6}dlOBl3@!iH`f{*7w zY+v`wF^&A>%3b9*&0~HCn2dfEoE}BdfPVBx)BOYta7~B38i;1q-{+G3?k|6>{65si zNpyet?&Q`Vmn(<(Riu_-N;JCg6j)1EG~GA7mY#Gk;ap8Li4fk&4}V;~Vbb5QNr@(t z37y}Ls(&>v);~~w$gN1;@nHEwbBsZ;dyA3ivb|_0#NKvavgjw}d2Yq#H~s`F7rx(Z ze#y_u+sc!@vaJIHDoOL<^4uH)PJh@i0LiKojaFR1)#4w0xV&FKLPl^X4LhdqxXow0 z%`I^?^Tcy7FId<07rWP?#g)WrTo5)Kjs!wju$}4Yfw|TO}fYT z359s^ZbEX)6_q!*-ek&^m1`#Y ziJc3YL0KXmmE;RoR_63&0Zv(;FgbT67cbiCjWf3|ysENa={Mbz;5UE;CC2ve1AV{ERRLWV@V&ks8|3yg+|tB|8@kPgdcTSQo;9iZ3{szUwabsEn| zL4ll@7buF!B8Y#PoO86BGPOz({JPj{pdaL>C+w~KEG54>+HohfX?ZFCREjrSuF;L8 zh*G_q4+#9u@;1A?ZyAo>mTusNnl{7Bk`z16U)EZZ@26auQ>Q?hE+eRxVCx`l1J>$n zgKATg?yp>o&6#{u!Q|LcEf=%JCX2br3;mA$lZKscr~q!_gcFC1O*8-IyXs>1s6(BH zfIMn($ehCA(oJmUUSb&LNG>w4vu$%=H#9Oc0Y*FO0bB-EWe!WA`E+N$if{@txS6$YZ}H?s)&0m5eTNhr6qi ze^}xcyr`#%E6i+G3H#^v?{Cc_kp;|Nyp6OsdGdZNPo#45e#e=7l74Qky#UFNlhclK zC(zOdk8|(jG3j_qz2bQHzi9H?$Gby)llwiBCXYEGYf_(gG&wza?+NaZ@!CBr`Q}Sp z(|t5~1|FzT|?EW^Hdm>+b zEO{G`!H=PlnHOxtS|%k#CAt2kZdUTE6W#NvZ~8K~;;B^orDg7Ql$)`_z1TmZ z*{hN-o#f6UYs$%P4UZ3WsKvL9o`dB3W$^0bj^E3u0IHQeo|xs&*$*ss4{ zK^H!fxYONa8XY>_y^;c}Pj~O-@gJwVw;~L``K9iL`KM~~st`UVZX+a22hl_A){$tc z@h&3xbd*R&{tO>bn*3jX<_;dtx`pGGbqjS)pQk^YzxZcvnxn1no?(MpcBZ>opZ)$! z{~7!AYC}6ejRp%}`dPC0EcXVL`@~r+;BS&|p5>-0`Lna!8%eHs86E%4=DT0!j&d!n zd&I%OjSiw{U4{~09}++U*3m7eww~@PBVv4z2_U3?Eg&G*@?L*!^F|jx{4?|q)6=iV zeoA8PLkPM!X~Y5lHYjlxiP37Kn?FYC;hs_{B>(#rwygKO!p)f}k}uh^!dTRYZWE@Z z;lYmI_ewY2U7vj9Rql)Xr$NrVs&Zsi7Uj&RaV{dxS@O<5chkd-Ym&yR+~kQ==9dGp z40kH6_Lu(LyQ}Q&f16zQYIoou zJvGm~66mdAzv*mu02|b+&UUk=YMKuyPV>#=)DKSXI@>iMem^2hY9NhXQ$kR# z{!y!Uf*%n;mbM-ytIl@&Go~-UhA{yf@qxvPw;Sjr|Na_xa_f2lp4cSB---Bty3}p{ zJ#ym7gsCyiE%;K1XX)D0P~zGaR}umu)x3zPm41K%w(Fg_H za{wL(SZBjHlH@Y2g#tgX4TA>8CbkxN4hmLd@majr0H4PINgimzCy>1?Z6d_g6;bG#$L)e`Mu=zXQA ztbH$;^;*!olDy@$ZvR*w4bhvgi zt$t%N{08%nTI3(E^pC%OgMIa39=-MzPKWiua>034?)vk<@k(+JDbd^#OUwj)zOR}=vN@yy(`*kE2dCt>2jkhAS;A+f zTj970sx&qnVRFgt^6+@u7c@&lu9Td7MP*JGngQHql3#5o&paR5t`?58|3y{v?Lkl8 z=_k|uEW2oG$tQ9zlQwZqS=v3REy7ja)EcJVJg9)?1?b@x1+$Y!&JTxiT^Q(xcJ#WQ zYIj$qTnhWNR|)$#WmZBmXUqFf06pxDrGtMT6dq0to4&f<+F2NAiZUB)Y51xiw-_GO zfm1%An&Ms_y|uW9M-OhnJc#4i&4Y6!T|8P{+*QaEnqT6f=r}xNG@yFZ-SP{!}7e3z%ArQ+qZV8Ng8z+muVu<`AU zqI=)Xc_Kw8MA=d@d$k(|ys>ej!}Q%Rdgv&O!4mV(K^TEoFu#x!pu)R|NG73#JdCWj zgaSc_f||r!ipQjdh(C?D1Z?)xTtM42mxeLm3L8ql!m(F{5VKQg6o8sAtDsHbKn#Nhl>MJUUbG!KTHvT$Gxqq(W?M#+Vi5KI@-TE) z1R^}T*5J$pZY&D)6UO^x)We(%W-fNqRHz!2b$70bLuJnMlhr4YY;K4^0`Z=9J{#mF zEJ3KjQJ96HLVRGXRTOey+8mIPLg%|_!Y>Z)u`SCr94*y@sYsW7>i#x3YnMr(1Tv|q z4wS#C$zC3C;t5Cd$asQf98ZR^1~igl4cgkItU*6#07xhDgf(b#bJidzjTVSLdfuS8 zFqGJrHFS6jB~lW%2s55dgajPI53FpsIB0YUsv0GLR`L&{Y~nvGPr`2Ry(&paia^}) z7;Jh_QLK;rCc8W6PTirk4ZJv;y{ zx3kU6WQ>7kp;2v)Et7)b`stITZq0}>*M)TfCkBcnvXM^s7$13chFKWPeWvWQXY zVkcopWK*-fFa&cnt0UR~=Opk?bNi-e0y)_STK47v$v4l-+z&c9ee(EumFcY!1K$y- zP(i>^Ps&EUjK<3k35+tVA6aD-u33?{Zj%9X^rV7oEK3C%j4*a6-toLv94DPT>9$|d zOXXf{tUM(oHm^Y|ld8~Wi(0B>Xq^NtyR|Ci8n!1CWtkg=S(hT3>|OyMqIktlt;}a- zkXvm@LeZzgc_nHo=}W)n^9xOp{7cX9j}|m4-gvtrQ;9I~P<0|s3fC1vsp2)i*c$L_ zWR*YdV(g42+I6c&8dHrtRN=#)dx{nIzufo?B$BCY6FhZ6q>yR%NC<2(H8%5-osB+< zfVI&uwV;{S)egGsd=o5<+94o03QE9-D`+^oGC@T+nR)D(rXaqK0q3W~-&xMYm`D^3 zlF6}pNGk+FQbYR+vhKCe?$y8g}8;W|y@rvQNKUBcxDD@>{5atz&G()UhJX&-*2?Q-$UN z#(-42$h%x8(te2~H{=kS@7 zUm0)66Ng}|%oB4~#(RF96(iK@HAHyv-%Kthd^U1I#9J_eK4331(B|iE{fsPEet;gI z0^rx^h#6SU26h}P7*MW2)43Hb| zq{}hXOyX#*{#Wsjv$P_WDB~ezNOc5bQ~F<&?j;R&DSZROv^rw{;G$0@9{jXHM zO8O{Et2gt4QyQ|5!RePObt+2%*^GdiVZS)RykJ`Lf3Gh0wQjn3EQslNJ{)bVw1DxA zdgBUjYBo|6;-jGUAEDRi8R!WOSk!ufQHenb*yaV)k{L5Y?f)F26}ghb)XDh)CWJn6 zCo5N?H9VaT0P@Zshh`%$O0GOHHv>XUmx9f`>wCJ@X_C_VV#jTRW)AhMi%4YAIkHYv z40Wt)u(Q1kp%9=4WW?4Y(FO9GTppY{N}B+Xd&@aMEQe09vIiOi6f??_y7W^ z#+OYMv!dOT7Zbi#8O#i6)m|h8Klzp9r&}v8XqClGMX0~~>F*r84BtPbK<|Ks->qVp z6A^@kRd__&@xq8Ov@ro9)w>c?XV@4ljo>9e0l&78MU3PgLPt`Pk;iwIpqJXID_V+@ zmUg0FTWrA)F29ja#_p_4IYA_YWel{8CO`5Euhxd*2XgKmM^=`!U5{16s^Gy*fsU%| zmW_R?%Bul{Q~6Yj=;M6Z!Fr?1B@bWCUcZR_f_P#Norqnd*A}s#eNhe15&}UZeh^%O z$T~IStQTCirXeL%i->s)qQ3@c4p^7dRjQ!6V2xchs@n&n=Um38kL$nLd|OvrHVMEo zhdX?ajlLQ{b?Ln=AA1(-iw-^@UPqkxQeWZ{(@}1;=pW}9WTom89%*{ia#b?179ko) zJEXzlD-CVc97{yiFLn@KHUs#*q)j*yMH(Rx?5gIVxJzV!QDtx*?z{o_^G6K{*ORwR zDmRiZ&2U{UNvOM#P|MQfr?>={DgGnhu(#P4cS`{5(~btZ6yOIq|0M{ToJxF&02cjU zmLZQyWgwUM5!iz{M#H6k)_q6@Wons^cMHg*TARclFQODan^vTD5lQVzBF8hfa5nV*7=3VW#VG{%`$ z*yt6C^y*8Nyiw_^fC*37Mzfcu?+#wlkJwCMz8j?x!WQ}8b^QiUls{TtHiPQYg4U;a zf7V1Zwpb+FkZmE*W$GM}Ckx`uSe@C~4a;`Qlo7&Wv zH>#Fjij5z|YxyHf#037uuH~2%U(Vb5e3^UrNBz)(Rn6}Dzl4om>r~{rlQ|nJ4V>!Z zs+Oc2A1daHyS#SbRuy$CbvQTKUxM7duFIbs=v5;i{3@G`(#rT9tE1jkpjWpx#wD$` zY-)Y0;_lTkNE7v;zv)`l=rKTGLDU?iOK(nY&1h8l0BjAS9=IAu#_a}83p4^(q#4#p z5)!Z$AIdR+VUW6J@QEh;iQko;S6x+WhRC8Ur`Q8Q zKdYw-XgMGM)$1#BU6}m(`pOZl+io2rvb-%?34Et0!XOyW0$MO5rlEu+s~ZU8Lowz7 z)4p&#rY}z-QHonH;SW@;`(!(6XcH<#jmqpardcgoQz;D3hH!9OLjv4DO#*< ztqu=nxNidBa|msHxphi^U1ktG-6+a-6Z=T)M3Q*;wLn;jgZS=u(~o1+spQumuUJs6 zl`v6Ct!hk(iKEB=il;|q8eLQpk3n2t&1^uo5vahp)iW?z1LFmPl;g2HJ-BGXYfTsyq$f+_@p8xAwWKH{xpQy}j+3mJH@z!9K9WohS z7F8}ZzYyk+c~(~x4xGQ-EP-)D*l3w_x-8K(?d625m&Km?YeBq3;d{y+MV+o<6lCzd z(cD3do1**>bZq~6bX^#aUUKQ0x38vTCE`6hwUtqF`X?(xu+FYRSL4b>QP+jyVK4^? zJku_wvi4B)zB_$yN1^La{2uxca)RawzlSY$#jPi404K68&ILYU1%PAM6E`pjr^JPHG(QGAtjR0AkC@NiOCBv+wu@vJwjS>~d7c8SB z9TbnUk|itI-DXx)Utm@of%c3q2PdsAh!|=19=Do zf!2YjzEVh-TyMH&$kz-ap1%8_(@#Fbq0`7{n&o4QqB3VNhs6pZe>I>6#6t(I1;`9>+eG3M9!s0q6VcM^j7#L6a@b zmFS6$hDY+=n<@viCc_%lTP@I_Um$CQ$qds4N`(jm2o|UhxUdE4(*kvC+8L`&J0%M| zUjSt5)6KyB`k+#?f_>TgOl(UNCV5=g>`#u_RGD?;_#?z0Jzc+SbZm3J^Rppnl>(xMdt2$PHs)3F=(;;vr-=~t$4KFBb=-G8eR%BV zt-g69$Un!)vnA0AHzz#8ZL7(2#6J(}M==E@{<0}BqhtSG#HpX9)U@>1j}nl>TH$i+ z38Kkxwi>2cFE~f_XrjO<-f?NlTF8jOllY-DqlJ7L@Ax{q`ML4!;y?3PEp#(tZL&dQ zwnmV_J99D*%Y}?vSTp$2-5JA5KVbGSZb`}50ra+#xcUR|b%@roEE zSsX#pFQNmX1pEAEx>GSNi)`t*pF}h%UB$xi5T5KULik&K3GyXE6EFZUV<%b7BJyiO zgBtuch74Gk?fIn$l*-ZT33Xkcl!V(_=3`^Nx8@I)Yxxje#bmW<38iUV;+vu3w3!}J zLFuU$2vR}@CvFGOl7;zSprn^<*et{L9@An(1LQ)rGR@2mE#yGtH};w$vV;+5XCQ~R zwAZSV6RvcO^ zC#ABLr0kU)I=ZS~?EW+qqH53OC?b0Fx#3(BKqPK>@j27{E?{;lR8p~^IhY;HglMu> z<>H3oPJ&1r=Af-8l*LTIz_GGL*1n8%ygG`JvBO*$c_H7jG<;<@4Lsi41 z3s8M6R#b?}o5P#EAiM+&q&C(itAOsYpd#U7U0GjR!u-9ev056sAW*%Q2#IrhkFQVN zY*&e7)~p!6?XpUmy`pUVsWwZiPNrrgsi+H>P*wxX;R*Xiz9d&pcesO7z$lx#&VCOm z(MF^24v2-IpOG``IHRdt6z_tdlvckL9{u;Xe(DFMw+ag5=@(1fUp<4|UtM?S--kXo zw2VJ|Zq09<&nR2Q4}Z4y7wLDcs^xs{EC0Mb{ieQp292gem+Wcxqi7=8KbVvm#-F%< z<9*>vW&m(;U=)G{F)K6U6%?~baVWGvEQOLp6Pb!`D385jppg`{wvMeis@aFnhg4vi z)ny@B5h?)m(h|1b__gUc3Dex{6?p5LQq-KJuO{6j(v4tK=H4ht?2`z*a3S%^sbsP= z7+g5joRq3m?6Ey)VFl5LyB~oBR=AT^xZF>d8dXAMpzxsjIv@E!D3gfj1s;{;yCY=x zr(VdBDwHT0k5UT&<_V6fL@d?zmH34hMUyWC5wH%&=G`MaE}Idaonnqgs+|9OAX_K^ zv?=O>K@n1t4k4v`JwGipYw>L0%Y2MbF!V@ZtzowZcuMSud0myXRr4&3SkSS=7hxiE zu^_7CD9AyrMaqL>=>ZC|c#xmHP*4;|i)x}hG|*oSAWtQ=cbc?C+{Mk-<{~Wt9g`1} z%nL$Lrf>n#8)cmuj4D{C)CYQ})owK=L99~`Gp0V8gw`lEv6#qT@owvt_|YF~%Pu9S z?3iMkDV>VvAr8g^wj9V!+9hx_=|J~0b_v>EaOGCKO+rML^?5cf`-ZON#jFKhU)VHqBuG6?mb8V zEPOi0BAtfwAe~4ak^6yvIWg5hanZDRUM7pk)WfiQdFlC~x`S3|JEM+E{+{J(e0P@8 z6jkGo{HMKa-78FtOF&~aUh^O62mM{BYaqq(I_1n0TG;V+&3HWeJ7!i~8SIPq{5DNU zbprSHD_*L9UK(81*{p%(c&C?c#5gd3h`B)V4+_s{v?i(48I8IxzCdy>8HalbI9*{@ zG$ksCH=wJ5lDxHSLYOJTbJ#H`&lFbX27>@5j~Yq4+hCP;BGH|i>_3aw#0C4 zw`?rv48uygNTq%*Q|Pp#^>75mL|`4-fZ`=}@4ZrrAHL56X~~WT(@4SC&H_rd5-$by zA%fb-r~Ee?HH)EHHU2A5fRuvcqBrECnVJ&+Ld$pHu_X$R)O?Da2xzLZ&&c&>nRfQ@O#f)2D7w2oXkAX!U`84a*zO#MJ=8>#Zkw5v=f zukcM`{rEu_kS-y&e*FfJ*~Agk^{d9LpKcH^3dt1!2NAjb$iR`qW#<+&*3-*6kz_~{ z{={xxBs>#XBk&iefQ_ zd`E3iE$gWUR_Lkt+GL>rVU7#eFLkWi0P-fPW_XI1mF9CL6^X-Az%$*VKnAe_VNvic zP~Tgi>P;67l2Owzz!>CDNmg=Hnq)n^0mORJC`oKSJot67`oM6Mfg{dIN^{3~_FiZL zuH+({j6X+0X=|f?wM#uhQ3RWjvwozxg(6#NZ1wNR`l#Mu>_GWQk@^Rm zmy*~Ki{jvAC}=q<={XibuTo1sdu(Oun+d}#KWqW*qhOsmLc}~GGSDEL!sA{@kGU!E z+q@9j4)XITc0en5vH)tA>*ClmAEl^gyu}q$9r1evb48)_QP$*gHK{-`eD*|5ZhMMN z0{J=tFcaGn3=I)?pwERCroGzJTVwkThNgaEI+ZITn&PB6e0{8#uCI)5DyCtww-(cQ zS!^z*55S!;OSc$|d^zOFIZr!ZdyMD|LEZrcZxFdIcHlR~G#&UuF--?9%^C>!m=63+ zF--@qO4F_22|B>(g}eiO)FIAL?7&xwX*%$YVww(orj^pB}?A8Gr+lt zyaPIBE#b7-fepnp9oSe*(}7LJG#$9Dn5F~&VCkkEiyF5BNMVA%YI53=%8Zs^um8zm zZ!I;B1Mc+{-0duxz#VC=Jv4<06fY|d?d!!fL;Logt*v$e#`c3^0mk+~G0oT>YNy8? z8p1(7w|Jr$+bnkAqwVj_`}M{>twIm!z~2=M(1Cv}rs=>J+UapSfEqIIfZV~wp^F`O zq?l#~{-agQpaYk*8!-5OUolMwu572r?EsQ?-T^uMNW>|2;O=6Y4z%tmX3&9qi)m(H zXE99&?r*2Z?SRCAjKattNzzoY14%JmU)hR3R?MISHx<)#;MQWA4&2^OkJ|ybZr%Yo zKS^*acHkGqv^>dvQ_P?Pe<-Huz@_a5T}%hw+fI+$fw0g4`C&;$EOy|F#WZ*B#a}6A z(1C9h({$iF#WWrGemgyG2V9{86o@5_7CW%6kX{A1iZ>K8kO$+9#WWq*R7}%>TiWUI zIv}=-Xc_uFgwYl$c#lfPmR4TMVckh5R_2D^36o<^tgLp6l3P!#yvaX?lKIOj$MX9x zmsL*nS&ihrWtINcNEBw;C9#sD(2Szz`VIViZa9J`kJu{*3`_x*YOCZ4+M&uq-cf)d z^TdTOd&9FGg)P-DHx4}_<)(3JTpA8F{g))bl?xl}IRCdPNwCS7ikP}G9VYU|Zy!lM za8l*bkmuTyE1O%8M2ra+#rGm#Y3o>jzqZ7h7j}I3^GfWHq;T(#L|G9jY)nRDB~a8~ z+>d-F$=^P}TA&Z+<{zL-Oh1TtLV90prv&^@{I8O&r&MrBfxDRKA>X-0O*{oDeNcEb zY6Mu*e0NmxU#C>&oJX-R^V+4?QGD+jghGz4BP*m=-+1@khv{s*a~IM*hupnoCVr5z z@jNO(*$`jzZW1@}M+NFI=t!=S`cthRnk;yE`0M7O8kBrN>4J9ytxsTQ+F&68M7cpF zf4112Fx{MqEAj5n$lp1VL??IC3bEHdyVxCsX0)~`nXsWUt5wc40^vwF!gvwA%Z(mu zMaHTgGrsf2j%ucF{8>;m(}?0aSAmJ3N5?uSCcP(mC^ie)0TYxns6UUN%)T6A@BllU zhrfAMs-lq7M{{`k3s@uN$hcGX-9-|W(H|9G6VZ_E#fV&%O*wo#Lz8I8 z4#aP4?FAz14Od%*PS2}}&OAl&3HV4!26p}>%O}W~^>r74t^Fax^l{)`b z$RS=Z{bonh#sB7*(W_~vx>a%CTWFH~vy_Um4$05CZxyX*@hg6qE1SWoW`L*pN~9*^ z(Vc+V5%D^71NArQen4fYMKwF=g(&E}AV*8OR^E5KN zr)rg^yQNdsD#ZL24Phx}dc|=hq%p%)l~*!t(9?+EVAWOhlGzbt^x$nvi(OKQwlrsb z=WAS5M#=V0OJm2Pu@FP%q^ZM&PPBwQAF~?Omf^Km(R@>LR8z%rOu%QdMvGBR>rkb_ zroXnHaB%-&o;XPq6X$1wJysOFt0%X`Se?U za`3y|f?@BE#@1Ds zz@)Pt@!iDHs?Sd@dbc|;+E={Ti@eOoc%KBw&)nf**d@b1ieJ}B7i_pyJ4V4 zoU5VDr@hyWb~%@9zryX$tsjqG;SL}*e5IR1>ewsYFppPW>1I|AtKHSz&nLqPpHz|$ zUFmAhZNBkJLalIX%NMS4$KoLRpI5mLy5Od(*E#-6K5(@g>Du{s4Ck0+$JOpwI61fk zW^$8X7&oH<2q-`46u?fP0V^5QuBi0V&gA5b54ev`4+4=1qq|b*P|VO_f=g=a-J9s# z+tw4FW+3_gdiQ5MUT_UTHC(dl8h0Y^ZokGYatjmp`|^|lj}ovW=vg&RUb}{XGfR`7 zu5pWpwD~W=j5y&Bu@E$J#__0R_6Oaeu=3MC=>CmLMy_?!6qV(b*Fy1kg~>avb(c`V zAFp+bR}{1dO)3Dye%Te@a5q43&0YMtPJf#51|IeM!kQHsioZa7z_0V-a{akRf2PFi zcu*vHU*ax*!MAQ76J-J}kVfSI0P;W~&12G2Ec%Hw@n1||{g}&I>oc36q%Pdga_ zS}li(mn0whm^%q|YGyI7vGvYAD*^RsWz%x&V`klh!V9o-%jqSk$^g4(?rSHt@ z>Wd}|HxdJBG&yslJ8y=%YWD|Di$CVeLDptkmC3(ubgyv7C5K+`Uin0{<@)R0E1u|G z{o`)R?B_1!7u71W@r_UwrjV?tn>w$zT9CRAS9-fW;5PXdfKIZDnsBgr=cio7NuGb; z)9!eXd-Fk`aqpaZ1W*_qByvl!4Qpv1-&BM`-=Q78L+y+k(jNZcPWK8Ch=q4Sf=)@^ zdY3zu$JV>tf|;tYLI|69*ZX{J>Q}tm5=sBv?x+`f>VypUMrK)o*+dv+!muy&eYyDk zFArLfTynQNE7|^dsWa6Mn!brjOM)fa{_xv-{{9y~|J}a_OM*J3 z-u->I%GWrW*Z8GhxT!OJE0`h2`|8*qxXZ2WU%IKoR#Iwxs(<%w@BPN;CBM7wFkky0 ze_(yvJawmA8|s7W?{kw|d{BxvhV0Jd5zw^+ptR!TDh#9iaGKR(?ijg58LD#f5|GTw z?j~0m#QbC=suFh)Wl*Ef*q&xkS(>4WT57eUrqQ%qUej%R*R*Tzns)gvP3x>^-QE?A z?OoCC(n=tRPWnD2U;2@2F1OzA0!kU%l0R<#kH>Ev{rP?0_AA7=h<{{nIwzMGfQG)< zdre=k%e`8YedR7UZHCoN_zSoG>7(c)zkrm`#@45ZqP2(JmrL&SB+IX-TPRV|GV3*;&H?y?!tbR9m9=6QF%{KZhyp`!<#9;ap%~Z zjd+aCiDvNT(%(SSmnUESjr$^xS3F7p>rMX-fnA>by&!` zZ{2x3e)ca+4%wWsGFUcPdjzVU|+uAnS9wf*k|5@|M@v6_TU*! z;&WQT{|p>FgCW?GM^6oO^0Et`AHL?`6}A^xQC~qYfBmete@^p1b4YliWTE#S68;@guh}%R8T25tY%8xIZt zywv~aa!ah(b?f!Blb$2OWv84W1uwpW^3KFp5GSEySJz){K649CGbJyoHYw?Jat3RV zv;a~|qkJvL-RG}d8F#O`dCBM}D(|YFnSAt!aQ4ZsDKyg2V1KV%AyI33w^JP2+4EP> zw&B%c-H3F_AKjRfbtAz5aI8BsIdNe)dph;%l-nU}@aoi?|2{hzT^N2_W0Tt2UZ1`B zoFl`xyM8{A!XnN4$x-1UVR-o!n;2DbJYkV*4b_GtMD{DzAQ|WL19BC%^IHaBgiv+#7FQ zPY>>=_^NQIx$TN{euH>Z_R|}gq}Z-~tn4+SryLvZ=U%^g)v@TlfVj0I;jOa-ajpn) zq}G+@H1_1JOTw4Bmu>#)l5mc56O&&a7oHVvcz5#Y z-<2D^Qn|^#SU)^@%kkmSLnx&6Zsc<8+#UT|SlM#naGd^i^!ti2+c6Miv0d|)? zfPFJu%OxlXh^YHeK|m3_Z^7GLJU~Q}D6H5xXY-xM zanhCQi&O1mM{)rh%hxWMOziqDnO09@P>)nC)9mU&?3+fLthxbiYEK=ZHSc=kH8m#r z1NG8v?kF{Kg_8((S=O&`8q`rMoC#|2bUWSO|3YOV>oG z)nn-R3fnCmL(z6i?(OOJ-d23=BaE4YRsCW1qt@o!4-T`>vU{whUjkI~dCjkyIn%z- zx=1}e)4sc6jVN*1%rrQ^V3ys&hQ!8M_K9(=KE7A=IKr;Q){DV|s#NP1<;7@*quAgz zj7w1&clf|?(nM|c0Azbumk``8HS)1jA$4M+$)@XsSZ^NfB$9nb6ttWnQ1>5UA3JWL z=9lbqnc7kkmJ`NULWF|{_MBHs^B$_D#~f+@dDudB=)xUvLyz#Eb+VK5rd`-gfq5@X zexT01-D_0u-0eJ@-EA(Jz3hU6tHqX>UbbNTFn}>(?j^zZ2LPfg#m3lfk^g!SWI!Fd zoWPwY81eKuQF%xQOnjzOdoXtn)` z7%MU$#biXd4DUsV%Iojh<<;Bz;iiD>)B?wZm~4jP5=hL0TiV##izPGs3DUu9Ua;;Z zU|stnf7Zz#Ke&&F>>mBXib;b5pM&^zi&}cLeQEtOPmpAIaO;yiwphX2Pw{-lNwr(5 z?ihPO{e3yY&B4d=xXTW1{1(qgZ25fDG4|G8ONp1A7tzEAIxtO*Jl38c%@;Sx_Yvyh zW9^JJU%h;+nIeCGtUYA-80}k%EjhT=eB&uf+vyj{J?kE&HV3&!9d$e$PVYpqHL5*t z1>Es;ygl4niywbX0LBQGgkipSJk}oXKc=cqV0UMX8g+tQpWUeUjr0zk6?_VTAx!NV zidR8x&?DIT3}F7uP6{HS!MdO{_<*ROcaRKzE-$6Q>+*0@KezRs4vFehKehEGNQTJ7 zFFmlOu94Xf3B}?qh^2+{)H|oxb-j@s=cO&52$>XoIqOTJnYqeS?OU~``k^^?ADM5r z&awN+b4U10oM!hk8f;O8mQBhl3mvh!6-A)Y>_Q4_(YnfoR4Jn>bnp>lG?JB31llpb zloaJr1e!J-5h@_bLJFxQUnq@C{eTcKtLV=3= z@Z5)I+gIDE?>^3K0YyeLfV!sD?$_@{iA_MXy>T4(6`8c;tR0hPL+TRlklMP#t_m|HcH8K`kZZm)nrtkx6&8X+Gf3wVM)#fdq|e%3OQ2v+0qi zRq6!02`+KRTzh5JrpIV&Ww4cp>>sPAFSL89ZRgu=?#T1(8{!SillN&06cEQB< zuSuNRb&=gCzD~X$tkMhYK1Zwx)50f~;B*JxN%zIhpDQEO_PBm0$^FWR?M-UuU-lhGV1mv!OnE9`;l_g}PUkZ$KHd)!|77y;|>@OLo>2o@9{dgVL*Sr0n^Z+7l>y z!=-j_hy6;mvE4pUm0f1vYTc~9a+v{a+hqWDb2ot9z8Ju6)_{%d1lZmk0c-9A*v)EI z*1p{o+7TeX-JZ}uWe(;w8N{e56|9+k;U604+=6zn%*`d&U2cH;=H&plx(jd@kwg$C zh^<&+*VWLt^1+>;;L_10K%sMYgran*J)tl01t|6|hN7W-pqh1$Jy`9$6rA0?6m%&y z@k#^H*;fLQ>H^U~HTuh-OLg0ia$=*XANzL#R9WR2(~sU&^rJ4kBfz<uc;wO;LG})Vyo$*?lR#0M4jla2m?-Aasu%sFBM--7V_*JGfBZM@6jjbwg@6xpc z)x=vMxZCptxA~TEu-0_~>UQ zi978^i|gRF-(~M1=t-;vraJ|uP6hpFtvzNG9|{HaFD|H|yjLgS?zGB#8sKK$4RGC* zx%wV^Ohbnv)5S%q8TWv$yVS^g4QQv{3ut$B1KN=L>@lP}{swsz=uNX`WA!IAa}i7E}mDSIX-AaC|kCX`4jt{jsd^;ONT&n@s|#P zv3$wqi<3}3uuYUky9uoY;=;EMA>W*8HnIc<^CMYeiP&8mNd&;JkWeU#WRr*@C|&X& z36&H=G#`%uQ85TNZb3k4$4OhTPQD2?2vJHHLZ85bQxK&Mm4H_VNXLpbmIoeepHvcT zl5|&~!NKsB#c7mFFp$qB_R{%uQWwqXuC7RGS|g>5$es3peWTNBD>9h znxUjZ+EKWms#aB>*lBg;tDyA$Vu0`euLd}G{&sss9^ckaxvC~Y{*`YD@*mJ`QA>Yj z|AzPfYTSSRF1UZF2;PVOtKrRMe_@{&je*=p@7dpVBGi9-hdtQ}M}#W*ExP2d=Dzt` zd!A)|RSkQ;nEcC%N2&P4%aebHQ3?|-kpDk3N+Il0L;jrhC$n=tKJd9KcPUzdVSYua z9tGhbP$eJO>#c`F2p|40g>c{8emh~s5zqPfqdd=1eaf5(xjjC%uZVg>jr`Oep?>i{ z(9kyNHRxoM>di!6kl$fCVsxIYJqQM`Wa>i7ZW^e<` zwd4V76Wmy+)|4~QTAN#$a!RdXJM!wT@C|j>GUAHWU27!FzWm0XPF?QJ9!{&{-fflF zs=F_8MytzeoPE7}2qx9_1D*cr-5O^J@=cF+r!kkUbq=sq{d}iN9e$FtzuM5xNvK}+ zPEZnxOe#Cj>8fMpJmrDNfcRK%PU@ z#?zsb%xq_q`>0i3ZhGKJF179=s_@_xdU(4tD(0bGq_T(8-*rVpd)n_?d zV5&3Db{^)zu&B$`oa0nk@inq8I5;=-T<2BWdh8o>og=NX$HhN08UfHXXNgm*~&noO$rn)B*OM!Wlsbuj|XO>NXE<7~X=PIYc;+k+>{SnLL%ia2m+Ifw0 zW;R+xL5(=0IHEar3 z!J??(_@7>`#aPs{dSe_EeJqJSYMlm9nWSdG?1(@t0V*935T(RC@NzD&0F^B7BdD~F zAgmd?05bI|kk*IR11`q6S{pqhN0vsdbGuxECN3Y$E4-{1{!`lMQv zbpu=b&tK<^8oPe|X7)#mc9G2PIch|9g4oxQ+_B&jDG4*9Xs=1>lKMs^?Z499>*y{e z$+366AN4KS#VW2NZOJCm?!L)hliruMl0Q@*uC>N#s}kqHa$77#v`tA%ba<|lrHFcH zg>z!Ids7qH@Q&<6^g&j(H+N%CGKwu}nr%+$+Y2(WV9j;;&9i0EYaSc1tXJ}^|OcRQ8zSoZk+>`k*VtM+`@=`ClEO2H)C@1%I}ofjXx|BeryGW*o( z^oN}bO%bvm+g%ZVpa?l!Rw^66-IU-z;;cMGe`f=?n*zwluB?3M*a|s!-UXiACyzK= z?L#R}Okm{9&!qc5x&GHzzx~+um&1ctLV5bbSAKlkXBt=IsXpn4QTSH=X8Ft<$J^K5bA9?PTUv2tz9(vjCmM@(etol4|$bqBjVTz6= z=MI0|x!>k|)}OxN%_|HV|7=@wv=#-VrfwV_T@LgBt=-CgDaliua;; zF+}CQq9`8wMX@GEj3%CIi=ueW8=H7BvBHU76mLiI!`R;oxWp*>U_~FqZWjOL`FOEa zZu+jX*#LjD909##3mD-=edl}5Ux@$av(Dbu%-kQIbzZlu8M)`SI>+)<>F1qmc&vY( z6$j^?o@W-HkvruF&S4JE^)EW>Y@&~T*_lRg?aR*194-NaLUl|Kn-C^uwn!~NCN2q;YcaAz>1|N3tf!U5!bd_|1wr;89~pPfC!ubV{6vEHyJeJ)pokq<r4Rg`rt+O^`VVo=(RB}TKGC5IO$+2hJ2xB+aTXOK zmg$Ig%fzVXf9~wJ=V9U@d6@-0`-6j$g%_n}rcwQ4P49c5b-f!t6XvPL|H7G6DOOae zJZ99gUpViSf`Jh+pJ;XAdrob3rVoXLndCLH*5@^})|XfY`VglDi1dI52Rv~?#EXHF z;3x_4gqIdaj}N19{*#ZU^5k2N+;6y8dB1deGkp5~(m7)9kh6?)T1@f9l&K3YrrwzL zCj?u$@0ZS5Li@>IJA>^fex=6z+BvXchl~$x@e#hU1YY?ver5(k!;-IEaV6z z;zA1SVmEo1x^ss!%bJiXb~BC1G_LUtImRj<>A5r4a(T>Pg^@#)kl(luvB$VKR$DtY3Ze+pm6UD$#7a zU8Gs2ZlK8?exBaB2q#}ef+D`X`JTUiv11Wg$q|*IzyF;wT*_Gfw&gngoL-fzp7jS{nSdG097eZp$)KJ^QdrepH) zj(eaTUrQ1ACAr*l7u#8^9Q10;U4U()U!xn*lZ#-iATt*WN&a$`T?3yyq9hSU%M0Xo zJ5Pv(dnz=|h&3@Mbj0xZ@h4dFqo}6M*1#4f? zxwi7qxnzN8FTm|4=DMEz&4`^WixIhQTl_^aDG4l`_iWgkBSOWP za*a-PX1P09!q=6%1Nol&dbvA7q>kKs6>h@9JJ1J}?%@8P3e>AOjq;rcAYOu@!3$w{ zQ=(4IX>xn38CC98HG&b@(G+~h<%Aj|>bF(y2n+`BBhI!r+aU54+r%$J!NE2jx}EsS&tvD^Ffw?L4hlvGYoc%$9twHp*xja7Rr{vT`8IzUV^%|@# z#*=!ex7#pPU&b!oh3~H|Tx~uw#)cDFx7+I7L##v8F(9 zwr;RV2h=qU?zk$^OhCx=njXz+TZ22mnyUV%!F|n|u723)&c;c~9(~+JQp37F?qqrH z?Bn*erm4!l?l74;_wDQM)3D}6rcQ0P8om{#&g=UApJS+J9^nPl%=yr*S2Gtt-Yxyy zFFVT;H5{_*t=XFCi@jzl?`xz zLj@lXaJP!_B3tybwVcmj}B0X&Oxpc25;roj2IsSDu8YsZR#GH8PH?n%z+r z1+@%uUknSf{3>0LW>Iua|Do*} z{BDG)@6bKmY4Tys9`2W^BG)v^y}&wtyR^&b*BG)+18{38r) z#8qLcRQ-BS_tuIIgFJWLUT&qekM39$Pcm}WKUXx8pr`u?4>2y~t>E^2!1#4nw+C+1 zpnUf5TKUb-%iICu(%#!A$qi<~n>*F?^WC#l`xy7vHi1evV*y6qcy@SlDuJxJIN}}`Su}hyX4Hxnd*+SrGi_hyZ6k{U06bIz)RcVDlG0NM7;D9 zu#l$!C}L-Ect<`EdiuvTYKfXP!>v(w9qL|Z3YIzj|Ds^e6uj~+MkM!`Kg!G*u&k+(HCqw+#OK8{w1NsZF1WV&#m$-RXY!Nhm=%@fcrDuL52y( z0X)xybH~keEv?`8nN8(T%sul&uCF;U229 zTy%tcP%)uAeT3UrDCylJ+}o;O z=2x<^6wI@z7tFtPN)hHOPK_`xB$LOyI`~v~uYC-1L@{V^Dxwj71m$!I#^Im?jA>*y zOybxb+Xv=)7)I~LQ{6-R7nq>zS8$;g*TKlFPB#9UUTT`{E`x~H&2|R~#<$IO`|9@X zob7TkgC4vgEnf@oZI0;+zBD^uLb!Nn{4LMHf$Wd@Ksb0^*{ODeJ_0>sOPIJ0(Gs|H zzMeb+gkoK_=rs3_0@it_yC?TDRJR#PTCD2s(nR*w>F)j{$n`zLeMK|3Kc4BH-=X0g z={Kz=BdzvZ!<&H|7Ij zvoLd!X^p zC!1=b{*4Y0>Qj$BiX%kDU!RXqi`w0{v;617R5%%UdiNcDxK?zqEWmPmH)xCfa9qiz%1 zWLWm1=*?i^NV`bDjuDeblz^Q#ZdgPeoT2GDft&!-U)8Rq?okz+UjvKvrk)YG*;l%Y z^B}(WW%o22OQl1ua_3urP~W}^=J*FSYMDD$dx8#(RwUpUUjW4${{mX!RWng`Urmcr zyfJ~WxgET(TPxR5(n1#K%#U_zOj?do23bUt5NYb=W$r{S6sWw~J&?x{S0f33q}E)G zkJ69S8&|u(EH83Oe6O0f-0fG@{wGGJt+!1Ob^b@)x!kRtw#v%HIPM-3=i*eRhaIwl zMlnY$!mEVjBtJ{@n7Ky?$beWDEV3O>NWgW}XUmboIc+&?1*`8ds^y1X4Xpaq6>dhR zFPmNw%do2OE_x?s;Zb_h#xJkA_u4nE+j+72$_h7K)lV9&eO!pP=@s?r3U{ot<@Kjk z)oQ0tbilhw?RA|y+POW9?#H&WWD@NMXW;P+A^Z3RCm5@)bNlq{cy7EA(ABKD=(9H! z4uB@FclSyC>UFxB>v=lb75r~36t+uA+%(j&B)`}~VfOzI3x#siel`HUX{q!YcliH@ z**TmKin!0xpDV53cz2C^wBdBJM*ep>w_1El#8{=cPzWii%T+Jj;@0Z8 z&%;uGzQxVtXL|oucYv9~x(B%=xA0c?Fst|)uh-)aJ30L4Xn)GwbuDC-W{99*6=RHG zjksj83O$U!XULGnAS)K%l`{g=rU^PSU9pjJ*%L^mE=wbi$Z*4(ICn82IZ9mCMA~7b zq%KM;p2sC^r)anPXs`QdxvRLFLP~VB)ZekrMEA){yN@pGK04Wbba|I(xqX$<*Ck#@ zS9Tv=)qS*F(bP>ldvqV&(?nB$$L9CyK6!Qb(Q;vWH+9x_AKklaG;RRozKU*3sqa3z zq5J5@?xXv3AKf>MzMxa{`*ojOu7mH;PPi{lRm5$I43Uid2_6p3xY!FIsJ+;5Bdg2< z`nD4%`Dnaa41k-})vGX0`sjVP4^vmw2krqp4*$TND{|A5AGq7Ck8-Pj@7`eZu7>D z`{%tj#5&wo^VUc1krp1%{^&OHILtiG{i8dW(5gSW1Dqg7Y zmoQT3U6{3|C)zG_^UhVzbo4CWfVrcAFsGe^a`|a)<$VEOxl(5yqAGsr{@Sj!@oAm9!?R??EPjCG7R`u?m z+$QUz)qh6)4u#hJ*_~)FxAiR#_D5F!ng{&p{n*nxi)!s>?#jp)&PJ_Q&F%NOySFv0 zlkOFtY+2K-mX^r$z=0s-G7{CkOAC5icA=yE=;jh{PyX(s|F(G&^RPEWU(f*xnfX_# zZI*XH>hUYVC38?-1ZlFpekXlHVr8acb{T{V50RakC&Zd)ciy{46R!IC#Nf~pAw356 zWnFalHP1vk6G*8wxP@>yBG|0EM%`k2e$|6gLkY#An`nm4w%XqQ|B82zQI6MVkS8}g zLm(q!xH=MfQ+L=#1AL)L^BpWm2SaMNINtOzT?8rs7uO*$AeV;fB0BrsQt!B4zdm8p zb)#r{5Vj!}450yiJg;80xt@Pim(hads42gzmy;OZgyZSjGg@la0Q^zffLb!&EdoGO zX8`^psYLe))`uDx_LVzjkBtD$H05%~tZqi3%<4 zELNMxy45|9dA8vb1E*OG&z81~tA6NtXKODtiPqGOFpxCME8`VQd^YrA!{B{s9pf(>8xt)=86>HK7-Y`VMHq`{%do-x@A4U?cw z=g29cVEnudUtXGF`7@r{y~8QF0zQ~7moCz^`+n@o43JMO@NxYWan(!Y9~gI7J4lh&lv)s!%)=4zxgj}Mn-ERy+rg!Fi$ z@j(IuJgK=YT^@+QSeeF0kF4%o0#zG?#mW&qYyNZ~)XfIbiPqN9u^8=-niaNOE*KHd zeC1Q~?bnF~1^Kjs0$MF8xoY(wx`YeBOpp1x$56l?LRGlz)b*jgyb1YL(r{PQ+_dWe zh|h)gS*Dc9nX+zDQ>I*vCCg}vfj~^3;rcLvJ*>Wwil@HPOUice134jf2)m4?LW@W_ zSjbgfa%h_?xbV=%bxjb}h}Zy0DaB@Js7{|rz?7sIF-U42=Nuv!V%P2_bxDagw26FT zt_AI4X^F$5@pOC1m2_oFx-zLYmv{~Fq=b^{-4d^ncX}+Ttb})4_C?F?DD-O%6qC#I z@*by0hg8sk$oN4uIb=Ti2Pv%N%!pfbu8(?L+=b}^H6ArP zX1oa^#Lw!4SiWY1P9tdLZcBKpZA6T{lHN=nOOoD~c0T#mwS7#dcVHh6ZwSXwQ6g**SA+y%olxuZWr@Xa&<`+8y5Yo*cl;*c@;6zp+MIZ$q{Q5;^w`q5y~Q5<|i7l>*3QB zhIeerMcly6*mjv@n(Zunha85UE1|>6shNsKP@k@1lzC8amD~bWB}7_*%U2RiAwONh z)iHPnMAes0!h1Y%*I7cyT!ohugIv)RIlw8k5itcPvLzu+3t7?(za(pjTi8bU221fv zrZ;j+I}C&9Hb2=&OZ63YJf_GE+MZB6+(p{Ya1)9*s0bM9P<{q)jPmfNP?$HB{OD-7 z$gUaZi%#=VT-z<J(b8hbOn=5kafLM=Bkc4r1sCx0DpR zhjgzby6+WkW(57FSIWa);X?05WP>c-zZTOprrg-EdQ+44&S`8|S9>f3Sly@|b$5P%D9)C-D zi|o%Wb#V`GEW!0XyuI_w8ds%0VDDyE4^LKeeR_J|XZiMCPwy)h&ugl^8N!Ol(WpMI z^+r24w(cqRcD`EP!sgE5y}kI2J6%^jHN{P;8NIy{wQ`Hqpx!NWiZ8<$Wo?k4=k zFFp`p{y%eWYcNXA-NRR)o&myMP5K)J#eP-t529l+;X`LJO%p zwM6({_?k>N0 zg{(u8(jm;~3plq?#s7Nv98Zn7D!~rX;7`yeAiq__>mau_oA8r9f+%N+1x>DSd+9x?k7IF^xM(uKCgHqjjb*JSa|t9yGX|I|NA+8S zZdZCR71steA~@1H3xZEEy)T)U8KF0O2QWpoV=Tp{RlR7>8-hezHoZrXN)H9^4So;W z%>F6Y}0yy~8@kw`$_|6YK29wIO&AJI5p8Xf^df+2+F-KCPmFh~r)TK@1E=P+kejDZe#S zkwq{qMQgpIqM2(0G(`}72r85Es}l@uhK=hsW2$7q=&sWzp{~*YLxQ;<#O{W zD3|j|d6b8!h)~XSWhOidi%%zigmRhhPQZkbiB4ysgzRJtX~8Xn9<1xB%P$pSlXXsO zx(5U!{UMA>dL;IL)gw;SBiXK9^3V5(`!DqfnC;jjae5@%wM+il9+4wo|8|dX;~X-g zRzXBvqQ%1E9w~H4ribrNg>&`rYo}M}bv1ngsft0awGa*!-T*8oj!NdGll~sySy&qP z0BZr;V{egp$~Xp>RdE)2WR{Rud4nTr(qiUi6$Ufo+!3@ZQZM6A;KTf2A|#y3fGX2DU`e1tFmPC7N@)C4W6195feLe1W%YJsGjrgwf#j(0r4cn37~ORm*zT^H>Te2 zo#AU+rZ+`0f`oMF3ucE_zgA>Osmia1Rj@rJA1U)N6U{BpjI8o(d0AL(Tc)oRBB|I3 zXO(vt7@-Z4&_M%2I%WqcML22e^a_2AI2FJs4d8_m(sBBZu`hQtVA9@}?yHdx`pf;k zvi$2SO+wW$HA|qL9u@G1i6F+GSZ1VrtS6O350}ZVS*BEwe;AKpqLLZKgxRcnxzr~` zEHjWmCN7f!H=N3cbWbug%Pr*t(Ng&X52wcl0K0r-O^KCR1^%j8{P_ce*rBWyDj+>a zUgNMCdZjH>O~L7Yh$>>#fV4q5QdJWIvxqN#KY3}kms*(`Ef9b?g3B!G``#VR3}6%V7Xh)CriKJ~$y5>!F6B-O87sKak7epO*Cb_AQ<;3x(}}0u(fehr3FlEq z7-abcvIA$H@68til)?~$$fPeQm%0fsuJz!zYJ3(}*kY|ZqNdJMMcFiFg+IYPe1V?te<_UdPA#ArmvHKHXW z@~!S25EOPIGC%zhOEdHYM<6<(5iO}@4bTfz7)0kmGr^>y2YV}Mtsz?@l+y`=l^rlx z7h+IS<}oN03kG$|J75sZg&3^yC9CO%O5UPD&V&y}nzZzZ(xp!j^ZSLOXgZXO1kb1h z_%y{&^Cy&xnnJlKLk1(0_V+V>|EZZhcOw?<$)F}dUMxz7Vi7uG=@z>seCR&nLPnI0 zLR(rB07fTQh^Ijr-HB*Sd!5I$9?OhrjyuShM#bcpnK6yt!Y>mMDLu#x;)GmKM4v=M zI1%j1T8HVojx;i%FYdI<@+3^ZcPAdZ9)0fBxBBiCUd zSBk&Ej)FuY6zCGDtBPd1gpuu}4ajz7p=?*KnUSbO>I^%ODr8v73PV$=(4FfGY)~|C zz+DPdTBe6{8c#c(-%F^pOjD_(PM7sb`o5-JA*PT_VRCvXv%fje>z&mtOB}2#m(j0# zNRNIrZ9=;;nyFPv@W4{>=pRC)jDAU(r(HptPAupSnT;9!G+Q#zHqV!=Lc3Yu;x*|H zLe5POk;jzuAYlQ`b~~(mw~VJ?Syqlho!v6W5kkrCN>bmU+SI}yDLPJ4MNaY=SqUIm=gfEDtCu$^j#D}Av&|G9U zJyGLJ*5aOk4-fK(G!`#5KpHa(jD>GadO_#+>$=Eb0&kMFUB6JO1sui)0AVlCo}o;NBfPk5V_3$?5wLQ7?y!Bq>1vo@9o49=l`;cy6}qDU64G@CEN zD3Dql85~U_A5BR!<4HYI9l%@%ue`{85?1!Fqb--OG#m7F4aOy$UK-!wa@~WV&X5%rIK3aQjW8sAP#wq zmYX_DEhy>)Op#hN2hm_T1))VW(UH(1DrGGzhHMKgc3@jI;b0MgGe1~7S&R!cYEdt& zE|OYM-sxK7zT`D?u3ptJAnN?4P-xK{YxliUZD_Pw_+o`0BB~(GNd2KKDchcz8fh+#Qx^(7(of9fe44^!mlt{>Q726XBlP(EOHa=X z%B$bv4pV_F3iL1-#A1a62t8RN`PM;fP#>DZB5@}K3zlePq9UOuVhmc=5uV_5<_TI) zTUf|2#YaL9N(>QtDjL%nwzR^f3-fA$2gt%jgdWFG8-0WeQ!KT0TK(t|t=q`r zN1*Ar_z|x0_vtv>U+7Y%Ig;iHIa>W_%y%GU5D^lx<4e{&Au}Omgv0lj$CPviN`}L? zGb!wr#S1eL^-VE1aXR&HA3ZX3A4hAW!opPdF=K|hG-J@>PuLj@OJOFv=^DLU68@`a zjNSB2p=?u9I(AL9>6)yb5{zhy+|Yr?aOg{SO$Qzmbxl_uQ|Ov% zU$Pc;4J@8DPIvN9RtrR05_0@XEf>i)uqe7=M_LXNpDSAHDpE2IQA7}8-^x#BP;s;h z;mcDZM4ygn1$0dHsMCm*BMw!fdO;^EYTI}k(aHBRF55C}pr*5W!$d1=2Z&fD=k2P{ z)T3Q#@JIR0QqVAepg&UE{|xtsL(ifE!rMwK${lf0!H$$@wk#=8szA3GEPF2$x6sy^ z0OD4(;kiRTj-YxzW`oB?MJK`-`A|lRGHWO$Yn|EvUp8KfPdRUm^45wfEsm;)Z5otz ztqmf290cHY@-S<2;e)HJAzrQCaTF*-VU_e7xE7U>CoGwzmuTYb)NNdzTmrXy%nap@6I*!@Ecny4~t=Ywm;D0*?}Tc0iK5>azXHzxAAWzR%^ZS_;*!fu3^ z#2COY^OIUfKG+{jz6ShcF%^hXSwl!pE5N&wKr82*Kep7R!%+-H$SNv1Zu-*Dr<5$) z|DqI1m(rCE$7TNaKuQllx3CqqGC^Mt0c)w92dmxw?*VIN0W8dR{~NFc(Obd0rstZA zdQMXm?8VXS1&R^QA{1o>@0rx2|3PX!);Uxi%lE{Vm>OA3AVtF>B#>w{MFg@nHdj+f ze#o{L^DKNTN%4AHb!*J@etz&++_Idn8H+rjQf+$@Qxk@JRp&;~cMn8?M*npX1f#|^ z+oaeL6_%Q@C+s+?t}rMJ*A$*9au#M?&9MPvV@oh#kk|BBgd{&^gr%`DUg-h(u*8xc z87$YI7Y3y8E-p4*gge8KTV}djRv$k6e-p_l!>*IE_$-7 zPTJE;lo#ROQ``3Ss!xt+Z>`Q&OncB|C(>J4hymY_qyGNCCcW%`g!DEWVv=)!Png#p zA+}ZT5ne?%!`pDqNG210;($#jSXnX`qoNWSbM$)&2JTQ97B0Lpa?{xR$aqa-R~0Zw z)4h4ZOwzKQ02fQ6TGKqQmU**>-xEp*UT|8q#RgVuNJ7<7@0c;lwqz@|NqxtAb*glz zm&`+2^cgPdv^E=G6rn_e$r4l%Y#|;>b+if8qKibP3pVSxMk6Ysac&D7LvCyn=E;pk zupXLX)@^1Gy@{A$VW$n_VzS+lCwHtg!UCYo@0N?L{*B3kBwduNrzZ-E+5-q8_cCxfTWwxJRGM zBE-SBG_~NwLGuMOOR}8Rv!v*N#)B#z>{9qWo9(sedB)mfOxyr6t_wlYQzhF1lD5-a zNpl&a8p)|U^i;Xj3TMco<*DS-n+v7EXO>nZ1iN-=M$~cLX&H7>5EV#n7DIyhNTM7uO^#GwcLi z6QaQGniVbHN;|E=Ww}f?G%si^uy{koB5N(tz6b@dUX_RIXQlDjyml5o1aC2Rt#~A4 zF{{=1X|iw-7`7iRjiUz0 zMgTP095GwX!%D$nWhb|D(G4%e%6&B`>p=^1R%Lx(YB z#xUH<41c4*DdJXe8CfW>f~eDPHpUb%Ac<=n8|x)ok3AXA@CmEnRctroQ61nX-PEg@nENmmInL`gJ&S!V88+GiD-#-P0K89EBYJJEg7|m z(Q07lmmhd^gC@CB_CF_Z`inkhr36L)oeXp=8?J1fnh~x|zJba}9DZqD-av%zrk;!# z9{!MZZanNWE<_QRvO>Vwm=J(C=OH4$S`i-;dLj-ph-jY7uyIS%DeD0+C{op69a@W| z3vD<)bEX@Sp>MSXLk-ulf?)uqr_F@u=V%K))%8psM%V3k4 zbutLgr|$!ao1*F|DlETOJ~7T}5lxs=VQa<9>LkQ0@s5yANkC@}h$X)kk50Di#o-p4 zHJto6H!8@%yO_nwU2_wT%E~&)F?8$*9m|pX8ui5)Ue8)hk7z5}f?m-KQ%!A;K7%oNhaB|hT zGrgWI(b~og6W13xfx#qH#sMYN;X(=KBL^7%ZlAzZSXOk`CpRIHt(fIyc~l(XiEE=H zkMP=gymEwhv5D-X#vSEVsEdxo%g{&a>La~XJQ|PkF5q$PQQiV;#p)&1odZw`+WkM)iq;lsyzs1~z?YLA9(AwWKH= zDrf)TW-Ks84X*Az!J8t7s@^}r8)FKY%;TIBz5RIHf1)>y$A>3+L#+>0pOd_igbqDP zr@Z_mZ*M}mle~j?d}N5Wc@Ixkv*&n&`i?=YXDcv;G{Pg1v84C3 z?BS*+sC(y-e}Y`=^+$tKurB#@p0 z=@VAEhw9zx-P|7z%zyEggE4=?I7o^QK=NP%kvR*da-{lUtJe&EH!kvOrqjas$e4#> zAwD8jl14rE0AerJAt?mpz@jGJImpEkR(%>DzX`BEv>$T33)=`Yiv)EG{4#Y+n|FwH zq`IrkYdE2F5{D6XRUM-hh32$Rx7D;1RdZ0c)f{B1X%QK>)>s!u9 z?_3|aCR*3GtV4alp#2y>v2=+!otT7bWzob~WNJ2&GWTeuOI7XyZ~tsr>+o1C_)c~j zYT`1unkXNaUbi`xq7c{|H$#c-Kk=N#WDN6NTwukJ2jEa_8?tnfJ+Cy>7)a$np8GPE-jPGR1*3|l}shEv+(3lwPDgOYe}Y*YqgFdapk2ftSVLeg?M@? zRo}hP8&fI3F!u%@uMl9AH_!94Wt44+bs%<-ZTKKA6V3=th#w16BN&-Fff8k=Hr^+7Z=c~2onk!Ig+MjItb|}}L!fHcd@tQ79jGmaZwkp|Wr0lg zy~uk6aDI1@H>W@Lh;kf=t!^2To`5AC8Asm{V6VmuQ=%?d;AN^1{>`V->7*}HYZrL6 z<>Y2;t-D@#(lZOZ3wxE|(aofrIG0wI&GoC)!54db^k;;xu?j={c0Gs+e2{QLt-jbB zc9K9YvKqzQjvsD`#r|)$kwK0$x2f0;!lxi53ceBc41G8A&8^-k`UPcL0_$y}E9abo5ol8Qu<48X7~fXg{9J!oYgu1;Z> z8mZ2&5M91hI8$scaQDJUDB0k1c1^u;iMMyQa#lzyQ9I~R2H+&?Qa*^Rk85h+Mj+6T zW=ammo+*d{u?WvXADCGeqBPmbW}Y<}yGD~h-o@&uY&|0`cBFx#+Ao!n-lzJ=iwfnH zPHMl`%fc{tT%IXa4=wV}N;yovPH?|sAj4lW3FUvm+e=GIR`AltiRp|3$X=cH1q8-~ zTJZ(1h1HwxI60)(i`S(rg?X*zwU2)5&D#L|R>#|L{Z`N082u*R(I@IRwzz^V@DDQg z;q?Xc+Lu>0dnL9XuWy>yCTZ7NEA9I>)`TnRQ;9eyotDRi>5M$qSn2-K&2`czBVy~o zEd|_Ui}3iKCNx>I+QD*Sn&coMu=Co_ynf0!=xbg-=CzM`eV^Ax^ZFjI4ZMo5@+R-~ zCK}C(UuRxX&HBB~>*Ew$YhKs$+RMCd;kCp7@dU40d4`ZXA+}wlU@S$q(k#FKIA^Pb zGvl1K@=T9&*2oj#WiwAs@{YCFNrXSn*(T5Gan5GRSUHYco=yK=s%M7Sk&O=^!%n;q zOeBH|nUrNXGOlG=JaMvEoca-~fZ@vACqqTfKWw37k(V}Wb+8xg%Sk~^e=KaD|B^SP z>a#C-4FfBLVk<7z=b|b?_Ctyn^qi_iD#}$8FZFg=C#s2;vB)Sr)zdq||?uBklB$saqC%dsd!lx5$5=i}9EQ z)%L|+f2TBcwEBE8JGp;fs0N5K5DIX3iM6mt(k|)=Y_|4?4AF*-W6qDsvMA>F;3fxz z-yW=uJW&zYaG(Gdhd%(UEMddGC?JfVKyj%qb9ZUzA#mM0>glYPIgK*Bd>MKHu*wPU zyN$U+mOyMz$oImz)0suz%d8Qr=Z;tP0H`e#Z!;DTHxr# zxC_BKm&T2_JVQy4LFt2D&`)7%r(K50@fzW-80IbvO6KaLdYyjT!KBMTtgKT(WP7*a+Fjqn-ux-u$K7c z>X9pusZpj%t3+#;7N0CevVbnif`mDJT87$Xh}Ij6A z@(x}|s@VAlM^plCIR_$hy@nneG-el$!*f(GUWGXg0-^q6~25qeq`4+DuEbtzo1{^QNPq>1H;63wsF`YGlH`))#9`pslGa zY_w=KbY-y`8U$4eooE^Yqv1}nP*a&fX#~jVJS50#04G#0A`4R8s5S-#(28ajn8kSm zBM$c~YX4>4;OwX0cp7e#BE30&RL2_;327WR@NnU&K7PbLl{AsH{i`-x05Bq4=kUMB7rH$yNw}kchOCfe?)c#)1u* z!o2lzNt2qW--XU4H8i5B-RYicJJPvu6iw$WQhmlp5uFbS9rRR*cN!7j3xrNWA@q>W z6PnHsr%N26S1du2!^bmHXSUZr=Z&eqb;*C26_{;}A z<4&gAZ0S9QC$>a%rA8Z%VwB2}O6gBwr|<T@ul`gyq9Wkv1*JX zq>XM_R{SS|dfa%B*1ThkB=dj7C`*LEbnl2yG8hiq+MPWbK~p8#_}CR|qRMoP&SL&` z+R|m3a|+FoIl~v^u6MG6f$E(gf?i`h|sf~VVS~*15;Xs=c2C)Os2T?5F zQXd}R4$caXlgZZ?Ns`!{~44SjAMPd!neDE7r`S0qwO!1OlC!(>38p;o1a7ipF$_A8V7w z$Vhq2iM%u#PiGqSn&x6cl$tthak^d)rVR8q{QX^6bX@YB+FX zK{ww-=FqOFtmL&;)Ks)cHtH%{`V$$0E=he|L}bhp5X8ld!;Y!nA8$7~Yp#4oUziZy z!jP5KL3BOB^OlS3r~ezpt(0j{N5K@Txq%rkAVc&%()nF|-ODnK$(x6)g#-dEdb+N- zDdmKK#jw?VP|i(pq%Cg^Mx-v2jRZ{{Y}}%x5|)YJfoENI(q!Sxa1`3p4CEjB5Lbs^ z@12tR?DzCcdFP(lBkGx5*L$Nzbm^Ic@o~bjA>Fp$N&&r!3OH${SC@KAV!PY~k?-(l zS9;wNOuRf{2Qh@JXP28J!kp9h>ZUDn$i=kf z3CYXE7Lh4pYPn|s+903My+xloQSdKr@VcX5XlJ(+j9!1Y-%y3@_Siq(NV|e;Z%RQ+3TBHFXi;^Sk3@v z6gdxD?M>+p9-|~<1~a!ngz&79a;iI*vu6ZBl`8af9)g(M+Da(Tj|yMXqR`&`H+p)v z`RM8Hza2xN^mO+h)4~-D1B@km!JYz6n~9LJ{a1*hu-Z67@EaThRiKhnx1g84{H%IhbA(OXs4IZSxP1sS+P|YKB#?P z^u`?K;b^l{hOu~P7cnF+_)q}5U9#v4OEyzgD4d1WB)3?Wn!{CMsRv{ZVJ(^;f!Zto z28m@=C*-nM=#@YF!1~Rx0QMOr)!5SG;(qy3JV_N8?0^=XtPm*i*O7M6?yVlwmU z*;kfwO!#?M$XIDHb#V3=pU0R3iR2-w#^{aNY77x8Ka~#Ubd-o-7zxQNHi}19<}0zD zl3aL{=B^|;j?8e@64U?#j)*Nbbn?&C^vU14uc`ZrvNH8col#BDDuYW8Gp0I#%hx%a z(~hQfwA0SFGkn`y{Bxpu?56f+N|a6){}J-$7d1FzT0F{NA2`N@9Y8v{M=5!ILS6nn zuUWO6Zk6;mv!xiTShnMO@sdPo8DoSMpVmOpmI}4)dtUW$1!JI}WJMecZYB_3ZlQ&U zM0q*;hf$7lm3o#Ot4P#rOq625)da#-5j;%{J(5_wL^+abrsTM)P;4yNMgQh=piWCL z=@y_W(@+imoJ4u3J|)l*s!~Bc623}r^%{GAtSJVJ3B`!2xNRDnVe*9LeA&`emEYw78hmPLlFDc|Vi(>Q+!B5GO$Hn$Szz#~r&ZGdb z35p89KqmsAP{1pNJh9;ALO=>2KO#^bnVv>w_9YU9*oXz+Cy)nOBU9{-q8dd4>(bIXO-lYIcoe-)?RI!6{V=pI~K2*dFxK*m3#5B4Cp$)>dUE0!C(+8C{4)B(?V zHHQ~TzhX>LBTFE|*cQ0a(EUF~Ay(Auq*w$ieF=Dn{rjTcr zKB`SM>nyK+s=ncn{bMBJfAd!M7ti*Z#^;O6-)JK&4~2zrRbiwI{C(xIZ)_jI+l1J7 zVR7SC<=I}QpUpxT>SiH|LWZ?6vcDF_&?ezCN41>I7PGE_2@G9^7)Lb-`p2UkA+$gj zCF1)KdtxT8?`_@Lv?Noa-~1!Q7idfXk$IfEUNuP4se0KEh=@_R^-+{S-QZ-ef9a}v zt$EkCR~opb&~7mkI7V3$0V9Hmo{5;31UeBK0atiRT#lB2LCv#u%Tn9}0qRP$(`XV* zYF;)mCbJ%1gY@|{JFv22?crpGLS=5VgTZimxWCL6hnmMhJk~zGnQ}p(6i?6a(oDib zc!G-^JD--C$G4`&$f^{1Utg0KmMf(Jsg%Y;N_+pYLTTB*Tbu}fw%qc;0Avzk&ezl; z%I-SujR-d_&`*E`i5xH6VkidaIg>U_fyf|JWd7cx0`Z+8)TfOz&GJsn(2W4*TN@_% zU!LS}kFV^j(ht%|PFW4$qQLMZ{<8LGV!=3~$8+EIhsZFuUycPA#!Zz**11V8NP3f_ zkS}`u3m5ae@FKQv%T?+n@54^_^3)zwobg>dSt`6r8ZC0=1IvE&)T%XK-F1n`l~4ce zjw?R@>#FayXQRqqFqI`=RX;^Y#J;P?OWsavZ1KH6wIjmvby~`wglt833eVB(CSwk$ zgG5aK#wzR2ZjD?e^p_v8cQ!%aB-E+E+|jRiS6S9-_13H2r=xKUsT~&}{b+?FGeM|@ z5qd)}BRZ7GMC{#NAZLUsf6Y5+w1~x`p#jC*BtW95QOhOKn|6mFaR0F-BR6y%lsoWE zZx7#kQ@wPXJ4%)A^6I?rO|s%>UBGp zw&U;EHZQgpzuK|4+qo5q_)tq;zF8W-$Qtn)1%+8Xva;mj9x``%*QXtacy zExr7L#S0ONH3=+A{2>UC!P?G=C+K5QFc$H5!XzSIU1=4Eq*!+F2X=0AK^Kj~z!li2 zL(bMR*x9q560Y=EB%#!-PFny-je4hSL=11~G-5qBqzJulG7vS9Tk#OZK;SKMIH5(@x8fatf=RPg+S9)A#N*n+jHuJ%6MZ|8=d(K zVz_%qs-^5^(9WoH%2B0)=L#QHuUzZ`5(FH5oJTCUp{&#;nwXN1GRVIqO`;L8^0aK5 z#z4A+WYYj2EPjlK(KI6q)&=VMRc`WMU0{lloF=!w6ewJXjoJWv_jD$6Da-3F)HR`~ zx&I`2A!P`8#nk1M@jCI&Q%!4y#5&NDZla4iUK%dc{4aD*Oa@j+RO}wUv%)Tc3AJyI zCGW1p9G-pDSj^?e366%?Me|P|IrrSt_LzTBESBPb86U3UH%(co_~;SxI+0)W>C3!( z{KoT>GUT@pKh|Qgebt;)JX;~3>k2iKD|Kg(KEjPUL6vU=+g^Qv6Fee z&abj4yo?g+bvSot&-iK9et$kS6+4}Xhxky+GYY>*xMxxLmxQID==%&i7AsS;7Q|<( zJF4T2nVU{G<=hjN6NR4*%em=v^+t7k#y~TG)jR%j`GQpJJiZKz$6|v7$f@e3ns|M# zr7r$pd5;tMQjD)Ds^7r)xYFS@azAh^cBndKVEn-Be1d|hYk3MH?%*kaKgiSJIh)_^ z(!4+#+8w*_%PIcGVy|76ih(a(Yt;Bd2v6lVjh`?7zEn26Y&Z}9GXJvXGPikn*~uzB zI9}6m{>A5MQW{yCiZzn@On%k;MlDjop!n9_9VurzUk>GWnCdq;-mhn@rMJnoj^6D@ ze2Y3}aC~%nG0{H3dHe?Ri^A9P-XLN1(BSxiL$;AjTK{vN8J_d!oPYM2{+Z{W;h%f{ z8D}o?tLub{`L1f4hYyRFS)F4g; zGych#_=cxr$=jOL?PtXwx3?dojyOAhX7=3k7tWb`?iv2vGtWPJ!8!h%1wI)f3VHNc z1NHV}Qn53LUk&*Pn8jfkE>ZYNzRS>w!rKT#2>I~e2#+cX_h-ivW?x7!9D0<&6BRIm zu#kHcUPM?rJ_=t&SkNE?NAM;np208rzMSuS6@_mgEZq@(zmY>~9SVH*|1tL-;8hh{ z-}vm3T~0`H(ktXZAS4hV2?PQ{2pvQ^2vQ6oIU#{GQs{W7f{F;D4vNxj*Z|A*U`ORD ziXwL8DuSY7@5)uL{(o!s*(W;@?)AOj`~RMAKiMa_qr?z@%mE_&)L&@9=mIRUn4i5ngNo9|25_)ee6Ju(N=- z0F%bF!;iOsPXH#N3518TkKlnjL*c>qRA_C++X=sGkuV~pdB*M*@HoIu#8(Hyd7C*} zL2IXuqR5$C$zpc_@Lb@{c!&DwV7p9Dx=grkRhU8Ckvo<`jQcKlm z!ig;SfcuP|{H}WpH(yh!MTD(}H}PsJ-VFY9rM61+P^gEH3pXEErA3clINfTI6Bl@k z=Xz=y8$2~-9-%-O+lVkC=7>O}sHm<*xYac7MHuNfJFLoEUtc`i%MMNFH&tm#eNG}g z+7VvwEvfT1c;wJ5o?g5OZB4W$3atF@l3?RYOD9yKW&{wLr#N|(rguhwvBRgD%) ziOOoUo>6ur6UxR)#P`%_yIi*a))2m}T1(({wZIUGHq>ex!kSqY)Q=_ell595|J|pB z@@wX4rmu3Qo6Q9d#ADJIHcY(=Y>^;55^mB5b~yDA&iKWEsWdyBOoChoTncxd1NOp= zZo?N$Pyr8T0fZDTrVNI!fSZuC3s~6#UIo~R@EX8m0NL@^!tKm|9pHSFAB=DXJaQZf z*29hY(O~%1E#M6;;5z|35xxsBHD4Qnds@Kv0?tDD!3g)k?X1Cmz)pl70qi8{^MFZ# z*#*4V0)7dwv&M-~>75<%lK`hXBY5Be=@MkdKVgB46$$vG_1a3;9GGa-=8O{m&<0JD zlNRe|Lda+U|`_90P;#p;_MSVBolKP{1(nDc=`F%^Y?eg%s zeDqSSBVURi`JK7^j-^_s5Tnw~+LLmK;qNWiTDO88>{eA%3X`!%C68LBb(NP^@`1~= z^lrByW&(119REpB$YL4nEvYH>PO7c-)(!VAoK)>ySnDlm@RnZWt>!N+*Se-QR#Z12 zukVqD6abl!gb{78qlaJTgDLmMWm;MXTVKzrsj2kTd28#u_1@}+;)aTv>OP)-K(<;V z9sl_~d~&&#%D-Ny=~l~oZ=)75Cn$|H)`>+TTRKAklXN-@I-f51 z0TNw^xC8JG#7pi$c%NME76jM@w~$=Xt%Z-9RZ~}A-leX-OK$e8?!`Uw^UAuH^e*jQ z>g`rinwOi~-P=vX=tkq~dT(7o%14NSNWi?9Rnu5qTF-8tC%SafIsEaU>klNaDoGV18|}PeiLwe2b_qGzR&^p2Tb|ypT{3vrS*xj^L+&V_#=O9 zwN`M6?Y|X%@pV zb&jlei(dVE85Ru{RL}2NqxC}Qi8b1-Jp!ZTCxJ`)UoDD&e|Hc3w{@}oKh46v%Qad@-$KNpno+k!+GbX9 zWpQJw#p)+`XHRITtEiq0#{dz%u6V&< zZ*@&oMYYgxLASTm3NNL=`o>vB(1K+Z3+rnd>q@*0#dWj24GlGOz18*Q)H8WYQGO-f zR(J`YD!lpBRx|Jtd#mx*;I$*v!cE~6ncjJLC&H$#FZ0&%jhAaF-3u35o zV&EXpD0CD&Ydx&r16OH!SjDUo%YR^FU3HUxM9_aoeMw!-f?!BUQXkkHwoueAwPs8Z zo2_2TUt6y|89o@ZDBvOU);F-R%lNjdwK;s?1}zCq?+AjZf3_!DMpV_p?5}9(<6-Oz zgpnK!sc$I8V4%L-TT0%T;&2NsFy32RS-fa$T}=ZzKJ-=m;07(L9o1&UtP(;V|14bL zm`Kc{~%?B&gCM_@yU;CM)pF^kAB7 z9W;jVMa%mUJ{>1Y>b?qSG8v)M5n6(@%9>q}O!Aq4C`^u` zDqJnW9#v~^!te|sZH`kw3TmTlgLXZ93VlB!D>(@m6d}C*NB9Y#V?ayFFKGDr8b8W1 zbX}vrhT^`*l$0v8!iJLoHsVUzFLN3D>~{cX4N2Khj2YT50KDLmt#uq4cWbrfM3A(yL&OV8KozjRVbU=iQAlhgos4GhItSOMAR`veRdjfA>e04 zQqCI!3)*#H=&UNq+k;up%4EXZ^O`Ct+IXuR&r2xhp)OWA zp3NwyS;!|buxEg$=i`N^@niYc+! z>oI2_YF%C(@EHJV9a{zhQt%q&)GV^{+hE2haBR{R)lWUQ*+B!h#NkJ5o}2U4yA{a)&necgDH1J0^Z8?HEb@3cc4I z6hEQ4yG1^}U;)mI9PU7Sr^>4@-+?{^l}gE!oNky%sH2*L(naW%prL3A-FIU&0qIW} zM>G&w+yXXLBd^x4OUlo;f_%BCbn9?1P8KR>C|e2QFD7Wk_YRQi}?$SB485v6WLtJ+5)9wcfOa^6w{O!IUDB9yWa&GEbd zljnq?$Fij0z8he!0jU_PBlrx0Ciex)6n+J4Ij}F}8gxeI1(^IWIIhg-j3FCjE#xf& zG6Ag*Wji$Bb%MOUMZQWVN*xBV4f#%LCnhxVq?b4D(Z-?^vb@3zfh~W3k5-RHXL((bMP8MA@#&4kS^WcDCOJZt4hZE9w&Z8GIz*5piG`V`qU~=px8R#78|)7kGy@P}33C&`D4Y zSXvJniM+klB^Z_=l2$D11BJhT>^^Ov)IPn~j)xqUNMTXY4v&aZDJ1-rnf&4VwSLn{ z5h}Zn;0rm(9$zO*Lbu5MfSU5?4#s{(HR3lzSoe-)?8c7(+>9RnD&V1faxY9Q^4W&q z$Md1zk68dyo_!3$e2kBIKuZWInGUhxl@Dl-|0T5i7d@!u>b@fw7_38u<5x-X;TuWJ z0#Dk7a(L9ILu`<-v{7#_S61)DJk}*i);`xi{GY%U+sWBTNJcq z7IhMZS`Wkz*~Ev(pw{-2Hd=Q0R}8_VviG5$hPm`ZiX35d5$o_qRRGI~;Hb z@ig)`#Quh2e;2F0>i$=(k%RR4l$S0IbK^!2HrCEem(aNF2f;nfACOy-?IW@?%Qa^hcow9DZ+n`&kfEhvl(?>COjpb%CNppOwL6}z{ zy01k(_fpRDw>xr1$H%nzFBT&bKmMea6tey@LhGMTYBQx%eCktLXJ2U*3XA4ysIEe=*_ zI84^uXnAn;02V!+;@u7am%{&m%L;_ALy?5bM3k@_?jSCQ0Q{fh5($kYld%^6;zPl9 z#ANEbR1Z)VjC=&dP95FNzP_Nzhsh(+vBb41W9)PA zQ7S6brMSq;(Hph`v<1K=92y_JgY*Dwj~n&_J(vJexF!YrDE&*Q7ua%RK!H0=e%I4l zhHrR(^0FT3s*pJC!vY(G-I`-+Z#&>*kEwm;Xa~t_FVcoM;G_wrzR&^3Tpwm?2OJ)8 zxsh7F15WfUHI48beAGbOiMiiwtyKW9pXkncn$OX4q9=?u>m0Dvl9>LQBgdEr%ven< zFAL<7I59>0!BK~#v2m{b!Ahf(O>JiY~pd9IjESqDAEndsh-w&pX=IF#s>p4@d%Yuo;>RFsc7 z2))$tGFW>gSnq}yyJ1$0)!;r667Sv$fCiK1YB_@GgA1GLAbk?jLGR`8NFAH+CfK*l&mNhiLU*`riZ(ZI>KXfqax1$UJeSy_KuGH z0q!m29?-${t}|X&G)JaC17g|_3QsZr7Vz+quOKmNj^WjkX<-;h_7@NoNbCzBLCDz; z5NwG&1?e*igY^f~zX*xecNJ2A^`t&3nZ~$6#*QLOPn^^$TJD0c+5l4DO?4maidaxu z>SQT`xn6gOc%|C90x=&o$wL%J)Pbk|q_&|A@@x5~*qVZ57k)^fN5 z`pO`EwS^KvS4ESqIs^cEDw_1vS-7F2B1uPOVCa+VBmES41=36bm^hL8YI<7KF7Lmq zos}1QmX?}%sp%%^UT2{GR6Bw7ZeY-awLr(h`V8DQ)&>SvLa7R*GXOOq9eoe-ETjk* zM(H~pnzZc`A<6pX4iEi0)i4|25v0G~8!C?JnGUwMeKMqtp5pLOhiUP8o|{8v#xs9u7Dt4hsR;LcAMpTO5wT-6ReMjFKjCXd%SsG%rxT z{ru1Ei$D|2N2$T;l>@M=x18L8Cb|V~XYKY;q6f@QRvY!8qqnQK5pKJBGzDN+k8e1v zwf`?oh>rREv%^}7Fd@D{oyvlN%IfwAvsptv8# z;@J_f4=Lbr03D84^DxqN*rKXzm}pU*@)i#*3y_aVe%(Xhl?jH*cTx+mIcuAy zcZ5vBa8l;x%UbJB-^>M>PashpCAA(g&$8?gc!4=r1Q?kxs>=D;m$lKpn-@(k9%B-Yt&F1LO6yrI_z!xagU94c^&GAhBzA#{}APc zVa3L5baZeBhZROLQvcWE3jW7aTD)%*>XHGPxSxoPJ_uwB$sMS~6mp{yzqF{t)c;{6 zwjzEl<%LSzj7n^TJ5ULzqJ4n=k17$z2fd<2_l|iOHZ>*`z#(&TnoU%}$^x;XL3jyf z_S=PZOuvdCeMfJ8%`2KG`tM{C1s*e7?x0_u!f#UbWM4+S6vgz5fu;ROR~n}Ni-S`c zUMX3h2G-b*(KjScuW@)}lu3QfO`x^?WYkI~Gq*Vk&6q94HutEIBJq8A`cZ7vdb-vc zmq~+V%(mYQ)Y1D%Dn97k4K1HU?I1BklE1Oh+~ zrkYr15qzX~)I=i*T7L%`yY#-=Ix-j8?g!AWTO>2m!x40pg1$shK@L}6)iQewLQA(F zsV(}_qg`zf78oYnv@woOQ2RE;9N2>wzpCZ@*D2%pyRT|4-&kkC(IKh&yDbZ@Xi@NV z2WLBfGkJt2S#y*Ui3D$ih00)@s%bRNiZd~+|q>^soHI55B8JciPBd);FSI$rvAYII~c}kog87z zK_#|8Oft7gx$eiZ(VPIF5+>6-JLab{cS-GhuG3(u{iJtsGkt-Bl9_&KkV!e%kCh8< zp9?;j`=u0pq9a@VjgTk+*qu;Jz0!%D%WR`%I6O#)^bD{@M$#LbJoE+G28RG!z0K`y}zT{ zs>_W2E7cbDk*8wzMfQb(3$4IsnkUVmZ`Df7#)Ac`yk{bj8@c{Yw$S|j*M9N(AN~1Aa%A)aayh}K3NG)C=$B( z4zwpIBaddJDKF*JH33$5;l<<|(cdf2EeY_VMrxYF5{*;`mjp9ilQ5-xFtw*t`((FZ8~f2#(EVEMtHaX%27_i9DLIBOmj=MDIe1-dC~+K81k)NN_># zrrfCfi~{OYSlrfS0g7U=29vJ^?UcIVuxf}zOE7~!Vo!h&n<$*VHOmyHWIhuVY4Up# z0t^YY9zmz)G07-$c|+1G9DQcSo08AezicMQQoO#w5k*jMzQZG9nS**%M#i;&>NI@; z^3)PVRt3nj?wsWDwG_w}^q~UQv?TvJ3CatPa^W28kA?Y~5si<=57aQ$iGWF<4y`~6 zHa`$S-u^qL)3lXN8|1DeU5#unauPrCTDgLOnAXNOFH4fGMMBn$tQ)?yRUt8u6cn`+Rd*?r4Ms=CU%#J)naYoC70~jx9|6Z>DgYGNC;3 zlmDAMT)uP6s$Jvc`%W|7yxHL)Zoa!eGi1~8m}OsS7{ z)IrnE2NGR3IEW#ip=Q9E%kbT1tnn-o&7{2VHQTtU1uq0350p)2r~@#R0pyeRnsLz= z0NM=*l+(MyZ9kr+QiPbWJV1fd0uz?U0N4|j-@tut^6A5!8C@#1);}(^^F?O!r1n}? zp4jDS`4VYT^SJk2EnPm?nqQ6ozBi%o>C2GxM2i;sq-pBSWc4XC>VIEWy+WRuK|DH| z2sBWN;L#ZXK_1b9=KqdI2hA`rjq~^L)gWg^psFvKMkmbuCLmYoWi!Uz8UWcQ)>ng1 z9dZE8n4qh`&3-IR=!hXdpkY|hTriH{cwi=1A&qjNQ=rUgE}sO?;6gTQ?b>pTB=TTw zU?E!5+BF$#*OYOa8EaG!ay`1+XU5>We>i~4uxw>O8MX+N`mA#s3ZF{KI4<#_K!lZuFlk930xh`{1c{%xFW|Kg zK6j)3>I}bCkE|bwexFmtzammxjIJ$i=v2lAl65iy%Tk0xyU4OY6fCS&Zg`%t6NJ|o z1p9>R<&6Qaab&eH_0!9+j1x;Ql>$jV0#2yY!M6qEF;Y@5L{B!0+><4>a~3*yOcjy; zzYb%Q3c2Bb#U6Z%3i!{v7#jjCf2JOUl*~nrN^eQM>jGG|b3iL4Pm-sGi8VpK(Y1^{ zMaUOO`VS5nQ~KjG%C;`H$RWbWw)!Rqtafsl`aMn$tY^_#x268m~E5n0jI%50+YM7InPo>e0t84$&t^X`ea_U}dtYM>?`LN2;w|Qym`4Wbi3^ z6DB0`skCX~;-lwSxLbZI4mo&1 zym`z4+q`+ZC2#T^H5a_`I(R|6>EnR^2fVos_zov(fj51D>df=pmb8p7-eJIClReKT^ zzWrqDZME-gijr)$O$ygnI3kE|@-rQLpl|XM9I!n_6MZ34mB*q4WO=nC2U@~$BR+rH z4=v$X?|=i}TcQ_cqx|KRfh#5Y&1_7g!A+A$AHZ!t+BQrgEp`+yri`i_9zlF)P_j>C z_gBXfRDV1HgtR|wy1*|xuJvDAsvvT}PXLYR0xrL*(v>JAdvrX6IQ#gUo~Fi%ghHamaCY60ywMj3agdOn(@a zd=u*GC9E)3W?|pS2LLv}M0zL*n>kJffSWLOrj0WyHqIz(VGGcf8kM%x^xTFmAOw7l zEiw%^GIo#eeLy$D5OU9lMMJ`GQtV6hdJP>bq2X(9iDc8ZSz)+45B4)`8{ z35s!#+A7RF4FNQjrG|w;5i4iWO z-=`5)A?*~fwl0|9_)*}lVfB6QDOeW|x|2KaHfyhxc3e^q9)LB6<%i_&bT91z6yHI7 z#CZ)(U>dm*=gLc9uz{K6ju{MAkQ;G?|J3sZ{Mny*EW*K{3an_`_5w--@wAR@?I1zb zPp6~NDZKbItzDawLr@~c8Pq&Z+*UAK;X5hLbp6v=`V1fGq87q5 z{RMHphODi)9J{j!_z=MJcVTfJjB62ceLpPdhpbVEWp1m9$bIxDh#Vy~bI13=nN%vxr1u3txK}AsWhyhTZRa(Av(|7TfvwD70xLYdg^= zsdEvjmhzxT&s+QaqG_Ms4?t9Tr_{ot?ISQ|#5hMyypFM(D0L!yN+?p{Y_NjLfxcaJV|{5;(wUj?Id;20LI=XcMQ3qCj!8qjMt<@#vVq4O&=b8H=4j1 zH=GmV++tl)Bot9IP~j9eQ8mb2qr9a}_*o{63}0cfYs>ljI&cCz6{kL+X3 z&`F;c=laH3){Ho)*BUetkTji^lfMRswLxmd%Zy=n8tXLPX3Uw6jQxXrz!=05JM5aa z!KFV6`2gEOXy;obn)2rWd?B>;t^9H9^?}%lpu^})j1K`&$YBT`ZOaoOrB5-jE$@FZ z#7BAm`+}xyfE|GzOrVf777c^|N>d1>P>oh0LOz*EJ6;00oj;^^F*e9TgNih-TnzcE zA&9b@HXeiCUjjij?HPN601EkNJ*+vxv@5Xy@bk3p#HI)Ir@f*ti2GwQ^}vvfVE1&Sk*W zh=K06)n^j6e}ewjY*Ni5mY|9KpO4^c+9}Xm?En1#H3a1c3%X{iMLU7Q;}2N0BNTve z^gzbGKMt(t!E*WnD(o-;wCnUcAbV;Wl6}A=4=( z$~JHfZP~~4wWL__tq&7!NIgYGKTLBna(D8IxfP8TO7J8b(HP| ztgQ&jIABLw!pc}|LEFB{kqc99tVIs!S@b(qpCosOj#Ul}g^mHs^d8{46JW~Wxq+a~ zsJatia?>8+J+Pc_d>xA8N7$x!6fsr;Dj0o$VbVOzve>|M0JGi*giM@;3FbRt_lkIL z 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 BXl(!h diff --git a/wasm_for_tests/tx_proposal_code.wasm b/wasm_for_tests/tx_proposal_code.wasm index 7a285d2a1ca66d1d5681717721c3e7e2a9c50672..15221d0434cc3642b7c663c3c1b4ae7c0a8336e1 100755 GIT binary patch delta 174103 zcmc${378#MdFNeKckSDK)wjE~ucxkLt6Q=xc|j7Aja6;D2-~p@!I&)}lfC6I*fC6! z2V89|NRu$oq@75Blk@}!YZ5sT!AX=s!?cYFf{{T6lgNpmd;%Ot1QSGJ7y$-fUb2#w_!U^z^M$r%s(Z%e%ktIp;5bZ{^oMx9XzcXMXip!XOC3uZ8Y)r8G^$^mS4G z8@w(|1IrCwSK(R8UD;p4G`#ISn^vx;xPL+-u(G!WRB)SBL$Z7u|H$FzJ-=~%DG1Z| zeCmbiKk*KB*Q3bat(v9mqtZlqtPHeG(W5^CbG3hrBSUkD*j)pOVra-&;IrBPf1;S zdQ>>9^@d?D?=v)17lzd(ky;p)`99g@D5A0aMN!Fq%H^db$?E)5eps&Q?Wo2>B}%2J zREtvG)cX5dElQ|V7|`)BT*m7)ibiygTe`)!d-N05YPC{{y84#$YE-hy$fk8Gf}m1e z8T75gJg$u6!$-jv1 zk9J4bF8@;aC((zZ*S%zT-R0N)^oF0k{ukEW^ecPAU4Ib%VfZKEpN9VuemOiDem(qe z(fQ?i6TUaREByU%I{a_p2h(t0_}|03!#@f?5bn4CKev2;^gGeNh5rsL{%iP^ z@SEY&;djDQ;r|K09e$Uu-WeT?ek*!s^ug%;;rqg==yxgoczEe$_eL*%{dDv*KmVuE ze|jQ35&m=dN6`nOyQ8PVC;9)a@O$Cy(SL+r<^MOrXTtI5o@ih6-=ja9AN_uGD0=Dt zh&~cEKJkJ14@Lj<;qtHVxqduaJ2hQu>{&!2xK zs_gmN>&lfq-|Q{7rv9MRr~&Kxzvt_{56}i5V(vCQYc69%DeZ$ds*xr>ymp6HR zINBG+Kcnm2Vf>T2-WkT1xxNj-WElUaXopM5I#g*_lhQTi9m&!pY?a#8%bI0ZO%{(O z_kHXBU}F$$4+8F6W66H*M}lCB-PgyGySwl2DsGa#q)a`%s;4(ux;d)5URT{5Wp_dL zT;EBlc>cDa&gJPR?hjO^>UuXveDK~;_x5%$TG}0lePJ&{P)*9a+RCF#yVa+8pL4|#QpqL`D^eu#9y8KRs1a^y^_Bbq#f5a{GG*L%-?4I&gPG{ z#OL#T8-H6!pTpma_-pf*UP9*OBsTK5iSz=lSMj%lzwO+=lIwN+UC-Y|{JosNpC*4P zP%_il)h?|mAb=JF8133{RBlHahGIG+#_r71x%|22bI;ayl~<%TagnC6YvNSnY4gV1 z1a5Is9pho^*3F@AO54LSm%4w{ut#sY_u^2~<7zHS!d;s5nxol4yP1T{XW&Y%xq-ywqZ;Yu5V6P0+8 zntgXQUZCs#YP?O)@2kdZQY8*nCN{!A0HdME{MMAD1E{~4u zpQuyvMBo;=MO=*340}p*)@99>%JB)t{V-Q|*5>FzT^WQK<@4#dA7Yb3^xLy}Ye&hv z%bVO7N>@|YpVS53e){@HB2680JFK47bp?FZbSAPf3;*Y5)A&QrVk&yxxU4zfHT*O- z{50BKP8JsF`654!iwbhS$TeuRu)TDF<_pX_PxH5c5pK-Jer_}DZwULF+kz8i*G$Sg zn<;N4;bw4!>tp{%jN*inWId%T_s4Z*Sa(yD)VQiTNgnAFViM!3VzMMmfOxbPc9_r* zEC;NeENuZds*E#5_Lj2I_QldRof|q+=lEjK>*|*Q(erLj`Yz{MzT%c!7=E|L)#0b* zUcv2X&{ytY=WjBxFR&hrl++K=uy$ivywEjx&n6XAY`qD>;A>8wrE1r>7aCB#mr-4K z4?s)n-*m74(j=^)2cDS23_UymU7E1Z0ej`kVZNngraTrm;3aNXyDC=V%1lDLd|7i< zQg#gy)JhWGnzmbxS6pl7vNqSGd=1xR_@~QDFwK?mK)@*HP-rQXl>QF(dKeoaSc}%v^QL3bCj0-YlZ`S`F8N` z*CgSnp^Seuabs&BY{Otze3q_<%N1Ad<@@fev3Aw1kq}aK(Y`c;Nwkvz13Wbm?fPYQ zV*{GODbI$-;z$s%t6ioybY~bc)sD(3AJJpcHQ;#R=riFRZWV(#I{v+9YIp3WL5qMy zJ@I+Z^xr{KDYctoj4_nE(QAEW80fXefRyzl*Z2>FwRvJ)E z_-5n6qmaI7rhq9a?JWTxFS>#;C?(V2_KRF&S28T(52m-?8}+*Ip15jcu`JMP#C4-Y zA^}NwOWdU7w?LH5No_1%uE)^gI>eWOsB1hIw_8d1a?zj7E*c4z2ntHcNnROZJX&4S z0$GD()$^?7+&JU0dd0Q21*bqgDv^94VkL^BU;5xl(qx3ZlGFc6K6?@MMCrB5Vx*Me zXn(1_A{pKluVHkTOtiO+J`>zAdiz^Wf<6a>wY@yB77P?eMy0pm(Aju7`%w zhTpg+DZR-Jj8Q{!<4w>FL12_OT^td>103~E_FlMaUKEOEHU(&7$^6#GqRZl_kGYzY z1EGjq<%(vQglcWdXDhJec~z;!QESR>g|cR$9gUsC6qJ+TCm`=D+={7W*@85Zd}H(wa!|hP7J6>@l4Lv) zOv!;u*XdH*nrkre?9Ck*gGv|8trSo+=Z$8t(ng>gSbQ^+;7VGeCeBY|29sWoxo!6( z&jdHOE8BvAL8JOzH>_aOT9SJ2HEOl0(KRL|ZS!3q##daYG%0`Hhg` zIQ&q!NtaVbQqmq5yWfXwfzYt81aF7HFyf*cFLHH}7p8|y*pDGlT`&(z^i&t{^Hf)C zvY@&UWuv;Vs{m^2ffDc+F|CW(q);1?-N?vpRip}@l+#-%PHhZ2ZgL6zE{F`B<$-{v z<=Lyc<4Md3bRv-$uj)WI0A1B^j8Q0OtgVzB<*v?{jASE$JW|_II!r-_HuspW@TM8P z=vA)Ark+`~FlEG!S42=vf^A1HfaVC65V_lR5g_Bih^A+EUJO0R`u8FgTH&lLm75p~ z4CWu9F8cyVjG`k&&P9ApgB0DGlv9n{*_Snur`_3>12!^Md08Gs-H!xYV)ZWE9lz3@ zrG~!}jzo5@HpbWa)~i|9XYIMDXwS!KI|c^Rs8BG=FTY$}w?<#pRj5b3W7a}4vgl77$D-!c(CPBX^Gki|_WGH1hI$l*L3|VW)+LRLngfFt7gy39836L+yI)^N{ zbt#28gF_NR+MCE*+$n~90n;ycGkvs#_GYrW@bS0600fD|NUmrYy|OQf*}I0UX`z?F z>1gC-@DsH$7q$K;oq@?bS&LCeFb~uquDu#HR~>6dLc_o{1)oi~h8jVMiY}|%Il3%# zn{;tT?oh5a>+bT51T1A1`?m!z=CX842`oZ)rWM-*sV$$tgs~hNrpBV^LNPDbr;L~` z@G0o5^Qqx6iCucSX-wjoQd`G-<02U+n(Y66-n;y`^m1b^$+i`oM zh83p%Cc|H2vK}jrSBl4(QUr)JGAIK58TB{y3P=RX_er!WOWLJK{nlGHM+YjPx4;Zz zpbT^a3BqQBh7@JuMUk|fF2g4nF!gV1YH63*^M}K;<<$FJcqZAEL&R6{|Vi)x$5nuBUXWX(af&14DHx-d-$)4{Yf5voaN?2@mA zYHhOSpxPyFO*g7FtXf00HM3D|qmk@HsJ79g8gMCln!)4wz3zOsQNRT^McEQySA=Wl zc(e*VTCMVEwa_j1xK`C=fotp1!8L3a!nLx;H4zMtQ9yf+b!Ef4OERnznQ?|{B0?@T zToZ}%xF-7LaZSX{H?AyPJ7*5AZ8Ti#7q0aiu8EshGAmW@7LO%hiwsAy2DG=il`zqz z8ia!tbP7*o(^~7T%(9seQ93gIOWVI#5o&Ib6^Fhg-$e8fk^VOFRv2svMK>j<^mBcR zJlUv3|BT1l9V8Dfq<8-aWvyS6Wp6=kT|;$1f=Vt8w}ihVY8h=0Z7OLjQc3teRV69J zYj-bDQ^u=Fctz6)CI&E6o);jPKpN>0QiEW1#$I^ZjKelwvZ#?itA!&dO5UIbHGv0- zoS<>PJ=4@w7y4&k1w1laA_iI`ToOtQfn3Ks} zZEUyR1dvV4rc8Wz7U3a2-<44FqdNgFcfQ%Fu%e@&dxPm_uE2p5_%@{d5)mzBq__k= zMSpY1O+MO?aNO>q5XnBW8g8@bh|J#_#y=;kpRE3lw%#`4aY&!vY~`jKUV-iY^(J#T z#I=F~&}2Z6C!aYW(hOwEGdRl$>nDL(vZU-@h^Zs}P%E`zCWyMM;{;O_56*B7N1^ z^i|}5F$4%{-r#VQw9z#a*#Q|oOsxk|$Q`~iD2F9hNy1{ zDiaUEa?o;a9ZL?FeiQebwu_nq=O3h2sbXmku9IXAk%{%B0O1Mpz+MS@d7cJIn%TI` z${sw5`(+Laor!d+c3ip)@}&M}L)J`s znLaEG*qP%N+2y1nJDJ`1TMQOa$N-d++Ac}K6s^XxV_LEjn~fji77__o^7yjmS{EmX zd4Cn^3OPAIBKus$wUYE#5sU1x*XPcIZ8Pxgbv$dsfO@)tQ>^d;tPrrm99|`xZY2wj znPs^iOF=BLy{uSod<3YQ!6L|hnzMv0a9q7SgWaFlacQK6&-viOo^t2|^ zu#xU2uPtuq%(DDOU7jhcU;Q#gG5SI_U3)YO)J&gz4(b6?x#W5i9s0r5a7@#qey10E zxZ>$LwQDeI*N@z+rUxS%Mm1}&XqE<+9N#RNHhjChGwWi5HcxutUF~z-26ee4Qhb>E zVV7k0O}8%Vhe)9}O~6HaB!;T=G^Iz}aPjtD?$^3=i+j01meKz_Lv?r`2$SDG3|@La zc@VGj*u2hNjv^Jt&dDd+6E{U!ll#K4jUjG-qZ(1WYGB!)(+yBb)=3DW=!TUU!bDTa zsm4$$vZ0uP7Gy)_KuaQ=a2@JRju&KOWER=*RKtHvW;RicvZ360u4)J)bMqW}F^f`U zG$K8XNMNJ7wuxVb_d&FQT@KsTx;ZpL=6+4#>VihBW*7?^F^DE5SQpA>G@?qDz(7+k z8i8X+65L;5JMWN(4q-SmMHsZfHTJZcnrNqhJi;*MR;!T$hLTk#OQcKEi}{rTn2M)erXV=F*F^|SO~?s>EDAXx5DNSG zu4G?{>?`8$^l^x|9ePoGq9BjxAdV4Oj8yb}iH)e2j3Sl7 z6dogXRN$X4kuZ#X2w1#EAx74r2Bne`jt=V7hXpwI=|kYStdX)E8H_Y#juMQZXlKlb zi7hM`y(pojohUJw4tSp2=K6O*Ebzoxm4{@+r%Ks@RcpV)q*mHxHTP#VGrid}pmy$~ za`7mf^T=1{0l8CEOJLWq)?!spN@lA@3+c}>G{xV+g!bnX`ojPrklO`e9?u1an$P}X zKC>J|Zkx}tpHKR)2^DTclD%8H^o!@x5}QwruI5$kB;kQ0&$P*mc*{?+0sX)=Ag}c* zpVfZDg3hek6llPPTsmNbkH5~Q6<}0jn9s3zfW~LaayG32517_6)7p}Jij2X_ zkWFht)9>epvA2pbV%gN`Q8uggC44!c|C&cNr~V7w%q%hVjkQ_uX&+2Az_^l}WZrcpFP`U1kW*zw_BkGN-klcl&&`7izqIu!FLcR8UeCy#Px{-Sb{9wXgj)=lFY9_C`vh}h8#`Vjj* zNM&aT_WBO&ZQ!z|Nxp@%B&@z3!W!}3m{Wz9p|ahvlCzD5FNDuP&%eKQKIT)itcFwX zf8ku6+UwJ%O^=rPhGf^jgsH4QX&C`hEE}-Heomg@tB>j4&{25UI@M6q)&pd(dS8`6G1NiqwJE^ap*E!3Sr2+ zL>4k-Mmax{Bc+p=2_{RUA$4Pa!LQ&{@kvBOuUY4c1n6OT)SrLL<`^yX974=&lH(;v zj%#V>DA)7&yMRB1`}S)_mZ8tB38psQwDw20ok26_;Q5PQdK#WDNr0X*YBvBIaH$a# zBRp7=VED3#>?INsp}m1TCpE?%-ELrxL9LCr3pB`0uuT5AvRmesC1w0^Q8HQc{y6hV z_oTv*Iy$U5^dQrEu_rm$9E)SFPhvJ2;ktk6SbQD&@UJj|3#0&=r9P-W2w@s z1j84vR^7hJz5xhKluc73=Xn622cLPWGI^;h++)w{jz0E7wwEqek zS0GhSa)PX#WW_li^V=d@o587)pexa^MoAFC@J=Tmb)b)5X zzEsx}&G>3vPd4KlbUoFKuhDgUUi=DOC+5XB>Uyvl|E#Vv&G=Qi9&V=b&nYoEFaCL5 z_s)xdLD%Vd@h|GSZ(e+}t_SADx9ECsUi_=N&diH%bv?{S&w0Vod2eYiRp#M&cWf!` zBk7j9TS-p)yx*Tob<_?Nlq+vsS5uh>2aU^3nWi1u_}I^L|K zee!2X?p5-}(Xaj1olm^)Pxt@M;3)jDF(}zy8nP`|i}=zLyv$N_1}1 zw_)s8nQKPhI{9D6r=Pj^fn$M{>AGd+Rpf@z?_t$nWlw!-OX;W{T;27_xeYk10+)7u zaaIFT8ia9DWiEWq1{_q8mz<#i2lQa6d*K~ze7uv~2uWLi8Dcp|!kb&0(Pes)BMZme zxyfCj>Fi`|VAk|(0Nc5(8%Uh2*&-)IgmydkTd*8YPGW@d!hGJ7%mAaUZiqf?8HIJS z$F&Tg4>Ua}^9xu5(8eSNvgW0m&AiOFY171#_J+LK%0`Xukr+U)%z-PPPm8P#mbF2* zk|f==&5EZcEfE%MxDj!;_1L zKYi(npTLtXsdoX0j7*fQ%Ng~ZA_EF2(vS*2=5HBQRJ9JBCU(w76`r&7p7=b2l;e_D zJZ*0!$8*xR7cjUYW4ArYGd>WSt_;X}V%dq4aXe0%Q3j@iOb!OF6_uo zGFz$HR$DqUJGdxw5{6Q176=y>n9uqRcf{e;#aFCIdy+qEjy#{@S?*WU?uH;D|c|JOx+z+#dWAas}$$sWDWz9f|(O~V8LO>wKMm(~0Qc!0T zFt)1qy+Zk(sv!}vjwUdjl9zg~0)-pG0_SzvIM?A0A}J%NG);IIt|WR5g7!#)KC+N2%KuD*o-?HQczdvrv4~I^FueP8g_<4E)fgsA zc1UMK1G#B^F&`R*!w9-qc9836q-R(t6ZR&%L*p2T*I0LiS_hh%DCTmE+KvLzKb0X~ zY8qBg0^@A|vIfnH0hGpE+cu(>#f;jZvYaf0hV@7}IEkF7c0Vb3k*mp#QKPD*;56_sdo#*tjB zb%zyra*=m9+e=IxH62-qjfqe+#i5F`#y2rQWgv=xpMS(quq2t}BEx~^PQiS|pfxpU z`EWJG?ecMIiq+*KBxatE5Tjx~P^Ok45&+?R33{5V^;%4&L6MVYCr#rMqmJn;U zMWB~a1@ZLu%6qOb4!8nDJ=)t!FtrbF!PQn?loiUKV3jDVkxUQoTa-8Bf5SxaZ)wXSln8_fvw?s$~> zJ`4+4UwH2TR_-@+c)q6ZWY3n;LD)(17^=N7u@3t>iXZz5w=?(9Qy&>S-HUSqlFV@b zVs~Y6|5w~!>0aH0A(wL)%(>Zjf?p_sJP_azC7+Z0s-C~z+cE4M32{^j2r z5L}B$HC_}r;3N@rh=jqk7*p>mV=1h6Wq7a)Fgs?HgQiZhjLLj9IYpxe76d6AUliY) zoD_(pW-7uzbP5s1$ zCAtt6MsQRKuZ=h84a#`OA0LWe$n|2bqx`)DWIf)(#fd@hf}2+V=rZo}GUyE!CcMGI zgf}RDg{*;j%A5Nzjb#($V|_8)p}TnFo>#Es5Yh`5$ecIiE^rqlLzwdzy3m{R&Nb$g z1nK7JItb3Wo1?3_^lXlEf+i_x8y1R7nnUUB?yW z0X2{O1}ur|H0Hio`P^^ddW>tr^S z<6lNa-82UM9l;zOztJo0Ytb^7N?6SQ!vIYhN1l>Sj9Vs;s}ocy#)Xl9VA@^*QS#j| z3&0>NN`7amk(#F#NpfQl0+ypaf41xX-;0|}X$0UKRGs3xlv^8GBp5i^TcS;52U;hnRhf?d24EjHg9*F^*p+mTu zF~8zfna2c|w*_!}0!ubWPxrYLm&)eoR38}zA9z=qmfky$!QbuEGcD5Nqy;wm?HgY~R@aTkCY&Dp$ymIeC(NXZ(`BM(h@9LNZ~ z36F4I3al=&;A@p+f~;KjFhR1CjFXj#9|+tH3W1)-)nRzbV_YS0AJcUo5u8-f%tLX7 zA>(PG$}<2ox(_oY5&Cfpz|On7xBs;6IzBW^wOYame)Yl$TmOsXFrQ?X2gPdWalA}J zbdX&Nq|{RX!D<`Z!)aQVNmTVPuDC4+vcx$#WJB%=R6+jmi^xtsk z9uYDDK;M3uyv$4iOP=H|VwlW-3d5ESVdxgLK5e73w_hJ&`C>oF&h@VC{k`$a^q#4E)OW9YS+?REZO=fBUv#2& z)H+H_TMvgs6=+#QyD>Fz!BWf;M$Oo9@G`m zm#22!xHO$(?O4Wib*2PSOl>lku5S~~kmq@BF?uxKB<>VSK;)gov;q-K%*s)~JbZaj zoTfCzT$W2WXihoUn>d#PyvDtdv@48(T=WL(EzxT!Wia0i-ek$f(TT>R5~T1JM}z3bdp4*owf8U;oEu^3fg%r`vqy07VA~ zG6X+W7lJ3pu;)eYwYlN9;+7#7RozSJtGD9YP)bHK%cf*4Q4Lv7!+QYfhYU6LH~b1~ zo3;J&YNi&6m^tvDKAP#>BhUt@XeBw?7|WA~JIR?W$@@*0B&R+CI*2&vokVPL{E*MF z#Vm;!=R}rsp)Ug?nzWoWXkV(V4T}8WqPeuUHb~2`(i^1bTcKa}ZONMSMt?T~gygBk zMXFV#5G#FWbOGPl#XwZyK!dhdh8IWs8iYbe3h1sRQ7@$9VyU?oPhIkwB`MY_8`t4^ z>`GS+d!dj$3^4|mUA)Bd*#n5jOjIvyVVKd7m;vol~RByESvYn^j;Q9 zO1MkZwz32vqKqqmEoOm%>oG5X9KBpHkVQu+W@Q-ZHsTWvYe5^EG@2VhM)7KNw%W~Y z+QtXb19qVVq6^YG^fqqWJ@$dsNe$6x&&^kcYtO}ZlMCmpFt_L%sS8LH7I)5cE*K47 zrYaqzb?Wxl4yEB+@KOL2BD#8q*c?UXv| z$38@TCwtpJuX<1Pwr|k&cyId}`j6|SxBZIzLjVY#)Z#-ox@+Fj{)Jf|k|WpTq^v#e z75b1QCHE@#b2r@UXq>y6R=#=klHa5!7rv$aiqW?n_|1pLZ~x|97reRsDt2OE8M>PK z3oiXdUf?6{#{7k=-3@PPU(@}U5W8<=xM|W%Tg3zj5R*{`Je> z{Ey(xR`*fWZB^11>ibFG7WebBi>lW2WN#l>YODjJq?Z_4Ih<<|1$o$o!BTOo^%|~y z4K!M112>U74@!nS>Y1xvdf=eSN>qP4T_o;HGd;RI>Fa_NC6ib;j(B!zJ09 z{%2z)nNf(4jn$0Z9@lL)R);O;L|4vH%Q>0n{1*!PRa89=ott=!s=(w54?^rKmw^z} z0QrFPQ1SdFQ!2cspAV|ph zLU96U2}lOQX^f~b&KFaaaK7vVtYxFTTi4Z_k=VH+FryrpY9&}z=X?3&{(jM4uN#5@ za=v7m;pI)_NU`+&{d7UBlk`3=Zoor0NoxnD5-@ zlJ!nX9iToOol5)UbRqR*xzo=RXd9U34Hdl8g#u6ap&u%jxB3J>8yIpJi8)<}eE@bc zr%PXNasZP?x6j3^fSs|5^07+2Q8HH@7!y++@&Pi{AsZQbXx4A$mZ8Bih+N~HE_1&o zD`D<)Nsb?(DVvf>LMB=}rDr5}_m7z=jO6~5v{6<+X~D?0U7MYrd3Ozk3eS@Bb(in10YC3Y z!m?HWgwfFop{055fD5fw1iKYb&^M3%Yu?Q3#1c$G0XS&hdgNjz+O?9(Yn1)7}0XyQk2O1L_l>XL*6IK z=NWUnPc83#YBmwd=qjr0YT1UHZgmcrKrd?gXqTUS1^98tR8X}uy+{$XS7XQ!1 zO~cjPG_8%YtrF_kN*v5EQ`BPInPjY|3~yO~bos&UM2)K7*TP3C0%i z1H-w)oHol%)9AQqu+6*xOwb(QrlBKO(*iEnRos$ZeaxM!u`uPyFc9R3#`2+H)$b_s zQ%K98P7iXm@M%b`kQdqA>st6HNSd+*X`*rs35~iAyYj_aRzE0maHU)c7}NFgpj-*4 zhe5`w`ugQC^y-0!VPs21*rYwOVTfhrnlc*96~*#{+fets=vC!~XD|Ziu|;dOgV9&pkO6 z94x@CL=Bd|$zvn-Y|fgtv)F9D7qiOW95$DbHCX~Znc3!O5QkjZuO*Ig)XXf(+^Wl4`1`=1DoRHzq!Hlk)6CHecmp zpHpNnLu&8Hfdu-yS_<~qx0HnXYMFJ`ygxiH7}gD9TlUD z==z+aT#~nhRpJJOvjzwq-T^q zA@OA2pxE4D;&SqBYZ*Pk3rjZk4GQ;af+?G45DU=in55Y{m3^T#ipU0HIHW=Jd;pVa zRKjvHKaa$Wl0UW#9|h0UF~lQ585>{l4%W45yjhqqVabs|9*ucWo~=;)0nA@P7{_YV z#DG`=UFaE0VX2Y6Qut`1c~Zz4I_e5T%7pF2tlZ{waBf*Y8x&kSstO zQ6yHwkW8Pz!DqT`5xvO3lCj!c=HhM>^V|ip{H1~0>J5SG{bhr{L<;!NlgIL8W&n-! zKvSdZK7ubspZL%N-+TP<;~%>OYmV-px%+P>KK`D6c1~}y_n1Sv(E?6w z#~i`VKFdI_jc81Oo8Q8({S9KYWitze)g74MUY6V+TnlEcrd%Xr64EO`!bdj*e=$D^ zTIUP4HUuAnd>uEKQ&2w|NtWJ6`e>G(CVeq0!FP=uxneVShN#1yd;3i4Hc8gwH2Eq3Z z_P22@^(N!%4Ab=0i2<6zcjcSNEA{=)zq>4}5bCZMVV#D6hKRJ2J0aL76=cAEhWiFl zTDk5YBy!LmwaeIO_Ik6@9Br!L@DN@*K7+wwq!T^zEv)$K@^}$*g|o+5*M?=!hZt-5^=$3sha|?L z5<01&oQo*fF2(XZc_}C3r?Ihdg(`r5QSv^aKW`TCyATr4d3y-lLS9OQli3(k1ex#~ zB}L*=p*iIVSVEK4BGSF{#6By8`2-~f6xn|n)L+cC!fU>U>}77Skd@gCBHMl@N>-=| z!`<}eAi-(khJ*tdkz)4pxq~&LYlucMC7lTw5qL%k^ODaBWCzN7Zs)zDY@{>XdE@q! z`_-M&-@veQE{j-w^gW!HdqyTR7evVu-0QRbd3+DpI#Oz3+Z2C~HUn|tpY&AA31w&}BZtWl{k&bJbogPfhV4_V9G3Ny^rxr9j8jTtT zZYlm1jfVEwi$oYQ3WntargFMj9PUJXd+qAKLTZZH;^hN%m zW7@#YPcCUhC4L#XqSRqZX>lrRSCuC-IGdGPv5CUlz!F)26GC>BO|dLpmJsFBv3B~qwDb|+0tXar~O53OfM&En))<5nrsv^tDOcHjTpBa8kZANmSB^W6D(Q`j#{(w zO4Nv>G-xtwgs3br8`ijeq)*h@0FN_TqP{+}9jpR!Wg}Q6mQ2I6+pikS*2Jy%MSTN# zX1Yr1KrvM%t)lTNzdunz`}&u3#Dwk|}NR7R1VwR0ca+S`_WoVyUYRoQlZY~V+(AU$wvfb?RY zzeV?&CcjduxdJ}4Ea?tAgaB%y`2n)e=C8A#aj$!&)m0@|$0J248y{=m|HqIq%66&tYe+m^z{RLahxtIJwqVrJ4q8NRYxJA*_b2gc+}~TKh|fm zE9W?6Y%fYfFn^-YbmzJ19p|`)lp}p)`07FzY>FLBP`P8+<91LC*LSTL!c84_fNvy) zhAHI6+{*4DJwU!cT!LZR91zB;j_iXDx7_R9%h{Uu)?2iot#-5f>0~j-2{qgs+rXQ^mg+a23XcL+UjdIIf)lVlALYx zI}1%@uSnhRYBP)Q3*2B>FhnNU%$#Tf#V=Okx)s8Y@He%~swa;8Om#K7H3; zMwj2*u_CYsw01Q@8GrPV`)3_7_AvLEaKtepXi3F=g%|FBWNO>nR$RmeJ)*l^=5=

};SAP|;FpFidTcWD?8`L?Z18KUi9@=j7jnY)n)tOiD5>u?GS@!_pDOk?haM zKlU!O&eG8yXr@Mk^+efdXdS5rqndp1E8qRhZI`zjs1++rV5M^_$^TB#a&G9!9iQ*& z8TOLjPP;P}5E#?Bx4k;x^DB%jYulj5+4@np$17gqtF8%>I(z=wt`TkHuSgZQg6C`! ztJ7Jy*6oxQmjAALQ{2z&)}b8im+H@EsbiVY$t>FD$e_^TxT=yE>|_=K6DVxb91s~APtZe#7gmQ8m=ZtHx z89_GVW*MaE^<-H#xF!JlkibBLjeAZ;^#VdzJRcDYZ?pvlCLGk>k&~rPBrcn$E88FM zW2SO3a+;rJWwQA)*r^t{J~dPJ>(D3?!I|OH04S|O3v$%hdJ2Lj_3&jpv8BWEdTqsuLY!UvS=>_L6Wp!^@{=Sb-~LYb2BUW+yswute{>9jmK zE)65rpFw8}qpbACzkVSIGE`Ex@1R7#W+*ay|Ygo6Mu!%JP{MND7 zKhjIz0L=y+7>eQMKKapq^2Jx@+THsAqbgz(OtL zT4tdIp>2X-cBaedKz6ppoCQZ0po`SK-bcsI@rUqKQ~Ib8-4K8%m*cGg(?&{ga4^Nj zzNWUx%^*!i>WH@oLld(3Sjs$jrB`#J;$v~mTtcS&uuz7|i5`Yh!Mj{_V{pCnG$R64 zHU|7jH6GnnJFR42VBvQiu9i1SkMl-lB|vNfBs>Q~Lj&fr8W~chL|R1jSiQCel4STP z$&91k+HIkkcCQtFri*mPHo1mTl2)_<5EU3UjI-$>M4pQNZ(#$LDUS}Pw2*@Hx;ohK z4_(-R>6N7s-Po|wo78?(Y)DI6si;7Q{PSSLKm4HBFw}(&%Y_Xq+z|g)fDK#?8;1DS zO&mEglsxp_4_Ac^X}l)d;g;vj`2N^H z+s=p$*?i2!hH3QV=fZ|`Hah&@#Riw(b)kU& z{*GOIAJ~NQ+<0BV5H@*+z`Ch3F@)1lV5OnJ%2_C|mj$QfC+|A+u5f3nGy7JYjsg^V zZWLI_!BRg46i5q>aAC#~hNDfMArN^{Foae&L!fPEM1gEVx=~;&3}G)8n$u9=)fq!b zjU&9Wiz7s@c`k-vjmj8;H7fE<>G?8*@p=aZq>iUB0dWA-YlHt0^0Gs0uW~)Nw6BLH zmc&jT3|VVaCCq`lZGNjb9LbVD6O%_xhwGKTYljk;b!BD8aAF}vnPCN+i-}<1k~SvS z2gG!mNEp+Y$Ph7Dgy+0cSo4Ax0D^r7enkWT0zN;wLb%Fp>|KyB;K?t{1E}p0@(JTu zybUj<{H*19y4#gzTfya3a;3_I07PVORaC=8fB>s)MSU5;~=13KE*6k5wk4_?W zvTNO*^n~tpd*b@t>-IS7=Cp--r~(rCY?+%W0A+ut?Ru*y~QGIr`ln)J?BN8PCvb%x1`iMhzrmi$$WnhyenYj=MUa7sXR*KB;powO0Tz zOcu0~UpLnJ4CD+zs&5F!!+cF&YGl>Voem*;oxGd2l&P*QEZc-0nZwf-G}X29V_WC0 z_)<<4kCW9dvItPDo7-kZk%hWgmkp@BrpQ8itm|y&_F4H#cJ$`WYMT_ki+BhL-^xX# zxXu~^W>^qcH=17e>*3S}x;#>HTS`|ZTaADZA=2kPhT2<~yE+UtMrhtD8jjx1KJ~4X zMSWylWSfH9!?>as7W)-_)V`R5Gh}7NeJFpw0)n%3e2ByxLB1uD1Y=kj#Z|r!syU%h zzI+b`GR&clRlgLX?RNXMdqcK%k3&thcyECg?>Uz5E&NHANi5LfJ$|m&+C8d^SzT>k zx|(L6%DAJ{Z;gPN*Thnb3IpAsI~>S(@i(DjhW|KwW6=%-NUI3us~#?b6Ug4 zU|gxs5r~Fv!R6}SxJ`8|Ktt1$j?v<>{9f1g5l$sRozpB4w>+dJd_#(BswG=@wQK$m z#{}90?*_spk`o`=wWM#pnPs}x^i^5YH}zllWJjLYw34q2MO488e^h-QzG++^OPKKi zX>Dp3EBVB+D#(?c3G@1o%+;vLXP8VVh7Q8;Sb)n^Gp5)(8)qqjcI1!-U0SA{IOJ~t z`W)QNI+6pr`sU8cDv+87s?Z-+-Dx;IPt1T#3bh9&srfV^E#OWEx?(Sn9aq0kR=iMW z(>6Wq(@ji_!j7im0)-qNTRL%gptp3Z*c z9<^eN`5{Q(p7Z`w9+Fp74c#L?*pouyYM2TUImOhNNvM{aghor+U$mI)n%VLf?siV| ztqDfpw%rR*|KP;O1p%h%F($IZYzVNe$Tpv{scZ6&m4QfAX&cHw~(AJ9aL z9cI=!Q@_L@fPW1hDQU%=TAcX+@B!7Z=SoJv*oB47TjVo&qL(-$Pk?*WCV#*eO+3nm zC>R}+zSjSjTasj-#|g!+KOo63?#nk&~PJ+`KjQfbZ*$u{XK;G_jR*UbV7kpWbznf&%;{CE0SeMjd-{2YZV)+J7B0A}{ zJ1hDAw-cE#MLkGYuBY|6Y)rACF)iM)Wj?OdYyoQo7VKGJwt%yA^CEQ-7+H~-03?x_ z0_3Zd$)PZHVRylLE9852GncX9S$h5FHND3PNoZGWUo;53IKp^jR8sC$nLC2(<+Qf# zTB^xA*l-zgZyC@e*$(Oq_V96N1zR;D!q*Rkfi;Tj; z20Q*CENs+;!9-_6)$xUM@CFtJamGv~!@BVK6kHH*qwB);Zd02C;bJ2n;xR=wmtWOf zkEu;(FQWKiTsWJPoR+(@(X6-^DmxWW%iF3Rhq3(1UuwX<|BV|=f z;IITm6V3#!s^U&$_pQVZHABMI3ZHs5(M`!G5>kqLCSIaYanHPq*cbQ4gk9&JS(jv3 zJZI9SFc$Yrc`B}*Wk|?npi!YW!Krqz@qT8+WAY-9q1ztU)g9T`k(h)R9G8vDO@n4@ zMa^`t?YtqQo-F6u2r~ncVf0R!A49?A&X5mxn<1Z$1oswpVe$3?ixG#s4NoG!LYED1 z5mJn{+p9W5w)5&$omVaBBZyxvnI?$a_NBBEU&6`LrQ)b}$hVZUj7}gXwX({hf12sktJJVH3w@Eb)yFy;rZ#ql5EzxG>dlb zG!LLV8-c}h9D$q3FP_;5+U(fVHWD|qN%99PmA3?9pUji<1d1@eg+9fuK^AXQmnB|3 z$g7nhMq|LHhZ!UDl6^J^zJ4$LXQQC~bVUy1JcK71DL!*v@#z<8cd0<*MewPkxJH7R z+rZ&_20Q=2QVZ_BWXBR91aNK^^V)R`p-#>s$FnSb5)KQZz_6-$>?gi+%Xw#44&hOh zxw9+hcP;0=$t9XC7;eL6*}Uc_t}`8d}7j{%&ziYI`(oruIrEy$rPZx)2iXwIV)~R-FS5kcw9u^!Qkbf4J&~Otxc!QVTLT z?h+QqRhsrV2ZpTHbM8^cljCgDv{6^eU!mvoFdK56=^Kl8>Y8F2dohXWUcV~jEO+tc zpy#=l-vYGuc%%oMd&CQ*Gi0#TBfPA2ctKAxtowo&^hiAsP8a3j1vxpf`rrk^HQ}G| zMY|4=QSbtqjNfm-oyF+ehO95fuRreN`*=opC! zC~ZKk^e^I8Vol>jZ{$?q7hskN}>crDWdPp;Xp z>p*Rc7+Q3-!mSm=UH~Ia*oLg|)tyJi!g~mo2CF4wNQqQbl$?4SsH)7a1*F_oGS>(3Ut%8t{ zZB*!@j0zD>G@*DFku*DZZjD(%XK*@*u0TWjJPirrvpQ5mL<6x+M5D5c>R;ck>27iR z99`zOdpyw?1d|3K5QCmD)I=Btlh6Oj=OYn@!DPI2Qwqo$Wf)BUj(npGoFc#&4G@-CqXrk!bCTK*(oh=%{{W)z-F%t)ZGl2ubZ(=dGb7fD# zSk|p^?$;s^v*G?Z3O-*824#iUJx+U{P zVBFar4x5_tx;x8*-B~=$d;}e^lXJL$MkAVQ(%EqL&d)q6+#!S;+_l}yRS7U-a7VVm z-6a{^v8%hWyOr)L{0;@&k!^4{;^8iNBr~_C)A6%Hnx?WF&i2-RAUON0uYDu9ic?4j zlVkOpJecX`ugUI!8M{Kwg)^AW^MNz$fba}4mlMl8Y#oNF2(}I`AB#u0var)&@G#E> zg9o`^#C0F{E4gYfK{hAD2Dq4oY5-#fi|cY&gl*T80|N?WWM!yaZA%fX*v_*qTF)98 zTd!evTAeYmRAE3_IhE3xsiHsj;#!_%fVD_ay3(N39}BxkV7jscrua^DUx4W%w=x58 zPCU$0Dr1}1d-^RX=e_`SVFC9ADqO^pj>pBMz#C}NQNPbP^6v${KWZ;PPVgAwO&5^k zyy^yW#H=nLFC1YW$fde(qMuzrJ{@-FeTlOHCSO9$Akdayme#iG$%7v3N+P>t?n^r%$yQ`aK1I?t=qv z`x74;|GnS($|o=IN*W8=M?du0e|gV${^sug_#ObD7xt0&hsVD4=mXh$J>^ZOWIp`1 z_kHc#pSkllAI=J5FdO~)SD!ld$JshQ6~q%Y`lly9_tc;N+o8X{$k&G_YV;$Y{*woL?s>;($i#oV1LB;<9emGCaZ2XR@$tHIzdPLYi>@oCiF@MmzHVaB!%01ai3kW$m{HKkK)&D+YubK14I}zz7D?%}ECVY~bk5K)sbm^l zq)Z>v&DaYa1qvemZSAM1f&2$g8c01FYTE_YVRPP-uG3MRVxSV4h@GyJk(L{aKxnzK z7My53FiUGfmMU>Q8OGaK!ru~yjvJ)rTdGDM<{->F_Wk#6epn#PqksIJuXH0!I)@>C ze~kG75T@t*BTH(`@VT)?76T8R9Ijo9pd=;!a%7PE#t>u+<8E`1<59a&VA9F3%_f&u z$X)EVWN33j^F{PYjyLrJFI3i6WTSxvPqGN-ai&Y@FO&X)()}!(w{2!vTk8W08n_rh zu9Iv6t|iTZTn^B&6UC+a`8KYO zYs@E+{5#1TeDYsOUcDtC$fR!DcU|hU|AFiaee$y;?Ocj_@+p#|KKB8VTYS=vnDO7h z?P0P>J;4=XzL&b(BhD9$vjxy~PSwoZBP@F1q>z9^XX< z2|7qZXP)LKi)q#<2pUdwK?8X1q2cLS#e8iyoB?Xy`L`ZwFh>%mj|_8^m2H(ZfhZ4j z2i*x@gH|rWEZU2ZW17p_4clF;5L~9N|H$V!&&rdjM{JucEj$MOp0pd1n6Gg3$ZVVe zpiCUfC?HZN!ziPWiP_)EG78oTvRFpJTG>@gyLy@xqQ^k8lANqg?fB?|6(_1PKQ`n5 z7x$10js-9o-h#qz3ckvwJT?rI6}58e)gSE*ADsHdj~?T9$N&0#T1$>&0+h{98U^?Gt20jVaY}2&&f+jE;5Hs zo*}Ac-qSoo{I!CzPcOUpDuOIr@8&kbLV-xRRHyJpa%9(uTIYP8zKy%oDt*gU7DoC$2{QdqgJ@xkw zZl=ZaKK{m?f+gQpYqo%d{u=BZED83s+k&SKAZJ6tslrnZ?;%>uLywp7rjC64b15Px zP6dtIkGpHnt7oXfQ|=Q>!OrcU5RT42tlzI5eh5s(^n`gAj8W7*S7EF$_aH6@f93^q zjemg+={U$QIUadD>&)hg0xA7Ws{0;gHGXoNWGYgDwn9W-BK1X1Qslx}m|ZT4h*AtD zcbCU-g3KxcItmPF+Mtw9kJ7$bnytCb(lG)ZNh|OR!O|vlkb|{X_ zI4h^V@~Pz=GEpG*W1q-`7b6p_X)i&e3e@1s9yOjU@LJU5S&KbAtJrDOq#zUTob}by z%H`DLuIE>5|Eyxq+PFiXzz$(H7VGP?e)Y(#>U12}B(%y~Y?utfO-$^2=O^>p5_Zpk z$aiDDWpwe_gO+h!7p*&BIo;gMGP=2$Wpr~h%jhC?t*J+jh}5+ndgG#X0E!1AQK=58 zyX&DPooVmPXVFJ%ygcuPfH1>n3lzvIakZ)OkIkRD_cLpIHj1Ne2>zx%^{!9<%Cipp&m3L$|5CnTsBfS8&e8P% z$J&SAxNd|AI0IYInYHSRSc8ZJhp|lk?Za{SiK!D0pLJGzXxkz2$G8pF|! zSi9yek#fw-OQyDec6BQD(U2I6s$|DfG_EP{DD9AI@c{Nv1;n%YkO*{?A4}121RSfZ zKbC?mfK1}@$5L=oh6FTQf^+Lwekw(oFVs{Ky9jGBkey1g62v=a?y(eQJC=eik5#tE z0ti;xu@tmvYT$EA_Oyb=m%V&IAAGUxkDVxZ#EEL#L^o`+JT2dfw-DgR zz~YTFh6pF2)h?X6{d0@cjP_RiOOb}g)nfV#mqnT1)gH7)G4yE0=43Vry9)__Bq@Dq zCX{!?^jT8+)D@3x>4-lCLA%Qc+SKnovav%jU`d|Z?Jua^b2ET~t~_V4cjo5XZ0GD^ zr)+;;OZ-;aQ&)X{ZMqQ)l$1aQ%PX7x35psj9C9gy)VkK2@iZCrF|vPJR9JE6je+QQBRm!BX-^aO(kDX3o}9@+EHT z9GR+H0je}QHZ(CelIzETgih;A#{kiW;10H(M4dSRvo5S^W$ouay65h{ncS0`q00&Z z+M2nUzI4jFnabL;e~ulR?Wf3_ZHF#TCI2`C8JgkAEWZ-_nUOWy@LbmBPu`Y>=R!f1 zQja<|(n8l^4OIgd6mPe5%$umi)} z{lQLa;t3QEn*+Qc{gRlh%59|%Hjz&<_y*ehED|#V_g}~(lexg z&1WAZ`39fV21dW+llw@%&L^iy-sqEiNnWQU#=l9@SJJ*NL^~Xfjqbhs(c@3;`L}of zh52UyPx3x==#Kjj9{kdG|K2DjLr30hhYuYiZw{i#^4I0?k*QCJA+$@o{JB6Yd2{jZ z7sxV|HryoHqN|p4zRsWEW=J z9-IpRvq&pTdl+NBHBkX7MuQ~uNWuzs_PZsDA=~Y1T1mf^*j^=@CW8=Mf(~lQ2_@}x z-qN^|Ez3PcH?lxFf*=gpCRo6ALPxamQIW0)Sq%6vz6Mb3E5W=VW7js%RtcKLuLl?_ z4Zr{;6r5mF7fF%Sf(A_)43}EiW8YPeu&z;g7x_PC8C`VgsAY7K|HGCc^6!0kAgSea z(SOOca**Ypk#!bqIzXR^hgax^cgIJw$&<*8dVYY=6|dVF#q!ksv0U2mJ=N5wK)>D* zP9|V1AJGTC82Rbh04mxzUE4g1=D5q73{_q)=9A<}s!X2F>m@|6i?lap-Pxpw@yu#k zTFN-jp}dYWm&=PCDT<-5mylFx`@s@nrd$t{Vuy#E)Zvn%0qU}=YrQK*b=J9hi#h^D zg$MvF&=Z(8BkiE1Ay~xR0~*?o9pb^!l6E2(i5@B8(&YMB z3D+h0kC);#x}GQzJ4pVMrFe_3Pib>c@}Djd(9S}I_m&iSWyi4+ag|JFn-Zzh>=1%9av}~})=^qH!MVPlv zhVffXu;ZClsShAzQm6&F;gl|Vn+38hQSPj5!4_!)TZLJ&AE6tt#oWFlC3#rjjPED* z3qnz8!t5YqHN{(3ldhew{etcrhzUyW`&O6Z6-R8>VGrhr?KG6Kj7ZyE5u5q`@`MvO#v8)$?o_6So8g2Q>FKR~<(5 z96;bk=ZHqf`KP`0Aa#V?vX&v_9EWOBQ51Zs%{ec9Dh3qrIb=$vBlHW&S&|xYZPMj|f>X+j> zGze!mZs2pW#P|KmAVD*&h;)h$7>G%rjvB=p$?6(`1sAfR?LW z`%hQ-lr$QyiX5`N8?^R{;55G<;nabjSdcD5MAv$GQOx0JcSuRD6Jf=%%~$1C$H~7{ zNDe(Wx4kGXqt5u*k>XOdcr41c#VTUwdt>9uSB!5@F@RPpEV@k;A9FY$bHYF~}Bhj6R6duSVO)nz5V;67)-%1DzbT z?rFHQ7CNh6(^bEMjKjt%BHic`6;H~atQd(;)yz>nn5-IkEbXnvkVkX*l#&NKpI6m~ zsBXruunxXOK*Wr2f#qjvC8fiTpy{j^poqc zs@9RRS8X+JYm|;xt!_=ezAHI8+Eca0^bT&HU> zOHX0RwfK5n*-;AS#|ATZ)(9vkF5mj5MzdhrL`?!Pu2XtYxyyu_n7>Hs~}D6m|H*CA;Y9vJejJDm(l#axkRKn#lQuZx z1&SiK)dNAs*BUa92@vRS5(`Xz2g(R2_yBro4<8_d>T*s}PX2%L-UMFGqRRX4dY-;( z_r2*PH_5$O$kUw=5=bOy7Dzzu6Io>u7<6z!qvMR4@H!+QE+Bnb)Sw_iz$b%I;us(f zTiCoH0YV585H*fCqT>>j!2luwL4&fq-`}a{>F&D(ar8g$d_I5j;XYmU)Khip)TvXa zPMve=YRj1DGZ4@6@lBR9wO-_Q%a~cuxW_W))HBcnQ0soR4DscQwJ~b5bxIp<0bJyE zZwda|0;dLN>;_4UN_Xy69lKR})07_CmBnmmDm`gtjObe#nI({oE8`l9->TxPw8zlK zsdSqZN!r?&x`E_!f1M|T%|?olLhV^a$dhbiv+WVGCOcFU`^EvdXRvtRq`!L7%^}!Q zp;T9L0%6>d1^j0%{*hL~(wTmWpIQ0~g?0L?UI|(jYOy}E(N5HvM->`DW182AbN2I+ zpK78kCX)y4$Jp<$rZ~Uo{k*`GI&q?(R-4BG0g@W;BZ2_^enzo0bWRgV%NeFiOltR* z68t~{Wc8BkzUVMj)M;Df^)>A3YCdKlnUvo?s|Pzsjmvs1j|ALhGAA9>rD0WqPES@q zz^$Ryt;Aaa)Rs7W%Y-s5TaaE+)ZZLM^#9M|=r2Q&@MEN9#N>%CB?=pM5o|gL;R<9p zkFktKY>zw)ofw(0hfZrsi4FYHRBd7j*)gFfz&5@}C%EuR`&Pvp!yKOGOwW(Sn|6Xk^Whu^SbvMfrr`#(_Tg4xwbO-*OT#LY zO>*Q0tuic(gOt$ci~wn>tWzBJvCuuYU& z9-3~5S2s^(^1D>g@V_21nJKk`t0*|KPGllsEvNCPY$|~1oX*?KN!Eb&B}cpbF z0TSj&9Sr73efBU%YV%6%oxl+Sq3k=Dj_#O+Ij+c6Zuk5gHzCJb#b@P?^O9hfW|7x$ znq)XpX|Dpc^i-w2j?~gKmG(+hOV3fdcd0sSY0g5`Ax_ymKDi2cLGB>hM`%=7ALJRy zq4yYT7@LVx258qpHrMv6v@mxth3>OLqtZf((8m2;oJgdrI9Fn1KY#iMt>S0|Y#C=* zS%28c;E<<|iU{j-Q;;(PckT25m$6e&M#ADe0Z zw2}M`dsI1e&Z-h@g^MzL^b;wYqP#B6@SE)M+5lbBY`RMAOQox1OIu!`eGT0;1uD`{ zO2O=+$&&&VxN3QtP6`+VWVO6CY^LuZ6B2oL;#*Hp*lnO}aBSZ{WsF+$kIv(jEFZJ6Ms`SnF7U&<}UOqw_ii%d- z?rguW0y4_-Hw8^+cRernHBcj@Y=pGd==UO4Z#62-X*TD)=F}f-XiTIz&DaF{K@n>P z7%s*}MNE2)N?z(-(}U}+&!^)&lg*PLzN&{6rw^HHXUpaKBT73e=o zg(hjAP$)GwDwK4SlAvhRwL){I-K^`XRtN0+p>Lb2<1|$%V}lG)G*GgLfX)rj=b#LN z{up|N9#R#|MN?pFNKg>jVZ!z%uud91QM=vIG#Dme83I!yJO-<^J`6lQ&R=a}?SO3l z0(gi$)**H~1W(YxAeg=gTBOEf2s1UE6uWY({ ziQ1^=JiBSUTe0g9i@T!((LWI0vO9V`tBsyS%y>x8L*@rWqC)Fyp2XeJdkB%C^bt%a zJqa-N8$HVlay#^-uhZuTtN3YBzZgOdaBU65F0u?2?w%jqtisg|_A<)29+CKnBo$C2 z?&@pxBsd34Vt4db_Pcr#Lx%OUJ35xwRC*FDhIO=CA+ESqibFZnyL!HTegKQ^mY@5J zf^Zu1TTiKq^6?UzqCv z@DW;-OQ%U#=#HOLC#L5{6G4p<&{=6-?iuGPQ=G{JfdQP!3j*7lnEy4_1g^xBRdji_|^pc9v5#^2mXAsRPO5P&72FCEH~#4ovEC0+g%(z9E4 zj;IU5j;7Vdb_oN4g8!`fF$b~J<1IW&g#L?)GlGNQTE*!>F9!;(;yXp#?sswrm>q)i zYHL$;^dab8iaHdv;CQuDiKC^hYprs&!?6Swk7zMgs$3Q<>dwA$3^gpm?DLZOauuS+~0YFN-W4>zf|M{{p<6F!8T zJQU>Ow^Zyp1e4)bqOIC0H&LYtWWfow#QVIJJWBceG85La=mF)E|HJqoRlD&#_ zD#td*m)8cZP>+rW&+m92of%dN{0!bQAr55Rba3i5Q7LHgJXjo#p%xBQ-NEq|Xx75yW>lDqCPqh> znshA@rXv$Mqy)<IMy=5m}CB+5yxpvK=SQ9&gd3o;N)Z`(bj2w=xTgSvbvJ+QJ~m(*xyn9t=wnfX>X zd!1)M-IdPlH_<5-su~$L@DH`-59@8@*|`zz)Ksa+f>qJZ-qlw=m2M!db#R5O zmUjAY6|z#+E5!D?p~eSErBzCH0oNQ-iyd->Py5AZ%F!1qXgTb=z#_|ioIL#!kdEey*+ker992mE2Q)VDk2W>H!)mn@>tFWDz2 zB%fP$aS>|9wNgjoEaN(5(J#ag2`2pNw3X~NRNFhC-dC?P?(p~X7JVE4nM%4@?^u#?CgRmlypJmalQ?R9Z)hH#PnzgNgLZdDPlVCM52}>GI z11$qeWV@y*9zk4mKLwDQ6nmnQ(mr@yViO`w7h(AE-r#gZsYO!Jz9ybA26^gW9Vj=) zxhjL>Nj)meg!suij1CoM;iYllGNkP0c=9q#Fq^Sl6YE*beVBS1@D`a{+2SQ9AC7Sm zl5$);bvd><@u*X=7TS?prlO&WqR2dT`SLhsit&N&lO>dh#(oSjz9qA3mYnC7SaEr|^-8#8oNV z9FsIt-D1pQ<@aMniFMk1F{k5gPe|%Lj9(9aeiOfr;TI3>T;NyRd0T3QRgEZj#*8-4 zxpB@DHiWPQadX5emFCi>07J}TypOhz#*dDSidEG^#bQqf@nBy|t&7gW)@mi@G{;~J z=9Yru5l};X#879&wbomi$7mU_{aWI>|^GWF&N&Ppk7 zl6Dt{r$j>04hx+U=O%O6bI=eY%1YS^!NvO!kW?WX=PH^CGv`&_g|G51|0?gAcvsz3 zgUzqw(TwtU)>HY?%^ z4@{zEOj;BRA)-jsd=$nh;%O{L>zZsoz`Ty{svg-1HR**KVMW{;+?CL%1b(b30)q5zpOZ689)n70Gng4`6XI3BwX_;i|n7~SabS;Hk z$fTu^l~sHgLtZIFG$pv!GwiOMsqP8~KVq+hqYZLosySu#$l`ePb0@a=_ zP))R-U&4Yb+F^t8wHEop>1}m$jz}lA2M~qOi?;l^DfC|FuQvtZKBoa z5CZFrMr$53(ct5n7VR4=pS)XH#wbN%zQ%!p-K5h}q- zPzAKyu$~KN{txu!mtywH2-A!wXs0*rrQPY2y^}99HO1J{!VbZweNtulhT*iArcsU$ zn~>4GHmEHsXk2&*5lB>p21MW!t)wABe7-a4VF_11HTGO?r~Vlb3@B)^aq3LRKI9E! zkI`}bo~qht*Vu;|d(e~>wXqM?!?HfGvB#7l9sADMk9~W6?5W;F9s6c^4I`n@#@Kfn zVhV#0Q$>Jjz>K2&ctmDM0b}3$47QtgdyW3IR#@)Qo=JfmfA3R6UU72)L)xLPlt$PG zVs@5BSInG3%MlS;za5L|qN{36piKwS83%s_xjE(!SQW7nL)Xnt?-6mN^tz}ycyN%Dj!j6&qYQ$v4Y!Om4?#LlP97V?;#<>vp@!_z6!nq69NU;IHoOv-p|nxWmxLt9PPz2jWU zpzDsq0vxO;;r$w|S_Hsi;YGhz)FC=QrKk}D;{VzTTZ~&4IvwT6r{MrYB9(?Js;J-xtP!qi0C(U8KKI767-$r@;^PjawwAk~3;~FMg93OPb2VJX{wBWh z`mg_Ic#0&^5&pV%yw~U|tdc_lbrxLBosKz(l z`~9uyH>G70XjIELSg-}I)6fgY5kRrT(yu+T=I6m76STVIw6X~E7L^+ui>xGq;{KvO zJI%;QD{M+~Mme`OY0crRMMQ*5ZnTI-xEQZTJ}Sy+LmbGY5St}@fc+jdxvNaT=wMHu z`#O_qR_z%kbL_D-cO1(kwXAsxrqWcpC+I-4-pQra zA!4=hc$8LNHfBs%N#eDX1p69lbj>8D%ez8Ms`b>UL${_8=_7`>OC*Or(j8`P5|G=) z|C-U_qinSJ!--m($+B~!_NvW&69n?y6Ge6&#tu3H?awNT>mPjl2&BZ8nwEjUuI$83?t0Z!tZJyD!CT0tR6Ke z`TarWfOJ_~q!DW3I`DY6jFh`6KXys{!0&5_(+O0(+0t0uL(V#bi8nMBxVe@{F$=_4 z1jN_?*w#UqwNwLUyX(W6I!j0&qERgZK?cq^TI*aAZ}SaQYt8?p)*=P|y_TpqKaK{+ z4rrcKL-W#%rc^bkdDF@_iJIQf2*ut@;uor*I-S!67U1|<(lr%dv?P9_F^w_sFB{Wf z@h6RGkofP7=?7|wfxUP`y%&n@LGrbB!vu+V{WAa=csU|omnE2aMy19CI!he6iWVVC z&B>VC2%tkIcSe$899(SnTu_FDM6=~q|3z;~#RqBD@lLV}p8{xl2HPbe1RXoK6cysp zslJ_B3UhNK@LWTIC3OMzODy72{o>cfHoQ=tt49kf43CiM&hz;6 z8ZDi9kpN0;JPcSW<0B6vEGgqF5Rya}rh^E75C+ZGvNK*cfV-#;dPlW3z4fbF>z-eT z(8$+lL|qY$FpB@5kw|qo8X?%Vh@xSl4J47VyP|YmS=o+sYbe#R0wxsQFBO)ZN0VIyc3kauFmFMOQ%;W3Pgca#vV(@SqTK7f* z4>abwEceUgR%?Y7GR!Or`o-$ADw?v~&o<_Atl-Q3OzS{awk6A5-{ngAfAt2r@NZ72BS3uw|(d#CoY zLk>yIevR{zmqmJq)rHPpit^5Ox3&z}!HK54HniI5N~y|U_LpNd70j}!X#1~?NaX2$ zn$>z8hqWNkY=#0RWW0Wd&ZCvJ6voU_Dv?sxw%r@fT>vnUY2zbA69!M7EezWZ2;;fMKjKRei+_xKSeO z^02*N_NKkio<-d_mvgk(V==58hZd+A0-;_g8xrb&_P@Z zy0{nOz2pT3pKjFFf8PsgmGURszuCOm{-h890mt~^EKXl4$O$KlqW|=O65WUI3&X4# zBm<{TFMBb)AzWZ4&&C1GB4xFY;$l^Xs5H`HLuM*y*_7x4*EF&D=DDh=!tNh<jj?lQ+2}@nwt{38j)Gz+V z(IYH4W!<`WO{!X4g&m8H#%k=S&`ES-;u}PBWxc^l;+JhEvg&TPx;16Y`r0&D=1E&5 zL#iXNM>OOcxw_>{P&sS0xWv5xj6veBkU{7P=RTgz=r!;A_iXp9KTSCv~!IZX#5 zN;GL%^y6Z~idt8k05Oyk3K=?~{l`32iZsqP?1;3|jt(fZPjy<}%w}2+5#mtZG_u{3 zE%sT`)zCW>Kdf4Y90tFj#wgr<65=c1dqNpugaWUOIn<%sT9acyMwJSw2vJ(0LFX~> zU~$YNd2j?MKE_yS!M2IM&@GD3!{{rXKpnkH%pjk9f7HOB@E;5 zuss7&6v_{lmC|^xuhFP5r{j=m63@cFyjLyDPi{@3O%ZkUGV5x-hiC#=HGHrB>W-rr5S2;5R zLGU{1qPx7ufaEf$stbut%??B`W0@hC2GxnYq1Y=rMF`%oPtbTmsM+ z-GB`7x7Ikp04vk?4e=OAjpecKAKqI2wc3@YeggEW!An5fb+n*5uEKS0YHma!hjITc zFptjI3{K^`qV*ydv7q#w;|~E#lEwo(@oQmY&)aiqvh0rhxMb|7!_JG34%?G$hvl1+ zrrq6$o~q*?jt6VNdN@`7WNf`$>5D-!eRnr`M!|Gcon+56>KIcR85OQ77a=Y@gCZdo z$Hl3YLUQu%ZtU1G^Ma4z^uvVVggPq9pDc_D$?Dx*Wn2MI$o+E{%7bpTt2sD^$q#pT zv)qp4?bF=kG3$Pfq$&_fRPCg`3gaM|Kh2fh1}jl$D3P2q&7Bau_*n8YKIKlCm!`QX zdpAU9bF5~K0e_s>Q$ut%N5G%tEqk~FRLAG{aQl@;Ey(pblRZau2F)&)JoveM_bBgp zIUP{oDG`q_ans!%Zfr7Rx?7lj+>=zNyOvprdFBB;PEO!ON3HBV=7BCKP!eU)7$qah z#7b_O?&ghCFvDOfQkg4{QhyEqN$|xPu4j~d(Re3sTgzS*Bzw+qlSk(>|6`)6@oqmn zcanF_aM3)xjI)w*TZ}Md3ggGvvXGdfGZ^m?TmSxtylYNwnBk^OpJ3mXbr?x-4-31t zk=W??qohlKlR(~faB|kk`O$wdqUY`D4yXmWBk+KjA|dt^A*orRkr*utjnu5rNF;n{ zB&NUfM=N#{xr=~7qJWPWg%BW4Id(w~SC$mWv7KSyh!zFPb#X}=i^A#H)+Nyw^7+3Q z^4<3u0=W)Z1Q%8+f{QIZPWDJ7f$@3+i#%E0jzR)2S`JJBI+h=uQ8_J@KoDb zGZW_n$oA>I-0L{_50`4aQ_FI)lW+(^f{Km}RkQ3aUzYN}EMzLPKDlM4 z+dUg>v2bFjFT(>>yN+Ezlll-gLW3lAjpPt(x&}|5;8`aLnjjSXZ2%}00t}vDen$g9 z!II>NgWQA+BrqoWgVX<60z??JHsFbmxRk`$Hf~c;INq#aGc1^WKx^|8in)gx-+LHx z&10E9u%pals?dj~3|IL)f!VTZKh0Ylp7SQ)+UklYc)rqVE_C^*^(t@59mL;4HlgIN z>?9usucQW)8DV3u;O{kWcy|4yGGBL^z%4poUFbI3sLDMaXMFz~pa?9(Z~UV3960a< zKphr>Tl)Pe6iN`T^|~_2G%0jKWp|^DCkv6-27mo|_St7Na@&bHke{oOz-l9^`a1Fx z6!zXo)3@*lRBAYdU0Kc6dkVE33~6^_T5|}`{GrS30*a1o6FTB5cBY0O5mpUryZ(+D zWAaS>Lx~sGZD#YIZA-JBOZ6~f95~>LTsYit6B0qUYw)iszLl>ekMHfq9QPM4QJDmt z>K=?2k5WzYeOFv0L` z;&{4Ey9Fz0gMZkeQ@*BAfwyH{K}IRTZ4ptdflA(6DG0q0zdeFW12{fk564^=HAHM3 zP9_+HaO@oOC?G7>9czdX-jqDEkE@4lTVJI$fabm$G*MOY2ytwrle5023w2 zx434DZQEidZ(4mmGH$`xtiE1_!$V&Oo<0vF(F}z?91OkslBu#7A2noncQw5G9$_j5 zoC2GBH*!QS5%kmmnE7v&1&G9?xIg|_1Lwdmz` zZ>Oy)F!a>e&F(F(v``MVUdww7)s(CiSQ!`~iFX*}>1qkvNo+vv0(Mut;r34+f| zttD_Tt@Sq_ZzGlA%R&ZzkV=XAa54{~gPz1=xA{aG=1I(mKAxCE>`j*!<~~H5PxATh zDLxF$?V*Brz+idNlp-S^VMd!AIB?AwkIu`~3)Wv-b5gOilvL-t ziL>eq!vht$)Lox25p>+7LajPl>o+D_=DRgjwzVfkoCYW|C0n?8!TWh|9J`oD&wTu& zau5Wk7{oA_6417K2l2pIwwK3*`MD`Ph|m^FOYOINL-&R3oBh5}=xHp3hTp2R-6Mw# zX(?ov#!`)iFs@Gv!Mtl5P(ubAPrJU%mI;hk>&QJZ+zI5Z2Fpe z9V&!pzUHQ&OO#>>(3mHwUa3LlCLa46Z{lra25a#K5T+|rM^7kAs%(gWAPh0rWDKNW zOg;cYAW>9j9D2(X)dtnxC7)UAx`1W%S~s1?y=z_h;Jmi8dP~79Xo2QcCGZEoIx?tO zL{7~zxOW;_v)R86bxzlu>~)2EQ%5apj$9T;kLR1-1Ymxh8xr62r zc|UPg9G(`RzzGLdXiLFUr7auAwrm*Nv}O0vJdR{W#Z&PQ(ZIBM1)*)Fvdu+H)Nu}g z$U54NGa#0D`biZIT8VCEBhSja6cQnvNAj(&n+EDJ|7iXjsB>wM0BiB@)VnRj z1F$BQ==0Ns*`wS6-N}LknC)EJrCN{xbR9A`V3A4on;*QnM_V1l#7A$JG9%&_k8U0= zSv^0P;adAQTn3$%SJ0ia2_K1M$+hmV zy$@gk&}s9mT&cwGQZ8ci!Z#1e`|bx46%edgT@bu#RNm zH{2D$%A5Ls`3-ks5Pqe(aPi3eShE0*gP7)KTq3Bqyvr}3`RT_4Td3IqVD0lbV#Kz0nVl# z6$bYR#u{RNZ|43izBp4ygzG(gf^VeVZvCXEZ2|y>%+PYE9G08H-bIYkKDpedqvE;c zq9IV8P-1}1umVBX20&Yd<8}}(>~BmHaS(N|tZ2-v9`?erudu4BzAn}!?!w?ZqM-7x z<8UHoa7}4kE#s;M0;(DoBpA)goc8~-c_2DY=g~edn92j)NrWuJ#GObAxxAYPoT-Zk z!g`5EaXtc1&QJJUyub7)EV{ZdR^XZsJ4JI2jLR$q^OvkZ&Ru0kSijNx0y+_plCpHvJ29C}w}w zET6+0N`4Y1fuCV4iOk|80KVDG znU${A@qxX?iO%=dI!k;ID*ztG$5jUzWiN9_iv$Aj$OJtF8@Hn95U!$Mrol!aVI#!Q z3Efiic*LW}Ayx%TnBiZ#l&LBaS9L_Gy_1~k6*?tXx+1_|yq@*G7M66&lcK|&qL6$| zQUF`4*yP&&$v%Pj@}$}6@Cz!EzG8$0*E9 z?e3f~tQN;6z0-p?BwOxu3!C~}acm*^$#>n9WUss2Ij*n&`n%ja0@s&pYYmP}-nPjd z-h3;EmZklZD>k`9-C4;~o7}&;v-(%w<6NHP#^>EXB|GnP_qnr^ja%GW9tXbcCiM5+ z?;Z=4r}y9aj(fqKlic@R7J`cu>RN_qT0z{}z(Sip9x0A9Cv{YnANrLsfE) ze|zeW-D>x#WaLlW6+E5^gTGJq{g+5=>GlR+3D^Gla3$zmfzi+Hu=(v1DZTG`S?rj-sJuF zyNL;3q|NR9ikkbU@3;>n*9BZ#b^%V($&O8Kc5-GIEO!_5gRRS)yTB98!%e|`?(@l- z=3p%k8_2~iLDeM_f96K?|F$)l@7#sSwD#a|w=y}oJ@^_AL-*1`@Ftgh{Bb~}R5jVZ zBUqpO@aJw~e=8o%E?M}9fAhxVp%KC6{6((VA}FRMn@Yi_+(ikb+ugr-WH7=Nx7-X8 z+n545Hi>t?J{X^TsSC>3?WeA-|C&+3SAyh+{{=8_9}^tx+!vC|#|9gC7_1*17d#b` z5J~obdqObDi&^z+Tx&lqU6`{1tbkLms~4a`z7|O3G_oPls&46zzR#uJBlE zQobP^TSZ;LL5E-V`RfG${%UdW(hi%zT(?Uet&P)aR6?>LgB1gH zuw>bw7Sy8IL_@7>xmd>FQu8AhNE!M_ZfVt2e+t<$o{ZU&j%B*pF6T)EGAS*vMa428 z9(M!_G%B2nS&EktkEA)Y60CTD)C3sF+1Ms{km}H|sTv3k657=qOR$c2_i4me?9k)m*mH{R+L`tfmJ7J1PWShJY7 zYQ_fDFsL!Cw+t|)fvu|2hBgIsW1Gk!!?y{pBu^8BD5o7C*PIF;c6^u@62Vx`JOWGz z(atE&pQ?B$o_-{)>5WM(=0>lB6v;{wl#D|2Wku0azz`ltSbp;+PkdE@N+=B(@@6~! zCXPG1vM|1;kKMQ+k_^>kCpj_)kT(F*wT{$EBJPR&=7Jq8_j%FH{8nrhn0CnjZM26y zB;%yxSB$g(35|9vcY2u+=;RGj6FG((3BzH^Zlq4xaag`dzwH2n^vk9&{)I&%lC&GO z6KO*N&;zTh%V zoOdx{7UU%M%AY_@Ht|~qASL@HSdmM~Qzu(_g5NwkRcOmi#mLO-gUi4e^oGeJw>jeA zbNalj(%c#5T!;-9NBvqSn!!m(p@IM6jlKfyZ1gj>Tf{c|Obh`bAX6AKattqPg$!FT znaQ-o+_=k^Uh_LWkdIW6D0#8#Wk|gcj^0#}K&)@JO)|GCMWH^M<0M0qa6MezWlo@g zM5L^mBfu$krYh|;uh>ygM5|!YS-8oUbi@t)&{k`_hA&N<$#h0jc}Au67*0u*%M40i zqS;`_e{3b3pd+R>_7x$sMEsQtGP`1-!M2BFV9Z9fB&Jtrni;~7OM7v_c#)GZuPTne>p~u2R9*RiN zoyBN1X3_UqX2fRxn8m%e=)M()S*vfw#lPXT^f)eUWjeo9k!Ls0>0@g}nKK8JLMJqp zjCG$&E3oskQn#^PK&-*lR}h4Q~b_XIOzZ&{Ymv& z601q#Xk`*M5~GZ@`5U>B{-3%hbVN{SoQud3Pw}f)MWDR3o z04RkMRt6eJH5@|2RLJ}bjClNl7KNg104i4jUsHi4TyxpV3Jh^rmE}Uyu*o$I88@qp zVf$%DNW)U%qZ)KkqE7WHOlS#()*eWgZ%T>SrXB17v~L(RRgD!$kP#c8Fo%wkkd;K^ z8r%6SqOSB`nn(aV%shHd0LVKmJvG^gC#^-6%Gg5H^PL)B0Ds08z?QKEmR``uV0p^o zDYg(ATOb>zAd1$GND<|17DkP;X)~7s_$A5?IC5GL8>bQ`7|_G3%%z*DfC9{=^q1z6 zdcj=Ms0g}U&~^i9*Xc&fOid0_l(ItnqqE^Sa8IC!x9j&cI$P3S&-ybg16j|6^c;xC z0RHhHlRxb-75+K?arCeZWNVFqq`&C&4zU0+kR~r6j?#2y+~b`$Zd7!XEkWUdsmU=e zoaojkzn&i~IaZz*p57s@2CoMRh&o!eVAC?5$SdeYW1J0ci^EUe_*rkvVGSklP5$TP zqJ4u&GdBn0^-1qi(1S4whbtwSxUlrBflQ@KU#%zURWA9_zQJytwy}dikL0sUo=wi& zFBp3gu1x1sOtV@hG4ajMi%|OXrL@Q(?il+?Y<#;nn++0&TLNRO7|bfW+?s6^j%X}< zXlL!B2HV>30T;q>v@oE>HzJ%5XmQWKJkgk@iKlC61<|PWW+c5)(1secG<5~(?CNZyQ7wtDZ7e`rw=||{ z>yE}WZEdQh6#%@}7W)^Ag3#F3PNE{z(lphVetJYs8zI_SQLBqIZLMrf)7GW6v=k;H z4Q(N252lQs>HxCXPP4-i;_QqLp~#dFskYvQPJmD_iyFwF>#oK$UAV6?O%J|j>4_<| z(1jm07N841{ay0N{z1=ipKZ*i;x9C&sra(SG!_4AW14#Ztual#*Z(d#@_?YHs)!nm z0RLBGntGpVOjGakjcMwAsWDBxXV=HVgDvw496EQbfkkMv=CRk;R*N6q(439TQLXMT@1;-8m?Da0L{MoGKhnqhaz!)SN<{<27Qi#6N~@l09A|V}wPuY% z95L?3UpN`NZ(jE~R#cv_!RDpX?r`GAc_uk42mtJ>f@nFpsVWzz}^?u#hEKL%NCPkM_*BsujptoRSMQ>;*82_!eCHf@Mb_| zdMVBez>42)RxX42Y;`baU&lDT#tCH*>Y@}BVCs

tO>Wak*-&&@PRX^i*TIRE0_# zGoYlG8q-kHr)rc#ZBWu@YiY1mr&=jc6fdT+t&NRoCdih?G)--N=A8{6(AL9^1!(JM zwX|3gnjbBe@vL;o;<*L|jNNORkFobdyfI>y9WBf0Wd{at4L%nn&m0)+!69K!Z}4GP zN>=m+AL8+BZ}3(ghhcHyGiPGpX|$<@IU`P68X;da=otk;peDiN>sM)M4E(37BO0Ru zvZ^aMU%J$b76=nA>b3dMT)Myrat&l_bhNsg_e$yEmh2TuG} zAU>fXGdTw&n4CkJfqCViVC)IVIZbTJ`8R`i^RX%5r?8|QF+U=ILkdAk>XRp%Etp)I z9JJ!eY_??b%g!f~4;~y$JWBlr!b~=*u^=xHp&`%?k5+W;fXz&7;RtlgaCzd9zRMf< zLRiW74i2X5h64aHWW2ZEw-u!<63aoZQw>S!kl?@xn&?I8h-F?E-@fhcTs$}40{Tmb zCrb_q-iUReA*DA;z4_7C!cwG66*}WwjlCRHHgc-0+ z6C`Vt_NZ5J2`cTji2-p3X?&*#y}ZaUP(3t|VduI-15RC&2M!I!?~OYY>fN$tDI3U$ z8k?iGluhDv0C{tP96g|ihQ!f_5%;#XpXEgmF&lhDjP*5x-WA$+>;=ZwF*A37-FU^X zKrx#T`3T3WwvjU>zEx;Pbvz`!20A6eqW^rW65B+%d|uQxAsO}NU|iFr!rIQl#pQ5p z|3PmK_GoFET)3yDgY@|R&mJ4>?%cbQ^~VJVx)b|%92fk^HD4n;yh8Gw4SQ3icVH4ScJeH3$PCt85xZpUpH$DO7U>D9bkWt~4d-Iw9BxmUQ2${BOq({hGTy zsG{_(Wb41=yN)k(+Quec-$(P`4hj{Lbp6TZ_^02ZUk~2PpNI6P5^v>EdLS(_HoiTG z*L;^3o4=!S`ZFfp!lU#+|2N(qoDjNM$%qq!)vQx>(JHOX5AE zP;LKyaqvBNRPwF&^7T>4jQ0g+@VN4Q!C5?}|3k2YG3N9j+>%m))|5rl7Hwk0LoD-N1<>9z+K1AvUGoP-I>8V z+_#dpYH)yieR61(<>mFsrPW~eM18G`~uH>5S^b^`)pew+4`aU*yNbYf_!q^ zrJ(8F-hdqFm|Tcfy5nl{? zX8Y3kQw%9hHI}*MkIz4N;}ehn{&zX6ryRw#F|7-!>0I_dpQ1MRXtt8wXc}hmVyo5P~SV_)9^##emr6l8e3) zj0Q*F`%=)Y;SEI!bZ^lh#JbhNdnoqa)j`i3D+V*Qas#Nqu$aCWRKaztgDZTaJ91g0 zAO5l*&eZjOXsciSa_|JLp8u7gdv9wMEwF$a+~_b6_y6E4s14klw{B#)_Zwe zxIXwlJYHHKDD|Ejf)DVx{YHvEc|-6PelNTc8z{?qXB!(KVvS5F%}FM+{E~a@O~Gs) zXWs-zo|A0!nImrrW;S63La`|~+xz2h#=mTS!i(4aMUU^_6TB{*N-x>Hb{+8gsE*$??RA=z){#R5mY%gC!rZwFCS2X<9NB?^y zYYo5duV^^!+<&+I^z;8`{r}s5|Noob+c-t9ja+JF+kYLrXU};i&mU8ox2$A8OZA`K z{wH2hgy$*EOD_I(@JSA(Ms5!dL)Ll6_Tc(aGAU!%hVmtdS6{$>tk6HaT4^-VN9W^~la?%rTb5q4sPAh{XO#}xD~~M5ha&xE zl~34#+G1hy&KrV%%-xt_Dw=ioG(mAQf(Y=>7iLl>R+7db6h>=Hgc$A+s?4(wP@y^eQ2$B!{DR>k*I`YL}R_0)N z^VRBb^63|Y+j9oR78-uR=Jg2^MXaFEglh{lJ#vP?}1F9+jM zqfC1_IC<)WkQHa-L9Z#!&@rNw(`BgWWZ7TH|2S?*zWH)6anv;^X5^{gtol#j;gany z2k)EOSkiyAVra<^zXB1oCfB_ZOd!0YVF{~|Qj|RQ3JQ_EE-i=e&NMn`sWXjEAg#*x zf0943nrd_gy~7~JEU)_-^f;!X_{8p7g8tgEs&8_s4HVW?Ms zBQ4oJ45Q1V0oyA%%7rJpmgG(sel@)2n|CMg3&JbCzUs-OGYsd2+it%*IW!DsxJQ#^ zVK^1V)fdC?{nOuS6wJXO?=^>&mYpcp7I~sA>n&!H@vr+U`LNl|cq@7;*{(@VmFe4d zT+evl7u5B3%;~o#$2Nt(I$)Q@Gxc0K{!vrB?fZ25;jR4Hraw{qQy!%Ul4Z@|`Q6Xn zN!HBxfxCEYcJb%$=D8^>y*nAx5>D-Y=r)qmCnYZQ- zfciMLM0+x`HC&!i>Snz!CRewH6?bg%-PX`guHUqV(>o&FC@S+2(0goBZVUh9CMA>F z!y`w?!Ut|?l#Eqy^0D@CX1L>)jmg3l`N^o9QSb(M^nSNJoDptFv)>mp#L~~Q*!KU@qi3nrUN z!wynsgpui@Oexj6X!Mz;2)JlOI2k^1gMWOVhu0ku96xGbR8uj8(3YtWQUwp1EOanZ zZI!%&0o4&1(qnXH8SJ5>HZll|@AXtts3{{=eRyq?8mU!EhnuTp6tYjG0#YlrN_V8K z)G8$agf&~MRLT*xQma(z6SY#Slp|7ULOQZWPy<*azRtgc4fdruwMzS_L!GNtx-o5~ zR;d&&YNb{yN-niBLap@2Bf~R7kJ+s5CWfHG7aliWCB_$I0)2`wz))9AXCuXzItvJO zT_9ZnMm1bVvG7$>`GHY=U>zY)ss$ymI#b0^C?$viUdvcRhQ6t_%FwIM3>ns9mWV}L zDqZ_rl%wL==Y;2RXjzza{kM+^&kNk|l7qX$SrcEzt1)hcZpW0~7Tt4k{IEW0OP&{8{LhC3o);PUp?QcYMHlZxPV%g?HP`3(&u|7K>leigZGi-t8>G#oZ#F#SLit{`N2GE`Ner*Qap`s zIN)nHvblVI_>a6V9~#Dk5wf>Oh?N7em4R@aRxBq69sv=p+&4VK>a4->{o^2_H|`fM z>^`jlA+sA1a?ySuZ6U|9k>wiJ~}LTdkq9{w}Ie&v^dFw z_xkQz{AjY<0fwfz2MA3cOOAb`q3IKEWPm;f9F4Q$?YrJzbg^ zlLj{WvE;=Ap<#VlYt3fV1HDj(qC02Qu~#E9CdDA*bOVz(!gd*77S(g@Mw?vFPBvqu9k{SO?3!K(X2vi;z2rcJ0G z8=BdNgr6*af`W~dGcDP6NO&NHMLvV4`|bnBhx=n1dC(`4iw+I{#cHepW^{AnVbIMd z8laoj09|s!Vc{e0^yHGm!c0Axa87q)b>)WYlHa}sq|1YS;gZ+! zfJD^zW zJ67jvz=LfZwN*@4-?T(Wv7^gA5&~);u`<@m#k53chHEKK#g#3pq}U(4HM}?+y%Q0} z3?ifkTA?>>$yeVO?w34rLb##X*VF&Cw}!&TBxC1Zzw^S}qfpSk9= zt!qCNK9m0LU%Mo{%hfB>4%3(FKkcOOhM@kYz5ZswKZi|0i!YW-_BxqGY4Nh~k6D$A zmxu4KwMAlb|Kq2G2iJ1ud?b8FCLpd$MXqJ1VTALIkAzpasTpN6@C+{glnq0jx!Whz zkB0wD@Q$-?5BG2c@A&$L@R;}R%*F}H3ZDS5s=;l?c51~ID@b4TA5;xdJf4;G zUWv8Q)#rrADsk7^@R(`?m`#mf;{2C2DgC4+JebXOFcp%o1}68n)WH;a8_fC!^CJbd z#Bz-tdFEW!8n2>(j(DA#k#h%YOLF&(1Qfjfim;sQehw%-w-MlT|5pRt-}&kAf*QWR zx*|NYme~DsLjI?$FUiyk!^ipkzZ&;n`8>E^(E#s?|JCsJPrfMpKiM4U|H2o-d%}$F z-}%Mx5SPw~QpGS!&kEf50r2KlooXOCf9mLW%sb zcjV*n!}90MCP7D=&4P3iBv)M)UgOSBAw2(oDTE9AfA(cqamI5lSySUV$%g)LLH~EZ z8lIhvM)JL{hqID%z83y-a>+SJ_-$*$Tap*g4Uhd_%6C>03LoK`bZDn3puRf64z)`~57A z+cTsj8@^Ga(SB(3!5Zp5e;Zqcj6eL~n_%nGbQQn!e{vO%Sp1}f=JqkKOKXiyzgCEU zCyC@cL2~e2VI1yN43hW$B!7Hz^v1Bcd-wyAqEnIa$S-m%~zwbLj8Wj{dt)y%t&6kBRm4t_u+Sjy9etC-Fc^J#mSwJY`imkJ+JOQFYmSD zBkoGo<9po|PKajH(dhyGMg8qh%bu&N1|LN=c+1A{m@XYn%7HwPLn3Occ*SjZC(Hg2 zj6tQR|SJa?a|KuOcs4mknTR!H3-#L`ZQ>`4|T}G>!dqYiE8)Q{UE>6W*)jts@GaGO;L4V?dXs#8ckL&2g*9opmMdX zDB>E_>@7hQ)nu#e_Z9VVrpF9hJ9U`NnL4aJ3T+Mx&bHgC1K`myde&K?5ATy+9>i@$z}ZV(5pr=m$EixFK9W>Eo({SWvtL5o@xGv1-8F{sz9o{1(4*+GFM=C>dQ{UI zCpBfoQ9@nF^)g;{%uXLy^&&@mgsu&U`61NEKV$%2@*Xed-}YzlQr#6|c8M2Z$m1nL zfk#iwxg~fOhIoLX*MyqtuBZ>(>;g4xZyRx<<7Yg<6?D^w^X2qc_%|A0?bFjnoZz*);QXdOFm;t(3w{VHWdIK&&&>!vtvjSxuS`x0qpLybLk^bU1 zr3iq;9NU+lx*A3|!vLBH33xo~56_x<`2}Q<7e6$^4uk2zJ+y<}SIjBb zzyvvr9gb6hMq?P=iX5O(hTTvSrd4U+DqUh3imw98$z~5NYYc0!YqHOdU3D06R~k7D z1s6VAgVlvFY!8Csg=$XMd743lqmgE{=V`E-p{)asgL^a}>b0~CpP*mF{4$0jgov&< zjAI0w3T+Idx@?vUequ4Zm=FzQ_6|>Fk*KVN@?y2gFZRyQtm>zgghE;w5~B@U*m8vl zzNAG4p+2b02-mD6?ra|tRu?Fnw5;i$SR4^{BR4arcu~2d^Sy{LFh>O-Bj}wD?%b># zmPw?H$SI?JS_=V2 zD;5IGK3z^w+*pD6Alumro;EEF0+@oG0-x`k-onJR|C&SMZzs?EL(o;tDb@6qTXbw$HIpR5h${N)ERLI5PMzHxVsMZ9AQ ziP9a9l+p=!7cAG(NNl{cq)~ccl&9BzKZHjfLlA)D!^)Po-Z!~TnEe8djELAqI zppEIMdXo1ell_f?WZORkQ>#sowo~Ywz-XJ{F?w_u7D112HP8oEkWMm0d;x4DF$D4&9$b)@@(PPv|9zJtv6(>;Qn5RVfD)iA-r;pqK`j98-YaoxD zkw>mZ9t>CnfxP>u*G?Z=Z-=1|S?7p8;+lb-W>D!WSJ>8)xPx6+btUBJ?}BaD8lgqJ z+%5)W^h2dY6BnlB@7io(!&^kZ7AjxSt77y}@CVqX~||H(^9s?R)({3xOZy zs$4nYBV%d?S1_^zL$b9?g)yj{RxIF~{EHO`hvh!21Te+%L}h`?LGq*sBFQJ0AYUql zC1Yu10VyR^c%@K5?2nWOw9`x4BYa<8(2`(@3MCnx#3S2pg_51#PsYQT1!})#B~F0# zuy09s7-Cz#=3~g)ZC;>rKl6Xn1;M_VL9I}!TH#Su<=LJwtz2L4^iq3cJLv^8n9CuP zQv?%GMb)611vv~E7AbZ{Uw*!7w%;3b^98#7j^>9a3=0bMXHk7%)#g9X#W&W|K5=he zHS}c#S!A-UQolT8fQiJ&s4!(2q?8W}k+pAoQJ+$L7Z^C+qxZF806fD{xUV20aIANy z_p__OnuiOhxed1zew+MVQ}{^rDp$_I{016nYHui~=x))njg9^QzPZC=a9A?3$#IQm z?6B+GtFQ;}>iD2R_ zzZUiVe(QNFw|(~}v)D?`+!V^OHQ5v%DYv~gv-ER#M4tWCHIHrh!it-(#6k}j!j~y0 zch0t07Hul<^sNmHsXNVBFPVK$xXf1|yT4&7*lfWTf zIV*)suTle{UB5Z(a`*P%yE(i+p!9|Jd7uImB#>He(G@@V(plHMa_v)(d+*-6?+fLw z`{Vn)@s=BExK~&g}*-U=Qln4eP3?mmaymVfQ_p*(gG`e89OIk`Mex_+g;B?qRfi&-_Pt+@yId zJ;ZF4Rlk-W?$pql)O?zEidg)ka9q#4@vI>TH1*f{^!3X0wG5-R)}&t}S({76MqZEQ zYxsQJQh#mlp88R^(F5`!!0P^SST0=81Z2TmL*Rn`gMJ(y;0iaoNSjIJB zWzP6bc)oM{CT+hBk3Vn@FShfd0D^50gz!)VQ|Zh`!Dl^8Rw)}QpSAP$Wc6>uNqCVK zo(}g+9{p`N4G8&tJXz1|2#>{mBdn0MdPhC$6Fb84fGqjHQefZY%g==Wz;E|#_!%DO zJsY0hG@lp+^OCmTh40^Q9#*htue_jd%`4Y#dN_BicZX?N^!(+|{MXAbUHq+osis+N zi=O`MC68Tp^)+AU_BqLwzYCWF(5UCa<2&bNzB18RIn12;T=>Y5bE57=bQ&AeVA0D& zKAg;V;DDKQ96qtE%AWYr(S1NM1?M3xooc9%ta z#4V?tQkqvr@WLl3YD#^C^69hHKa=L2;i66%dzWg6NZz(H{6!m$&dNn(CvSWq9D5?x zjl>X0KjvrVbHvZo=ST&ZzB9sJAhKx;I(VX{;)jQlOB9r&Fo)%T4F5?~{o_AjD1YM*lihyBCo_+ibm^_t=sz^`%OM?co-2VK$c^$ovoZTP*>>FqWRUsM!I0!_Ob zN}^7mx1%v{gwNaQmW-?wR5KdAOP|TV)H1J(XbK9SclkZ?^Oo}~3bJ2E)P9}Dud3d% zrE_sr#{OI~dy26)Raej}v_8m{>;WFT?e-s@emL1-W0}Bzp1wX}a1~*#idL$)edE9W z@e>29sOpQX4txJ9b>ykz{5$S{d6!_I|J~3NmkoE;4tDO&=Z87iS(6s1Id=`MFOD3Q-|RLeBS+_F^H?-Gf9~`hcWN84Qo=P)N!0O3or#3;`FH6J6UH_l$!|yJ zKRNbpyD}g|ElcipCNB;VrA^6MUHN~UbdRKQB69ZH?!y;>p~TLLgk$no20QLdt{#)` z=^Rw`zmLg(%H5ruFgE`d>><~U&7YCK#}&imon!L{C;w+$zMb5UjmxhtTmnc6zI#)9 z&&9`vW0Dsa=L`J@cIV$7%vY!BGIt~3rxA^@=uM-dQB0c3NqBqkx3tOd(nOXD5pU{L zK>yzO{P;!d?&f52dc2i~VlfeA8>L0+CAclkj33=dq1VT&vhR6Huax4AzuhV_lI`NzmMQK5AJ5je`&Y;bW+nM=cl<%{YOpCzs_N7 zeMXdTLwT5dx}4u*$__zwk&Dm0$AcAL!jE{*cw?HrwY57bzmpI;e^{I!k-YqW$$Rhk zEQ;-ad}ep{X=zV-NPQAY0t7-Yq2-|?C{;nFMFGY>4-QVn?sN@q3@y-6xv>_llp-@AJ?1$!oK7&h&H6oHM5*R;>&nuJs0L6hy8MAjz~m4FXo6hdCv`0quk*0c&Tb@j%pvakijFMd9ZD8xeeE? z_Ir&)Mj~a1xdiNZ(W-FitU8&6+lU&AA35r6VlLO^ zn72ia8W_^}JQ4#akh!_I21L>G#Q=PI>cV_63$JJMMSpb-k_#4o@c7cb=C(VU$Nf$L zvSAC+F`9OzLE>rMQouT#(n2)G=ag2o5dYw<<)oHkJZ@Bav8Cw2pOEeX(Ww>5h8Em` zyY?{e;D@P+5rX=>wr&DAq-6U*fc%fknFS&Rgeh8fRH%Ks7;htPA< z+K`&Hr>+V8=ivg;Id$_}=s(dQrUornZU33u>aWqX8(qC1IPYG;uD zIeB$wXgviDlzO2^bU{!tT_dYIi^Q6?h2mp786JpH$taz$U$*E9eYC&k(XL`Tr55O} zQd`hn^hQ(N-yI^WBPBVuo9Hgv7on*=Cod@y1L3))Nc^Y`IYQ+pmbrov!7vaHp*v~$ z*q3)dh6+>D3;st+$WMETyZm(;9*bBjPtOrI$)*QN!wp1wT?p58ZYZf6SBcQCuL}!m?UrPD?ZTxyl@rdR+lX<7CAmlrQmx< zs=2hjTD)2FNPqD!&i^LYT_A>Po8OS1ULZCBTsBZF;CrQSkT}Kn*8Dn1T!n9n(rvmH z&mTisR88+DeUcqKK&m`a%8T&nbSuaqThCeW5tI}`4qqg0;jhWbgGIY$2i~N@4}n;l z^I@al0-`@&)YTk0FW|jn)tnOMdZ2>LThise!D3C<1Ijqe>&b@UlFB#? zc`#_AH6Ep>{s$bXIX*=6;lz#R!vsDZc2ZtC3|zwp@w)RQnbe05<%e;LByWb6x4I9% z4A577_-1tWEBkWl><{+E+&^X_F(FSn3g@$edube-)sIh+^M;8l!QoTG>T)=JxJc8M zZj_yei!7vj>2PrgUOR@1=6Jn7Toj;LM2b25RB!{d z5C-&x%+lNo0f%K0GWrWx7KXolQx48W?L3!??dKbF^&MqN^5;uMldSdivRLheZ+uI3 zzf24|-_+%kmx(@5ZtE@w-9C_WE*H&|iJ_LyeUo6BjVMN?F7?P$>zY1OTN|#P8Q~c<=yj9K_uKd}{DG^;O>S zA|3us#*1pxWC`xVVOAB)4Di%=sQ6ahc@zG&9NC?h@OR|X-FZXA`KUW2?H$>*2j2=w zJKjS{T6PidhR%Cj5l=;stBUwt`oYMQ)wA^Eny<#gpeaNfF`DXmLolmvwh*k4)66Y; zaz_hMix&=^g^~KJvh@{0^*ZHOh?_~M^a-Mzq-^#Cag?O6UfpY|OT>3*HHYPulf{yR zC%9HbUtKv#RIo4Fh?xR$eo1zjBGMDqC^oIa;F0tQ#_5VpQ%p8ZQEZZLO%ZK6n{>p4 z8&@n)77tbt==dtmGl6y>F7ZZ7?MXM)>JjFQH<;AD6shTQN~y@nsa2-vhiE}#vHD~Y z#YWDGx7$m_^^mX*Q(++Omt&@iEaLS1sp7{ApMa`q!HyYFFrZ|*t!rpxh{W4vQjWdOBFYFRJa&QO9+SD273-;IC zKSTUoX`=-*MJA}Xd8WumtzMm3uU438H>-q1P^G|BGnLqKvFN<5xWAQ~CEVGSC)K3P zLiY=q8&e@jyWLhH=ACzfomPlEIki&!6ViHNl_(@RxxPv)j58^FfIFllW40iH56%|t zNbcs$7TK1xoFdx|(sGjCTyCEY%P&d3Iop&L|COS16xn6UWUJPuHj=;15&!;Q=%Z`U zNB34d_z(TF?B0v7gA)3CFP9a#3%7MaG_! zP*@D6i2A%O!eVZcPC2oN2k~Lkq4$aenR<^z&vxM9Adng$qPO zdHc2EYQ9S*&ledn+c33bi7OiZ+p^buGvLDcqKBL0(i6;*)#W1Lf0pHpkjnDY5bUMB z$THkFp@rgo~`;dDu&VP{CEM6$anx-k%bd+Y;rkmQO=7&XSednD&hC|)7-##`?_YF%hXdZPX ztTxxdpJ3kL!@x^Kc9R|7fc_ez$zk*~Hjom;rXsC~v?z28z8CVv5|PnXoqJYaLc^jX zP5_p}z)%u~nNW4Mo*>$HhJBT^4i1N0uvDZp?+Pc@;jqSFEitx&3t{sp5N!d2l>kg0 z*6?KLIt*2XTzj3!X#%~eVX=btqQ$7|lUan$)SlWOpIiLR3mczU_dSdFOcwcY_UD)D zL_`eMB&i-qlO_fB$b_X(*4sajgO-X6`zDZpoV8T6)fSuBr~vI})~t&_LHUZFG&%qpA;qVH< zF4O@-&W8R=C_eTAWYY#O7G(VhqK{D0atx!T8a+?`ayvAN^OlE<&=#0PwR;`0GQ?@C zA`9lI9gF6M8;Y$;L(FaDlsLddwFJCT*nIQV2rqLzHvkt(k4@ol(B{ZIB(L^So`U*= zDhcMD5->4DH8F=gXev}SsuQLYQJHX^Kvt>&DiD!PwNiUGdBh^F z6TeN4D~6k;Wwc`tMMhz1j|pW?*uFp;N>Ddv zKm<^&7x7FknG~~DyGL7v(2yHtYu;0D=d8ZcMc)x4k|3QN`VJ#@;J`s^5;cfiH#SNxAscOLVu+?{0w47 z%s;y*{#PjsnPTXa!jwNDg|wT#judu1lZ-aeO)?S@(RLIWY3sG>68uv`Y8NzEa`f}> zHotiA#YfuEeho7HgCJ|qM}E5Jl_j4nJ$!p;Xj(+R=>cC1Z^-w~=l}in%H6MQJHUpf z0sL&u>4P8tboVcx0F1cTZk$RcXhUS35X0zmU)UuZg3zG3new7O#7EMvTQa3Sw}&A< z5OoDgNrfebs>PDxa z#EbIeX|0vqI9BuEdb~gSYoqNu>9Ci;zLQRUWuSb3cDa3&s-9mqO-CWcYR8>)bs_ss zI=N#IpL5<-R|e>BAJ?qCkgw6{GxXE>NKZ}_9VqqhmN#H32}rqbJnty5cCuUYS-2qZ z$S^*Z&C}BY5X?xI2kJEK)+0H_Z^;G8=%WH%EAG=W`BA24ePvdHf^z8WBBK<$; z23aiL50Dwt_^n`i%{2ZXcw91_e{DkD z4()477xKRwU{H@`d;(r~E#qVO?iw_b5|ME7J<_~K$iBn-5WO4At8U^nJ$m;|yesM6 zf8NA9oVTXyw}MkQQFGl2Uc~uS`Ql3cdFWvbb4$_j(S8pd-vaN57jitLPlXOlgq=rI zRj1Ty9AE^Ef^BAE*Q*6P6RQRH0tz53?)u9?UrvbzPXd<`z(q9*TIIsVqX34@NYhRw zqy={YEdo(iqQ?N~0nE#oQD|E;tPV4Qy?|h5+l5koSWQqNH+v|az*oMgz=r^liEO4) z?NzA+eadeKAlmD!;)R08l&2m{P@b!NG`|OPK_$8u5Uv)mBAx;iju_k=L4JC$jhP`J z6wQiP9ck_%JkHD<0mcTfsZb?Kd(ct?u__`i4#r^5Un<+*!b@|f24Ye3D1>Sd2tZ8` zu^Xt`#s(4`#+u`k}mnuMxFY5w; z_A%wHgg}| zqWKr_MC%%77{It`vKWJIb79I2$;M>yc`m49YRIp?fT~skbsCE78b_`rsiN4$SNn#F$!51tAs`{0_V7bSR1Gcew{W8Ti{J22;Z!u z5iX+8?3e8{*|VMl3sPaj3XZXK&_+|MHg|w;<_#6l1;ct;7sP@f{$tU@pHy3Ful!{T zZye5W+<;!}HY zi$)(*U;1dJU1KOgk_?oyh^nGNsz7y?jV6mz(TISvXag}dijyt@-J(-S574v?jeS^u z-208Nrd-D$7|y!T0t^|_U^@`HKbFRFt1#JsS*)ho8hjN|t;?LsT9o5euE0?8qG`)1 zctPwYn$UQ>u*un4V4fS$RA@gwZe1Sm4#9zVc=ya()|G{j+j590rf`T`NC+SO3k&5k z(|wwbC{)8u!S1%+8|aPei6PETu?7;`v(+aF)#mfy$XlGFGf?O5Ap@{2d&D{#|*V#6Ve`NjHFy~o`pDo9rPNSBVq8Ncf!!8&rMgtRDAi> zbVgIKwAu!XOuOcQ9_b_&1-y3`o&*;VYUZziHR!@4>6F|CY&HnePW^2Vr|K^N?D!3a z3teo5p1V#n1|QDgdi(#rFUnqFsU@My)Z+7R7GW)Z1$}0d+G=80HziuS3Du<&h+)-~ zK)cry!f?P)s)=fc_Uusa?zHTSU_N>L7XZA#?n0b~QNS_5V*H zarFf43jwE+x-brx-ho9D0<)SOaQnRS3PpBdiEz3)GMcMLuST;zv>Ob3vFO7<8o*)t zuwXL5(U$-JsI%+18O;NUMSwG;qw*-_dVL+V8a0nRT92ifA%tipqwnJBR)3wGNR#!^ zs_3W)c5)#E%ouHr7*n`#G#~S(eSbBk1e;*-gS10lA>$hXKTX^v#Z-Z#wK$x3-u&N2Q zMCwIR@E{5~eFMmz3$kzCp!o5{2GP)EuKCFKH;TM;tCT8#lrm`L#)3_e)ukIn=Zqg7 z+{`;WgoLi zq~Ut+d7DIry8ZrEz=1CfI=5DPa*bAl=*kK6P;AjO`Oi&aatKx?8Tc&6i!Z(gaVQ)E z6NV#Yt|TEiD}U;rs2m`k{G+0t1X}&vWrv;0Z5YWJt$=Tk+fMLPKBVf>lL;y z?RTS^ISYYcM3WRsfLgp=EzpS4G4%mXUQ+Vjpa&><;!e>N-`I%Rf-OTH*^nk(BO}fp6)zPf4yC{ zob|Mbm!}$GyE%q(viM%nEYg-0ddqHyL}mwA=x21d|Gl?e{l?U!)h*m^WjL>E!z1$R z4f8azn0IrqA3K|$q&F<)U3lkdySV(}kjU|4krX0MYjm*hVFJhwZ;0GW(XO${os8WM zn4m>7ht;`@Mo+bNA#HO%f4fliv7!3K44X=9vGhX{hO<_d zCEig#g;pJi9fCmvB19=;doFH1i7=Ew^ig7dlUyf*Yw2syVEh3 zs6m|S8e9b!k>ZoxEtoso7l0T*?VuPS8x8Tuoe;pa0UDjx*D>a!Mh+6Y8w4FtB#LxIpzB0SR-_ zXL+Ta5Sal86%Mgcisxgm*5GjgQnfaAR1Q5R5cf$*Zy0blU!gkR;4{pC{FYXe6&45Gxq>G?kttknp zVO&c=jRqqq%wy0U6*!HFV-3@A2p48a&=!+C_2N9G4T|#2s)#S78YHCRIGr&u_lt?JX#)nNEGDZf(?jmB#4F@L~&fN14ToGrh7unxgWZ_!z~*=jOE#` zvhTy9bH3d;FoAQ1qERwJ^FEr{AmjrNi|jPwpf>BYEY{!D)J%>Gx3Pkc5whE(qMK$H=zUi10; z`hYa_`-a@}D0ZYo$)k^ow2&J`f~9~(=pkSdwsW;$J2_Mj9ahNG9^*OgVAO1~Tc<%V z-1y|+ARD?`QM_4*BSrf2)1QC8{G1CT@TGBOB)(z%<+={PK$j+1}5=!+!B zAkm}z)YM&5-RDP3a6K6CL#7r!CfbLhhFYRx7a9CS1H@txJkYkm66lEtGTN!6IYysZ zC=f%5sqZ3Q_6fLaQHq1O#RZ!?XF-do0OYQKeE7Db#j)X396y#!Jk-+DV~1DPriTvo zE&~_g&}%`CM-F>jO!CuRKYH-Bb!f;m>~YBhkBc_+wMX#!_3u0b>AQ^qm-Ia$nt>hd zpAbd3Lr?Ju+d8x3;arRZ%5WyanXX)zsR=exu4K6Kl`93VcFKjXXyb}Ria{sW@KHZ< z;?gX-=Y^b@&!XEc$(aG?`=&FK>b03?X9cw8H1!Jh0Wl>zK(F%bYBmNxgE}U)A>A{boM8J&4zp6h5-V1z+K3WkiiIbaR7A)z5S-mn-%M|e zqt??K{n=i6W9+w$-dP>>L-daAsPCnCFkWFuDqdcrdI2^(Z7WVmcP)o$6cLQVO*s|R z(!j>9FtCDM`=*geYpVeq5}=-09pb^%`SVDcy3oC9ROk+N!L=4j*xa&WmuNbmAJO#~ zHs^Q|4wf+N7pQUIj%(=k@jig#2_&&mv$NgE)UdP-d|<93D`mK$_VV~HaT;4Zjz5Xs z-jGe75-k!{Pf3Y9uR#amy;sZe%4tuDu9tb!pcSx5MK$*zuQD43Y6JTRxrN)KpV}kT z8rmau$sFATgnU|X!BexL_(3kzQQ7GryE3U=lJ6D?nW|p9sM9}dn6|q`kLdmysVb08 z^?tH?w`iz)@Ey{7cZzsxx*`9zp%K`b^%ezAaaJCtf zVn8apJBIGti^D-d7|t}((&*YUXj;<%S7ric(yW8`(?9MqTzXu zv>l^lb-Xi4_8-Kj>I>tdW$<1Re<|G~q_&Y-M5fUK8OJ`T-W;%T0BS~4K{;3@ApMN{ zT5!MN^c>R0o&lJZ3Gyog9Oens82C>?$h^=8%6FcIs={o1-h6Y^2o}Sxg|&kma@4}L zdjKM^{uMAOG08_4Zc*3jA<{?DOb+pl1Rcym>IO9*b)_&AFrE5VH=aQkvwoIkgCwTr zptz>-6I#;K9a`3N!NSGMR&Cx}%@?7up8n(aUw{1i!)H&k#n>&tP!_W5P=3^s$_2qi zr=m<1YDDJkYse1Tqp4b1p*b688Hn2+vcO;xLBd#e6iL5M;tHECC&aLf4YeMu=D5WS zC`QMX@}^Iv>H-*r{w!}=c8pe}V=RIt^T$fijkV=6N4S#8%M1XGgE zTX`rG&{HVhId z#%X9G#%&osn#ney zsV;cYHOHWK0TW#q$V1~tEZO3|I3&gcA-#aPx-j(Xc}N~}B$(!8=E0``rs%;TN*9J> z0F&FKZ8X}&j>`!O8M@*Y%tM(_a#%E1+db(8yw}DfAhM%qUc&oqyswZ!+yfzaVC|^| z_Lf=+MPT*ai~!Zz0!tniKC+NSn)pbpH(Ka6Cr3wO%?5*0oK(qW-3(ey|pk0d)nQXxw+@kxYFw(-e@EZM{-6F$Yp zW3DpA#N#8xEm#8^-=L6TF9{X20pU|^d}<*}RacziO&)2sM_M7njvR`BtvD@Mx{XgS zWa%b8o$whpKBJIjnD`9BXWID8LY8UbGYOw%mgwM9| z*@Z0I#Ag#e$HwOrvK$kiL->X^zF{G2XyO|ZzLAY@RLB~c_(p_poK1EhNpIsq*4XrD zOdd__2u%uE6BFNr@VPcVw~*zU_*`fw`V0w8HPf69bon%^MOrwHMFw=+0a6hisYPKj z5parvLkfXiKRTxK!vWBmY0lY(aRpLX5xULxe`aoK^_28CnePD<1v7h>h!nWcQ+x2j63Sj&R7OX^K`J-SK4RqHf1WHL z^w`*ffS2l>Km+tm$OSiSLXA_$fw7&M6C*M>c{XwY@?ib3z6wWU{i( zQ0p2rF4Uu%M=-S}h5)3pDKs>>Qrr>vwkHwm+$viq!5pl1z!abeC>5JVrN;zj#NC>Q&zGnECX$-F z;irLrSO~=Ej5ujq1=JT4BVmJ}=SFt2_45!QbXeKMDvo}VI2PQiO+%r`DnJoQiV&9f zgn8iXRrwIrMPg((4T8aY@@um_$QG4RT_*82#|6nF&JK@fHF1ZL186vR;Zxgn}fO2EQbX2LX-80gJmIW&s)4S;|4igm3I21E8jDShL6=SGp> zu|vv%ubk~8Cvwv(Vxac;k0v=u^vseMC>$q+p8+nE3b-t*Ka?dcFAV#S|R|xMo!D|SH6Z6PW~V_zXmQ8*bz?w z%CIV2yGTzJwwLH-kJqhgTn>-gD^7ZIs*h#PC|0O_bWb?=X* zgNt;3>}Pru-MA1k&!eH^pgCbo31I^zI=q22xkNZm4m!Ii=uo)*InfwWkSjeK^e4UN z*NZguhnX>q#izDrY*|mfNG-DAZgzCccm}2gTxr-rj7vEK-T`SeqQS1b?AU-e+l}c$ z9Xlukk(f3{j!@y`@1wZ}`ZN7#MjVO_k#ex~mJool*CM0IB2k^QF&N`JH~GT+9*j*A zG^-AMifp{}#*_z&i@B6&wSzDr5QCW%q~XDG2hkF`COwi291P4(VIP&6Btufc>Mt#T z5~rkTYhmov>^PMJ=Ez9LBCUAJEY8je(}3tx>2iJyd$9iyg~$Pe1DMuCn#b%6%nDsW zdm0mHY6C^0E(}@F)-QK93Pd$FtB8qW8fbA^xlMp@(-Z?;8c$!Mq46Gy2g;DxW3I=% zDjXGipU!K)D3cG1M(5u1GW@XUtznnTt%t>_|F+8|`NC+biIp;>C=r0w@X%h!<-rxi zSjXS@>&AtD{BrXP)ggl3Jo@4%Tb}vy`A0(30=F)E^O>91KX7`EJpOl4@2V}2nh_Cl z`P(8T)l`Ch>c(|cEx~Nj)Z58t-WKhWaBv$muv@z71#fhzFQM1qDxVqZ7N47r2)_uJ}E4zP&3tcFS( zPUu@Jum{Z3&aH@1H2G$IUWL}`agM#`V(XG?I-v`eH3Et-m>o=!Mc0XhUW4EPCice5 zJcL|iB5)QS4*yhjeGyzKaM=go>9Z$PMC0_v?9eI z>nts8CLn=PQz0gf*azm4H9=uv_90R##j{$n2y0W$>7N}yFyW$EZL z?wnWif=X4usKPvfN&nUzLT6{dvRVdMl&`}CGuQKDf>|750yG2&1;aC#&<}?&Q8d&O zS7c;!q-%a#g-2{~^gGqiV6;(*NCpLx4I*SSy$JQwF${qftG+~`(Kje?O+GY;m};^O z<|JqUq_`4Bs0hXkM0Q{c_5$Zd){Xb?G1^XDTeXghWL zIE!cwTrfI_`2g@s9Z>I^!_!PXv;8!zfT!>;Kp68@g>DcnRsL+Qq2TCiQ4yH2p@XLI z1!CX05rDvnoKAR z#(;{5XpCZ-L?h#!2eo__%%J9Lv-Pp|IZK*pqROJ~8tN|J zcvp%FkbTaQMYJ+&;P}eiwws<)i(W`z@$%>7s`qe@ zP?p^Dp2)08JSrYEA_J(kj)4;KUv4-dIwUD2j=3k=$ASq+8lGYYtvq=GxBGbHtN#)m z%^;YpQrl(CAlSzyQ;$=S8eGWKg16lJ0t!n@J?J1v96f5Bj3sN;>Oh|zb^v(KlKzjy zaD3%%;>Th%c3|%NSPX(?7yXGyRh#UZenEZ^?Owv4J`tJ2 zcCXajpj7o<7V%Qll;X-MZ5U%7JOWQ!Je3s_3Z|D{(XyhFu>|I4KHzfj7XuH z$Aeip)=^G5DMC@?ML&+GQ_XiLalMe1H%qqvOmuHk#}EE_v;1rf-tXXvcEHOJxq*Uf z?)ePYOSbv7*v}>aaX&n~c-!!6fa4tS3BZ)luf?+S7vj2{^(AJ!Emk}m-fzWQUm`#H zLJaHN8F3=AG|9H*`TFj1C)7S>aWg?^}Wo>t(P4-1V2LR5(lZ(fOZ-hIQ;I%g!y}X+dimLy} z+7FCm?{Ppx&_~w(ZZ!9a27Z=Ap&Q6H+~}Lq32-9d3-NA__azHVGRT{`5%O{teDg5~ zds~^U8Huu$X1HWq%}8~x!V!jud#{osG$S8hbzPttZN1N@`c)y0%BMA>iS%c=5-uz# zFRrR8sVGwoA}h_$vXDnlv<7MvHhc--Rt|WAi>Wf%{u3=&{!26RVwWJ52!vjRH`QdD z%+QVI4fX;gDSi!aqVt=0Q?<9rV%_MHZO0Pne%1u&W_VM>ZG$(GN3f@LqhZu|fW$jD zL?J_QM{;2#$1VrHG4Rx$)A1~?!R@S{8*xK&{IpA%4Z)+{IWO5Y=J}v`2 zjK`Avqx@_lg6K%rVCr-65i0}oq9H~DsTUcAp+Ip}AT0R*LTIXj9cvw8QJI}EH6|N= z8vfM4Z1@o1WC#2!V4|<>-%z8zs}|13T_Q>z?EozhfZCA_&jm~ZWy4DW6E#StZSB|3 z;lCXIZ5;3|fQdJD{MGf++aChLS>o3Kk8nhI1u*g2PT)h2!yIsEKj=+-wFA6d4}J|WX$9N=b->OH-UduM*7kq59()uq$&T%R0&q+%9AbnZ z9+%85pI$nlv?@?uTviJ0ZAbXFUV<@bGtLbC_28j^ofTSK&wq&p4=J8Fv9wGc=xH>| zaH=TMCQi7&jOk^}$^7@_ex-N@lwDaoy>w!rvUGA;anM$3bE zho+47E8TT$jGuwtb&_)?Jpg{|5tg0{;2wx)5FVeeSMlu1l8J#U<`o`*AJLy^_&(f_ zX!sABIia|!bY@wh@1*7LNVhw?5P>ejGZ>Gx$5@FPF^QKG}~jKFiT{IQpj zFntULNPAGLn?q$3@7R|Qn;PzF9X~O`G*5s z4@7H6gynD-IN+P=!7J*)TLC)@d>>$Hh;{)Us0VKcjPGOC26zx|XAO1%b{6PKz{Ey7 z!585sb#B8i*MkoNcGkE7T5fYk_*B5HodE(sP+N4i^h+EO+Q7eKSaynVWwVj)D}x6l|JeA+el`gK_8~Sg#D)(5CjH_xh{@jxcL7Ye z4M>A}raA^PU`TiMMaER^4X5yV|2u<&L3f zYpB_XVa5&A=8g?Bx`*4`2Iy}u;sM`kxX~fUZp-Ozb7*738_-KpKaFt9hGUPnjsHML ze3^ZP(M7KORQP185k|CDG(#4RFmjQE(wlNJ0G;ifAyei3inFI?RGY3NhM3Cd@PkW@7yVsp}6FxxWp`HTqj&iZG1ceEVm+)=5a=zQ8~GC=In|I zgeS?jwIs=`6K)8YaEFl&)p#;b0&MtJxJg%Z#$!{E6yj7oqzIiYvJ+sr^I{`|3d%6d%^>~{kp59d zDh-G8M;Td47(!L^%1bH>N~)%mRFuq~F|OpwlCr8wj4{?DQW4@ljE5A+gqgFaPb|wN zYpSBMB+$o-5P;EBf&MaZ65#^~=Zu#Hm`a64te)xh;2EUb2^pyHvogGA;wi^N?pb)8 zkt^!Km4FF1+KO8R_iQ}QxHg-v1fJ5e;eK!vZhb|_nWEIeWHUAax)4-^d5!hQam>8m%)8Ko*VGoh-Wz- z>XXl*byT57!~b$T-yuy>H1(CvgyJ&N`4uHI1ORAIwP__! zj5BUG_Cdl^YYl|4)T5C*thjC|gKA@Is9es*gN$5j6ebD~t@{q_cLZ z6jkZz(9E(4CF3TPOrPGOb<5TrTeemMrP3MY<0`9Yati?u%=fElv3uY~+F|$sxI5r! z1r;>}+KnW@=7J)PMXJ&8qXO7|6Q&eb6r+Q$2;}Alx?>Kdd}d{->Y)&5E&|8neIwq{ zc$ZpZR+H*B6i!0k+280pu3~m&)r1yPO6Im`)3SBTR;mitgzhUyXFSsR15YL%C)@)t z{hX9pT@NNo$#1SOnuqK;L(oNKP&K8Zq1e*{_uJwvdrUCWwLJ@J#!fH>YB>{TmQB2}xFRs87}FZ0lqzSJ zm(Q%AGB;kVrVM3vBUijE&v7O8FPJ`a!n8`vrm8qdyg!0;LZZZW5}B}Kn=regvh>Q5 z>GJ}`6K2gWttbi1m|ayecNM}1P=;`lt*j)KuM)#u2tv|kC%G1GyOUjKM}4ND%M-FI z3iDxFY1!lg#gWnpNX5UzujrJExJ~esmZ8X%Xp*o*$l90|sH_6(*a(D6hQA%2Mv4TJ zgx3ZOKngLcp9oIdChPQhY}qJ8rvA*1+y?GgJasZt`9OgnE+x1ZaY%jGamZ4MbHG=@ zoh9!pF}8-hpu^9L{Oly2cksN7XBVEWcy7mY3!a5|=HQuuXFQ%u@C?Ay4NpFvR6LP* zepu*dpW*os&p+_IhUag1cHy}b&ssd!Y)*znx^<&}If^ohIWMhi4QXs*kFZ_5KLB*YP}y=RrI#-Yy@SY%~b@F%V2=$7pzn zUvi9ku_4(Ye_15{%|H{;KE*#>yMVD>(B0Zf&h?3{L1HjSdxdkR9gBc29$&%Oj1xdT z4@@igp-(e^?k}mQVRx)V>MJP3E3R_HpkGbjDY)Ck^?eQu(i3u2snO6ic_5`D7nB+) zp|7b3jotW1D>UN!mtnXZ#nkgPT(sN3*b5Py+)QhRAi@pH?`dFiM{v;zJgts|?`Zf= zKwN#>y=Z-e8IBS&trykgPD=EOO^p45XqoR4o(it-sbXyYr!Z?8A+Ww71$e}4bV8#L zsyP29vOMI%zr*;aFGKS?`4gJR175CQQiR!!lK@slaeZz(EGu|8^ZexDdd@_cMc>2c zB_G#{pn0_W&|dX!Fl{gU0q8?MWB}|GG&QXanN?!blbD?Q9PPR_*Tw@aVxc2n{tSrr z6jo~iwXMB`GKjmtl1Lislj6M1dP$LjUTSa916fRNn1Y^6AMP6^huOvpPBp#o~ z*xnzpdeMw)YbZ2TQS4Zc*=WKRaPbI612=u4-0&IjIQU6WGgTyQ303HRir9+g+_huG zko?_@U4T;ho}_|nJK?QQzK*f`z&GDxsxsOvV#!>RRL>+Rjt`;Q{enVIBx-&Lm)`Us zV;>;>*jM49?FRw$#k0{3pztZnHLl+b5pG7nFJSu}ok9OQk$cKdv?igg0+F?a^iRpi z_+`YMp z4!ju>Tu&Qr<`X!Gd{z~h`20?D z12Am~Ny9)2G&M9BOFZ$MC9cKPj;eLREDI9^xm*iij*#~fGz@*JBe4E^UyO&qrNo^s zqTLc=L5LW%oEY>Dj4h%<>PeGz^QrFbA&TN-$=GdRPFf=_I^P5SUmw9(S}YemVJb`} zcLaIV;+^gp?HPNKA`j+b!_|zk0G&N5mEM#)-DpLq zqu7I%u6%Dmq&o?7?RLf5lB^szCUQYmBkXFW9Twk^gZ&W%X8uFznjdB@@E1etu}xnCh0iAq_Cr8VDL2!aQuK^~75y^A4qjo! zz6yYz!`RzU23cj4)B*$+pWbDmocMp*~Q^qJ@}o`$cu z=nhEbKVknI<>z#N)E#5Ge<92^6cg;oC-(yM9YGj~^)TfuAIGu^0dJRmqHffe7L-TFtqE%%x^Y#2t{}hc1CFgGQ`A3N;N)h{`2y zL=)O{9b+3QwOia=&)tcsjN<@?dU;5H9V+8a3df<+BVkCqNa4OmuJ8Vhv1iFAfJk+G z&Y7#lwBK4`F`r@u;fs5^5__(nl`DP+CK%Z+Q;a?|&N27fsgKsB>sb z^9M1}?udj0_VCpVpJhDBL&r-Q`vKx+;A7@>;xO%XD(C7cnAD+UT10UDohIn+pm~h8 zUr#P+Fx2|ETQ%WBzP&;?lR#bHG=B$Ky^$GqZpCyPjD0L65w#(&&&%ebpH; z^LwI(^1Vux57pDJ!m|G7pRcjy2_b{Pnfyoe`9-_WJDnhh_|qT~M6+kv=Hi;6qt zKr}k)&5hgFw>CthwLCTP*@3l)%x}`8A928e^*WRUi+h*aiTa^6A(`apjKMaBv4$6E zFoWV^hd5A__b1_4;M;c=~P2MtiC??dJfMRQjmd;`tL^2xmWp)L%mmozd!ecam2%02l_nw>X(I4j~AS z25`R?{FRB-M8ilTxnD61-XzZaEqL;>zad{32>xaWbE}-Sz-Sekk6wYq-=B<#=>5ze z#9zN77kS=KQQwNq0`ZPesBaQoN7~q-2=3s0#Q__{(b#;1K1HFwO>}+h@b!P&!F$31 zBXlU4#Rp&~c#yUK_kR9(xC3y<{f2~Gkzl<2^Z(w#d%AAqWajJa;Hv-kL{A~&ccQf1 zyq~`XfFX8b?k3K}+`NOg0TAQt zmNyxD%X0tgI>vg!yd?Y$7omuzn`zYilB4>dvn2X2MLK&qHpV1hf0rZaL<%%}J_5;o zi;Ps`sRi(q#}*mmwb(-0bFtAZWc0VIhGd2&`SLnA;Nc1Jec^u$g6S_w@boNzpC1E? z(cBwP_ZkNPxZ=I;MDnnHuJ3pQ_g*LB1kZOr=irHJ5qR>riIBZlWAg6z#YPI1D0vA= z6!%|BG#viRkTI3$BIL8pS)v2~DY4n$I$Sl1K>+#qy~*VDEYR)x@%5_Wns zTq^+Bzxd|uUCSH|DSr4c@6`_2Y*@@4<0w+XBi=+;HwU7@h;&zLM;%g!B>BD%!{~|A zmS$n}x?euK)acM@t0N}Qbj7*XI1tG%YF=-mL;90vXk=u0FX$*+EHeV3#}N1cYQozY zaUP0!3;@OQQ>>o|Kn(Y<&NOFgnZAad?J6V0v*`r5Kk8SD;}76i&Q3?oGj7t8yw5lh zD|9bw2uZ ze+9r40N%w~40A8&;VAAWMq_uC1CiBVZ|nZLd2NWS7(Lg0n}gNjW6kR-a%h0;%ZGT| z0!A}4e@ZEn)4kFmK>QQzDp=@1BrNA%_dW-K6Grhr#YOkSVWEI1UdsKhn+aj(pT4|- zi#w{6o*(BU!L@&hO`?5K4*$e9aj}g7SA#-{o#SI$!@Zu|!N$G@&TuXLNW%Rsh-v?j zdAj$6g$NN=k(kvj;Z59Yme7j!Ckm(vvBN3^70&*lfb|PcUhdm^gzFEce__PCKXk;( zdWw5JD}XqJBm+r2GX6KP_#}X=_p|)Zw4fBA4M*}A=F=S#o^>(}Uf$D=DkJT5cO6yq zGo~9!6{-4DVW5lRy91GeG;vhT<;^Fo`AaLz^`3A@y7(a~i`CU!{SyD0<_hP3hU&63 zS1Xi)nt|0)Rwc<%@O(FO2VSdtB)o@$d%0t>}Jw<}1iYHBV006X9JZY();D&~Z zB@LD5K}|!XonrSx!bbsYFe1^aa=qHM|7*3g33!&u`ANE$c~F0lGwS|s9gfxpNzy)#c8iyjye%eTLuXG^XYlO#pEf8V)yE{X}F?T}; z+q0iBGTliIgy`o=c3sZ8g#La645sok4D^9^<>p5qw!xfAVy;Q-YRVNAOqqQk_;OVe)POy1ELiHf7XCt z$;GgI=VIi*f8?7-~$XLXsU;=aq&=6ySXrC$-X=RBl4zj?tX1!)&j`0NIRCtk^`BM`yqh4j~ zBbeFn@FW;8v1G?vjEho#LTdiqiSc(~@Z?AT;=esM7Wb^Myfk^oEk;r(tG}HJ{4j{= z2tWJhACl-Rb0{A_1~uamzKGJn5Mm1mIUertpr#tY`3-7721HS#8rgj{IK7$DemXJ! zVU%|lxvlaZBKP11z7xRPKmX8lU;cTPe+I%+KTG9*2<7*{stlJu4_D}%X9cJ%wdGHi z&DI$4Q6ry%w8SZ1c)QCB))<*lc9l8m;=MJdciPO@KhXo@&Yb5oFTxN;%5#gQS$cw)t8hA#c6>lb!`9qNrI})mY7l53CSmv1kpF`yHC445x z&(<2v+IGvgiw=A?`oWixjr|jtH*-}vIxyEa>E6k*-#VksUy8VWosr%40%wl#Mwa{S z`Z=zsm*awU(n#ns67l-Spp?IhCZ2yH&UgT1-?_eWB;b0#gKJy8__xc!5>sFi;FEa2 z0u$EIKDifjwH5=xG=FNMNlY`fxkW|y1tPrq7&3Fq3{!;S?)G;ZwfaFw=b{eHSS3-d+mDqp`)Auq z%-!EnZQ=9A|Cws5RJ6-Dl>O8waN&;((4VF)b7@yV1r(wv>}^hD=6(cBdvp+@^coAZ zlj1##)+ajW;&c~9JIS4rV%*UQl%|w(uXgE9m2pk7`??gn4yML%J*^NH|8uYP`|4Hb9SRIfV}?@(O4 zml5ZS>{MIO+|(uj>SngMUgAsY!Ap@cb|0?9wp0?S)-@V#Gl>o)G#gV`b1~nC5 zX7bt@Ce?&Dq8^Gd?YPGeQu^bNd3&Dh4mEew|4dEZf&0(Yq*qs2C`&HUMB(?{3BYN8OYgu6kN@5fJAsl4u@BG^BE z$b0V6kixQxlfBW%&TV$U%Ka@LmCXL}!w9HhRe>cnfbfN6=|&?n^kR+Gmu~cW9|$*A z6l{~%>!InDa`1w0_9n;E)XFjdn6!weS(R<%&X0?wG3Noe&o!HJjQp(Gl%L2QZ0rip z!ayKdVo}y#nL?~hgwp7g_E*yIh0ZdsbG?Jj{7!GOcbx;FZQ`BWcR3Mv@HXB}jxnIN zmRB)0ze*sKzDEaX>h5C+@Ht9)11#Ja^L4yQ$FQzW6CF_=6sd`Ae-RKIUf~o zGsU~#jJL~-r=lG?XEawbiedi(59hc#I{K)RMpkGVYIPEc@%y}~o`F=w z1nerP##9x)>B&+EXvzy+FFRi-Wz%@KZO_{AC&&#bTvuyzEDYLzBpPhW} zCC-G#@(g!FCt?Z)_mLd4l^&03iUaz~3 zs6OkB``;JUiAtQAhgh^3dYU5_#iCsR!Yn#Oz+Ym~^WG@$19IFJqtTdQ^}ifa0KJ%U7PagMp*`XPd6C7jEk<%^0}L`n%*%by%s(O7 zq_!9pDAYPE);hLpnH#i5L%IE(^ycN*kK$TjZqU-OK}#Pa8M{@7&sr?st{Va|#TY@G zkG&a9N*Q|{-K{nsK6QYQz28g2elPLHtFU~*S?v?9J3Z>;Q@asN!>+OLMlcN9^g26UWFjza{5ME1`a9k5zsq1-OjJar-XI ze_(l#*c5?hGfoYCmTyK`+ZS)irmFk%QX zkc-_bz!7qf&Sb#b7X)xh~Vqr$PsNwYW#=D=tu%;JG7JE1;UveCH+n8{r)&GJGQ(7@fwa@vnjS9t^Sc%cIp2TmbHKE!?3Te_ITTikJwI*cz#< zxe$NMV$U(x?_jI_(?7xzm4nj6oVE^FG3P=jf_M{$8rwfLnt#i|3*yaw2dsDl&bptk z&zxjOMHO?dcQAvPGsXe8`qR9*48K|bV-uMoIhN5=h}otlBd0{DhKU}Ig=f* zV$K35f|%2+K6AcwFoT$L%mIg)^G1E<6gaA`n6uWw3}O!52xkBOJabl{xO*+;bU;au zoSQi})?-dpJ?3|J%q4GBM%x$(v&F8JJsA1^dF&E~N9)}1 zV8r$svl#tE;!?O$tmw(AQAMYB_*-S;~D)za`1n-8S+Xl+`^Q%lU(HF0u_0}(C{4Na<5dVgs|;riJ@mexrbv|?_leU#>$OXhY_tzyJq4aTmgQlvD9 zrxNXeyG@?=FO6}UgdBo}M3_qb0HvOqr&2%lKS{k_;=Z;HMC(-QYeJejctQhV{f?(_ zJmeiv;vja@%888q_$h!Du#{$Dtm-~#0sXInuwqNQz#$QR^&0d12(DA(`hYFaB^2j# z90!@S5u=??0Ne_5sO3BiN z-#2FiHZ;f1MkC9Td!adsXF`!g?0dIB%(348t?oEPJ~$bXwOl7?^=iYmyv%n z_|JR=5bQGrQ1`IPZ30a1xE3$zwa0W!1BDCqk*z;BxC;so(V9Wp4wTaqkIx=Hw(otW8+N!c%iL$=` zIE0#xe7UXwbPvYys<7lW@G+|zvYwAQ+R$@uviva~G6yYshs74^B~;crE`n|0OlU`# zI-wSi(`t>N9z&m@Q-IrpmOeLxc2B@Rq4j@(aFAO*h?DalnE2goVE95Rf!95U&`93_ zKA*^`&bsxy64$t)2JskEEUz)oy=CLv+Y1-rl0wKSxvxi<6iNbNPM{srdAN8w59dKU zpR7MK8O1;;vY(^{Q*}C}@p>=`F|x5|*gQ4Ycq+z36c3FYmHXL+daZr|0~|Ezy!Koi zK;zj!?i? z;!M#e0I1wep)!&nxXS113Ap`%fdB0CDuVrT)JkplD6C0*Xr)#@9GwYNlS&Pj;4n6w zAWALi}r_glz=AREBO6^hdgYO^-wg@l^8)R7Dh1XeOu03Nuv;+$wE*-88QW4)g+ z?ZDVNz^#Wdq9C6v5JJzKsltVrdo>7QK_>78vi;!%0)7i01HH3~brj9Sagz2J?Cg{1 z#ysD{sy$td-7+ASrw|pnstR+4?_0phiRcC1wSaL@xj)*#Y4i`n-UIz964M?biUbQ` zu|L5`1wTe^r@bW4F2kAM1Bl>Gwq zn%+Jl#Py{QfLW-C_ajj3D;Sm^f=J$xsMdXme29QW0IJ60{ugK-<@3rW#$F|We2)Gd z`}0sX?<+`P0Q$502^b8^eaN+prJ{Doi0kF3;zp=D?{%nL#@lGy07BkJ5oz8$EOQ-0 zBm&M|gnvJUo7y0OD!E=4;|?5{vjo&3f__KG!Dw|Lg7>7`{V@zA`{(`CZMfcbAfm35 z8BZFqp>#6u2Q7iI3?auJ8AzIWaA!ZFh7UsfoCl$O)4%&xv&~z#~3|ObCAM1;| z+@jGXosoW2ep#a6%zkoV6hg;)@J4LYsY2{0hNBpP0Dw5q&$gpS+<<#xVz~@HWh8`tYiD}We_q5h z>8<_j#lOd?J@#LN8;5jkZbJ>3_9Z;^=hAT75mIk>AsK=cUVkjZ5sGPdK{aTHDf;(! z;_5skR&WdiIvWsDUN{_$(89Ic4Be_VKbIQ`??a`ufv~ppq&IK@4xqOCsc>`{5tTK6 zYK8X53>@2ne7C*aw*wl^{QBo$puq@NOf+h)9KkoWA1 z(qAH*Mo_IWFCm0F*Z%(%_a)#_Rp;MlwsSKHBq2KqkN{x`gfROOLj)Ig%BqM9VU{~2 znq=Z6Awk3e+gf+6_0Q3|SL;?6u-dU&6|GxqZPnUJtx|A7t#$vaXkEVFd+xoHI}p_N zd!FyR&y&g7mv=kw+1~S$8QQ(}QFO2UyLoh#J$-;lhl<%~@YmSbBa;sIPJ#aVCOl-) zVdp{^v_eD6Rs)?o)?uxKJm=GrBS} z@HVj8M1|e}RBMU6<6*%ATB&CWW!H$1YO`I}%~&sptg@5Xfez%@t{Kqg7nP5LslVNm z{^oSd`YHi3_#40t&8Z}V&e*U5#ANR(AA16;Y9Q5`vn=+%twsVcK~1&AN?~^AS=tUx z&5tawECaboXF6*pTkNpR&+Z2ZYEqW8{g!jkYVw?x6JZY5X+=(!{s&DVz>aa{SOfSr z%K)pvcDAzXpoHu}UVzJX$CcO}*qerL5dn|xa@i8!2j@|ORtzk=oEnPsZp2DAvxiS?q>YgIZWMpzAx2zlR8z>aZR8Fa}z5qc#dz-ZhBP z9lTs?#f@Wf*m^H1gxa)a{|XiCN>=cf6` zBDL|A|6@Xa)a>6gA%B6{e-({r)_uhBa;0+HQ84EZTuw`0L2#OjSljK;19nr`%TNb3 zzP$DhJxDHL#F5=*Lwnx^9;P)yo$=fWz6Cg^&4iklQE_dZVva}5IS#Yydz=zwO4?l1v3M%r$`%s@`W@PB#qG#I7YhFKC3N)P zd4s7Ax<7$x&o>24>o`=kPyW}{W^2{quUEU|M(=I*!(qBU?k-e1CxTp9wxbKDA z@wT*-Ai=eV;tX)*0oWS2oTmbM##hTNlUV7R*??yi;9r|Al*g0iBlRJVC#|DM<9N~_ zm8-+%Vz`Hg4XP`>suk)ZrA|Y{0#whyoqJJoEk>|I+?3pp#Kzga;?Z!=8Rn+ssM#T` z&>^-qJ%j%>OKk9O@i?m{idm03{OON4BpzwoV+6V|3=cO zNOh_;vKwTc3<7%~bKh2NewADP8~~k8|D=60`0tC2yhuG`vT_Ls#rUwyT$6jucMa}A z>_(@I&!XfxOQ)f-`KV#a-WugHz{dFK;H-Y2Sqqx-N?Fc%=JXUCXwI$RmH7wwrx>M4 z`%kt#7-fq0Lf<$W`ok+xF!Rfr$7C65-fAW-L)F7Fl7jI}MOq1y$D?d}mN51L zRMbg0L-ZXz+PXw8oY$VO!Fke|+KrY&MQW>~WIt`N|cVkU@GU}{M7imd) zCZYjfL({fChm_WCYR_jF_b2cA1IP=~$TAMvhw4CZB?Pr~EPm`yKr6TqaYeZG!mK}n z%lbd~u^+b>rk61(ZG-}aFb{Is2Rk65pvH^?ViEQ^YAIUxJqVg7sW{vZ(=1mN{o@ws z6{y|TMjFr{N|8KfC4DAKjo?#u&1ZdSnTs3LH!%w&?06vFt(Z8d(c4J%bTDuqEP!7(z?iHokxI~0hjH$ zhcu-(?*g>(AdKyw#uz2bJKv(v`99-AYFIfieE9Et&6%HGY)aw$f)H%ma00GN0rq(V zI1jgEB6JAgC2s{(ZgSXaq2iNvmWP)B;jLJuwKPw$TBaJM8`N_#;FtHB)mHM-fDwg& z*R7)){-gzH^fzS^Pz2Ra`v6_bH51S;x~800Xf2$8DvU30j+o6ccz5NPYt-mWDOpRQn8IQG1dfAjL)H zJ3#I9$^PeG>@2x`rWQcI4v3P58}t+uFp5M;xx+NsL6-6r%P3C!M(wC9dCYD2FiK$b z$t~gmppQOHeh9SBcKJ=H|30kwk0`^m*(^a$1L=J58u;iL!h&@tby5!3`PAo2F>u?1 zXTa7Qe3g?Z>sBPISh_}&l@&iFrX_okm4Tgl&`%ITbFADk;+*tmJ$?ay*@T0Nh0bDn z9qF>MDE10lQHcRBRKqH+wAFtHc;%gmv^%MQYi;$VH=j(@vgW!+cIXJX^?< zbe8S|o-QNz&qn865BGMsYp%7Hl?r&Cgs19ECx603OKYm`ox#5{1(dz*%zwx1zjFal zza}f?P{ac@C5ue{vb3d8xrG3ue`PRlrfd@@{SJ0xeh&;8-pm$SIQa$5*qBqMnTibrN&q0 zAK)ihrR+1cw&FV5XytFF0RB&lCGR5y>_^ek^E4V-T&sDUcj|qiYO7weOjhnkS2giU zA+K91^7ooLU$$RU^-4Z8@ma<7wy7mi9~Gf}jxBl4!W>Sd%do6$71B&a&6J@m1h~wG zXB2M1maXyP(gi%kfF)gN_>6B9uD}**V9|m`(G}R-(s2bgH$Yy2Wx3>kgRSX1kiW!V zw*M8#BoWv{4gKy2)LZ%;@%MxrQ?xu4KeJ6z(qvQ2_#7!`>$KMa?z7Q`il5}?b^_XV zE=fS<<v;Q|W4O?o`Mh!Sg^HBLT73}>Cpg#Wo@@Y3>qs4TUt z?4B;)2`wCgEmo$=}TZxR_h=@?s+%Ibb%R@EKD;t!67x zrkFN>UJcfv#s|u$-G$a(Om3}LX?8PthUwPOXo+}qYsR;9s~hn18v;{ceYn- z{2_Xk{_nfR@X3=k2T{pg=96wq5-MHn#R60tW^(A}9PC#Pyp_ z9N>cXfTYT7(;!D#l?zS6L09tYSZeJT_(RBFL)|+tjzfG@~m8m z77Yy&EN{?K>s$sc&G-s-<;kA$D@}p<(?>hcHTerhSZea>0UG^F1$Bw3%bJ2J3$^|5VTg?Pw2{vC*(w}&*rR(M8jYp<*LBZWv>^ya zmVkcfeBeg+m~3OeiZzOb*ioZGZ7*!;z9A!YmHCcB)Y}d+ScTw%l_Sy?>v)JfTR{v8 zG~x+G6#$sR58+vjfr7HjWOLDcr2}EoI&N(3QaWsVd?mE0ch5={#6q_3fQx(fi6{jbGt*x4=nCt#KT8z@QkR}ixtyI0nY z;6A{)tlN^c*jsj>#ojXFA?p?-v-LfPbpU{_`~g}&kn^Dj(IndyZI~5;LQ66f-hlMB zeHir4r=a;X!ok?m&KzcIAAeFuszv?Ij9jF+R!smmd(|yw1dLlp^m& zq*w-ANze0$Z8tom8*YK8>{tud0EW{GnX=2ot|jln*26DN6@pl*l$KFOgS0vnTIevo zlI6~6&Qa!od-FyxC21~oC2ktuTBPy6%vbOsO^&H`##eHTb9luSI3O$IR%uM!ayyfT z!mDr-ef&9C`FR!N+g6MHoog6d4D43DXR$xE7CWZo-VFrE{pAZFNJhR2{;#4_#%4?4 z{T{818ZzBrN>UpTg{>bx; zT>%j}mZqgvX|Efl;vCs8z@1>z>NUhiH&YAGgOIo?A3+l-NSfd_tZ7%nnzq@{cbbw( z#p_HI1}@P)gDU?yU(;o(cjqZjmm0Ci@{hb~g_{E|c1~A5Dj7;qGbewB^L}%{3&xB~ zGtVtK!;^jtr%wT{x{o!w>Yv$w z`77mEVPP%`vD6W6mY`TW+k^;XW%FLzv;~SvXiZZ8tV9|9ecoq72P>B?nQy^&yMicKYv#=i>SYUdnjkFkaP>B?%mv}PZX=jB9 zFiH%d#9tj)qAYL?QfX)Qsz538K%>@$p!&{JaNvME%ZTW&gKL@f=lHd+0@$AhH`qZ6 zw)T@4`!!H*!NpuFIRHlOGax5p&$aON2(GF}3(vERQjUkmRWHtd!Lg-~u5Q{#I}1)i zo7dHgid~K;)2APU>$b$K@-P4`(~P)kDQtt0G=XfFJUC+eU0BJW7T!T<(TGjjs3yrV z^1U5etPPJv-K_~x^Ak!br*^ULj)kty(Ldl@P2O)t1btH@f zTiF>sNNpg&g96Vo6?pbkw0j7vbP(0{*q=aYHekv+#$u)aw)fY8`G`EnS#0@`TQ871 zfMXqtFgno4I@OYs=08l&Y{@&Z7(g}R8Uj^sOY7BBpe;0yu^Xw%EjS(eN5(!T_hmT3 z_j??6qw3GKnA0%Z5ex97B_-NxvC@Cr(g8gGKmo79qDeKct=LP_Kd%c+Tvg4oDC+kJSZpSj~p8CwD-HzuhjxX+D?550$J&s0;U6 zEE@4ThQzu_m}Y8oD%;bb;U5(08ItE=1+P+^{)(fzc0LkOhOd#K$CTmuM{rE8SkKVh z4vAPHo}mnv=@~|2sNQMH@MB=+M@3qO<6YMyHzXSjxAQizj`8tJozB_F_@-Wn7w+Q8 zL(RNsC*~nwXMT*uVdOD*_o2)2xwU#YeBNDOYq$FFlbFGM-Js7%zh6>i!jaHI*U&YR(#3NKZ6 z1>Ex~N{9D#UQFkThp(;>VrmPCDFA1Oxw@Y_osXcLb}AFq-2?ZmUPU7o23>Xg+*Y zoel1GBL=lSJN|5((SgFjI}b+FncH!Vq*APS-O)T@rT-JhZJx+}#UuRAJF zy!&K4EJ@HBCf#pA(W^CRTK!^Wf!9rZ00{Wpy} zI1gRN`IaUO+wO14lT*=#qqVyy_JsiX0Wp9-Jyj*6aw;nMGwkxc0g9kZhmJX*I8$jk zF^5E6`n%%w(8c96E(ha>&XbKv{|BU%&2(TMjAGgQVD{4?0*tF&_BR?eqNPhj!^WiL z{i0!`{-4C8jf-dBwFxhus^QpydraRoPYx8UTgbk=cFD@VJpCvhb|xPI=$FuNjQdyi z$0p*OJVc+2*#{Avf;gj_S@s7m#?*#9hXU%i#h99qw%C`On_!AMAm-4GJ9NyUn{C95 zF!xB@_qHRPz6FXIeGrmZ%6T-35dcaJeOkyQ=xJwa0eZ+y2$`J|@&-bd=Y)hH%X34{ zIVj{sgmgDqwR~D-^EVxs?Q(=nz_=&BJ62?_{g>muYBB9Y$D88w0=_?UvW3sLWUAkF z9A_2he(cyT?)%Vjws>*BW437i%+W)xb-6#GY(z+W{-NWwOrV%I*e&}q>&o~Q*4dU( zmJ&X@s=Lru*j#X!BT#TSRw^eqF+Bj@A6Z7%Vf>!bbU;#sLlF_PDtYDnhOx|DOeGyx zu%=)&QcoLeaTgy`X?Uko{E)Y?iRDyV5GatP%@B2?al)WUY^~%Kr87raCKZ-CY=y;@ zHHCK{f!bV|TPt~w)l!?Osph+F$}&66E*Ac39N%KAis7+gdeW6M7VYoojH@Yn1)f>N z8RPjl@r&_XD6?hy=56EnSh1^`PZbwV;8migo)?MVn&eHX->?^j2(2Ii7 zYl1^I0h9o|agQf|hXD{^LcMKDqXu9uh8LPslnhFb>>argo}!m-$)ToFNtpl>qmYNYPy?n`yxG2EC$25^JY^?sr(z12ks?o4;Y{Bq zQo|vdg%@@E+IqC6K}nA$2elfJsJp+5+J=rqQXPHmXGh_nDMAF8NEyP&|A`a_9!~shu5ZMgUg|&qMLeD zYJgrXCJzBU>rpa=lX=Kyeg5vu{04rm#o`seo5sgwUY^DmSPo068$hmVTUTVTqbt(c z*4q!_8Ds`Q5GafXXmO^>INJq)jJvo5s@Kwwe&8AwGCfWKI>zq_xkqqpreHI)>1xcE$mBs%eT z7k}6}N0QtT$)xzHn?GyClhFweZ?dgcBjOSd_gNDO@u-IfiuC+#y`9N{ww{Ri)Wff` z&Fw@gFJDud02+GR+q#q8ZTd6S9pX(dzpEg+u}|$)``ThDRc!O|SYdBpGNqzT_=zXG zZ~DT=E%`$)TZ>TzO0}p6@V^yxCL?jCy*|z6q+;UZ06)T+Q2S!-fL}Bd2=W?BeK62g zzj#$Wy*l0`$-GJDr>3Eoti{sT_!v?A4lghE2f}`DB;@mk1A&NFO^EHkkADSxpcu(FwwE2iwD;h%4mw`JY})rj~9pE$@7Z*zJ%&WuYHM_ zD;!nDqBVSjsGG~n#{|3*%+xD%gCud&5%LLfkf=Cg6>ltzxg&{iAnFbT{i@%UX`0Ii z>~7i2u;ew8er|81w^vQ|wQ293qjO$w+?(+G)rc?Ta{B_R7_}2(`>h6LoY=RJ|5jA= z@Y2bC$qjzuhQUC8H}M~`hCH!oEFAWE)o9e^@~L9^7Jfu#-y;4iyHFSKn#u0q0VLbk zj%*Q+Kj?D>+`gdS9f_)O@oWn}Ec4tl{-D$Emjcm8bmaDckPblto zx#KREUszAze-IyD&&!H~algl-x?G4?Rn-+2r*-j3nRid%H(SJpOZkk#fKPQNf z72qXAdpv=->WU_S#YhBr6SaH!)J&JkPqEfOm;@lX+M*kgFcC?_yopdG5Rdu;vCLEJ z`Q8!4JM~q*0SITL05<@q@pv@sbNiwYBN8!h_3>M+4b;>jdKhvcn?K5Lh;v? zyl_f=zdu^vAHu(QJ!R9g`eWfhC>r+!{J}U}5%FM%S7p2#`CA$}O>ld2{8GMgLG~dHVisfCrv|NL)8VRfZa8M0;h;G%)vMoGm@tO(F zOO%qOIDZFq3zfvZ9#`1y^#!6akW4(B<&d$Nueb8Atm3eH_=pK^O^P4W+i{xeBf*KB zZokVHQbX>D-xGI*GVlL@Z?+x{1f+sJkv=efx7(LHs(3uZk5ecs8jc0xL6VpO2%}8o zM|=|(e_g{TrhSQIC)MT)`@*3F_$m+y2V-jJXx4W8_e^5Sdyts=Mwy*DK&nd-(9|Sl zz`XuQDlP@x;$`)acKFfpsN*u^@p(NymoFOdg#FQYNVLx3M`o%o2J7YSD5vFBI=D<|8US z?qP=74XW5WhmS9g`Tf3F&=dE?qX9MU4~iXcpqR_r_%X$D?YeP#knd}r>xAKPKc*Gm_2fdz9C=MwX z2uKMn>aOAWnIGQBZT6wTHQ*z@9vvd}w#i_h;e~@v8Bq+Po*)=d4XGY)X6vndja_v8 zje9fVPO$AvbAQ|zrVWLQ#3?$U`l7C&FA<3aRF^jp^^4_4^U-3_)tEh8w1dy`5su~X z5zw>{AM~U3KBzgQFGLf5pT`?_`C>j5GSaVch+ka%G7wBumw9Ig-_E7zsPkwu0VwMj za*;V6i+B>KI~I-x0&%aO#M9`^ZTIp#-uO*C4GBD?fJOQ=Iw|ycW8s)D5e&N%ey=Zc z^Mib*O+vzs|84iczaW6<993v8}`6HI&XKHd-`we00(UwT51ocon5DiBoYAhIq z!|%~h78W~h=Myq}UVxf>)VH?vKn$SRup0M8BH?%-9`nYaYKV0UcwOl*`ikW0qM#aZ zgL5NlBpQeAlF9o!Uu!7}`g0_zh+NOd_;l4HN34O2b!}5;0*r70w;BmzkdCOqm{`7> zw@CgHSG~kPXv+%iKyD4+St6SRGCB)&P<4aZqG7KG>V;c0eV>OjkG#xx*d(dfx^+_5 zhpmV|7E;3yxIPtwxYsRSzKd5D1;bGwAwdH>co&#{3Dfq?d%3$#Yc6yYFXRpCQxnlx z%%x(~j06%Pk6Y9w`CRedUjE~Jx1>@jX`Y?WrxZt`;ea{3h8h`$Ad9f&>M8gDS~+6ZT|ciFQ#8U!U~cl{E(bc zzd!7WM1wA$OYEG_Lq&;16pCXEdaK9djd;bi|KdNLIMjosskq(?FN91ago%g?szuzB z@x8-;!o}17%a0PfzQ#II)|;Cl(VCm}0l&F< z6DEnB6TP0z&CNM-K^BmC{3GtMr?a|l3#3_dv!-V?H%mRM6*Ft-CBw#0zC%W2Cn&?F z=vD{&T4L>yR4eNj_WgWIBW5Tqgs4`AS&;6Ma~IvMPs5~7Ec}umQLIgGjp=D$yNDdG zl+Mt~$eDJ?JdJf)jKS^45Y|^<|zmG3QIZ zxiq(NTE@1od8N3zTXBj#s}yHu_m_Oo5i!wWi)3_Q7eiljGo~sMJHx@Yl}Wthq^F^B zxU}28;8kN~iQkZ-vPiDYG&diS9Mqup!DeN&*k)78#VI!BFXSo|&s8bq)q17F08q@b zE33t9hf*U>vMU9d3*oiQCZtQ6)4CEJWt|!YD25I<8BCW3VU__v` zDE0|;Sre`P&5Yh^?eC@pZOK&2WJusv7Ht)K7AoV@_l`GGy2G1U7_Haxd4ikSi!HF; zF&K&Ut%U4YfEgO_n__f_TPsbMkn{{@Z+{o2<5*cExhh2l%SRiq+Sf+&F>JEqv=1$i z9wXOB8QGbjHBoWaiON`U)gr}dt(hloUZfmuo#7U5EmFcwGv;L50{GalaH=thzC8KL z3>@bL#rFtrIJC?1m}1$nba?>%4A%E4%S9Ea#e6 zgU}Or0dc`{Wo$J)R-};zC1R7&C%Kx7XE`5Tu6V7dhQ(({XTvMa;-pDR z)x=t0EfxWtb`O9C8QE`;6hf=)vagS~2-j@oXwqE@gv+U%W%;R?zEWwZAuIXW4!Y4n ze`8%J2>?4R~W8XG;hCVzb2itCeZuowzc} z(aK=NKz!xlW2~=C7Z-M8M8wElQN16sk_1C9tIa~+c8SiC#L2D+>X*I-@)_gyN3EAS; zg)}5(D|-n77V0TG9Yn6B@@{7PvTz{oqL(5&N_uHSqOWfwyBvr`)7e{M*D*?+&94#c zI4M0QAFd-28USfJht>H&Dr8q*jsa?1ip4X<{9~17+YS)IgCFqr!oQ#!*jDN2>t(hv zOkDdZZxmBjE2I3sZ31}{qk;*raRaf5?LE0l%(jDjVv$}9JL{}Q%R&0E_dpDJxVUY# zGPdYyU>`d{Qv5#*ab5=AfGy6E@LCcEczz4ZY z6|5S)lP!_tc^*o~{uH)w`dE3Evw*`9S(LhiAh&y^=o?^9h*`%gC)UtQ$_54qMy-sV zO;t~2n`FakrK(;;yiO)sWOeKekv>tW7biHBlHv<0hITsH)lE#a&cMFr!V{Fj#S1Zh z(<(fprz0crxGn~5kn$RCF=CRT782NUgx2a;fbK4}w>Pq0Wm}Ebi5)%4X!T>zQh#3} zMB7B@Yo8olSPUGI9fN}~hfa{yEW1UR87i!yfzSXzsg(^w$~S0|(wQCAz9o=ioe zohrMsMRc_&8g~B6iBvhGr;^leqQ^FZD%%FX*wV+#O4+B-zm`Sfy4uOgXEL$%YfNUV ztAKdtTBWd>p4A|vO1>q#d4M`*bnM-E!o60RI>Van5X4k9btk(C%ihn%xkHdqJLZbB z)+%*#=s^rg3?vFxf=+W+e%a--0dN{FWo!2pqlp-j(9Aeg9EOKK#P*3wnQe_B1HQH^ zBPYZSu zPLuugC|CyEgf#aT8Hax6=BZK#;dZ4tK(D%I0Z1~%p< zQ_yW_P8aVWO)}m{cS$u4`;)%1iqsVrf(RwKA;mM9F3YY;w6aYQ_9Oz?jk4ka6JOzRV-iR8 zP-yTNt9bT9KD+SrF^0lN6Lj(H63p~>e#%FTc9GPlc~gJ)DZLs|HrDxCSyHU&P$quw z=T)SB9ds^W1Ey#YS`lsD-OFYHIoa7%SAQqF0VtB(PP}AH^frm@9m--Go_Zo`SWX&v zzO~&Xt20|pR{miro(r0acJ#5Q^if~zjib3s*DCqx2laAI%I&>qq68YquRqquoNfRL zXDHfJZQYR$w3MDBCK7`drt1%6aSR$#8zi3$C7hiP0?t`aQ`t6c(kSKUjdGY0sSQfy zh*nkzM9H%muZpo<5Oa87nm|6oI$C0Q$p~GGZ)NwiptE1`r);h4E^$G(GR99&CH1y< zvR@DYV=>GKwi=wr$SAI+my=R^FQA(2>18J##>A-}URL`7vTAdu4jOmqEoSvpZLCHQ zwu(_nW$c7fltzb>B*oFQ%jkD+8*S|By~o5asZ`BsLmv_yDrTDJg0!$~$>^2MPH0&) zC8l#Gy^LNP(}xFb4hfyGi+vJc;=~VlY#zO!q!GO)s?Gp04b?_ay%BV$jV5kPW9WS+ zl5ZFlv*Hb}vT4+C@n}%PYL~dJM;SMQ469MetY3rQ^ETMW6MLrf%6ZpU7%(D*_pcC% z7`e1TkDhG?KxiGXv5tOQ*++mzqZ#)(-eWoG*NAypQu&V3O!8d#9BAkmS+ERo2JEyiL? z4_5X2q{b3Naq*4~j9%%KgLu4SJvdKS2<$ta`XLbu z)DJ$Tr}gO2(<$1R&+eEh#tkSgvA{1n1spIMzngi-fCRnUsHzp;q^aIaSK^diznjd~TEH z>g32p;HMYXH9^Df!Y~JGVqNpIEuLO*J$k5-#ahv{L77tJHlPWvH!0|U5nUS;?~!Mt zG}38OkVC`o;lp|%wVDos5k7j^Vi1c?)0oNRNpKx*`9!Q8Q0h8(@hEiCM;aDMtDuomok8GeE1Ua=lP4dO=CJv{Ph0jf>0v5Hgs8Eh`hx3@Gc;pEw~t zjU#vL34mX&9Meplu6klFs3zD|cJxi2w`0eTM4o$8;!PqTQXU9pivT5UftNiFC(YgIv1c7ESQiSvyl!?d!=(t>HZ87e4Pm`tujaT@LvtR4@xVA+|Gv4x=xo5kq1T)t+D%Cap7 z36VuI?I0iN);?4;C$%1HGu?gZURI5UwWCaSE*xM$0_IYHSt6W{U}?xcU|Xt@+Gk!) zG^Cm?p7sKC$Eq;~bqU8|d)WXA*FcH(kii`4YHy$Lo~BfsbO93U$6VR5$e^7$!{dr* zs5~usjMS>=f_PlblIq(aP@SVNO5AD!7)^k+K@NA}CH9BiLrKpCl;q|LI|7K)25W5z zdZyu*`t_w~JPCTX4ULOLlaMFt(FLt?KV}Ye1OCt(buXcNz&qQICLT$v2>TF#FZ z9iobEqS3&0Q5L;^gkAzff!!Ec3XnS_!)>l|dl}hEG4^EGM}Uu9wW&WyejRd?s!(kMXmfX| zIHu2lq1f^wFI$TT*7Pw|qp)7YWJflrP4(zA?dHjKTJ)QJgyxZo*U8BCAk=q2o6&V* zU1$vLpt65E#qFmn$F3zS5W~pZ16Ys@#SD|`&^uocy0Aha1ysxMI8JLgCX*6WGFynw zV6ziOGj%o2Ai>7~=M|t;MlY&RhsfjuI%MqI|0nyU>rk7nkyP!K| zarkCsLi%x{*unM)#5~p~$+V)*9ddT8;iH@yB=;cxo~23BA|A$=r%MY+ppB@L4wYq_J2Qd1a!hTi?vqZWC!#Mn8ws0>Nh5n|C8Sx97Qa`yH&7=iT_GPv= zAPh3NXN(98Mm0z-RR+It_qUa8Mp01h$%+RAl{_Xhv~!+bf?(5H)FlurD1CpXi5;KuDd}3!D0Q6GW|4=1qZJJz9l1lJA4@h4a{U7k4Ms2Y zrmG7JH)I=y+L=q;huXVuWtb8J3oAd;*T~Z)kv0*w8ajM z6F)=Rd7a5907oWNvZwJGZvB(2j!vqF1lGth1R^8rprjAz<&vpDYOvV*8L!DFV~&~% zB1WZ^aYwt*yn#q>mo&Ymkp^~7e+3A3B-vLeNg|+)wo}-Z@M_h7m*^Y7pOsA-5L34* z8?84Di0ij1bF6O+h-<&EOcc&Dl}c-9P)s;e30uz@)B?AhsZ>~B9L!vHrh+%Y=5M0V Pu=VmynZJKuIbQicZML@z delta 153040 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 diff --git a/wasm_for_tests/tx_read_storage_key.wasm b/wasm_for_tests/tx_read_storage_key.wasm index ad5847598731f92796ee4c4e231bca66a58e853f..9189354e11b0600e47c796ef5bf7c06bcaad9761 100755 GIT binary patch delta 124316 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%0A)TX@p#Cm1&x#4?7S^NO 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 delta 128400 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 diff --git a/wasm_for_tests/tx_write.wasm b/wasm_for_tests/tx_write.wasm index 90f2c3c8d5b31952f100dd4442ff0b5e798317c3..1a86b771ea40c16ffcdf5d740bd8c1e81e5d9f05 100755 GIT binary patch delta 138501 zcmd44378#MdFNfry?t-rrMuN?S-VtKl1xi>Tk;~wNVZWmmRF2z7Kb%4SxC}y80 zJymsY%d%O*Jl}_D)j6lmJ?p!_?>Y6<%*s!{W96Y8{@C8&&4C|QeBT@N0^jq(hF|UT z{E9~jIWJIRD69pZ=l6AP@^861T<-f7KlHo*tN9^WDpDHi2jPma7V@l9UjCnAb>q>b z7uG9vOI5tiS;6u(FMVk>^u0xW%frl1CnsmUX6B_cA9%<;JHHZ~pWWtrso#9bcm41C z1B3tS|C_)5g#R6X*~(RGo2%nj{Mt2_zxb7}|J8|U|F8Wo_+Rv|`j-Ec|DXM9M>lSI z^;i7n`j7cv_1Asbzvu;Av^-|)ZYf7Abe{A%N?pBjF1XI1#S!HM@jus`hggA?!R{842~ z^Thif@*;m*;5GlI^SR1}XD2~gxwP%2K@_B|J?Yc_jfs-M)hpBB@-|h|C%uW$z)OPN zmOI|rRlPQRpw;TUZtdcAnV zRekB%Nu}tjQgl^`D&4MvqN|F!3ew}9J$(JI5z)fAij{>6zKd?2fn=RpSbiu3ckuucWiVp12YPROMaZepusI zS?|x1+BI#j*^KI`zbCFowQ>K}xXoZHQSEo{OT$~zSKKg>G}F;NW1g4v7t7XPEL(rn z-(9w5x_(d6)D-%)Zq2l!=~2TUsy3rq)Yc&D?G@^;6}8xubo7QKm`M9sOZ}qS`9|}B zRmXkJmmRC}JmT9DRalZLjdfak&OEKcSd)qkI~)yW{Up%aceR&f`o$v)e_<^JtV_V? zhG-iwbIZ(+0*$Yp#y7Yj2I*0{4FHAS4j#@sP+iryA+2jZVftu3#<`+LdZ~8ywWD); z8%U42*9Nwl1C1Q7dXu-_ZN5mYVgK2LJ+EN3tH?)HPWiI zCelauKLkP#eXn!*&>OZC89(-#sUZ{#(gLR((5PfvP>;e0w7NM3@xDCtJpX&0!9^F> zi=TF0z36wlnSWk1#rxL}U+O>F`Samre)7QI4{xmdGo5d)7<@ZZt->Z-9-d0*BFeWm%Y$n%pQ ztX|%E(du9JKi~P#>an#aSN)PYsL}xg>bKgz>Hl%(#x*I^ePGR|TH-bT^p7jQz3&t4 zU|nZ!ZKbni?XrD$9S-aJ{_o|LRh0DY3l6e9f3db&gWibd?EAO-{nd+ZBcrSe?YZxv zbNnTpx33-8_wYG>z4PGOb_R8t?sw#=FY+%6?(w6($h#=m&oez#fjp%9{fVLBsBdF< zs1idGdj7kuCrx?OHyRwTB)^abmw-2Y=@$KZzA^!_JMuKDg1Mf8C{xL?o|0uMINDP% zq=F+o1$7n7_7v>5QGVa>8YpA@jP-EEx+EDqMRK~rk3Q$?r*~8*AR9Z_;=Xh$v!dy$ z@HzkOX7BduPXAG@1MN&zw}+FY$GpjEe3@prw;Err=iSx#=k(lPjbEX4Q2HLQG7Yb) z>`GS{q=8;-Hg?%WI{*6*d7C|NC*9gc&yfexGJ%@NV0z3Ixct#FleW?dARDToFkRsg zC)gHLT+Yki=N*mg@ai--`_wG60V$$TlL`)wM{h~I@o;Y}B5wn&#^X#d$e=sh+@@

^+E4({*< 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 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..215f5ada5a1c7d15bbc189690598264ed52a7b03 100755 GIT binary patch delta 102289 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

cbRFaEZGk~=G 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 delta 103089 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

r>xW7*jdA7ICc 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%0DNAFTj&gPRl@*AcQs?Qsuo7C%rIJXMk`u|(Voy4 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 T_o=s@e}DME`S&l_@hATqxiBrt delta 146159 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�I%kp;;I%K}cUsTFWzW|uQ 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 Wu3zGM|G&6oXL$0Goy)%d@BSC*Wf6}6 diff --git a/wasm_for_tests/vp_read_storage_key.wasm b/wasm_for_tests/vp_read_storage_key.wasm index a00d7f195c2ffd460e9f7bf8995f54bb6d500e53..a24d8e841dc39ef7fc94d6c9d07b15d584356203 100755 GIT binary patch delta 160353 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�l%_SJlS#Vh)@u7rrSP#U^#rn__Y} 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