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); + } } }