diff --git a/Cargo.lock b/Cargo.lock index bdfcfbbb8523..3839770c87a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10636,13 +10636,13 @@ dependencies = [ "ic-management-canister-types-private", "ic-metrics-encoder", "ic-stable-structures 0.6.9", - "ic-state-machine-tests", "ic0", "icrc-ledger-types", "maplit", "mockall", "num-traits", "paste", + "pocket-ic", "proptest", "scopeguard", "scraper", @@ -10674,6 +10674,7 @@ dependencies = [ "ic-universal-canister", "icrc-ledger-types", "paste", + "pocket-ic", "proptest", ] diff --git a/rs/ethereum/ledger-suite-orchestrator/BUILD.bazel b/rs/ethereum/ledger-suite-orchestrator/BUILD.bazel index 784f8b491660..45bf3b936f3b 100644 --- a/rs/ethereum/ledger-suite-orchestrator/BUILD.bazel +++ b/rs/ethereum/ledger-suite-orchestrator/BUILD.bazel @@ -143,6 +143,7 @@ rust_ic_test( data = [ "ledger_suite_orchestrator_canister.wasm.gz", "ledger_suite_orchestrator_canister_getblocksdisabled.wasm.gz", + "//:pocket-ic-server", "//rs/ledger_suite/icrc1/archive:archive_canister_u256.wasm.gz", "//rs/ledger_suite/icrc1/index-ng:index_ng_canister_u256.wasm.gz", "//rs/ledger_suite/icrc1/ledger:ledger_canister_u256.wasm.gz", @@ -157,6 +158,7 @@ rust_ic_test( "LEDGER_CANISTER_GET_BLOCKS_DISABLED_WASM_PATH": "$(rootpath //rs/ledger_suite/icrc1/ledger:ledger_canister_u256_getblocksdisabled.wasm.gz)", "INDEX_CANISTER_WASM_PATH": "$(rootpath //rs/ledger_suite/icrc1/index-ng:index_ng_canister_u256.wasm.gz)", "LEDGER_ARCHIVE_NODE_CANISTER_WASM_PATH": "$(rootpath //rs/ledger_suite/icrc1/archive:archive_canister_u256.wasm.gz)", + "POCKET_IC_BIN": "$(rootpath //:pocket-ic-server)", "UNIVERSAL_CANISTER_WASM_PATH": "$(rootpath //rs/universal_canister/impl:universal_canister.wasm.gz)", }, proc_macro_deps = [], @@ -165,14 +167,14 @@ rust_ic_test( ":ledger_suite_orchestrator", "//packages/ic-http-types", "//packages/icrc-ledger-types:icrc_ledger_types_storable", + "//packages/pocket-ic", "//rs/crypto/test_utils/reproducible_rng", "//rs/ethereum/ledger-suite-orchestrator/test_utils", "//rs/ledger_suite/icrc1/ledger", - "//rs/state_machine_tests", "//rs/types/base_types", - "//rs/types/management_canister_types", "@crate_index//:assert_matches", "@crate_index//:candid", + "@crate_index//:ic-management-canister-types", "@crate_index//:proptest", ], ) diff --git a/rs/ethereum/ledger-suite-orchestrator/Cargo.toml b/rs/ethereum/ledger-suite-orchestrator/Cargo.toml index 3987ecb077c1..15a70c27e75b 100644 --- a/rs/ethereum/ledger-suite-orchestrator/Cargo.toml +++ b/rs/ethereum/ledger-suite-orchestrator/Cargo.toml @@ -43,10 +43,10 @@ assert_matches = { workspace = true } candid_parser = { workspace = true } ic-crypto-test-utils-reproducible-rng = { path = "../../crypto/test_utils/reproducible_rng" } ic-ledger-suite-orchestrator-test-utils = { path = "test_utils" } -ic-state-machine-tests = { path = "../../state_machine_tests" } maplit = { workspace = true } mockall = { workspace = true } paste = { workspace = true } +pocket-ic = { path = "../../../packages/pocket-ic" } proptest = { workspace = true } scraper = { workspace = true } tokio = { workspace = true } diff --git a/rs/ethereum/ledger-suite-orchestrator/test_utils/BUILD.bazel b/rs/ethereum/ledger-suite-orchestrator/test_utils/BUILD.bazel index c582d831debd..314e6cb73fc9 100644 --- a/rs/ethereum/ledger-suite-orchestrator/test_utils/BUILD.bazel +++ b/rs/ethereum/ledger-suite-orchestrator/test_utils/BUILD.bazel @@ -17,8 +17,9 @@ rust_library( deps = [ # Keep sorted. "//packages/ic-http-types", - "//packages/ic-metrics-assert", + "//packages/ic-metrics-assert:ic-metrics-assert_pocket_ic", "//packages/icrc-ledger-types:icrc_ledger_types_storable", + "//packages/pocket-ic", "//rs/ethereum/ledger-suite-orchestrator:ledger_suite_orchestrator", "//rs/ledger_suite/icrc1/ledger", "//rs/state_machine_tests", diff --git a/rs/ethereum/ledger-suite-orchestrator/test_utils/Cargo.toml b/rs/ethereum/ledger-suite-orchestrator/test_utils/Cargo.toml index 0534ad48e084..5280672a5d4b 100644 --- a/rs/ethereum/ledger-suite-orchestrator/test_utils/Cargo.toml +++ b/rs/ethereum/ledger-suite-orchestrator/test_utils/Cargo.toml @@ -16,7 +16,7 @@ ic-icrc1-ledger = { path = "../../../ledger_suite/icrc1/ledger" } ic-ledger-suite-orchestrator = { path = "../../ledger-suite-orchestrator" } ic-management-canister-types = { workspace = true } ic-management-canister-types-private = { path = "../../../types/management_canister_types" } -ic-metrics-assert = { path = "../../../../packages/ic-metrics-assert" } +ic-metrics-assert = { path = "../../../../packages/ic-metrics-assert", features = ["pocket_ic"] } ic-state-machine-tests = { path = "../../../state_machine_tests" } ic-test-utilities-load-wasm = { path = "../../../test_utilities/load_wasm" } ic-types-cycles = { path = "../../../types/cycles" } @@ -24,4 +24,5 @@ ic-types = { path = "../../../types/types" } ic-universal-canister = { path = "../../../universal_canister/lib" } icrc-ledger-types = { path = "../../../../packages/icrc-ledger-types" } paste = { workspace = true } +pocket-ic = { path = "../../../../packages/pocket-ic" } proptest = { workspace = true } diff --git a/rs/ethereum/ledger-suite-orchestrator/test_utils/src/lib.rs b/rs/ethereum/ledger-suite-orchestrator/test_utils/src/lib.rs index 844f72bc516b..cbaecc4aa206 100644 --- a/rs/ethereum/ledger-suite-orchestrator/test_utils/src/lib.rs +++ b/rs/ethereum/ledger-suite-orchestrator/test_utils/src/lib.rs @@ -21,6 +21,7 @@ pub use icrc_ledger_types::icrc1::account::Account as LedgerAccount; use std::sync::Arc; pub mod flow; +pub mod pocket_ic; pub mod universal_canister; const MAX_TICKS: usize = 10; diff --git a/rs/ethereum/ledger-suite-orchestrator/test_utils/src/pocket_ic/flow.rs b/rs/ethereum/ledger-suite-orchestrator/test_utils/src/pocket_ic/flow.rs new file mode 100644 index 000000000000..08184e6566b3 --- /dev/null +++ b/rs/ethereum/ledger-suite-orchestrator/test_utils/src/pocket_ic/flow.rs @@ -0,0 +1,441 @@ +use crate::MINTER_PRINCIPAL; +use crate::pocket_ic::universal_canister::UniversalCanister; +use crate::pocket_ic::{LedgerSuiteOrchestrator, out_of_band_upgrade, stop_canister}; +use candid::{Decode, Encode, Nat, Principal}; +use ic_icrc1_ledger::ChangeArchiveOptions; +use ic_ledger_suite_orchestrator::candid::{AddErc20Arg, ManagedCanisterIds}; +use ic_ledger_suite_orchestrator::state::{IndexWasm, LedgerWasm}; +use ic_management_canister_types::{CanisterId, CanisterInfoResult, CanisterStatusResult}; +use ic_metrics_assert::{MetricsAssert, PocketIcHttpQuery}; +use icrc_ledger_types::icrc1::transfer::{TransferArg, TransferError}; +use icrc_ledger_types::icrc3::archive::ArchiveInfo; +use icrc_ledger_types::icrc3::blocks::{GetBlocksRequest, GetBlocksResult}; +use pocket_ic::PocketIc; +use std::collections::BTreeSet; + +pub struct AddErc20TokenFlow { + pub setup: LedgerSuiteOrchestrator, + pub params: AddErc20Arg, +} + +impl AddErc20TokenFlow { + pub fn expect_new_ledger_and_index_canisters(self) -> ManagedCanistersAssert { + let contract = self.params.contract; + let canister_ids = self.setup.wait_for(|| { + match self.setup.call_orchestrator_canister_ids(&contract) { + Some(ids) if ids.ledger.is_some() && ids.index.is_some() => Ok(ids), + incomplete_ids => Err(format!( + "Not all canister IDs are available for ERC-20 {contract:?}: {incomplete_ids:?}" + )), + } + }); + assert_ne!( + canister_ids.ledger, canister_ids.index, + "BUG: ledger and index canister IDs MUST be different" + ); + + self.setup + .wait_for_canister_to_be_installed_and_running(canister_ids.ledger.unwrap()); + self.setup + .wait_for_canister_to_be_installed_and_running(canister_ids.index.unwrap()); + + ManagedCanistersAssert { + setup: self.setup, + canister_ids, + } + } +} + +pub struct ManagedCanistersAssert { + pub setup: LedgerSuiteOrchestrator, + pub canister_ids: ManagedCanisterIds, +} + +impl ManagedCanistersAssert { + pub fn assert_all_controlled_by(self, expected_controllers: &[Principal]) -> Self { + for canister_id in self.all_canister_ids() { + assert_eq!( + self.setup + .canister_status_of(canister_id) + .settings + .controllers + .into_iter() + .collect::>(), + expected_controllers + .iter() + .copied() + .collect::>(), // convert to set to ignore order + "BUG: unexpected controller for canister {} in managed canisters {}", + canister_id, + self.canister_ids + ); + } + self + } + + pub fn check_metrics(self) -> MetricsAssert { + MetricsAssert::from_http_query(self) + } + + pub fn trigger_creation_of_archive(self) -> Self { + const ARCHIVE_TRIGGER_THRESHOLD: usize = 10; + + // The productive value for `trigger_threshold` is `2_000`, + // which would require `2_000` transfers to trigger the creation of an archive. + // We set this value to an artificially low number to speed up the test. + self.upgrade_ledger_to_change_archive_options(ChangeArchiveOptions { + trigger_threshold: Some(ARCHIVE_TRIGGER_THRESHOLD), + ..Default::default() + }); + let archive_ids_before: BTreeSet<_> = self + .call_ledger_archives() + .into_iter() + .map(|info| info.canister_id) + .collect(); + + for _i in 0..ARCHIVE_TRIGGER_THRESHOLD { + let from = MINTER_PRINCIPAL; + let to = Principal::management_canister(); + self.call_ledger_icrc1_transfer( + from, + &TransferArg { + from_subaccount: None, + to: to.into(), + fee: None, + created_at_time: None, + memo: None, + amount: Nat::from(1_u8), + }, + ) + .expect("BUG: fail to make a transfer to trigger archive creation"); + } + + let archive_ids_after: BTreeSet<_> = self + .call_ledger_archives() + .into_iter() + .map(|info| info.canister_id) + .collect(); + assert_eq!( + archive_ids_after.len(), + archive_ids_before.len() + 1, + "BUG: expected one more archive canister" + ); + assert!(archive_ids_before.is_subset(&archive_ids_after)); + + Self { + setup: self.setup, + canister_ids: ManagedCanisterIds { + ledger: self.canister_ids.ledger, + index: self.canister_ids.index, + archives: Vec::from_iter(archive_ids_after), + }, + } + } + + pub fn assert_ledger_canister_info_satisfy bool>( + self, + caller: &UniversalCanister, + predicate: P, + ) -> Self { + let canister_info = caller.canister_info(self.ledger_canister_id()); + assert!( + predicate(&canister_info), + "BUG: ledger canister info does not satisfy predicate. Canister info: {canister_info:?}" + ); + self + } + + pub fn assert_index_canister_info_satisfy bool>( + self, + caller: &UniversalCanister, + predicate: P, + ) -> Self { + let canister_info = caller.canister_info(self.index_canister_id()); + assert!( + predicate(&canister_info), + "BUG: index canister info does not satisfy predicate. Canister info: {canister_info:?}" + ); + self + } + + pub fn assert_all_archive_canister_info_satisfy bool>( + self, + caller: &UniversalCanister, + predicate: P, + ) -> Self { + assert!( + !self.canister_ids.archives.is_empty(), + "BUG: no archive canisters" + ); + for archive in self.archive_canister_ids() { + let canister_info = caller.canister_info(archive); + assert!( + predicate(&canister_info), + "BUG: archive canister info does not satisfy predicate. Canister info: {canister_info:?}" + ); + } + self + } + + pub fn stop_ledger(self) -> Self { + stop_canister( + &self.setup.env, + self.setup.ledger_suite_orchestrator_id, + self.ledger_canister_id(), + ); + self + } + + pub fn stop_index(self) -> Self { + stop_canister( + &self.setup.env, + self.setup.ledger_suite_orchestrator_id, + self.index_canister_id(), + ); + self + } + + pub fn ledger_out_of_band_upgrade(self, controller: Principal, wasm: LedgerWasm) -> Self { + out_of_band_upgrade( + &self.setup.env, + controller, + self.ledger_canister_id(), + wasm.to_bytes(), + ) + .expect("failed to upgrade ledger canister"); + self + } + + pub fn index_out_of_band_upgrade(self, controller: Principal, wasm: IndexWasm) -> Self { + out_of_band_upgrade( + &self.setup.env, + controller, + self.index_canister_id(), + wasm.to_bytes(), + ) + .expect("failed to upgrade index canister"); + self + } + + pub fn call_ledger_icrc1_transfer( + &self, + from: Principal, + arg: &TransferArg, + ) -> Result { + Decode!( + &self.setup.env.update_call( + self.ledger_canister_id(), + from, + "icrc1_transfer", + Encode!(arg).unwrap() + ) + .expect("failed to transfer funds"), + Result + ) + .expect("failed to decode transfer response") + } + + pub fn upgrade_ledger_to_change_archive_options(&self, archive_options: ChangeArchiveOptions) { + use ic_icrc1_ledger::{LedgerArgument, UpgradeArgs as LedgerUpgradeArgs}; + + let module_hash_before = self + .ledger_canister_status() + .module_hash + .expect("BUG: ledger is not installed"); + + let upgrade_args = Some(LedgerArgument::Upgrade(Some(LedgerUpgradeArgs { + change_archive_options: Some(archive_options), + ..Default::default() + }))); + self.setup + .env + .upgrade_canister( + self.ledger_canister_id(), + crate::ledger_wasm().to_bytes(), + Encode!(&upgrade_args).unwrap(), + Some(self.setup.ledger_suite_orchestrator_id), + ) + .expect("failed to change archive options"); + let module_hash_after = self + .ledger_canister_status() + .module_hash + .expect("BUG: ledger is not installed"); + assert_eq!( + module_hash_before, module_hash_after, + "BUG: ledger wasm hash changed when changing archive options" + ); + } + + pub fn call_ledger_icrc3_get_blocks(&self, request: &Vec) -> GetBlocksResult { + Decode!( + &self + .setup + .env + .update_call( + self.ledger_canister_id(), + Principal::anonymous(), + "icrc3_get_blocks", + Encode!(request).unwrap() + ) + .expect("failed to call icrc3_get_blocks"), + GetBlocksResult + ) + .expect("failed to decode icrc3_get_blocks response") + } + + fn call_ledger_archives(&self) -> Vec { + Decode!( + &self + .setup + .env + .query_call( + self.ledger_canister_id(), + Principal::anonymous(), + "archives", + Encode!().unwrap() + ) + .expect("failed to query archives"), + Vec + ) + .expect("failed to decode archives response") + } + + pub fn assert_index_has_correct_ledger_id(self) -> Self { + assert_eq!( + self.call_index_ledger_id(), + self.canister_ids.ledger.unwrap() + ); + self + } + + pub fn ledger_canister_status(&self) -> CanisterStatusResult { + self.setup.canister_status_of(self.ledger_canister_id()) + } + + pub fn assert_ledger_has_cycles(self, expected: u128) -> Self { + assert_eq!(self.ledger_canister_status().cycles, Nat::from(expected)); + self + } + + pub fn assert_index_has_cycles(self, expected: u128) -> Self { + assert_eq!( + self.setup + .canister_status_of(self.index_canister_id()) + .cycles, + Nat::from(expected) + ); + self + } + + pub fn assert_all_archives_have_cycles(self, expected: u128) -> Self { + assert!( + !self.archive_canister_ids().is_empty(), + "BUG: no archive canisters" + ); + for archive in self.archive_canister_ids() { + assert_eq!( + self.setup.canister_status_of(archive).cycles, + Nat::from(expected) + ); + } + self + } + + pub fn assert_ledger_has_wasm_hash>(self, expected: T) -> Self { + assert_eq!( + self.ledger_canister_status().module_hash, + Some(expected.as_ref().to_vec()), + "BUG: unexpected wasm hash for ledger canister" + ); + self + } + + pub fn assert_index_has_wasm_hash>(self, expected: T) -> Self { + assert_eq!( + self.setup + .canister_status_of(self.index_canister_id()) + .module_hash, + Some(expected.as_ref().to_vec()), + "BUG: unexpected wasm hash for index canister" + ); + self + } + + fn call_index_ledger_id(&self) -> Principal { + Decode!( + &self + .setup + .env + .query_call( + self.index_canister_id(), + Principal::anonymous(), + "ledger_id", + Encode!().unwrap() + ) + .expect("failed to query get_transactions on the ledger"), + Principal + ) + .unwrap() + } + pub fn ledger_canister_id(&self) -> Principal { + self.canister_ids.ledger.unwrap() + } + + pub fn index_canister_id(&self) -> Principal { + self.canister_ids.index.unwrap() + } + + pub fn archive_canister_ids(&self) -> Vec { + self.canister_ids.archives.clone() + } + + fn all_canister_ids(&self) -> Vec { + vec![self.ledger_canister_id(), self.index_canister_id()] + .into_iter() + .chain(self.archive_canister_ids()) + .collect() + } +} + +impl PocketIcHttpQuery for ManagedCanistersAssert { + fn get_pocket_ic(&self) -> &PocketIc { + self.setup.get_pocket_ic() + } + + fn get_canister_id(&self) -> CanisterId { + self.setup.get_canister_id() + } +} + +macro_rules! assert_ledger { + ($name:expr_2021, $ty:ty) => { + paste::paste! { + pub fn [](env: &pocket_ic::PocketIc, ledger_canister_id: candid::Principal) -> $ty { + candid::Decode!( + &env + .query_call(ledger_canister_id, candid::Principal::anonymous(), $name, candid::Encode!().unwrap()) + .expect("failed to query on the ledger"), + $ty + ) + .unwrap() + } + impl ManagedCanistersAssert { + pub fn []>(self, expected: T) -> Self { + assert_eq!([](&self.setup.env, self.ledger_canister_id()), expected.into(), "BUG: unexpected value for ledger {}", stringify!($name)); + self + } + } + } + }; +} + +assert_ledger!("icrc1_name", String); +assert_ledger!("icrc1_symbol", String); +assert_ledger!("icrc1_decimals", u8); +assert_ledger!("icrc1_total_supply", Nat); +assert_ledger!("icrc1_fee", Nat); +assert_ledger!("icrc1_minting_account", Option); +assert_ledger!( + "icrc1_metadata", + Vec<(crate::LedgerMetadataKey, crate::LedgerMetadataValue)> +); diff --git a/rs/ethereum/ledger-suite-orchestrator/test_utils/src/pocket_ic/mod.rs b/rs/ethereum/ledger-suite-orchestrator/test_utils/src/pocket_ic/mod.rs new file mode 100644 index 000000000000..7bf783f5dc6c --- /dev/null +++ b/rs/ethereum/ledger-suite-orchestrator/test_utils/src/pocket_ic/mod.rs @@ -0,0 +1,360 @@ +use crate::pocket_ic::flow::{AddErc20TokenFlow, ManagedCanistersAssert}; +use crate::{ + GIT_COMMIT_HASH, MAX_TICKS, archive_wasm, default_init_arg, index_wasm, + ledger_get_blocks_disabled_wasm, ledger_suite_orchestrator_get_blocks_disabled_wasm, + ledger_suite_orchestrator_wasm, ledger_wasm, +}; +use candid::{Decode, Encode, Principal}; +use ic_cdk::management_canister::CanisterStatusResult as CdkCanisterStatusResult; +use ic_ledger_suite_orchestrator::candid::{ + AddErc20Arg, CyclesManagement, Erc20Contract, InitArg, InstalledLedgerSuite, + ManagedCanisterIds, OrchestratorArg, OrchestratorInfo, UpgradeArg, +}; +use ic_ledger_suite_orchestrator::state::{LedgerSuiteVersion, WasmHash}; +use ic_management_canister_types::{CanisterId, CanisterStatusResult, CanisterStatusType}; +use ic_metrics_assert::{MetricsAssert, PocketIcHttpQuery}; +use pocket_ic::common::rest::{IcpConfig, IcpConfigFlag}; +use pocket_ic::{PocketIc, PocketIcBuilder, RejectResponse}; +use std::sync::Arc; + +pub mod flow; +pub mod universal_canister; + +pub struct LedgerSuiteOrchestrator { + pub env: Arc, + pub ledger_suite_orchestrator_id: Principal, + pub ledger_suite_orchestrator_wasm: Vec, + pub embedded_ledger_wasm_hash: WasmHash, + pub embedded_index_wasm_hash: WasmHash, + pub embedded_archive_wasm_hash: WasmHash, +} + +impl Default for LedgerSuiteOrchestrator { + fn default() -> Self { + Self::new(Arc::new(new_pocket_ic()), default_init_arg()).register_embedded_wasms() + } +} + +impl LedgerSuiteOrchestrator { + pub fn with_cycles_management(cycles_management: CyclesManagement) -> Self { + let init_arg = InitArg { + cycles_management: Some(cycles_management), + ..default_init_arg() + }; + Self::new(Arc::new(new_pocket_ic()), init_arg) + } + + pub fn new(env: Arc, init_arg: InitArg) -> Self { + let ledger_suite_orchestrator_id = env.create_canister(); + env.add_cycles(ledger_suite_orchestrator_id, u128::MAX); + Self { + env, + ledger_suite_orchestrator_id, + ledger_suite_orchestrator_wasm: ledger_suite_orchestrator_wasm(), + embedded_ledger_wasm_hash: ledger_wasm().hash().clone(), + embedded_index_wasm_hash: index_wasm().hash().clone(), + embedded_archive_wasm_hash: archive_wasm().hash().clone(), + } + .install_ledger_suite_orchestrator(init_arg) + } + + pub fn new_with_ledger_get_blocks_disabled(env: Arc, init_arg: InitArg) -> Self { + let ledger_suite_orchestrator_id = env.create_canister(); + env.add_cycles(ledger_suite_orchestrator_id, u128::MAX); + Self { + env, + ledger_suite_orchestrator_id, + ledger_suite_orchestrator_wasm: ledger_suite_orchestrator_get_blocks_disabled_wasm(), + embedded_ledger_wasm_hash: ledger_get_blocks_disabled_wasm().hash().clone(), + embedded_index_wasm_hash: index_wasm().hash().clone(), + embedded_archive_wasm_hash: archive_wasm().hash().clone(), + } + .install_ledger_suite_orchestrator(init_arg) + } + + fn install_ledger_suite_orchestrator(self, init_arg: InitArg) -> Self { + self.env.install_canister( + self.ledger_suite_orchestrator_id, + self.ledger_suite_orchestrator_wasm.clone(), + Encode!(&OrchestratorArg::InitArg(init_arg)).unwrap(), + None, + ); + self + } + + pub fn upgrade_ledger_suite_orchestrator_expecting_ok( + self, + upgrade_arg: &OrchestratorArg, + ) -> Self { + self.upgrade_ledger_suite_orchestrator_with_same_wasm(upgrade_arg) + .expect("Failed to upgrade ledger suite orchestrator"); + self + } + + pub fn register_embedded_wasms(self) -> Self { + self.upgrade_ledger_suite_orchestrator_expecting_ok(&OrchestratorArg::UpgradeArg( + UpgradeArg { + git_commit_hash: Some(GIT_COMMIT_HASH.to_string()), + ledger_compressed_wasm_hash: None, + index_compressed_wasm_hash: None, + archive_compressed_wasm_hash: None, + cycles_management: None, + manage_ledger_suites: None, + }, + )) + } + + pub fn embedded_ledger_suite_version(&self) -> LedgerSuiteVersion { + LedgerSuiteVersion { + ledger_compressed_wasm_hash: self.embedded_ledger_wasm_hash.clone(), + index_compressed_wasm_hash: self.embedded_index_wasm_hash.clone(), + archive_compressed_wasm_hash: self.embedded_archive_wasm_hash.clone(), + } + } + + pub fn upgrade_ledger_suite_orchestrator_with_same_wasm( + &self, + upgrade_arg: &OrchestratorArg, + ) -> Result<(), RejectResponse> { + self.env.tick(); //tick before upgrade to finish current timers which are reset afterwards + self.env.upgrade_canister( + self.ledger_suite_orchestrator_id, + self.ledger_suite_orchestrator_wasm.clone(), + Encode!(upgrade_arg).unwrap(), + None, + ) + } + + pub fn get_canister_status(&self) -> CdkCanisterStatusResult { + Decode!( + &self + .env + .update_call( + self.ledger_suite_orchestrator_id, + Principal::anonymous(), + "get_canister_status", + Encode!().unwrap() + ) + .expect("failed to call get_canister_status"), + CdkCanisterStatusResult + ) + .unwrap() + } + + pub fn assert_managed_canisters(self, contract: &Erc20Contract) -> ManagedCanistersAssert { + let canister_ids = self + .call_orchestrator_canister_ids(contract) + .unwrap_or_else(|| panic!("No managed canister IDs found for contract {contract:?}")); + + assert_ne!( + canister_ids.ledger, canister_ids.index, + "BUG: ledger and index canister IDs MUST be different" + ); + + ManagedCanistersAssert { + setup: self, + canister_ids, + } + } + + pub fn add_erc20_token(self, params: AddErc20Arg) -> AddErc20TokenFlow { + let setup = self.upgrade_ledger_suite_orchestrator_expecting_ok( + &OrchestratorArg::AddErc20Arg(params.clone()), + ); + AddErc20TokenFlow { setup, params } + } + + pub fn manage_installed_canisters( + self, + manage_installed_canister: Vec, + ) -> Self { + self.upgrade_ledger_suite_orchestrator_expecting_ok(&OrchestratorArg::UpgradeArg( + UpgradeArg { + git_commit_hash: None, + ledger_compressed_wasm_hash: None, + index_compressed_wasm_hash: None, + archive_compressed_wasm_hash: None, + cycles_management: None, + manage_ledger_suites: Some(manage_installed_canister), + }, + )) + } + + pub fn upgrade_ledger_suite_orchestrator( + self, + new_ledger_suite_orchestrator_wasm: Vec, + upgrade_arg: UpgradeArg, + ) -> Self { + self.env.tick(); //tick before upgrade to finish current timers which are reset afterwards + let new_embedded_ledger_wasm_hash = upgrade_arg + .ledger_compressed_wasm_hash + .clone() + .map(|s| s.parse().unwrap()) + .unwrap_or(self.embedded_ledger_wasm_hash); + let new_embedded_index_wasm_hash = upgrade_arg + .index_compressed_wasm_hash + .clone() + .map(|s| s.parse().unwrap()) + .unwrap_or(self.embedded_index_wasm_hash); + let new_embedded_archive_wasm_hash = upgrade_arg + .archive_compressed_wasm_hash + .clone() + .map(|s| s.parse().unwrap()) + .unwrap_or(self.embedded_archive_wasm_hash); + self.env + .upgrade_canister( + self.ledger_suite_orchestrator_id, + new_ledger_suite_orchestrator_wasm.clone(), + Encode!(&OrchestratorArg::UpgradeArg(upgrade_arg)).unwrap(), + None, + ) + .expect("Failed to upgrade ERC20"); + Self { + env: self.env, + ledger_suite_orchestrator_id: self.ledger_suite_orchestrator_id, + ledger_suite_orchestrator_wasm: new_ledger_suite_orchestrator_wasm, + embedded_ledger_wasm_hash: new_embedded_ledger_wasm_hash, + embedded_index_wasm_hash: new_embedded_index_wasm_hash, + embedded_archive_wasm_hash: new_embedded_archive_wasm_hash, + } + } + + pub fn call_orchestrator_canister_ids( + &self, + contract: &Erc20Contract, + ) -> Option { + Decode!( + &self + .env + .update_call( + self.ledger_suite_orchestrator_id, + Principal::anonymous(), + "canister_ids", + Encode!(contract).unwrap() + ) + .expect("failed to execute token transfer"), + Option + ) + .unwrap() + } + + pub fn advance_time_for_periodic_tasks(&self) { + self.env + .advance_time(std::time::Duration::from_secs(60 * 60 + 1)); + self.env.tick(); + self.env.tick(); + self.env.tick(); + self.env.tick(); + self.env.tick(); + self.env.tick(); + } + + pub fn advance_time_for_upgrade(&self) { + self.env.tick(); + self.env.tick(); + self.env.tick(); + self.env.tick(); + self.env.tick(); + self.env.tick(); + } + + pub fn canister_status_of(&self, controlled_canister_id: Principal) -> CanisterStatusResult { + self.env + .canister_status( + controlled_canister_id, + Some(self.ledger_suite_orchestrator_id), + ) + .unwrap() + } + + pub fn get_orchestrator_info(&self) -> OrchestratorInfo { + Decode!( + &self + .env + .query_call( + self.ledger_suite_orchestrator_id, + Principal::anonymous(), + "get_orchestrator_info", + Encode!().unwrap() + ) + .unwrap(), + OrchestratorInfo + ) + .unwrap() + } + + pub fn check_metrics(self) -> MetricsAssert { + MetricsAssert::from_http_query(self) + } + + pub fn wait_for(&self, f: F) -> T + where + F: Fn() -> Result, + E: std::fmt::Debug, + { + let mut last_error = None; + for _ in 0..MAX_TICKS { + self.env.tick(); + match f() { + Ok(t) => return t, + Err(e) => { + last_error = Some(e); + } + } + } + panic!("Failed to get result after {MAX_TICKS} ticks: {last_error:?}"); + } + + pub fn wait_for_canister_to_be_installed_and_running(&self, canister_id: Principal) { + self.wait_for(|| { + let ledger_status = self.canister_status_of(canister_id); + if ledger_status.status == CanisterStatusType::Running + && ledger_status.module_hash.is_some() + { + Ok(()) + } else { + Err(format!( + "Canister {canister_id} is not ready {ledger_status:?}" + )) + } + }); + } +} + +impl PocketIcHttpQuery for LedgerSuiteOrchestrator { + fn get_pocket_ic(&self) -> &PocketIc { + &self.env + } + + fn get_canister_id(&self) -> CanisterId { + self.ledger_suite_orchestrator_id + } +} + +pub fn new_pocket_ic() -> PocketIc { + PocketIcBuilder::new() + .with_system_subnet() + .with_icp_config(IcpConfig { + canister_execution_rate_limiting: Some(IcpConfigFlag::Disabled), + ..Default::default() + }) + .build() +} + +pub fn out_of_band_upgrade( + env: &PocketIc, + controller: Principal, + target: Principal, + wasm: Vec, +) -> Result<(), RejectResponse> { + env.upgrade_canister(target, wasm, Encode!(&()).unwrap(), Some(controller)) +} + +pub fn stop_canister(env: &PocketIc, controller: Principal, target: Principal) { + env.stop_canister(target, Some(controller)) + .expect("failed to stop canister"); + let status = env + .canister_status(target, Some(controller)) + .expect("failed to query canister status"); + assert_eq!(status.status, CanisterStatusType::Stopped); +} diff --git a/rs/ethereum/ledger-suite-orchestrator/test_utils/src/pocket_ic/universal_canister.rs b/rs/ethereum/ledger-suite-orchestrator/test_utils/src/pocket_ic/universal_canister.rs new file mode 100644 index 000000000000..8674006bbee0 --- /dev/null +++ b/rs/ethereum/ledger-suite-orchestrator/test_utils/src/pocket_ic/universal_canister.rs @@ -0,0 +1,74 @@ +use candid::{Decode, Encode, Principal}; +use ic_base_types::PrincipalId; +use ic_management_canister_types::CanisterInfoArgs; +pub use ic_management_canister_types::{ + CanisterInfoResult, CanisterStatusType, ChangeDetails, CodeDeploymentMode, CodeDeploymentRecord, +}; +use ic_types_cycles::Cycles; +use ic_universal_canister::{UNIVERSAL_CANISTER_WASM, call_args, wasm}; +use pocket_ic::PocketIc; +use std::sync::Arc; + +pub struct UniversalCanister { + pub env: Arc, + pub canister_id: Principal, +} + +impl UniversalCanister { + pub fn new(env: Arc) -> Self { + let canister_id = env.create_canister(); + env.add_cycles(canister_id, u128::MAX); + Self { env, canister_id }.install_wasm() + } + + fn install_wasm(self) -> Self { + self.env.install_canister( + self.canister_id, + UNIVERSAL_CANISTER_WASM.to_vec(), + vec![], + None, + ); + self + } + + pub fn canister_info(&self, target: Principal) -> CanisterInfoResult { + let info_request_payload = universal_canister_payload( + Principal::management_canister(), + "canister_info", + Encode!(&CanisterInfoArgs { + canister_id: target, + num_requested_changes: Some(u64::MAX), + }) + .unwrap(), + Cycles::new(0), + ); + let bytes = self + .env + .update_call( + self.canister_id, + Principal::anonymous(), + "update", + info_request_payload, + ) + .unwrap(); + Decode!(&bytes[..], CanisterInfoResult).expect("failed to decode canister_info response") + } +} + +fn universal_canister_payload( + receiver: Principal, + method: &str, + payload: Vec, + cycles: Cycles, +) -> Vec { + wasm() + .call_with_cycles( + PrincipalId(receiver), + method, + call_args() + .other_side(payload) + .on_reject(wasm().reject_message().reject()), + cycles, + ) + .build() +} diff --git a/rs/ethereum/ledger-suite-orchestrator/tests/tests.rs b/rs/ethereum/ledger-suite-orchestrator/tests/tests.rs index b69d3c1b9630..1e70b2e86305 100644 --- a/rs/ethereum/ledger-suite-orchestrator/tests/tests.rs +++ b/rs/ethereum/ledger-suite-orchestrator/tests/tests.rs @@ -1,24 +1,23 @@ use assert_matches::assert_matches; use candid::{Decode, Encode, Nat, Principal}; -use ic_base_types::{CanisterId, PrincipalId}; +use ic_base_types::PrincipalId; use ic_http_types::{HttpRequest, HttpResponse}; use ic_ledger_suite_orchestrator::candid::{ AddErc20Arg, CyclesManagement, LedgerInitArg, LedgerSuiteVersion, ManagedCanisterStatus, ManagedCanisters, ManagedLedgerSuite, OrchestratorArg, OrchestratorInfo, UpdateCyclesManagement, UpgradeArg, }; +use ic_ledger_suite_orchestrator_test_utils::pocket_ic::{LedgerSuiteOrchestrator, new_pocket_ic}; use ic_ledger_suite_orchestrator_test_utils::{ - GIT_COMMIT_HASH_UPGRADE, LedgerSuiteOrchestrator, MINTER_PRINCIPAL, NNS_ROOT_PRINCIPAL, - assert_reply, cketh_installed_canisters, default_init_arg, ledger_suite_orchestrator_wasm, - new_state_machine, usdc, usdc_erc20_contract, usdt, + GIT_COMMIT_HASH_UPGRADE, MINTER_PRINCIPAL, NNS_ROOT_PRINCIPAL, cketh_installed_canisters, + default_init_arg, ledger_suite_orchestrator_wasm, usdc, usdc_erc20_contract, usdt, }; -use ic_state_machine_tests::ErrorCode; use icrc_ledger_types::icrc::generic_metadata_value::MetadataValue as LedgerMetadataValue; use icrc_ledger_types::icrc::metadata_key::MetadataKey; use icrc_ledger_types::icrc1::account::Account as LedgerAccount; +use pocket_ic::ErrorCode; use std::sync::Arc; -const MAX_TICKS: usize = 10; const GIT_COMMIT_HASH: &str = "6a8e5fca2c6b4e12966638c444e994e204b42989"; pub const TEN_TRILLIONS: u64 = 10_000_000_000_000; // 10 TC @@ -44,7 +43,7 @@ fn should_spawn_ledger_with_correct_init_args() { }) .expect_new_ledger_and_index_canisters(); let index_id = managed_canisters_assert.canister_ids.index.unwrap(); - assert_eq!(index_id, "ryjl3-tyaaa-aaaaa-aaaba-cai".parse().unwrap()); + assert_eq!(index_id, "xbze6-vd777-77777-aaaba-cai".parse().unwrap()); managed_canisters_assert .assert_ledger_icrc1_fee(2_000_000_000_000_u64) .assert_ledger_icrc1_decimals(6_u8) @@ -90,7 +89,7 @@ fn should_spawn_ledger_with_correct_init_args() { ), ( MetadataKey::parse(MetadataKey::ICRC106_INDEX_PRINCIPAL).unwrap(), - LedgerMetadataValue::from("ryjl3-tyaaa-aaaaa-aaaba-cai"), + LedgerMetadataValue::from("xbze6-vd777-77777-aaaba-cai"), ), ]); } @@ -134,7 +133,7 @@ fn should_change_cycles_for_canister_creation() { fn should_spawn_archive_from_ledger_with_correct_controllers() { let orchestrator = LedgerSuiteOrchestrator::default(); let expected_controllers = vec![ - orchestrator.ledger_suite_orchestrator_id.get().into(), + orchestrator.ledger_suite_orchestrator_id, NNS_ROOT_PRINCIPAL, ]; @@ -188,7 +187,7 @@ fn should_reject_adding_an_already_managed_erc20_token() { let result = orchestrator .upgrade_ledger_suite_orchestrator_with_same_wasm(&OrchestratorArg::AddErc20Arg(usdc())); - assert_matches!(result, Err(e) if e.code() == ErrorCode::CanisterCalledTrap && e.description().contains("Erc20ContractAlreadyManaged")); + assert_matches!(result, Err(e) if e.error_code == ErrorCode::CanisterCalledTrap && e.reject_message.contains("Erc20ContractAlreadyManaged")); } #[test] @@ -207,41 +206,38 @@ fn should_top_up_spawned_canisters() { .call_orchestrator_canister_ids(&usdc_erc20_contract()) .unwrap(); - let ledger_canister_id = - CanisterId::unchecked_from_principal(PrincipalId::from(canisters.ledger.unwrap())); - - let index_canister_id = - CanisterId::unchecked_from_principal(PrincipalId::from(canisters.index.unwrap())); + let ledger_canister_id = canisters.ledger.unwrap(); + let index_canister_id = canisters.index.unwrap(); - let pre_top_up_balance_ledger = orchestrator.canister_status_of(ledger_canister_id).cycles(); - let pre_top_up_balance_index = orchestrator.canister_status_of(index_canister_id).cycles(); + let pre_top_up_balance_ledger = orchestrator.canister_status_of(ledger_canister_id).cycles; + let pre_top_up_balance_index = orchestrator.canister_status_of(index_canister_id).cycles; orchestrator.advance_time_for_periodic_tasks(); let balance_ledger_after_first_top_up = - orchestrator.canister_status_of(ledger_canister_id).cycles(); + orchestrator.canister_status_of(ledger_canister_id).cycles; let balance_index_after_first_top_up = - orchestrator.canister_status_of(index_canister_id).cycles(); + orchestrator.canister_status_of(index_canister_id).cycles; assert_eq!( - balance_index_after_first_top_up - pre_top_up_balance_index, - TEN_TRILLIONS as u128 + balance_index_after_first_top_up.clone() - pre_top_up_balance_index, + Nat::from(TEN_TRILLIONS) ); assert_eq!( - balance_ledger_after_first_top_up - pre_top_up_balance_ledger, - TEN_TRILLIONS as u128 + balance_ledger_after_first_top_up.clone() - pre_top_up_balance_ledger, + Nat::from(TEN_TRILLIONS) ); orchestrator.advance_time_for_periodic_tasks(); let balance_ledger_after_second_top_up = - orchestrator.canister_status_of(ledger_canister_id).cycles(); + orchestrator.canister_status_of(ledger_canister_id).cycles; let balance_index_after_second_top_up = - orchestrator.canister_status_of(index_canister_id).cycles(); + orchestrator.canister_status_of(index_canister_id).cycles; assert_eq!( balance_index_after_second_top_up - balance_index_after_first_top_up, - TEN_TRILLIONS as u128 + Nat::from(TEN_TRILLIONS) ); assert_eq!( balance_ledger_after_second_top_up - balance_ledger_after_first_top_up, - TEN_TRILLIONS as u128 + Nat::from(TEN_TRILLIONS) ); } @@ -255,7 +251,7 @@ fn should_reject_upgrade_with_invalid_args() { ) { let result = orchestrator.upgrade_ledger_suite_orchestrator_with_same_wasm(invalid_upgrade_arg); - assert_matches!(result, Err(e) if e.code() == ErrorCode::CanisterCalledTrap && e.description().contains("ERROR: ")); + assert_matches!(result, Err(e) if e.error_code == ErrorCode::CanisterCalledTrap && e.reject_message.contains("ERROR: ")); } let orchestrator = LedgerSuiteOrchestrator::default(); @@ -318,18 +314,19 @@ fn should_reject_update_calls_to_http_request() { body: Default::default(), }; - let message_id = orchestrator.env.send_ingress( - PrincipalId::new_user_test_id(1), - orchestrator.ledger_suite_orchestrator_id, - "http_request", - Encode!(&request).expect("failed to encode HTTP request"), - ); + let message_id = orchestrator + .env + .submit_call( + orchestrator.ledger_suite_orchestrator_id, + PrincipalId::new_user_test_id(1).0, + "http_request", + Encode!(&request).expect("failed to encode HTTP request"), + ) + .expect("failed to submit call"); assert_matches!( - orchestrator - .env - .await_ingress(message_id.clone(), MAX_TICKS), - Err(e) if e.code() == ErrorCode::CanisterCalledTrap && e.description().contains("update call rejected") + orchestrator.env.await_call(message_id), + Err(e) if e.error_code == ErrorCode::CanisterCalledTrap && e.reject_message.contains("update call rejected") ); } @@ -361,11 +358,11 @@ fn should_retrieve_orchestrator_info() { erc20_contract: usdc.contract.clone(), ckerc20_token_symbol: "ckUSDC".to_string(), ledger: Some(ManagedCanisterStatus::Installed { - canister_id: usdc_ledger_id.into(), + canister_id: usdc_ledger_id, installed_wasm_hash: embedded_ledger_wasm_hash.to_string(), }), index: Some(ManagedCanisterStatus::Installed { - canister_id: usdc_index_id.into(), + canister_id: usdc_index_id, installed_wasm_hash: embedded_index_wasm_hash.to_string(), }), archives: vec![], @@ -374,11 +371,11 @@ fn should_retrieve_orchestrator_info() { erc20_contract: usdt.contract.clone(), ckerc20_token_symbol: "ckUSDT".to_string(), ledger: Some(ManagedCanisterStatus::Installed { - canister_id: usdt_ledger_id.into(), + canister_id: usdt_ledger_id, installed_wasm_hash: embedded_ledger_wasm_hash.to_string(), }), index: Some(ManagedCanisterStatus::Installed { - canister_id: usdt_index_id.into(), + canister_id: usdt_index_id, installed_wasm_hash: embedded_index_wasm_hash.to_string(), }), archives: vec![], @@ -446,16 +443,15 @@ fn should_query_logs_and_metrics() { }; let response = Decode!( - &assert_reply( - orchestrator - .env - .query( - orchestrator.ledger_suite_orchestrator_id, - "http_request", - Encode!(&request).expect("failed to encode HTTP request"), - ) - .expect("failed to query get_transactions on the ledger") - ), + &orchestrator + .env + .query_call( + orchestrator.ledger_suite_orchestrator_id, + Principal::anonymous(), + "http_request", + Encode!(&request).expect("failed to encode HTTP request"), + ) + .expect("failed to query get_transactions on the ledger"), HttpResponse ) .unwrap(); @@ -473,8 +469,7 @@ fn should_get_canister_status_smoke_test() { #[test] fn should_require_to_register_embedded_wasms_before_adding_ckerc20() { - let orchestrator = - LedgerSuiteOrchestrator::new(Arc::new(new_state_machine()), default_init_arg()); + let orchestrator = LedgerSuiteOrchestrator::new(Arc::new(new_pocket_ic()), default_init_arg()); assert_eq!( orchestrator.get_orchestrator_info().ledger_suite_version, None @@ -483,7 +478,7 @@ fn should_require_to_register_embedded_wasms_before_adding_ckerc20() { let usdc = usdc(); assert_matches!(orchestrator .upgrade_ledger_suite_orchestrator_with_same_wasm(&OrchestratorArg::AddErc20Arg(usdc.clone())), - Err(e) if e.code() == ErrorCode::CanisterCalledTrap && e.description().contains("ERROR: ") + Err(e) if e.error_code == ErrorCode::CanisterCalledTrap && e.reject_message.contains("ERROR: ") ); let orchestrator = orchestrator.register_embedded_wasms(); @@ -516,7 +511,7 @@ fn should_require_to_register_embedded_wasms_before_adding_ckerc20() { #[test] fn should_not_change_ledger_suite_version_when_registering_embedded_wasms_a_second_time() { - let env = Arc::new(new_state_machine()); + let env = Arc::new(new_pocket_ic()); let orchestrator_v1 = LedgerSuiteOrchestrator::new_with_ledger_get_blocks_disabled( env.clone(), default_init_arg(), @@ -552,7 +547,7 @@ mod upgrade { use ic_crypto_test_utils_reproducible_rng::reproducible_rng; use ic_ledger_suite_orchestrator::candid::{InstalledCanister, InstalledLedgerSuite}; use ic_ledger_suite_orchestrator::state::WasmHash; - use ic_ledger_suite_orchestrator_test_utils::universal_canister::{ + use ic_ledger_suite_orchestrator_test_utils::pocket_ic::universal_canister::{ CanisterInfoResult, ChangeDetails, CodeDeploymentMode, CodeDeploymentRecord, UniversalCanister, }; @@ -560,14 +555,14 @@ mod upgrade { GIT_COMMIT_HASH_UPGRADE, default_init_arg, ledger_suite_orchestrator_wasm, ledger_wasm, tweak_ledger_suite_wasms, usdt_erc20_contract, }; - use ic_management_canister_types_private::{CanisterSettingsArgsBuilder, CanisterStatusType}; + use ic_management_canister_types::{CanisterSettings, CanisterStatusType}; use icrc_ledger_types::icrc1::transfer::TransferArg; use icrc_ledger_types::icrc3::blocks::GetBlocksRequest; use proptest::prelude::Rng; #[test] fn should_have_two_different_orchestrator_versions_embedding_two_different_ledgers() { - let env = Arc::new(new_state_machine()); + let env = Arc::new(new_pocket_ic()); let orchestrator_v1 = LedgerSuiteOrchestrator::new_with_ledger_get_blocks_disabled( env.clone(), @@ -598,7 +593,7 @@ mod upgrade { #[test] fn should_upgrade_managed_ledgers_to_new_version() { - let env = Arc::new(new_state_machine()); + let env = Arc::new(new_pocket_ic()); let orchestrator_v1 = LedgerSuiteOrchestrator::new_with_ledger_get_blocks_disabled( env.clone(), default_init_arg(), @@ -667,19 +662,18 @@ mod upgrade { orchestrator_v2.advance_time_for_upgrade(); for ledger in [ckusdc_ledger, ckusdt_ledger] { - let status = orchestrator_v2 - .canister_status_of(CanisterId::unchecked_from_principal(ledger.into())); + let status = orchestrator_v2.canister_status_of(ledger); assert_eq!( - status.module_hash(), + status.module_hash, Some(embedded_ledger_wasm_v2.hash().as_ref().to_vec()) ); - assert_eq!(status.status(), CanisterStatusType::Running); + assert_eq!(status.status, CanisterStatusType::Running); } } #[test] fn should_upgrade_all_managed_canisters_with_different_versions_to_same_version() { - let env = Arc::new(new_state_machine()); + let env = Arc::new(new_pocket_ic()); let orchestrator = LedgerSuiteOrchestrator::new(env.clone(), default_init_arg()).register_embedded_wasms(); let universal_canister = UniversalCanister::new(env.clone()); @@ -759,7 +753,7 @@ mod upgrade { #[test] fn should_upgrade_all_managed_canisters_to_same_already_installed_version() { - let env = Arc::new(new_state_machine()); + let env = Arc::new(new_pocket_ic()); let orchestrator = LedgerSuiteOrchestrator::new(env.clone(), default_init_arg()).register_embedded_wasms(); let universal_canister = UniversalCanister::new(env.clone()); @@ -854,7 +848,7 @@ mod upgrade { #[test] fn should_upgrade_without_reinstalling() { - let env = Arc::new(new_state_machine()); + let env = Arc::new(new_pocket_ic()); let orchestrator_v1 = LedgerSuiteOrchestrator::new_with_ledger_get_blocks_disabled( env.clone(), default_init_arg(), @@ -982,7 +976,7 @@ mod upgrade { // which typically corresponds to the new version of the archive that one wants to upgrade to. #[test] fn should_upgrade_archive_created_just_before_ledger_upgrade() { - let env = Arc::new(new_state_machine()); + let env = Arc::new(new_pocket_ic()); let orchestrator = LedgerSuiteOrchestrator::new(env.clone(), default_init_arg()).register_embedded_wasms(); let universal_canister = UniversalCanister::new(env.clone()); @@ -1069,18 +1063,19 @@ mod upgrade { #[test] fn should_upgrade_canisters_managed_but_not_installed_by_orchestrator() { - let env = Arc::new(new_state_machine()); + let env = Arc::new(new_pocket_ic()); let orchestrator = LedgerSuiteOrchestrator::new(env.clone(), default_init_arg()).register_embedded_wasms(); let embedded_ledger_wasm_hash = orchestrator.embedded_ledger_wasm_hash.clone(); let embedded_index_wasm_hash = orchestrator.embedded_index_wasm_hash.clone(); let embedded_archive_wasm_hash = orchestrator.embedded_archive_wasm_hash.clone(); - let [ledger, index] = { + let ([ledger, index], pre_existing_controller) = { // Temporary orchestrator is used as helper to spawn-off a new ledger suite. let orchestrator_v1 = LedgerSuiteOrchestrator::new_with_ledger_get_blocks_disabled( env.clone(), default_init_arg(), ); + let pre_existing_controller = orchestrator_v1.ledger_suite_orchestrator_id; let embedded_ledger_wasm_hash_v1 = orchestrator_v1.embedded_ledger_wasm_hash.clone(); let embedded_index_wasm_hash_v1 = orchestrator_v1.embedded_index_wasm_hash.clone(); assert_ne!(embedded_ledger_wasm_hash, embedded_ledger_wasm_hash_v1); @@ -1090,24 +1085,29 @@ mod upgrade { .expect_new_ledger_and_index_canisters() .assert_ledger_has_wasm_hash(&embedded_ledger_wasm_hash_v1) .canister_ids; - [ - InstalledCanister { - canister_id: canisters.ledger.unwrap(), - installed_wasm_hash: embedded_ledger_wasm_hash_v1.to_string(), - }, - InstalledCanister { - canister_id: canisters.index.unwrap(), - installed_wasm_hash: embedded_index_wasm_hash_v1.to_string(), - }, - ] + ( + [ + InstalledCanister { + canister_id: canisters.ledger.unwrap(), + installed_wasm_hash: embedded_ledger_wasm_hash_v1.to_string(), + }, + InstalledCanister { + canister_id: canisters.index.unwrap(), + installed_wasm_hash: embedded_index_wasm_hash_v1.to_string(), + }, + ], + pre_existing_controller, + ) }; let universal_canister = UniversalCanister::new(env.clone()); for canister_id in [ledger.canister_id, index.canister_id] { - env.update_settings( - &CanisterId::try_from(PrincipalId(canister_id)).unwrap(), - CanisterSettingsArgsBuilder::new() - .with_controllers(vec![orchestrator.ledger_suite_orchestrator_id.into()]) - .build(), + env.update_canister_settings( + canister_id, + Some(pre_existing_controller), + CanisterSettings { + controllers: Some(vec![orchestrator.ledger_suite_orchestrator_id]), + ..Default::default() + }, ) .unwrap(); } @@ -1134,7 +1134,7 @@ mod upgrade { assert_eq!( universal_canister - .canister_info(CanisterId::try_from(PrincipalId(index.canister_id)).unwrap()) + .canister_info(index.canister_id) .module_hash .unwrap() .as_slice(), @@ -1142,7 +1142,7 @@ mod upgrade { ); assert_eq!( universal_canister - .canister_info(CanisterId::try_from(PrincipalId(ledger.canister_id)).unwrap()) + .canister_info(ledger.canister_id) .module_hash .unwrap() .as_slice(),