From 6410717c3d4e75f10b51ee978edce0a6c3599c56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20Bj=C3=B6rkqvist?= Date: Tue, 24 Sep 2024 08:39:52 +0200 Subject: [PATCH] test(ICRC_ledger): FI-1397: Add transaction generation to ICRC golden state tests (#1478) Add generation of transactions to the ICRC tests with golden ledger state. Also only run the extensive testing (verifying the ledger internal state wrt. allowances and balances, and generating transactions), for only one ledger per test. --- rs/rosetta-api/icrc1/BUILD.bazel | 11 +- .../ledger/sm-tests/src/in_memory_ledger.rs | 62 +- .../icrc1/ledger/sm-tests/src/lib.rs | 26 +- .../tests/golden_state_upgrade_downgrade.rs | 529 ++++++++++++++---- 4 files changed, 473 insertions(+), 155 deletions(-) diff --git a/rs/rosetta-api/icrc1/BUILD.bazel b/rs/rosetta-api/icrc1/BUILD.bazel index c1bfebd61e1..6de700a770b 100644 --- a/rs/rosetta-api/icrc1/BUILD.bazel +++ b/rs/rosetta-api/icrc1/BUILD.bazel @@ -85,15 +85,17 @@ rust_test( # bazel \ # test \ # --test_env=SSH_AUTH_SOCK \ + # --test_timeout=43200 \ # //rs/rosetta-api/icrc1:icrc_ledger_suite_integration_golden_state_upgrade_downgrade_test # # To run the U256 token version of the test (for ckETH and ckERC20 tokens), use: # # //rs/rosetta-api/icrc1:icrc_ledger_suite_integration_golden_state_upgrade_downgrade_test_u256 # - # The only unusual thing in this command is `--test_env=SSH_AUTH_SOCK`. That causes the - # SSH_AUTH_SOCK environment variable to be "forwarded" from your shell to the sandbox where the test - # is run. This authorizes the test to download the test data. + # The unusual things in this command are: + # - `--test_env=SSH_AUTH_SOCK`: This causes the SSH_AUTH_SOCK environment variable to be "forwarded" from + # your shell to the sandbox where the test is run. This authorizes the test to download the test data. + # - `--test_timeout=43200`: This sets the test timeout to 12 hours (more than currently required). # # Additionally, the following flags are recommended (but not required): # @@ -141,6 +143,9 @@ rust_test( "//rs/rosetta-api/icrc1/ledger", "//rs/rosetta-api/icrc1/ledger/sm-tests:sm-tests" + name_suffix, "//rs/rosetta-api/icrc1/test_utils", + "//rs/rosetta-api/icrc1/tokens_u256", + "//rs/rosetta-api/icrc1/tokens_u64", + "//rs/rosetta-api/ledger_core", "//rs/rust_canisters/canister_test", "//rs/state_machine_tests", "//rs/test_utilities/load_wasm", diff --git a/rs/rosetta-api/icrc1/ledger/sm-tests/src/in_memory_ledger.rs b/rs/rosetta-api/icrc1/ledger/sm-tests/src/in_memory_ledger.rs index 26cedeac249..71eb30ad6e0 100644 --- a/rs/rosetta-api/icrc1/ledger/sm-tests/src/in_memory_ledger.rs +++ b/rs/rosetta-api/icrc1/ledger/sm-tests/src/in_memory_ledger.rs @@ -31,10 +31,16 @@ impl From for (Account, Account) { } } -trait InMemoryLedgerState { +pub trait InMemoryLedgerState { type AccountId; type Tokens; + fn get_allowance( + &self, + from: &Self::AccountId, + spender: &Self::AccountId, + ) -> Option>; + fn process_approve( &mut self, from: &Self::AccountId, @@ -85,6 +91,15 @@ where type AccountId = AccountId; type Tokens = Tokens; + fn get_allowance( + &self, + from: &Self::AccountId, + spender: &Self::AccountId, + ) -> Option> { + let key = K::from((from, spender)); + self.allowances.get(&key).cloned() + } + fn process_approve( &mut self, from: &Self::AccountId, @@ -257,12 +272,16 @@ where ) { let key = K::from((from, spender)); if let Some(expected_allowance) = expected_allowance { - let current_allowance = self + let current_allowance_amount = self .allowances .get(&key) - .unwrap_or_else(|| panic!("No current allowance but expected allowance set")); - if current_allowance.amount != *expected_allowance { - panic!("Expected allowance does not match current allowance"); + .map(|allowance| allowance.amount.clone()) + .unwrap_or(Tokens::zero()); + if current_allowance_amount != *expected_allowance { + panic!( + "Expected allowance ({:?}) does not match current allowance ({:?})", + expected_allowance, current_allowance_amount + ); } } if amount == &Tokens::zero() { @@ -336,34 +355,32 @@ where } impl InMemoryLedger { - fn new_from_icrc1_ledger_blocks( - blocks: &[ic_icrc1::Block], - burns_without_spender: Option>, - ) -> InMemoryLedger { - let mut state = InMemoryLedger { + pub fn new(burns_without_spender: Option>) -> Self { + InMemoryLedger { burns_without_spender, ..Default::default() - }; + } + } + + pub fn ingest_icrc1_ledger_blocks(&mut self, blocks: &[ic_icrc1::Block]) { for (index, block) in blocks.iter().enumerate() { if let Some(fee_collector) = block.fee_collector { - state.fee_collector = Some(fee_collector); + self.fee_collector = Some(fee_collector); } match &block.transaction.operation { - Operation::Mint { to, amount } => state.process_mint(to, amount), + Operation::Mint { to, amount } => self.process_mint(to, amount), Operation::Transfer { from, to, spender, amount, fee, - } => { - state.process_transfer(from, to, spender, amount, &fee.or(block.effective_fee)) - } + } => self.process_transfer(from, to, spender, amount, &fee.or(block.effective_fee)), Operation::Burn { from, spender, amount, - } => state.process_burn(from, spender, amount, index), + } => self.process_burn(from, spender, amount, index), Operation::Approve { from, spender, @@ -371,7 +388,7 @@ impl InMemoryLedger { expected_allowance, expires_at, fee, - } => state.process_approve( + } => self.process_approve( from, spender, amount, @@ -381,12 +398,11 @@ impl InMemoryLedger { TimeStamp::from_nanos_since_unix_epoch(block.timestamp), ), } - state.validate_invariants(); + self.validate_invariants(); } - state.prune_expired_allowances(TimeStamp::from_nanos_since_unix_epoch( + self.prune_expired_allowances(TimeStamp::from_nanos_since_unix_epoch( blocks.last().unwrap().timestamp, )); - state } pub fn apply_arg_with_caller( @@ -522,8 +538,8 @@ pub fn verify_ledger_state( println!("verifying state of ledger {}", ledger_id); let blocks = get_all_ledger_and_archive_blocks(env, ledger_id); println!("retrieved all ledger and archive blocks"); - let expected_ledger_state = - InMemoryLedger::new_from_icrc1_ledger_blocks(&blocks, burns_without_spender); + let mut expected_ledger_state = InMemoryLedger::new(burns_without_spender); + expected_ledger_state.ingest_icrc1_ledger_blocks(&blocks); println!("recreated expected ledger state"); expected_ledger_state.verify_balances_and_allowances(env, ledger_id); println!("ledger state verified successfully"); diff --git a/rs/rosetta-api/icrc1/ledger/sm-tests/src/lib.rs b/rs/rosetta-api/icrc1/ledger/sm-tests/src/lib.rs index 5721757750f..e99747182a5 100644 --- a/rs/rosetta-api/icrc1/ledger/sm-tests/src/lib.rs +++ b/rs/rosetta-api/icrc1/ledger/sm-tests/src/lib.rs @@ -210,7 +210,7 @@ fn model_transfer( ((from_balance, to_balance), None) } -fn send_transfer( +pub fn send_transfer( env: &StateMachine, ledger: CanisterId, from: Principal, @@ -539,6 +539,19 @@ pub fn balance_of(env: &StateMachine, ledger: CanisterId, acc: impl Into u64 { + Decode!( + &env.query(ledger, "icrc1_fee", Encode!().unwrap()) + .expect("failed to query fee") + .bytes(), + Nat + ) + .expect("failed to decode icrc1_fee response") + .0 + .to_u64() + .unwrap() +} + pub fn metadata(env: &StateMachine, ledger: CanisterId) -> BTreeMap { Decode!( &env.query(ledger, "icrc1_metadata", Encode!().unwrap()) @@ -1948,16 +1961,7 @@ where .expect("failed to decode balance_of response"); assert_eq!(token_name_after_upgrade, OTHER_TOKEN_NAME); - let token_fee_after_upgrade: u64 = Decode!( - &env.query(canister_id, "icrc1_fee", Encode!().unwrap()) - .expect("failed to query fee") - .bytes(), - Nat - ) - .expect("failed to decode balance_of response") - .0 - .to_u64() - .unwrap(); + let token_fee_after_upgrade = fee(&env, canister_id); assert_eq!(token_fee_after_upgrade, NEW_FEE); } diff --git a/rs/rosetta-api/icrc1/tests/golden_state_upgrade_downgrade.rs b/rs/rosetta-api/icrc1/tests/golden_state_upgrade_downgrade.rs index 8afe77833dd..78bdd577b1e 100644 --- a/rs/rosetta-api/icrc1/tests/golden_state_upgrade_downgrade.rs +++ b/rs/rosetta-api/icrc1/tests/golden_state_upgrade_downgrade.rs @@ -1,14 +1,73 @@ use crate::common::{ledger_wasm, load_wasm_using_env_var}; -use candid::Encode; +use candid::{Encode, Nat}; use canister_test::Wasm; use ic_base_types::{CanisterId, PrincipalId}; -use ic_icrc1_ledger_sm_tests::in_memory_ledger::verify_ledger_state; +use ic_icrc1_ledger_sm_tests::in_memory_ledger::{ + verify_ledger_state, ApprovalKey, BurnsWithoutSpender, InMemoryLedger, InMemoryLedgerState, +}; +use ic_icrc1_ledger_sm_tests::{ + get_all_ledger_and_archive_blocks, send_approval, send_transfer, send_transfer_from, +}; +use ic_ledger_core::approvals::Allowance; +use ic_ledger_core::timestamp::TimeStamp; use ic_nns_test_utils::governance::bump_gzip_timestamp; use ic_state_machine_tests::StateMachine; +use icrc_ledger_types::icrc1::account::Account; +use icrc_ledger_types::icrc1::transfer::{Memo, TransferArg}; +use icrc_ledger_types::icrc2::approve::ApproveArgs; +use icrc_ledger_types::icrc2::transfer_from::TransferFromArgs; use std::str::FromStr; +use std::time::{Instant, UNIX_EPOCH}; mod common; +const NUM_TRANSACTIONS_PER_TYPE: usize = 20; +const MINT_MULTIPLIER: u64 = 10_000; +const TRANSFER_MULTIPLIER: u64 = 1000; +const APPROVE_MULTIPLIER: u64 = 100; +const TRANSFER_FROM_MULTIPLIER: u64 = 10; +const BURN_MULTIPLIER: u64 = 1; + +#[cfg(not(feature = "u256-tokens"))] +type Tokens = ic_icrc1_tokens_u64::U64; + +#[cfg(feature = "u256-tokens")] +type Tokens = ic_icrc1_tokens_u256::U256; + +struct CanisterConfig { + canister_id: &'static str, + canister_name: &'static str, + burns_without_spender: Option>, + extended_testing: bool, +} + +impl CanisterConfig { + #[cfg(feature = "u256-tokens")] + fn new(canister_id_and_name: (&'static str, &'static str)) -> Self { + let (canister_id, canister_name) = canister_id_and_name; + Self { + canister_id, + canister_name, + burns_without_spender: None, + extended_testing: false, + } + } + + fn new_with_params( + canister_id_and_name: (&'static str, &'static str), + burns_without_spender: Option>, + extended_testing: bool, + ) -> Self { + let (canister_id, canister_name) = canister_id_and_name; + Self { + canister_id, + canister_name, + burns_without_spender, + extended_testing, + } + } +} + #[cfg(not(feature = "u256-tokens"))] #[test] fn should_upgrade_icrc_ck_btc_canister_with_golden_state() { @@ -37,30 +96,14 @@ fn should_upgrade_icrc_ck_btc_canister_with_golden_state() { ], }; - let canister_id = CanisterId::unchecked_from_principal( - PrincipalId::from_str(CK_BTC_LEDGER_CANISTER_ID).unwrap(), - ); - verify_ledger_state( - &state_machine, - canister_id, - Some(burns_without_spender.clone()), - ); - upgrade_canister( - &state_machine, - (CK_BTC_LEDGER_CANISTER_ID, CK_BTC_LEDGER_CANISTER_NAME), - ledger_wasm.clone(), - ); - // Upgrade again with bumped wasm timestamp to test pre_upgrade - upgrade_canister( - &state_machine, - (CK_BTC_LEDGER_CANISTER_ID, CK_BTC_LEDGER_CANISTER_NAME), - bump_gzip_timestamp(&ledger_wasm), - ); - verify_ledger_state(&state_machine, canister_id, Some(burns_without_spender)); - // Downgrade back to the mainnet ledger version - upgrade_canister( + perform_upgrade_downgrade_testing( &state_machine, - (CK_BTC_LEDGER_CANISTER_ID, CK_BTC_LEDGER_CANISTER_NAME), + vec![CanisterConfig::new_with_params( + (CK_BTC_LEDGER_CANISTER_ID, CK_BTC_LEDGER_CANISTER_NAME), + Some(burns_without_spender), + true, + )], + ledger_wasm, mainnet_ledger_wasm, ); } @@ -95,68 +138,45 @@ fn should_upgrade_icrc_ck_u256_canisters_with_golden_state() { .0, subaccount: None, }; - let ck_eth_burns_without_spender = - ic_icrc1_ledger_sm_tests::in_memory_ledger::BurnsWithoutSpender { - minter: ck_eth_minter, - burn_indexes: vec![ - 1051, 1094, 1276, 1759, 1803, 1929, 2449, 2574, 2218, 2219, 2231, 1777, 4, 9, 31, - 1540, 1576, 1579, 1595, 1607, 1617, 1626, 1752, 1869, 1894, 2013, 2555, - ], - }; + let ck_eth_burns_without_spender = BurnsWithoutSpender { + minter: ck_eth_minter, + burn_indexes: vec![ + 1051, 1094, 1276, 1759, 1803, 1929, 2449, 2574, 2218, 2219, 2231, 1777, 4, 9, 31, 1540, + 1576, 1579, 1595, 1607, 1617, 1626, 1752, 1869, 1894, 2013, 2555, + ], + }; let ledger_wasm_u256 = Wasm::from_bytes(ledger_wasm()); - let canister_ids_names_and_burns_without_spender = vec![ - (CK_SEPOLIA_LINK_LEDGER, None), - (CK_SEPOLIA_PEPE_LEDGER, None), - (CK_SEPOLIA_USDC_LEDGER, None), - (CK_ETH_LEDGER, Some(ck_eth_burns_without_spender)), - (CK_EURC_LEDGER, None), - (CK_USDC_LEDGER, None), - (CK_LINK_LEDGER, None), - (CK_OCT_LEDGER, None), - (CK_PEPE_LEDGER, None), - (CK_SHIB_LEDGER, None), - (CK_UNI_LEDGER, None), - (CK_USDT_LEDGER, None), - (CK_WBTC_LEDGER, None), - (CK_WSTETH_LEDGER, None), - (CK_XAUT_LEDGER, None), + let canister_configs = vec![ + CanisterConfig::new_with_params(CK_ETH_LEDGER, Some(ck_eth_burns_without_spender), true), + CanisterConfig::new(CK_SEPOLIA_LINK_LEDGER), + CanisterConfig::new(CK_SEPOLIA_LINK_LEDGER), + CanisterConfig::new(CK_SEPOLIA_PEPE_LEDGER), + CanisterConfig::new(CK_SEPOLIA_USDC_LEDGER), + CanisterConfig::new(CK_EURC_LEDGER), + CanisterConfig::new(CK_USDC_LEDGER), + CanisterConfig::new(CK_LINK_LEDGER), + CanisterConfig::new(CK_OCT_LEDGER), + CanisterConfig::new(CK_PEPE_LEDGER), + CanisterConfig::new(CK_SHIB_LEDGER), + CanisterConfig::new(CK_UNI_LEDGER), + CanisterConfig::new(CK_USDT_LEDGER), + CanisterConfig::new(CK_WBTC_LEDGER), + CanisterConfig::new(CK_WSTETH_LEDGER), + CanisterConfig::new(CK_XAUT_LEDGER), ]; let state_machine = ic_nns_test_utils_golden_nns_state::new_state_machine_with_golden_fiduciary_state_or_panic( ); - for ((canister_id_str, canister_name), burns_without_spender) in - canister_ids_names_and_burns_without_spender - { - println!( - "Processing {} ledger, id {}", - canister_id_str, canister_name - ); - let canister_id = - CanisterId::unchecked_from_principal(PrincipalId::from_str(canister_id_str).unwrap()); - verify_ledger_state(&state_machine, canister_id, burns_without_spender.clone()); - upgrade_canister( - &state_machine, - (canister_id_str, canister_name), - ledger_wasm_u256.clone(), - ); - // Upgrade again with bumped wasm timestamp to test pre_upgrade - upgrade_canister( - &state_machine, - (canister_id_str, canister_name), - bump_gzip_timestamp(&ledger_wasm_u256), - ); - // Downgrade back to the mainnet ledger version - upgrade_canister( - &state_machine, - (canister_id_str, canister_name), - mainnet_ledger_wasm_u256.clone(), - ); - verify_ledger_state(&state_machine, canister_id, burns_without_spender); - } + perform_upgrade_downgrade_testing( + &state_machine, + canister_configs, + ledger_wasm_u256, + mainnet_ledger_wasm_u256, + ); } #[cfg(feature = "u256-tokens")] @@ -199,64 +219,337 @@ fn should_upgrade_icrc_sns_canisters_with_golden_state() { "IC_ICRC1_LEDGER_DEPLOYED_VERSION_WASM_PATH", )); - let canister_id_and_names = vec![ - BOOMDAO, - CATALYZE, - CYCLES_TRANSFER_STATION, - DECIDEAI, - DOGMI, - DRAGGINZ, - ELNAAI, - ESTATEDAO, - GOLDDAO, - ICGHOST, - ICLIGHTHOUSE, - ICPANDA, - ICPCC, - ICPSWAP, - ICVC, - KINIC, - MOTOKO, - NEUTRINITE, - NUANCE, - OPENCHAT, - OPENFPL, - ORIGYN, - SEERS, - SNEED, - SONIC, - TRAX, - WATERNEURON, - YRAL, - YUKU, + let canister_configs = vec![ + CanisterConfig::new_with_params(OPENCHAT, None, true), + CanisterConfig::new(BOOMDAO), + CanisterConfig::new(CATALYZE), + CanisterConfig::new(CYCLES_TRANSFER_STATION), + CanisterConfig::new(DECIDEAI), + CanisterConfig::new(DOGMI), + CanisterConfig::new(DRAGGINZ), + CanisterConfig::new(ELNAAI), + CanisterConfig::new(ESTATEDAO), + CanisterConfig::new(GOLDDAO), + CanisterConfig::new(ICGHOST), + CanisterConfig::new(ICLIGHTHOUSE), + CanisterConfig::new(ICPANDA), + CanisterConfig::new(ICPCC), + CanisterConfig::new(ICPSWAP), + CanisterConfig::new(ICVC), + CanisterConfig::new(KINIC), + CanisterConfig::new(MOTOKO), + CanisterConfig::new(NEUTRINITE), + CanisterConfig::new(NUANCE), + CanisterConfig::new(OPENFPL), + CanisterConfig::new(ORIGYN), + CanisterConfig::new(SEERS), + CanisterConfig::new(SNEED), + CanisterConfig::new(SONIC), + CanisterConfig::new(TRAX), + CanisterConfig::new(WATERNEURON), + CanisterConfig::new(YRAL), + CanisterConfig::new(YUKU), ]; let state_machine = ic_nns_test_utils_golden_nns_state::new_state_machine_with_golden_sns_state_or_panic(); - for (canister_id_str, canister_name) in canister_id_and_names { + perform_upgrade_downgrade_testing( + &state_machine, + canister_configs, + ledger_wasm, + mainnet_ledger_wasm, + ); +} + +fn generate_transactions( + state_machine: &StateMachine, + canister_id: CanisterId, + in_memory_ledger: &mut InMemoryLedger, +) { + let start = Instant::now(); + let minter_account = ic_icrc1_ledger_sm_tests::minting_account(state_machine, canister_id) + .unwrap_or_else(|| panic!("minter account should be set for {:?}", canister_id)); + let u64_fee = ic_icrc1_ledger_sm_tests::fee(state_machine, canister_id); + let fee = Tokens::from(u64_fee); + let burn_amount = Tokens::from( + u64_fee + .checked_mul(BURN_MULTIPLIER) + .unwrap_or_else(|| panic!("burn amount overflowed for canister {:?}", canister_id)), + ); + let transfer_amount = + Tokens::from(u64_fee.checked_mul(TRANSFER_MULTIPLIER).unwrap_or_else(|| { + panic!("transfer amount overflowed for canister {:?}", canister_id) + })); + let mint_amount = Tokens::from( + u64_fee + .checked_mul(MINT_MULTIPLIER) + .unwrap_or_else(|| panic!("mint amount overflowed for canister {:?}", canister_id)), + ); + let transfer_from_amount = Tokens::from( + u64_fee + .checked_mul(TRANSFER_FROM_MULTIPLIER) + .unwrap_or_else(|| { + panic!( + "transfer_from amount overflowed for canister {:?}", + canister_id + ) + }), + ); + let approve_amount = Tokens::from( + u64_fee + .checked_mul(APPROVE_MULTIPLIER) + .unwrap_or_else(|| panic!("approve amount overflowed for canister {:?}", canister_id)), + ); + let mut accounts = vec![]; + for i in 0..NUM_TRANSACTIONS_PER_TYPE { + let subaccount = match i { + 0 => None, + _ => Some([i as u8; 32]), + }; + accounts.push(Account { + owner: PrincipalId::new_user_test_id(i as u64).0, + subaccount, + }); + } + // Mint + let mut minted = 0usize; + println!("minting"); + for to in &accounts { + send_transfer( + state_machine, + canister_id, + minter_account.owner, + &TransferArg { + from_subaccount: minter_account.subaccount, + to: *to, + fee: None, + created_at_time: Some( + state_machine + .time() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos() as u64, + ), + memo: Some(Memo::from(minted as u64)), + amount: Nat::from(mint_amount), + }, + ) + .expect("should be able to mint"); + in_memory_ledger.process_mint(to, &Tokens::from(mint_amount)); + minted += 1; + if minted >= NUM_TRANSACTIONS_PER_TYPE { + break; + } + } + // Transfer + println!("transferring"); + for i in 0..NUM_TRANSACTIONS_PER_TYPE { + let from = accounts[i]; + let to = accounts[(i + 1) % NUM_TRANSACTIONS_PER_TYPE]; + send_transfer( + state_machine, + canister_id, + from.owner, + &TransferArg { + from_subaccount: from.subaccount, + to, + fee: Some(Nat::from(fee)), + created_at_time: Some( + state_machine + .time() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos() as u64, + ), + memo: Some(Memo::from(i as u64)), + amount: Nat::from(transfer_amount), + }, + ) + .expect("should be able to transfer"); + in_memory_ledger.process_transfer( + &from, + &to, + &None, + &Tokens::from(transfer_amount), + &Some(fee), + ); + } + // Approve + println!("approving"); + for i in 0..NUM_TRANSACTIONS_PER_TYPE { + let from = accounts[i]; + let spender = accounts[(i + 1) % NUM_TRANSACTIONS_PER_TYPE]; + let current_allowance = in_memory_ledger + .get_allowance(&from, &spender) + .unwrap_or(Allowance::default()); + let expires_at = state_machine + .time() + .checked_add(std::time::Duration::from_secs(3600)) + .unwrap() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos() as u64; + send_approval( + state_machine, + canister_id, + from.owner, + &ApproveArgs { + from_subaccount: from.subaccount, + spender, + amount: Nat::from(approve_amount), + expected_allowance: Some(Nat::from(current_allowance.amount)), + expires_at: Some(expires_at), + fee: Some(Nat::from(fee)), + memo: Some(Memo::from(i as u64)), + created_at_time: Some( + state_machine + .time() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos() as u64, + ), + }, + ) + .expect("should be able to transfer"); + in_memory_ledger.process_approve( + &from, + &spender, + &Tokens::from(approve_amount), + &Some(current_allowance.amount), + &Some(expires_at), + &Some(fee), + TimeStamp::from_nanos_since_unix_epoch( + state_machine + .time() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos() as u64, + ), + ); + } + // Transfer From + println!("transferring from"); + for i in 0..NUM_TRANSACTIONS_PER_TYPE { + let from = accounts[i]; + let spender = accounts[(i + 1) % NUM_TRANSACTIONS_PER_TYPE]; + let to = accounts[(i + 2) % NUM_TRANSACTIONS_PER_TYPE]; + send_transfer_from( + state_machine, + canister_id, + spender.owner, + &TransferFromArgs { + spender_subaccount: spender.subaccount, + from, + to, + amount: Nat::from(transfer_from_amount), + fee: Some(Nat::from(fee)), + memo: Some(Memo::from(i as u64)), + created_at_time: Some( + state_machine + .time() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos() as u64, + ), + }, + ) + .expect("should be able to transfer from"); + in_memory_ledger.process_transfer( + &from, + &to, + &Some(spender), + &Tokens::from(transfer_from_amount), + &Some(fee), + ); + } + // Burn + println!("burning"); + for (i, from) in accounts.iter().enumerate().take(NUM_TRANSACTIONS_PER_TYPE) { + send_transfer( + state_machine, + canister_id, + from.owner, + &TransferArg { + from_subaccount: from.subaccount, + to: minter_account, + fee: None, + created_at_time: Some( + state_machine + .time() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos() as u64, + ), + memo: Some(Memo::from(i as u64)), + amount: Nat::from(burn_amount), + }, + ) + .expect("should be able to transfer"); + in_memory_ledger.process_transfer( + from, + &minter_account, + &None, + &Tokens::from(burn_amount), + &None, + ); + } + println!( + "generated {} transactions in {:?}", + NUM_TRANSACTIONS_PER_TYPE * 5, + start.elapsed() + ); +} + +fn perform_upgrade_downgrade_testing( + state_machine: &StateMachine, + canister_configs: Vec, + master_canister_wasm: Wasm, + mainnet_canister_wasm: Wasm, +) { + for canister_config in canister_configs { + let CanisterConfig { + canister_id: canister_id_str, + canister_name, + burns_without_spender, + extended_testing, + } = canister_config; + println!( + "Processing {} ledger, id {}", + canister_id_str, canister_name + ); let canister_id = CanisterId::unchecked_from_principal(PrincipalId::from_str(canister_id_str).unwrap()); - // TODO: Uncomment once mainnet ledgers have been upgraded to include `ledger_num_approvals` metric - // verify_ledger_state(&state_machine, canister_id, None); + if extended_testing { + verify_ledger_state(state_machine, canister_id, burns_without_spender.clone()); + } upgrade_canister( - &state_machine, + state_machine, (canister_id_str, canister_name), - ledger_wasm.clone(), + master_canister_wasm.clone(), ); // Upgrade again with bumped wasm timestamp to test pre_upgrade upgrade_canister( - &state_machine, + state_machine, (canister_id_str, canister_name), - bump_gzip_timestamp(&ledger_wasm), + bump_gzip_timestamp(&master_canister_wasm), ); - verify_ledger_state(&state_machine, canister_id, None); + if extended_testing { + let blocks = get_all_ledger_and_archive_blocks(state_machine, canister_id); + let mut expected_ledger_state = + ic_icrc1_ledger_sm_tests::in_memory_ledger::InMemoryLedger::new(None); + expected_ledger_state.ingest_icrc1_ledger_blocks(&blocks); + generate_transactions(state_machine, canister_id, &mut expected_ledger_state); + } // Downgrade back to the mainnet ledger version upgrade_canister( - &state_machine, + state_machine, (canister_id_str, canister_name), - mainnet_ledger_wasm.clone(), + mainnet_canister_wasm.clone(), ); + if extended_testing { + verify_ledger_state(state_machine, canister_id, burns_without_spender); + } } }