Skip to content

Commit

Permalink
test(sns): Add test that all SNS canisters can be upgraded using the …
Browse files Browse the repository at this point in the history
…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.
  • Loading branch information
anchpop authored Nov 20, 2024
1 parent 659d9b1 commit c4939ee
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 25 deletions.
4 changes: 2 additions & 2 deletions rs/nervous_system/agent/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
59 changes: 41 additions & 18 deletions rs/nervous_system/integration_tests/src/pocket_ic_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -211,6 +212,18 @@ pub async fn propose_to_set_network_economics_and_wait(
}

pub type DeployedSnsStartingInfo = BTreeMap<SnsCanisterType, (ProposalInfo, SnsWasm)>;
pub type SnsWasms = BTreeMap<SnsCanisterType, SnsWasm>;

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,
Expand Down Expand Up @@ -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,
};
Expand Down Expand Up @@ -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
}
}
}

Expand Down Expand Up @@ -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<sns_pb::GetUpgradeJournalResponse, PocketIcCallError> {
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(
Expand Down
139 changes: 134 additions & 5 deletions rs/nervous_system/integration_tests/tests/advance_target_version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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::<BTreeMap<_, _>>()
};

// 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();
}

0 comments on commit c4939ee

Please sign in to comment.