From c4939ee3e80b4098ac8a543b31c1b586dfe4460e Mon Sep 17 00:00:00 2001 From: Andre Popovitch Date: Wed, 20 Nov 2024 11:56:40 -0600 Subject: [PATCH] test(sns): Add test that all SNS canisters can be upgraded using the advance-target-version mechanism (#2722) The test is simple - we deploy an SNS, add upgrade steps for all 6 canisters, then advance the target version, then check that the SNS eventually gets to the desired version. This tests that advance-target-version's upgrade logic is capable of working for all 6 canisters, at least in a normal happy scenario. --- rs/nervous_system/agent/src/lib.rs | 4 +- .../src/pocket_ic_helpers.rs | 59 +++++--- .../tests/advance_target_version.rs | 139 +++++++++++++++++- 3 files changed, 177 insertions(+), 25 deletions(-) diff --git a/rs/nervous_system/agent/src/lib.rs b/rs/nervous_system/agent/src/lib.rs index d90b0b4d69f..e9862bb5d69 100644 --- a/rs/nervous_system/agent/src/lib.rs +++ b/rs/nervous_system/agent/src/lib.rs @@ -1,6 +1,6 @@ -mod agent_impl; +pub mod agent_impl; pub mod nns; -mod pocketic_impl; +pub mod pocketic_impl; pub mod sns; use candid::Principal; diff --git a/rs/nervous_system/integration_tests/src/pocket_ic_helpers.rs b/rs/nervous_system/integration_tests/src/pocket_ic_helpers.rs index 566f3c182ed..fb410b3b6d4 100644 --- a/rs/nervous_system/integration_tests/src/pocket_ic_helpers.rs +++ b/rs/nervous_system/integration_tests/src/pocket_ic_helpers.rs @@ -2,6 +2,7 @@ use candid::{Decode, Encode, Nat, Principal}; use canister_test::Wasm; use ic_base_types::{CanisterId, PrincipalId, SubnetId}; use ic_ledger_core::Tokens; +use ic_nervous_system_agent::pocketic_impl::PocketIcCallError; use ic_nervous_system_agent::sns::Sns; use ic_nervous_system_agent::CallCanisters; use ic_nervous_system_common::{E8, ONE_DAY_SECONDS}; @@ -211,6 +212,18 @@ pub async fn propose_to_set_network_economics_and_wait( } pub type DeployedSnsStartingInfo = BTreeMap; +pub type SnsWasms = BTreeMap; + +pub fn hash_sns_wasms(wasms: &SnsWasms) -> Version { + Version { + root_wasm_hash: wasms[&SnsCanisterType::Root].sha256_hash().to_vec(), + governance_wasm_hash: wasms[&SnsCanisterType::Governance].sha256_hash().to_vec(), + ledger_wasm_hash: wasms[&SnsCanisterType::Ledger].sha256_hash().to_vec(), + swap_wasm_hash: wasms[&SnsCanisterType::Swap].sha256_hash().to_vec(), + archive_wasm_hash: wasms[&SnsCanisterType::Archive].sha256_hash().to_vec(), + index_wasm_hash: wasms[&SnsCanisterType::Index].sha256_hash().to_vec(), + } +} pub async fn add_wasms_to_sns_wasm( pocket_ic: &PocketIc, @@ -1032,6 +1045,7 @@ pub mod nns { pub mod sns_wasm { use super::*; + use ic_nns_test_utils::sns_wasm::create_modified_sns_wasm; use ic_sns_wasm::pb::v1::{ GetWasmRequest, GetWasmResponse, ListUpgradeStepsRequest, ListUpgradeStepsResponse, }; @@ -1116,6 +1130,23 @@ pub mod nns { latest_version } + + /// Modify the WASM for a given canister type and add it to SNS-W. + /// Returns the new (modified) version that is now at the tip of SNS-W. + pub async fn modify_and_add_wasm( + pocket_ic: &PocketIc, + mut version: SnsWasms, + canister_type: SnsCanisterType, + nonce: u32, + ) -> SnsWasms { + let wasm = version.get(&canister_type).unwrap(); + let wasm = create_modified_sns_wasm(wasm, Some(nonce)); + add_wasm_via_nns_proposal(pocket_ic, wasm.clone()) + .await + .unwrap(); + version.insert(canister_type, wasm); + version + } } } @@ -1565,29 +1596,21 @@ pub mod sns { } } + pub async fn try_get_upgrade_journal( + pocket_ic: &PocketIc, + sns_governance_canister_id: PrincipalId, + ) -> std::result::Result { + let payload = sns_pb::GetUpgradeJournalRequest {}; + pocket_ic.call(sns_governance_canister_id, payload).await + } + pub async fn get_upgrade_journal( pocket_ic: &PocketIc, canister_id: PrincipalId, ) -> sns_pb::GetUpgradeJournalResponse { - let result = pocket_ic - .query_call( - canister_id.into(), - Principal::from(PrincipalId::new_anonymous()), - "get_upgrade_journal", - Encode!(&sns_pb::GetUpgradeJournalRequest {}).unwrap(), - ) + try_get_upgrade_journal(pocket_ic, canister_id) .await - .unwrap(); - let result = match result { - WasmResult::Reply(reply) => reply, - WasmResult::Reject(reject) => { - panic!( - "get_upgrade_journal rejected by SNS governance: {:#?}", - reject - ) - } - }; - Decode!(&result, sns_pb::GetUpgradeJournalResponse).unwrap() + .unwrap() } pub async fn advance_target_version( diff --git a/rs/nervous_system/integration_tests/tests/advance_target_version.rs b/rs/nervous_system/integration_tests/tests/advance_target_version.rs index abd09bcbbff..eea43207655 100644 --- a/rs/nervous_system/integration_tests/tests/advance_target_version.rs +++ b/rs/nervous_system/integration_tests/tests/advance_target_version.rs @@ -3,7 +3,8 @@ use ic_nervous_system_integration_tests::pocket_ic_helpers::sns; use ic_nervous_system_integration_tests::{ create_service_nervous_system_builder::CreateServiceNervousSystemBuilder, pocket_ic_helpers::{ - add_wasm_via_nns_proposal, add_wasms_to_sns_wasm, install_nns_canisters, nns, + add_wasm_via_nns_proposal, add_wasms_to_sns_wasm, hash_sns_wasms, install_nns_canisters, + nns, }, }; use ic_nns_test_utils::sns_wasm::create_modified_sns_wasm; @@ -16,6 +17,7 @@ use ic_sns_swap::pb::v1::Lifecycle; use ic_sns_wasm::pb::v1::SnsCanisterType; use pocket_ic::nonblocking::PocketIc; use pocket_ic::PocketIcBuilder; +use std::collections::BTreeMap; use std::time::Duration; /// Verifies that the upgrade journal has the expected entries. @@ -91,18 +93,18 @@ async fn test_get_upgrade_journal() { .build_async() .await; - // Install the (master) NNS canisters. + // Step 0: Install the (master) NNS canisters. let with_mainnet_nns_canisters = false; install_nns_canisters(&pocket_ic, vec![], with_mainnet_nns_canisters, None, vec![]).await; - // Publish (master) SNS Wasms to SNS-W. + // Step 0.1: Publish (master) SNS Wasms to SNS-W. let with_mainnet_sns_canisters = false; let deployed_sns_starting_info = add_wasms_to_sns_wasm(&pocket_ic, with_mainnet_sns_canisters) .await .unwrap(); let initial_sns_version = nns::sns_wasm::get_latest_sns_version(&pocket_ic).await; - // Deploy an SNS instance via proposal. + // Step 0.2: Deploy an SNS instance via proposal. let sns = { let create_service_nervous_system = CreateServiceNervousSystemBuilder::default().build(); let swap_parameters = create_service_nervous_system @@ -130,7 +132,7 @@ async fn test_get_upgrade_journal() { sns }; - // Step 1: right after SNS creation. + // Step 1: Check that the upgrade journal contains the initial version right after SNS creation. let mut expected_upgrade_journal_entries = vec![]; { expected_upgrade_journal_entries.push(Event::UpgradeStepsRefreshed( @@ -324,3 +326,130 @@ async fn test_get_upgrade_journal() { .await; } } + +#[tokio::test] +async fn test_advance_target_version_upgrades_all_canisters() { + // Step 0: Setup the test environment. + let pocket_ic = PocketIcBuilder::new() + .with_nns_subnet() + .with_sns_subnet() + .build_async() + .await; + + // Install the (master) NNS canisters. + let with_mainnet_nns_canisters = false; + install_nns_canisters(&pocket_ic, vec![], with_mainnet_nns_canisters, None, vec![]).await; + + // Step 0.1: Publish (master) SNS Wasms to SNS-W. + let with_mainnet_sns_canisters = false; + let initial_sns_version = { + let deployed_sns_starting_info = + add_wasms_to_sns_wasm(&pocket_ic, with_mainnet_sns_canisters) + .await + .unwrap(); + deployed_sns_starting_info + .into_iter() + .map(|(canister_type, (_, wasm))| (canister_type, wasm)) + .collect::>() + }; + + // Step 0.2: Deploy an SNS instance via proposal. + let sns = { + let create_service_nervous_system = CreateServiceNervousSystemBuilder::default().build(); + let swap_parameters = create_service_nervous_system + .swap_parameters + .clone() + .unwrap(); + + let sns_instance_label = "1"; + let (sns, _) = nns::governance::propose_to_deploy_sns_and_wait( + &pocket_ic, + create_service_nervous_system, + sns_instance_label, + ) + .await; + + sns::swap::await_swap_lifecycle(&pocket_ic, sns.swap.canister_id, Lifecycle::Open) + .await + .unwrap(); + sns::swap::smoke_test_participate_and_finalize( + &pocket_ic, + sns.swap.canister_id, + swap_parameters, + ) + .await; + sns + }; + + // Step 0.3: Ensure an archive canister is spawned. + sns::ensure_archive_canister_is_spawned_or_panic( + &pocket_ic, + sns.governance.canister_id, + sns.ledger.canister_id, + ) + .await; + + // Step 2: Publish new SNS versions. + let latest_sns_version = { + let canister_types = vec![ + SnsCanisterType::Governance, + SnsCanisterType::Root, + SnsCanisterType::Swap, + SnsCanisterType::Ledger, + SnsCanisterType::Index, + SnsCanisterType::Archive, + ]; + + let mut latest_version = initial_sns_version; + + for canister_type in canister_types { + latest_version = + nns::sns_wasm::modify_and_add_wasm(&pocket_ic, latest_version, canister_type, 1) + .await; + } + + latest_version + }; + + // Step 3: Wait for the upgrade steps to be refreshed. + await_with_timeout( + &pocket_ic, + UPGRADE_STEPS_INTERVAL_REFRESH_BACKOFF_SECONDS, + |pocket_ic| async { + sns::governance::try_get_upgrade_journal(pocket_ic, sns.governance.canister_id) + .await + .ok() + .and_then(|journal| journal.upgrade_steps) + .map(|upgrade_steps| upgrade_steps.versions) + .map(|versions| versions.len()) + }, + // Hopefully there are 7 upgrade steps - 1 initial version, then another for each of the 6 canisters. + &Some(7usize), + ) + .await + .unwrap(); + + // Step 4: advance the target version to the latest version. + let latest_sns_version_hash = hash_sns_wasms(&latest_sns_version); + sns::governance::advance_target_version( + &pocket_ic, + sns.governance.canister_id, + latest_sns_version_hash.clone(), + ) + .await; + + // Step 5: Wait for the upgrade to happen + await_with_timeout( + &pocket_ic, + UPGRADE_STEPS_INTERVAL_REFRESH_BACKOFF_SECONDS, + |pocket_ic| async { + let journal = + sns::governance::try_get_upgrade_journal(pocket_ic, sns.governance.canister_id) + .await; + journal.ok().and_then(|journal| journal.deployed_version) + }, + &Some(latest_sns_version_hash), + ) + .await + .unwrap(); +}