diff --git a/Cargo.lock b/Cargo.lock index ede7429082..117fa141cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1806,6 +1806,7 @@ dependencies = [ "canister_logger", "canister_state_macros", "canister_tracing_macros", + "constants", "cycles_dispenser_canister", "cycles_minting_canister", "cycles_minting_canister_c2c_client", @@ -2351,6 +2352,17 @@ dependencies = [ "types", ] +[[package]] +name = "event_relay_canister_c2c_client" +version = "0.1.0" +dependencies = [ + "candid", + "canister_client", + "event_relay_canister", + "ic-cdk 0.17.0", + "types", +] + [[package]] name = "event_relay_canister_impl" version = "0.1.0" @@ -4110,6 +4122,7 @@ dependencies = [ "community_canister", "constants", "cycles_dispenser_canister", + "cycles_minting_canister", "escrow_canister", "event_relay_canister", "event_store_canister", @@ -4994,6 +5007,18 @@ dependencies = [ "types", ] +[[package]] +name = "notifications_index_canister_c2c_client" +version = "0.1.0" +dependencies = [ + "canister_client", + "ic-cdk 0.17.0", + "msgpack", + "notifications_index_canister", + "tracing", + "types", +] + [[package]] name = "notifications_index_canister_client" version = "0.1.0" @@ -6236,22 +6261,35 @@ dependencies = [ "canister_api_macros", "canister_logger", "canister_state_macros", + "canister_timer_jobs", "canister_tracing_macros", "constants", + "cycles_dispenser_canister", + "cycles_dispenser_canister_c2c_client", + "cycles_minting_canister", + "cycles_minting_canister_c2c_client", "dataurl", "escrow_canister", "escrow_canister_c2c_client", + "event_relay_canister", + "event_relay_canister_c2c_client", "futures", + "group_index_canister", + "group_index_canister_c2c_client", "hex", "http_request", "human_readable", "ic-cdk 0.17.0", "ic-cdk-timers", + "ic-ledger-types", "ic-stable-structures", + "icp_ledger_canister_c2c_client", "icrc-ledger-types", "icrc_ledger_canister", "icrc_ledger_canister_c2c_client", "msgpack", + "notifications_index_canister", + "notifications_index_canister_c2c_client", "rand 0.8.5", "registry_canister", "serde", diff --git a/Cargo.toml b/Cargo.toml index 69473001a0..21da57bdd1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ members = [ "backend/canisters/notifications/client", "backend/canisters/notifications/impl", "backend/canisters/notifications_index/api", + "backend/canisters/notifications_index/c2c_client", "backend/canisters/notifications_index/client", "backend/canisters/notifications_index/impl", "backend/canisters/online_users/api", diff --git a/backend/canisters/cycles_dispenser/api/src/lifecycle/init.rs b/backend/canisters/cycles_dispenser/api/src/lifecycle/init.rs index 95d5037ac3..19d5eeb0a2 100644 --- a/backend/canisters/cycles_dispenser/api/src/lifecycle/init.rs +++ b/backend/canisters/cycles_dispenser/api/src/lifecycle/init.rs @@ -6,6 +6,7 @@ use types::{BuildVersion, CanisterId, Cycles, Milliseconds}; pub struct Args { pub governance_principals: Vec, pub canisters: Vec, + pub registry_canister_id: CanisterId, pub max_top_up_amount: Cycles, pub min_interval: Milliseconds, pub min_cycles_balance: Cycles, diff --git a/backend/canisters/cycles_dispenser/c2c_client/src/lib.rs b/backend/canisters/cycles_dispenser/c2c_client/src/lib.rs index f85ed2e7ba..adc2f0f3c6 100644 --- a/backend/canisters/cycles_dispenser/c2c_client/src/lib.rs +++ b/backend/canisters/cycles_dispenser/c2c_client/src/lib.rs @@ -4,4 +4,5 @@ use cycles_dispenser_canister::*; // Queries // Updates +generate_candid_c2c_call!(add_canister); generate_candid_c2c_call!(c2c_request_cycles); diff --git a/backend/canisters/cycles_dispenser/impl/Cargo.toml b/backend/canisters/cycles_dispenser/impl/Cargo.toml index df0afdab8b..43e0991d50 100644 --- a/backend/canisters/cycles_dispenser/impl/Cargo.toml +++ b/backend/canisters/cycles_dispenser/impl/Cargo.toml @@ -16,6 +16,7 @@ canister_client = { path = "../../../libraries/canister_client" } canister_logger = { path = "../../../libraries/canister_logger" } canister_state_macros = { path = "../../../libraries/canister_state_macros" } canister_tracing_macros = { path = "../../../libraries/canister_tracing_macros" } +constants = { path = "../../../libraries/constants" } cycles_dispenser_canister = { path = "../api" } cycles_minting_canister = { path = "../../../external_canisters/cmc/api" } cycles_minting_canister_c2c_client = { path = "../../../external_canisters/cmc/c2c_client" } diff --git a/backend/canisters/cycles_dispenser/impl/src/guards.rs b/backend/canisters/cycles_dispenser/impl/src/guards.rs index 3071be4274..94695e99b5 100644 --- a/backend/canisters/cycles_dispenser/impl/src/guards.rs +++ b/backend/canisters/cycles_dispenser/impl/src/guards.rs @@ -8,10 +8,10 @@ pub fn caller_is_governance_principal() -> Result<(), String> { } } -pub fn caller_is_authorized_to_add_canister() -> Result<(), String> { - if read_state(|state| state.is_caller_authorized_to_add_canister()) { +pub fn caller_is_registry_canister() -> Result<(), String> { + if read_state(|state| state.is_caller_registry_canister()) { Ok(()) } else { - Err("Caller is not authorized to add a canister".to_string()) + Err("Caller is not the Registry canister".to_string()) } } diff --git a/backend/canisters/cycles_dispenser/impl/src/jobs/burn_icp_into_cycles.rs b/backend/canisters/cycles_dispenser/impl/src/jobs/burn_icp_into_cycles.rs index a63e343c23..8e5ab7065c 100644 --- a/backend/canisters/cycles_dispenser/impl/src/jobs/burn_icp_into_cycles.rs +++ b/backend/canisters/cycles_dispenser/impl/src/jobs/burn_icp_into_cycles.rs @@ -1,4 +1,5 @@ use crate::{mutate_state, State}; +use constants::NANOS_PER_MILLISECOND; use ic_ledger_types::{AccountIdentifier, BlockIndex, Memo, Subaccount, Timestamp, Tokens, TransferArgs}; use std::time::Duration; use tracing::{error, info}; @@ -77,7 +78,7 @@ async fn burn_icp(burn_details: BurnIcpDetails) { from_subaccount: None, to: AccountIdentifier::new(&burn_details.cmc, &Subaccount::from(burn_details.this_canister_id)), created_at_time: Some(Timestamp { - timestamp_nanos: burn_details.now * 1_000_000, + timestamp_nanos: burn_details.now * NANOS_PER_MILLISECOND, }), }, ) diff --git a/backend/canisters/cycles_dispenser/impl/src/jobs/top_up_sns_canisters.rs b/backend/canisters/cycles_dispenser/impl/src/jobs/top_up_sns_canisters.rs index d16edef0c1..817a046966 100644 --- a/backend/canisters/cycles_dispenser/impl/src/jobs/top_up_sns_canisters.rs +++ b/backend/canisters/cycles_dispenser/impl/src/jobs/top_up_sns_canisters.rs @@ -37,7 +37,6 @@ async fn run_async(canister_id: CanisterId) { mutate_state(|state| { let now = state.env.now(); for canister_id in canisters.iter().filter_map(|s| s.canister_id) { - state.data.canisters_directly_controlled_by_sns_root.insert(canister_id); state.data.canisters.add(canister_id, now); } }); diff --git a/backend/canisters/cycles_dispenser/impl/src/lib.rs b/backend/canisters/cycles_dispenser/impl/src/lib.rs index 855d772e10..f6da08ad8f 100644 --- a/backend/canisters/cycles_dispenser/impl/src/lib.rs +++ b/backend/canisters/cycles_dispenser/impl/src/lib.rs @@ -5,7 +5,7 @@ use ic_ledger_types::{BlockIndex, Tokens}; use ledger_utils::default_ledger_account; use serde::{Deserialize, Serialize}; use std::cell::RefCell; -use std::collections::{BTreeMap, BTreeSet, HashSet}; +use std::collections::{BTreeMap, HashSet}; use types::{BuildVersion, CanisterId, Cycles, Milliseconds, TimestampMillis, Timestamped}; use utils::env::Environment; @@ -37,10 +37,8 @@ impl State { self.data.governance_principals.contains(&self.env.caller()) } - pub fn is_caller_authorized_to_add_canister(&self) -> bool { - let caller = self.env.caller(); - self.data.governance_principals.contains(&caller) - || self.data.canisters_directly_controlled_by_sns_root.contains(&caller) + pub fn is_caller_registry_canister(&self) -> bool { + self.env.caller() == self.data.registry_canister_id } pub fn metrics(&self) -> Metrics { @@ -60,8 +58,11 @@ impl State { min_cycles_balance: self.data.min_cycles_balance, icp_burn_amount: self.data.icp_burn_amount, stable_memory_sizes: memory::memory_sizes(), - ledger_canister: self.data.ledger_canister, - cycles_minting_canister: self.data.cycles_minting_canister, + canister_ids: CanisterIds { + registry: self.data.registry_canister_id, + ledger: self.data.ledger_canister, + cmc: self.data.cycles_minting_canister, + }, } } } @@ -70,8 +71,8 @@ impl State { struct Data { pub governance_principals: HashSet, pub canisters: Canisters, - #[serde(default)] - pub canisters_directly_controlled_by_sns_root: BTreeSet, + #[serde(default = "CanisterId::anonymous")] + pub registry_canister_id: CanisterId, pub sns_root_canister: Option, pub max_top_up_amount: Cycles, pub min_interval: Milliseconds, @@ -89,6 +90,7 @@ impl Data { pub fn new( governance_principals: Vec, canisters: Vec, + registry_canister_id: CanisterId, max_top_up_amount: Cycles, min_interval: Milliseconds, min_cycles_balance: Cycles, @@ -101,7 +103,7 @@ impl Data { Data { governance_principals: governance_principals.into_iter().collect(), canisters: Canisters::new(canisters, now), - canisters_directly_controlled_by_sns_root: BTreeSet::default(), + registry_canister_id, sns_root_canister: None, max_top_up_amount, min_interval, @@ -133,6 +135,12 @@ pub struct Metrics { pub min_cycles_balance: Cycles, pub icp_burn_amount: Tokens, pub stable_memory_sizes: BTreeMap, - pub ledger_canister: CanisterId, - pub cycles_minting_canister: CanisterId, + pub canister_ids: CanisterIds, +} + +#[derive(CandidType, Serialize, Debug)] +pub struct CanisterIds { + registry: CanisterId, + ledger: CanisterId, + cmc: CanisterId, } diff --git a/backend/canisters/cycles_dispenser/impl/src/lifecycle/init.rs b/backend/canisters/cycles_dispenser/impl/src/lifecycle/init.rs index 1862a8b080..a8c8e51635 100644 --- a/backend/canisters/cycles_dispenser/impl/src/lifecycle/init.rs +++ b/backend/canisters/cycles_dispenser/impl/src/lifecycle/init.rs @@ -17,6 +17,7 @@ fn init(args: Args) { let data = Data::new( args.governance_principals, args.canisters, + args.registry_canister_id, args.max_top_up_amount, args.min_interval, args.min_cycles_balance, diff --git a/backend/canisters/cycles_dispenser/impl/src/lifecycle/post_upgrade.rs b/backend/canisters/cycles_dispenser/impl/src/lifecycle/post_upgrade.rs index 22bfb1ef28..4ef3af1c7e 100644 --- a/backend/canisters/cycles_dispenser/impl/src/lifecycle/post_upgrade.rs +++ b/backend/canisters/cycles_dispenser/impl/src/lifecycle/post_upgrade.rs @@ -7,6 +7,7 @@ use cycles_dispenser_canister::post_upgrade::Args; use ic_cdk::post_upgrade; use stable_memory::get_reader; use tracing::info; +use types::CanisterId; #[post_upgrade] #[trace] @@ -14,9 +15,15 @@ fn post_upgrade(args: Args) { let memory = get_upgrades_memory(); let reader = get_reader(&memory); - let (data, errors, logs, traces): (Data, Vec, Vec, Vec) = + let (mut data, errors, logs, traces): (Data, Vec, Vec, Vec) = msgpack::deserialize(reader).unwrap(); + if data.test_mode { + data.registry_canister_id = CanisterId::from_text("cglwi-oaaaa-aaaar-aqw4q-cai").unwrap(); + } else { + data.registry_canister_id = CanisterId::from_text("cpi5u-yiaaa-aaaar-aqw5a-cai").unwrap(); + } + canister_logger::init_with_logs(data.test_mode, errors, logs, traces); let env = init_env(data.rng_seed); diff --git a/backend/canisters/cycles_dispenser/impl/src/updates/add_canister.rs b/backend/canisters/cycles_dispenser/impl/src/updates/add_canister.rs index be4d9dfe30..752078fe45 100644 --- a/backend/canisters/cycles_dispenser/impl/src/updates/add_canister.rs +++ b/backend/canisters/cycles_dispenser/impl/src/updates/add_canister.rs @@ -1,10 +1,10 @@ -use crate::guards::caller_is_authorized_to_add_canister; +use crate::guards::caller_is_registry_canister; use crate::{mutate_state, State}; use canister_api_macros::proposal; use canister_tracing_macros::trace; use cycles_dispenser_canister::add_canister::{Response::*, *}; -#[proposal(guard = "caller_is_authorized_to_add_canister")] +#[proposal(guard = "caller_is_registry_canister")] #[trace] fn add_canister(args: Args) -> Response { mutate_state(|state| add_canister_impl(args, state)) diff --git a/backend/canisters/event_relay/api/src/lifecycle/init.rs b/backend/canisters/event_relay/api/src/lifecycle/init.rs index 52762a10df..c3e022f8ac 100644 --- a/backend/canisters/event_relay/api/src/lifecycle/init.rs +++ b/backend/canisters/event_relay/api/src/lifecycle/init.rs @@ -7,6 +7,7 @@ pub struct Args { pub push_events_whitelist: Vec, pub event_store_canister_id: CanisterId, pub cycles_dispenser_canister_id: CanisterId, + pub registry_canister_id: CanisterId, pub chat_ledger_canister_id: CanisterId, pub chat_governance_canister_id: CanisterId, pub wasm_version: BuildVersion, diff --git a/backend/canisters/event_relay/api/src/updates/authorize_principals.rs b/backend/canisters/event_relay/api/src/updates/authorize_principals.rs new file mode 100644 index 0000000000..ec113fb7a3 --- /dev/null +++ b/backend/canisters/event_relay/api/src/updates/authorize_principals.rs @@ -0,0 +1,12 @@ +use candid::{CandidType, Principal}; +use serde::{Deserialize, Serialize}; + +#[derive(CandidType, Serialize, Deserialize, Debug)] +pub struct Args { + pub principals: Vec, +} + +#[derive(CandidType, Serialize, Deserialize, Debug)] +pub enum Response { + Success, +} diff --git a/backend/canisters/event_relay/api/src/updates/mod.rs b/backend/canisters/event_relay/api/src/updates/mod.rs index 38403c1f3a..d6efc3de7e 100644 --- a/backend/canisters/event_relay/api/src/updates/mod.rs +++ b/backend/canisters/event_relay/api/src/updates/mod.rs @@ -1 +1,2 @@ +pub mod authorize_principals; pub mod push_events; diff --git a/backend/canisters/event_relay/c2c_client/Cargo.toml b/backend/canisters/event_relay/c2c_client/Cargo.toml new file mode 100644 index 0000000000..f2a5eabafe --- /dev/null +++ b/backend/canisters/event_relay/c2c_client/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "event_relay_canister_c2c_client" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +candid = { workspace = true } +canister_client = { path = "../../../libraries/canister_client" } +event_relay_canister = { path = "../api" } +ic-cdk = { workspace = true } +types = { path = "../../../libraries/types" } diff --git a/backend/canisters/event_relay/c2c_client/src/lib.rs b/backend/canisters/event_relay/c2c_client/src/lib.rs new file mode 100644 index 0000000000..67838650ea --- /dev/null +++ b/backend/canisters/event_relay/c2c_client/src/lib.rs @@ -0,0 +1,7 @@ +use canister_client::generate_candid_c2c_call; +use event_relay_canister::*; + +// Queries + +// Updates +generate_candid_c2c_call!(authorize_principals); diff --git a/backend/canisters/event_relay/impl/src/guards.rs b/backend/canisters/event_relay/impl/src/guards.rs index 4da62a02a7..da91e75b67 100644 --- a/backend/canisters/event_relay/impl/src/guards.rs +++ b/backend/canisters/event_relay/impl/src/guards.rs @@ -7,3 +7,11 @@ pub fn caller_can_push_events() -> Result<(), String> { Err("Caller is not whitelisted to push events".to_string()) } } + +pub fn caller_is_registry_canister() -> Result<(), String> { + if read_state(|state| state.is_caller_registry_canister()) { + Ok(()) + } else { + Err("Caller is not the Registry canister".to_string()) + } +} diff --git a/backend/canisters/event_relay/impl/src/lib.rs b/backend/canisters/event_relay/impl/src/lib.rs index b50a35ed01..d672255e8c 100644 --- a/backend/canisters/event_relay/impl/src/lib.rs +++ b/backend/canisters/event_relay/impl/src/lib.rs @@ -40,6 +40,10 @@ impl RuntimeState { self.data.push_events_whitelist.contains(&caller) } + pub fn is_caller_registry_canister(&self) -> bool { + self.env.caller() == self.data.registry_canister_id + } + pub fn metrics(&self) -> Metrics { let event_store_client_info = self.data.event_store_client.info(); let event_store_canister_id = event_store_client_info.event_store_canister_id; @@ -58,6 +62,7 @@ impl RuntimeState { canister_ids: CanisterIds { event_sink: event_store_canister_id, cycles_dispenser: self.data.cycles_dispenser_canister_id, + registry: self.data.registry_canister_id, chat_ledger: self.data.chat_ledger_canister_id, chat_governance: self.data.chat_governance_canister_id, }, @@ -71,6 +76,8 @@ struct Data { pub event_store_client: EventStoreClient, pub event_deduper: EventDeduper, pub cycles_dispenser_canister_id: CanisterId, + #[serde(default = "CanisterId::anonymous")] + pub registry_canister_id: CanisterId, pub chat_ledger_canister_id: CanisterId, pub chat_governance_canister_id: CanisterId, pub chat_treasury_subaccount: [u8; 32], @@ -84,6 +91,7 @@ impl Data { push_events_whitelist: HashSet, event_store_canister_id: CanisterId, cycles_dispenser_canister_id: CanisterId, + registry_canister_id: CanisterId, chat_ledger_canister_id: CanisterId, chat_governance_canister_id: CanisterId, test_mode: bool, @@ -95,6 +103,7 @@ impl Data { .build(), event_deduper: EventDeduper::default(), cycles_dispenser_canister_id, + registry_canister_id, chat_ledger_canister_id, chat_governance_canister_id, chat_treasury_subaccount: compute_distribution_subaccount_bytes(chat_governance_canister_id, 0), @@ -131,6 +140,7 @@ pub struct Metrics { pub struct CanisterIds { pub event_sink: CanisterId, pub cycles_dispenser: CanisterId, + pub registry: CanisterId, pub chat_ledger: CanisterId, pub chat_governance: CanisterId, } diff --git a/backend/canisters/event_relay/impl/src/lifecycle/init.rs b/backend/canisters/event_relay/impl/src/lifecycle/init.rs index 0db00847ab..1ddd2f62d4 100644 --- a/backend/canisters/event_relay/impl/src/lifecycle/init.rs +++ b/backend/canisters/event_relay/impl/src/lifecycle/init.rs @@ -17,6 +17,7 @@ fn init(args: Args) { args.push_events_whitelist.into_iter().collect(), args.event_store_canister_id, args.cycles_dispenser_canister_id, + args.registry_canister_id, args.chat_ledger_canister_id, args.chat_governance_canister_id, args.test_mode, diff --git a/backend/canisters/event_relay/impl/src/lifecycle/post_upgrade.rs b/backend/canisters/event_relay/impl/src/lifecycle/post_upgrade.rs index 376fe90dd8..e2ae35ccaa 100644 --- a/backend/canisters/event_relay/impl/src/lifecycle/post_upgrade.rs +++ b/backend/canisters/event_relay/impl/src/lifecycle/post_upgrade.rs @@ -7,6 +7,7 @@ use event_relay_canister::post_upgrade::Args; use ic_cdk::post_upgrade; use stable_memory::get_reader; use tracing::info; +use types::CanisterId; use utils::cycles::init_cycles_dispenser_client; #[post_upgrade] @@ -15,7 +16,13 @@ fn post_upgrade(args: Args) { let memory = get_upgrades_memory(); let reader = get_reader(&memory); - let (data, logs, traces): (Data, Vec, Vec) = msgpack::deserialize(reader).unwrap(); + let (mut data, logs, traces): (Data, Vec, Vec) = msgpack::deserialize(reader).unwrap(); + + if data.test_mode { + data.registry_canister_id = CanisterId::from_text("cglwi-oaaaa-aaaar-aqw4q-cai").unwrap(); + } else { + data.registry_canister_id = CanisterId::from_text("cpi5u-yiaaa-aaaar-aqw5a-cai").unwrap(); + } // TODO: After release change this to // let (data, errors, logs, traces): (Data, Vec, Vec, Vec) = msgpack::deserialize(reader).unwrap(); diff --git a/backend/canisters/event_relay/impl/src/updates/authorize_principals.rs b/backend/canisters/event_relay/impl/src/updates/authorize_principals.rs new file mode 100644 index 0000000000..35138c58e6 --- /dev/null +++ b/backend/canisters/event_relay/impl/src/updates/authorize_principals.rs @@ -0,0 +1,22 @@ +use crate::guards::caller_is_registry_canister; +use crate::{mutate_state, RuntimeState}; +use candid::Principal; +use canister_tracing_macros::trace; +use event_relay_canister::authorize_principals::{Response::*, *}; +use ic_cdk::update; +use tracing::info; + +#[update(guard = "caller_is_registry_canister")] +#[trace] +fn authorize_principals(args: Args) -> Response { + mutate_state(|state| authorize_principals_impl(args.principals, state)) +} + +fn authorize_principals_impl(principal: Vec, state: &mut RuntimeState) -> Response { + for principal in principal { + if state.data.push_events_whitelist.insert(principal) { + info!(%principal, "Principal authorized to push events"); + } + } + Success +} diff --git a/backend/canisters/event_relay/impl/src/updates/mod.rs b/backend/canisters/event_relay/impl/src/updates/mod.rs index 1d191f224d..6532615b0b 100644 --- a/backend/canisters/event_relay/impl/src/updates/mod.rs +++ b/backend/canisters/event_relay/impl/src/updates/mod.rs @@ -1,2 +1,3 @@ +mod authorize_principals; mod push_events; mod wallet_receive; diff --git a/backend/canisters/group_index/api/src/lifecycle/init.rs b/backend/canisters/group_index/api/src/lifecycle/init.rs index 1841095dad..9c5a0a4b55 100644 --- a/backend/canisters/group_index/api/src/lifecycle/init.rs +++ b/backend/canisters/group_index/api/src/lifecycle/init.rs @@ -10,6 +10,7 @@ pub struct Args { pub proposals_bot_user_id: UserId, pub escrow_canister_id: CanisterId, pub event_relay_canister_id: CanisterId, + pub registry_canister_id: CanisterId, pub internet_identity_canister_id: CanisterId, pub video_call_operators: Vec, #[serde(with = "serde_bytes")] diff --git a/backend/canisters/group_index/api/src/updates/add_local_group_index_canister.rs b/backend/canisters/group_index/api/src/updates/add_local_group_index_canister.rs index 4453b15f38..4ba8fe7828 100644 --- a/backend/canisters/group_index/api/src/updates/add_local_group_index_canister.rs +++ b/backend/canisters/group_index/api/src/updates/add_local_group_index_canister.rs @@ -1,5 +1,4 @@ use candid::CandidType; -use human_readable::{HumanReadablePrincipal, ToHumanReadable}; use serde::{Deserialize, Serialize}; use types::CanisterId; @@ -16,22 +15,3 @@ pub enum Response { AlreadyAdded, InternalError(String), } - -#[derive(Serialize)] -pub struct HumanReadableArgs { - canister_id: HumanReadablePrincipal, - local_user_index_canister_id: HumanReadablePrincipal, - notifications_canister_id: HumanReadablePrincipal, -} - -impl ToHumanReadable for Args { - type Target = HumanReadableArgs; - - fn to_human_readable(&self) -> Self::Target { - HumanReadableArgs { - canister_id: self.canister_id.into(), - local_user_index_canister_id: self.local_user_index_canister_id.into(), - notifications_canister_id: self.notifications_canister_id.into(), - } - } -} diff --git a/backend/canisters/group_index/c2c_client/src/lib.rs b/backend/canisters/group_index/c2c_client/src/lib.rs index 5a4f7a548c..f4f33dccfb 100644 --- a/backend/canisters/group_index/c2c_client/src/lib.rs +++ b/backend/canisters/group_index/c2c_client/src/lib.rs @@ -5,6 +5,7 @@ use group_index_canister::*; generate_c2c_call!(c2c_active_groups); // Updates +generate_c2c_call!(add_local_group_index_canister); generate_c2c_call!(c2c_convert_group_into_community); generate_c2c_call!(c2c_create_community); generate_c2c_call!(c2c_create_group); diff --git a/backend/canisters/group_index/impl/src/guards.rs b/backend/canisters/group_index/impl/src/guards.rs index 90aea949e5..a2d04804fd 100644 --- a/backend/canisters/group_index/impl/src/guards.rs +++ b/backend/canisters/group_index/impl/src/guards.rs @@ -8,6 +8,14 @@ pub fn caller_is_governance_principal() -> Result<(), String> { } } +pub fn caller_is_registry_canister() -> Result<(), String> { + if read_state(|state| state.is_caller_registry_canister()) { + Ok(()) + } else { + Err("Caller is not the Registry canister".to_string()) + } +} + pub fn caller_is_group_canister() -> Result<(), String> { if read_state(|state| state.is_caller_group_canister()) { Ok(()) diff --git a/backend/canisters/group_index/impl/src/lib.rs b/backend/canisters/group_index/impl/src/lib.rs index 3b7c8a9a8c..d2cb1c606e 100644 --- a/backend/canisters/group_index/impl/src/lib.rs +++ b/backend/canisters/group_index/impl/src/lib.rs @@ -56,6 +56,10 @@ impl RuntimeState { self.data.governance_principals.contains(&caller) } + pub fn is_caller_registry_canister(&self) -> bool { + self.env.caller() == self.data.registry_canister_id + } + pub fn is_caller_group_canister(&self) -> bool { let caller: ChatId = self.env.caller().into(); self.data.public_groups.get(&caller).is_some() || self.data.private_groups.get(&caller).is_some() @@ -129,6 +133,7 @@ impl RuntimeState { cycles_dispenser: self.data.cycles_dispenser_canister_id, escrow: self.data.escrow_canister_id, event_relay: self.data.event_relay_canister_id, + registry: self.data.registry_canister_id, }, } } @@ -150,6 +155,8 @@ struct Data { pub proposals_bot_user_id: UserId, pub escrow_canister_id: CanisterId, pub event_relay_canister_id: CanisterId, + #[serde(default = "CanisterId::anonymous")] + pub registry_canister_id: CanisterId, pub internet_identity_canister_id: CanisterId, pub canisters_requiring_upgrade: CanistersRequiringUpgrade, pub test_mode: bool, @@ -173,6 +180,7 @@ impl Data { proposals_bot_user_id: UserId, escrow_canister_id: CanisterId, event_relay_canister_id: CanisterId, + registry_canister_id: CanisterId, internet_identity_canister_id: CanisterId, video_call_operators: Vec, ic_root_key: Vec, @@ -193,6 +201,7 @@ impl Data { proposals_bot_user_id, escrow_canister_id, event_relay_canister_id, + registry_canister_id, internet_identity_canister_id, canisters_requiring_upgrade: CanistersRequiringUpgrade::default(), test_mode, @@ -299,6 +308,7 @@ impl Default for Data { proposals_bot_user_id: Principal::anonymous().into(), escrow_canister_id: Principal::anonymous(), event_relay_canister_id: Principal::anonymous(), + registry_canister_id: Principal::anonymous(), internet_identity_canister_id: Principal::anonymous(), canisters_requiring_upgrade: CanistersRequiringUpgrade::default(), test_mode: true, @@ -381,6 +391,7 @@ pub struct CanisterIds { pub cycles_dispenser: CanisterId, pub escrow: CanisterId, pub event_relay: CanisterId, + pub registry: CanisterId, } #[derive(Serialize, Deserialize, Clone, Debug, Default)] diff --git a/backend/canisters/group_index/impl/src/lifecycle/init.rs b/backend/canisters/group_index/impl/src/lifecycle/init.rs index b680f0531f..ecc542de19 100644 --- a/backend/canisters/group_index/impl/src/lifecycle/init.rs +++ b/backend/canisters/group_index/impl/src/lifecycle/init.rs @@ -21,6 +21,7 @@ fn init(args: Args) { args.proposals_bot_user_id, args.escrow_canister_id, args.event_relay_canister_id, + args.registry_canister_id, args.internet_identity_canister_id, args.video_call_operators, args.ic_root_key, diff --git a/backend/canisters/group_index/impl/src/lifecycle/post_upgrade.rs b/backend/canisters/group_index/impl/src/lifecycle/post_upgrade.rs index b8eb669225..88e48a57e0 100644 --- a/backend/canisters/group_index/impl/src/lifecycle/post_upgrade.rs +++ b/backend/canisters/group_index/impl/src/lifecycle/post_upgrade.rs @@ -7,6 +7,7 @@ use group_index_canister::post_upgrade::Args; use ic_cdk::post_upgrade; use stable_memory::get_reader; use tracing::info; +use types::CanisterId; use utils::cycles::init_cycles_dispenser_client; #[post_upgrade] @@ -15,9 +16,15 @@ fn post_upgrade(args: Args) { let memory = get_upgrades_memory(); let reader = get_reader(&memory); - let (data, errors, logs, traces): (Data, Vec, Vec, Vec) = + let (mut data, errors, logs, traces): (Data, Vec, Vec, Vec) = msgpack::deserialize(reader).unwrap(); + if data.test_mode { + data.registry_canister_id = CanisterId::from_text("cglwi-oaaaa-aaaar-aqw4q-cai").unwrap(); + } else { + data.registry_canister_id = CanisterId::from_text("cpi5u-yiaaa-aaaar-aqw5a-cai").unwrap(); + } + canister_logger::init_with_logs(data.test_mode, errors, logs, traces); let env = init_env(data.rng_seed); diff --git a/backend/canisters/group_index/impl/src/updates/add_local_group_index_canister.rs b/backend/canisters/group_index/impl/src/updates/add_local_group_index_canister.rs index 58744c0c53..bc3f2e7504 100644 --- a/backend/canisters/group_index/impl/src/updates/add_local_group_index_canister.rs +++ b/backend/canisters/group_index/impl/src/updates/add_local_group_index_canister.rs @@ -1,6 +1,6 @@ -use crate::guards::caller_is_governance_principal; +use crate::guards::caller_is_registry_canister; use crate::{mutate_state, read_state, RuntimeState}; -use canister_api_macros::proposal; +use canister_api_macros::update; use canister_tracing_macros::trace; use group_index_canister::add_local_group_index_canister::{Response::*, *}; use group_index_canister::ChildCanisterType; @@ -8,7 +8,7 @@ use tracing::info; use types::{BuildVersion, CanisterId, CanisterWasm}; use utils::canister::{install_basic, set_controllers}; -#[proposal(guard = "caller_is_governance_principal")] +#[update(guard = "caller_is_registry_canister", msgpack = true)] #[trace] async fn add_local_group_index_canister(args: Args) -> Response { match read_state(|state| prepare(&args, state)) { @@ -73,12 +73,6 @@ fn prepare(args: &Args, state: &RuntimeState) -> Result fn commit(canister_id: CanisterId, wasm_version: BuildVersion, state: &mut RuntimeState) -> Response { if state.data.local_index_map.add_index(canister_id, wasm_version) { - state.data.fire_and_forget_handler.send_candid( - state.data.cycles_dispenser_canister_id, - "add_canister", - cycles_dispenser_canister::add_canister::Args { canister_id }, - ); - Success } else { AlreadyAdded diff --git a/backend/canisters/notifications_index/api/src/lifecycle/init.rs b/backend/canisters/notifications_index/api/src/lifecycle/init.rs index 386fac78d1..2ec2611bc6 100644 --- a/backend/canisters/notifications_index/api/src/lifecycle/init.rs +++ b/backend/canisters/notifications_index/api/src/lifecycle/init.rs @@ -9,6 +9,7 @@ pub struct Args { pub user_index_canister_id: CanisterId, pub authorizers: Vec, pub cycles_dispenser_canister_id: CanisterId, + pub registry_canister_id: CanisterId, pub notifications_canister_wasm: CanisterWasm, pub wasm_version: BuildVersion, pub test_mode: bool, diff --git a/backend/canisters/notifications_index/api/src/updates/add_notifications_canister.rs b/backend/canisters/notifications_index/api/src/updates/add_notifications_canister.rs index 61ccbee0c6..eeb9658b84 100644 --- a/backend/canisters/notifications_index/api/src/updates/add_notifications_canister.rs +++ b/backend/canisters/notifications_index/api/src/updates/add_notifications_canister.rs @@ -1,5 +1,4 @@ use candid::CandidType; -use human_readable::{HumanReadablePrincipal, ToHumanReadable}; use serde::{Deserialize, Serialize}; use types::CanisterId; @@ -15,20 +14,3 @@ pub enum Response { AlreadyAdded, InternalError(String), } - -#[derive(Serialize)] -pub struct HumanReadableArgs { - canister_id: HumanReadablePrincipal, - authorizers: Vec, -} - -impl ToHumanReadable for Args { - type Target = HumanReadableArgs; - - fn to_human_readable(&self) -> Self::Target { - HumanReadableArgs { - canister_id: self.canister_id.into(), - authorizers: self.authorizers.iter().copied().map(|p| p.into()).collect(), - } - } -} diff --git a/backend/canisters/notifications_index/c2c_client/Cargo.toml b/backend/canisters/notifications_index/c2c_client/Cargo.toml new file mode 100644 index 0000000000..145fc3f343 --- /dev/null +++ b/backend/canisters/notifications_index/c2c_client/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "notifications_index_canister_c2c_client" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +canister_client = { path = "../../../libraries/canister_client" } +ic-cdk = { workspace = true } +msgpack = { path = "../../../libraries/msgpack" } +notifications_index_canister = { path = "../api" } +tracing = { workspace = true } +types = { path = "../../../libraries/types" } diff --git a/backend/canisters/notifications_index/c2c_client/src/lib.rs b/backend/canisters/notifications_index/c2c_client/src/lib.rs new file mode 100644 index 0000000000..b076c50a66 --- /dev/null +++ b/backend/canisters/notifications_index/c2c_client/src/lib.rs @@ -0,0 +1,7 @@ +use canister_client::generate_c2c_call; +use notifications_index_canister::*; + +// Queries + +// Updates +generate_c2c_call!(add_notifications_canister); diff --git a/backend/canisters/notifications_index/impl/src/guards.rs b/backend/canisters/notifications_index/impl/src/guards.rs index d4fd41e623..ef906e2078 100644 --- a/backend/canisters/notifications_index/impl/src/guards.rs +++ b/backend/canisters/notifications_index/impl/src/guards.rs @@ -8,6 +8,14 @@ pub fn caller_is_governance_principal() -> Result<(), String> { } } +pub fn caller_is_registry_canister() -> Result<(), String> { + if read_state(|state| state.is_caller_registry_canister()) { + Ok(()) + } else { + Err("Caller is not the Registry canister".to_string()) + } +} + pub fn caller_is_push_service() -> Result<(), String> { if read_state(|state| state.is_caller_push_service()) { Ok(()) diff --git a/backend/canisters/notifications_index/impl/src/lib.rs b/backend/canisters/notifications_index/impl/src/lib.rs index 28c0ce2414..ced9400ba7 100644 --- a/backend/canisters/notifications_index/impl/src/lib.rs +++ b/backend/canisters/notifications_index/impl/src/lib.rs @@ -42,6 +42,10 @@ impl RuntimeState { self.data.governance_principals.contains(&caller) } + pub fn is_caller_registry_canister(&self) -> bool { + self.env.caller() == self.data.registry_canister_id + } + pub fn is_caller_push_service(&self) -> bool { self.data.push_service_principals.contains(&self.env.caller()) } @@ -120,6 +124,8 @@ struct Data { pub push_service_principals: HashSet, pub user_index_canister_id: CanisterId, pub cycles_dispenser_canister_id: CanisterId, + #[serde(default = "CanisterId::anonymous")] + pub registry_canister_id: CanisterId, pub principal_to_user_id_map: PrincipalToUserIdMap, pub subscriptions: Subscriptions, pub notifications_canister_wasm_for_new_canisters: CanisterWasm, @@ -138,6 +144,7 @@ impl Data { push_service_principals: Vec, user_index_canister_id: CanisterId, cycles_dispenser_canister_id: CanisterId, + registry_canister_id: CanisterId, notifications_canister_wasm: CanisterWasm, test_mode: bool, ) -> Data { @@ -147,6 +154,7 @@ impl Data { push_service_principals: push_service_principals.into_iter().collect(), user_index_canister_id, cycles_dispenser_canister_id, + registry_canister_id, principal_to_user_id_map: PrincipalToUserIdMap::default(), subscriptions: Subscriptions::default(), notifications_canister_wasm_for_new_canisters: notifications_canister_wasm.clone(), diff --git a/backend/canisters/notifications_index/impl/src/lifecycle/init.rs b/backend/canisters/notifications_index/impl/src/lifecycle/init.rs index a09759a912..983a0fc736 100644 --- a/backend/canisters/notifications_index/impl/src/lifecycle/init.rs +++ b/backend/canisters/notifications_index/impl/src/lifecycle/init.rs @@ -20,6 +20,7 @@ fn init(args: Args) { args.push_service_principals, args.user_index_canister_id, args.cycles_dispenser_canister_id, + args.registry_canister_id, args.notifications_canister_wasm, args.test_mode, ); diff --git a/backend/canisters/notifications_index/impl/src/lifecycle/post_upgrade.rs b/backend/canisters/notifications_index/impl/src/lifecycle/post_upgrade.rs index cd384aa0a5..19ffa2ae92 100644 --- a/backend/canisters/notifications_index/impl/src/lifecycle/post_upgrade.rs +++ b/backend/canisters/notifications_index/impl/src/lifecycle/post_upgrade.rs @@ -7,6 +7,7 @@ use ic_cdk::post_upgrade; use notifications_index_canister::post_upgrade::Args; use stable_memory::get_reader; use tracing::info; +use types::CanisterId; use utils::cycles::init_cycles_dispenser_client; #[post_upgrade] @@ -17,9 +18,15 @@ fn post_upgrade(args: Args) { let memory = get_upgrades_memory(); let reader = get_reader(&memory); - let (data, errors, logs, traces): (Data, Vec, Vec, Vec) = + let (mut data, errors, logs, traces): (Data, Vec, Vec, Vec) = msgpack::deserialize(reader).unwrap(); + if data.test_mode { + data.registry_canister_id = CanisterId::from_text("cglwi-oaaaa-aaaar-aqw4q-cai").unwrap(); + } else { + data.registry_canister_id = CanisterId::from_text("cpi5u-yiaaa-aaaar-aqw5a-cai").unwrap(); + } + canister_logger::init_with_logs(data.test_mode, errors, logs, traces); let env = init_env(data.rng_seed); diff --git a/backend/canisters/notifications_index/impl/src/updates/add_notifications_canister.rs b/backend/canisters/notifications_index/impl/src/updates/add_notifications_canister.rs index 809ab3e31a..57143ef3fa 100644 --- a/backend/canisters/notifications_index/impl/src/updates/add_notifications_canister.rs +++ b/backend/canisters/notifications_index/impl/src/updates/add_notifications_canister.rs @@ -1,6 +1,6 @@ -use crate::guards::caller_is_governance_principal; +use crate::guards::caller_is_registry_canister; use crate::{mutate_state, read_state, NotificationsCanister, RuntimeState}; -use canister_api_macros::proposal; +use canister_api_macros::update; use canister_tracing_macros::trace; use notifications_index_canister::add_notifications_canister::{Response::*, *}; use notifications_index_canister::{NotificationsIndexEvent, SubscriptionAdded}; @@ -8,7 +8,7 @@ use std::collections::hash_map::Entry::Vacant; use types::{BuildVersion, CanisterId, CanisterWasm}; use utils::canister::{install_basic, set_controllers}; -#[proposal(guard = "caller_is_governance_principal")] +#[update(guard = "caller_is_registry_canister", msgpack = true)] #[trace] async fn add_notifications_canister(args: Args) -> Response { match read_state(|state| prepare(args.canister_id, args.authorizers, state)) { diff --git a/backend/canisters/registry/api/src/lifecycle/init.rs b/backend/canisters/registry/api/src/lifecycle/init.rs index 0fe06906b4..7f9c1ec2a4 100644 --- a/backend/canisters/registry/api/src/lifecycle/init.rs +++ b/backend/canisters/registry/api/src/lifecycle/init.rs @@ -5,6 +5,9 @@ use types::{BuildVersion, CanisterId}; #[derive(CandidType, Serialize, Deserialize, Debug)] pub struct Args { pub user_index_canister_id: CanisterId, + pub group_index_canister_id: CanisterId, + pub notifications_index_canister_id: CanisterId, + pub event_relay_canister_id: CanisterId, pub governance_principals: Vec, pub proposals_bot_canister_id: CanisterId, pub nns_ledger_canister_id: CanisterId, @@ -14,6 +17,7 @@ pub struct Args { pub nns_index_canister_id: CanisterId, pub escrow_canister_id: CanisterId, pub cycles_dispenser_canister_id: CanisterId, + pub cycles_minting_canister_id: CanisterId, pub wasm_version: BuildVersion, pub test_mode: bool, } diff --git a/backend/canisters/registry/api/src/queries/mod.rs b/backend/canisters/registry/api/src/queries/mod.rs index 9eb4d30b47..0fd95f800c 100644 --- a/backend/canisters/registry/api/src/queries/mod.rs +++ b/backend/canisters/registry/api/src/queries/mod.rs @@ -1,2 +1,3 @@ pub mod c2c_nervous_systems; +pub mod subnets; pub mod updates; diff --git a/backend/canisters/registry/api/src/queries/subnets.rs b/backend/canisters/registry/api/src/queries/subnets.rs new file mode 100644 index 0000000000..696e37a244 --- /dev/null +++ b/backend/canisters/registry/api/src/queries/subnets.rs @@ -0,0 +1,18 @@ +use candid::Principal; +use serde::{Deserialize, Serialize}; +use types::{CanisterId, Empty}; + +pub type Args = Empty; + +#[derive(Serialize, Deserialize, Debug)] +pub enum Response { + Success(Vec), +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct Subnet { + pub subnet_id: Principal, + pub local_user_index: CanisterId, + pub local_group_index: CanisterId, + pub notifications_canister: CanisterId, +} diff --git a/backend/canisters/registry/api/src/updates/expand_onto_subnet.rs b/backend/canisters/registry/api/src/updates/expand_onto_subnet.rs new file mode 100644 index 0000000000..8f51ab7a82 --- /dev/null +++ b/backend/canisters/registry/api/src/updates/expand_onto_subnet.rs @@ -0,0 +1,30 @@ +use candid::{CandidType, Principal}; +use human_readable::{HumanReadablePrincipal, ToHumanReadable}; +use serde::{Deserialize, Serialize}; + +#[derive(CandidType, Serialize, Deserialize, Debug)] +pub struct Args { + pub subnet_id: Principal, +} + +#[derive(CandidType, Serialize, Deserialize, Debug)] +pub enum Response { + Success, + AlreadyOnSubnet, + AlreadyInProgress, +} + +#[derive(Serialize)] +pub struct HumanReadableArgs { + subnet_id: HumanReadablePrincipal, +} + +impl ToHumanReadable for Args { + type Target = HumanReadableArgs; + + fn to_human_readable(&self) -> Self::Target { + HumanReadableArgs { + subnet_id: self.subnet_id.into(), + } + } +} diff --git a/backend/canisters/registry/api/src/updates/mod.rs b/backend/canisters/registry/api/src/updates/mod.rs index b28cd51da9..e4339dd82b 100644 --- a/backend/canisters/registry/api/src/updates/mod.rs +++ b/backend/canisters/registry/api/src/updates/mod.rs @@ -2,6 +2,7 @@ pub mod add_message_filter; pub mod add_remove_swap_provider; pub mod add_token; pub mod c2c_set_submitting_proposals_enabled; +pub mod expand_onto_subnet; pub mod remove_message_filter; pub mod set_airdrop_config; pub mod set_token_enabled; diff --git a/backend/canisters/registry/impl/Cargo.toml b/backend/canisters/registry/impl/Cargo.toml index 14a73569f3..60313d292d 100644 --- a/backend/canisters/registry/impl/Cargo.toml +++ b/backend/canisters/registry/impl/Cargo.toml @@ -14,22 +14,35 @@ candid = { workspace = true } canister_api_macros = { path = "../../../libraries/canister_api_macros" } canister_logger = { path = "../../../libraries/canister_logger" } canister_state_macros = { path = "../../../libraries/canister_state_macros" } +canister_timer_jobs = { path = "../../../libraries/canister_timer_jobs" } canister_tracing_macros = { path = "../../../libraries/canister_tracing_macros" } constants = { path = "../../../libraries/constants" } +cycles_dispenser_canister = { path = "../../../canisters/cycles_dispenser/api" } +cycles_dispenser_canister_c2c_client = { path = "../../../canisters/cycles_dispenser/c2c_client" } +cycles_minting_canister = { path = "../../../external_canisters/cmc/api" } +cycles_minting_canister_c2c_client = { path = "../../../external_canisters/cmc/c2c_client" } dataurl = { workspace = true } escrow_canister = { path = "../../escrow/api" } escrow_canister_c2c_client = { path = "../../escrow/c2c_client" } +event_relay_canister = { path = "../../../canisters/event_relay/api" } +event_relay_canister_c2c_client = { path = "../../../canisters/event_relay/c2c_client" } futures = { workspace = true } +group_index_canister = { path = "../../../canisters/group_index/api" } +group_index_canister_c2c_client = { path = "../../../canisters/group_index/c2c_client" } hex = { workspace = true } http_request = { path = "../../../libraries/http_request" } human_readable = { path = "../../../libraries/human_readable" } ic-cdk = { workspace = true } ic-cdk-timers = { workspace = true } +ic-ledger-types = { workspace = true } ic-stable-structures = { workspace = true } +icp_ledger_canister_c2c_client = { path = "../../../external_canisters/icp_ledger/c2c_client" } icrc_ledger_canister = { path = "../../../external_canisters/icrc_ledger/api" } icrc_ledger_canister_c2c_client = { path = "../../../external_canisters/icrc_ledger/c2c_client" } icrc-ledger-types = { workspace = true } msgpack = { path = "../../../libraries/msgpack" } +notifications_index_canister = { path = "../../../canisters/notifications_index/api" } +notifications_index_canister_c2c_client = { path = "../../../canisters/notifications_index/c2c_client" } rand = { workspace = true } serde = { workspace = true } serde_bytes = { workspace = true } diff --git a/backend/canisters/registry/impl/src/lib.rs b/backend/canisters/registry/impl/src/lib.rs index 593d317cb4..aebeb9edc9 100644 --- a/backend/canisters/registry/impl/src/lib.rs +++ b/backend/canisters/registry/impl/src/lib.rs @@ -1,8 +1,12 @@ use crate::model::nervous_systems::{NervousSystemMetrics, NervousSystems}; +use crate::model::subnets::Subnets; use crate::model::tokens::{TokenMetrics, Tokens}; +use crate::timer_job_types::TimerJob; use candid::Principal; use canister_state_macros::canister_state; +use canister_timer_jobs::TimerJobs; use model::message_filters::MessageFilters; +use registry_canister::subnets::Subnet; use registry_canister::{MessageFilterSummary, NervousSystemDetails}; use serde::{Deserialize, Serialize}; use std::cell::RefCell; @@ -17,6 +21,7 @@ mod memory; mod metadata_helper; mod model; mod queries; +mod timer_job_types; mod updates; const IC_LOGO: &str = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAAXNSR0IArs4c6QAAAIRlWElmTU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABAAIAAIdpAAQAAAABAAAAWgAAAAAAAABIAAAAAQAAAEgAAAABAAOgAQADAAAAAQABAACgAgAEAAAAAQAAAFCgAwAEAAAAAQAAAFAAAAAAwtohTAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAVlpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDYuMC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KGV7hBwAAGZRJREFUeAHtW3l8XVWdP+fc5e1Z29LGLum+BEolnSKbpKBgUUQoibTgCAzQ0QEdEBxAtA+xLCIwH0WxVVFkGUxaLCBYoZhowUrbFFpIN7oTSumS5SVvvfeeM9/ffe+lL8lLaWlh5o934OYu7yy/8z2//ZwyVigFBAoIFBAoIFBAoIBAAYECAgUECggUECggUECggEABgQICBQQKCBQQKCDwCSKgFGd0hcPCvTM8/z8rijHuXmEmlEo/g8T/IzqVEtUL1xg1jY16GrD+aNXWK43qsLAS/X/9ZL4ogLXmumpD1TIt34guoDU1OtWpH6BOvna5345qBWrCAOxsxppmzrRzOxn34jsePeELarZlGAZLvHnx6I7c3wnI5uuqHca5zP3+cT27gO2v4bypqRed6srKkv1R5vXqpt1px6IjGlrjuTSo2lqNVe3nPNzk4Dvw/fByRAASNzXUosMMAJOX7BzGbX4ueOuzkNaT8L0C4xVhOB1XEtdeiMk6IfifNa984a0vjWonUjJA2qh/RMRRm6MpxHFNTTViZga42OXjhjuSf1Ex9TnQWoUZDEF/HlwOJt6F7+9jUm8Lg6+wlfNK2e+376bxwujnbIZ+wr0XgH7rWz4EQMWrFzK9eR63qOHEP+ycKYS4Ho9fEIEiP+dQebbFlIOFlg6kmTCGxOo6E7oJehWT0Ug77k9oyvhJS12FS2BNo9KbZvJe3NGXsKN9VxBDvqjZpbPjsjHTmRA3g5KLQqbwUl+Wo5iNZZMujdyVaV3D3QAEuHfZKiV0/jLT1MPBX2xeRm2oT7ao2UaNARd8YABJd81HQ3DLxMXbpwqpPSB8gc8xTWcy3g1KJAGAjl124rAi6At/02PRC4krKNR0zV/EnK72FH66/0Q2Yn5DHXeqFyojuzBE7EctxHWsBUQ0MOdA7YRPGaZzv8nFHK/OWbclQSaziTjQBTzxdKgornHFda6UhmXXuR7wQVWiVrdSr0qdf6f4xy2rqDrpxzr0f6jpoafcDnu+kq5rCqf13OSG3XdwTb+Le3zMiXUR6xMwJKp52/Z0cuiBVs8C7aYWKmVOZ9s28O2VLXWjXsW0QC7KR9SNKlyjQ1+5nNwxZ+w8zvl/F4HjIinABjHFNTCd4DqA53IfsSOByXTu4M4ApE4TTXJ+T+BH628nEnPHovds6QdCFjyysB/sH/esVlx2gRM5iAVUpLtovfq1Ub0WNtt13zsZEGVz02vSL8pK3LHx0pEL6JnG6muY6PvhSkttlVnV0JLacWWltzylPRny6Jd0p0iNsBRWzEDbfnT29NcfPEANQPGdxBl3C9BDcAwRT8omX9B/Ab9pZTwfiL0GcY0FxGt4/bu+EGevaaGST8tIGxkFEyBxKQSTAIsuYitqLLDYGpQf7oDWNQ7EVb36xXtOId2nNK2onDudB5du3PDobGht6RqYedNdHZZTOe9jFrzOK8aOAzEvFXm00ZGkQ23JXUlzdb6WRDrYH2BBtWBCmuAuj6ZBS3NjD5DQSTpP+UKGJ5GSG+NKnFZ2a3NnXxAPTZScYEyExoXYvq4HimdYsUjC1nUvuI7plsU8iQSuJDMsm6hUUtechM+noj6vkfB4XVr0ZIK6oMkMLD5oSzyohcpMO9K+TmP8nJa6EW1V9S1mS11VijoYqChwHgfnRa6YcBYU3Et+nXtjtkqiQ7Ku+QqJDOyHErohNFxMgngH2EH32VwDLRoHvwHUHvDQAtRnODLpC+meZEqu93QWVZPKIL3Lw64qcyeZHnTKfLQKE3hPgTtmxBKRpNdxvOW732PB7a3Mt2Mf87R2MnMvANznWLpiRqDMp4vhXhar9LLuySMPvDuhMrG3uHi4HigxnG5yBRUBSeLUt2AsbjpdbSktUHQydOu6qUtaT1s/e3hr9RoYl+lpq9+3UcbSpiJzxl0ID/45TINFbWWhs/zgwfBCFvSQRxgKdbuk6pZMdaCdB7xaHvTDIgK0uI2vAmNq3MCVCx7pSeLApLfYmJo0uxpA08W5dKF2xj+D+FQ9vfMaWT7kV6rjQOqELdvNksb1zFi+D9yGRRpkML1Ik7pHSL/u0x0ZV1i8pWanXOxtXbd2Aluy9wTG4jcv3jUOAv1tgHcNXB0uo3AQIA0YKG80gHop4Q2aMhE9IJWcsbmuckc+TsxyXtfc8Zd4NLYkKeEiEUBpTs+dEz1LjOeEDGF0AxypsXq4NY9putUcCFmR9qhpal7PEEPn06TglwCk2f6AbsQs6YAXSbwFAelyYJYrdZbyFJtmMu7c4L1m1cNqIdymec1YPLKEUG+n/uqtE9oGl28LRaKBTz3bKD1LdghVAReq3GTkKumOcgxHaX4stuPElmiOvP3n28Nb+lKefZ/09LYJXDPuFb7QxcpKgheTZInzcCOtIUD0+E2ZjB8QKlXdUjd2d65OzOq8yOXjv+QV7PkEfDr8BysLAexXFAwo1+D/MVjjl4Wmbgo9vu3tftVyPsRvnVaJyS0IBPW5SfQtNbLGWHDixiyQGj4bQkD8k47Bx/mveL1V1ddqPOvUjn7+g0VDI4lrh/3g8ZTYljKdaX6mpSQBx3TJ0AY+JxZHcPvffrn5B4/S+OGasL6naw+vaK5w5rP50DUYsL5eVLfXiqyPN6Vh90UQkEVaoGSI7O4gEAfSjeBEgJiI7bGUMW1r3bD9DNZZ3TyT82Zmtc8d/1lg8jdyhHHlBQ+jpzwaN10QGPtmyVNbHyE6KdatrggpNh8hGq0XlTDjzXuq3QWYnnHA4wumzYVIP6mB6xyBaEUjGAlELBW+4TllFhumlXAeM+f+80rVWENfUV5Xo6e/teGdkT95VuMdtpInmFyLO+gLig70gqsFgce5M2vh5u8vu676OmNLqEI1NYVdH8zto88fsuj7BzNOEce4J94p0k3zCb2o7EK7q4NcLPyf8QF72hEp4EQfxDnetXljt5zGrhrtWqTInIpJivvXGYKbloQ7lV9sUxBZsyvl7IWpnVX61DtvDhQT9wyZeSBOYu3bBYlk/P5TzsFkXyHgQCE4CAEpwUxAAhCASiKORXQmei9btcU1+VO3bb925CvrNLEplsoFD4aCrIz0CxOuinM1gXfDuJ96FjUvsg4HHtFF0QaBR0Zh6xXjI5vqRn3Z7moLa74AyFHk77gWPzMH3PAF7hKinJQIlEz8dFC8Qh/ULFYkRWkjggTTlq5RIg7uW1JwoM1uy1mvuDUlDR5Za3BRJi7u2yD3ndc1OATejnCl13fL2r9yXfyrGcAwuoDyR02XC12dSCJoGaWwNaZxLfUB+6DEsBVrL9WXbmPO1KCmxWyX89LgccsvvLqUyYZH3vn+b6+rXmj8bOu3yC884uJaVHAjceSm2lF3wuLOA5eRUSGW6wOi263Jox0pGSo/nT2z53dJkzUUm3xo3NHJvcmjQ5kLXpcl/xGaMKK65Knd7VmDc8REZiqODu9MkHEwr1/zOCxvvSeEaJmcagCYEWHGDRgYhIjgztlrUFd8/hv1pwVXvzveruBKS0qhgycz4CmDKUM6Cegb+1Yao725NN+EP5xOcGNDHZPEjeDERTIevVb44aq7pX+KS8LF4V1tNvP4v/6Xc587L9X2tq2Ez3R5tPdoafBS8rWiJ7ee4fpoyO+Rn9i72lG8lY5x54h46bZUiqwyICNdluFCcKSwoEcMQ4w++QTzX0TR8nfOM6LgTEPYSBNwAo+WGX6WHUAiA4xc/8iW8HbivgZWBzA/auGKuJFclI21I37tRDtvgJsDGYFVyMhvtmeaARDQp8Yi9kUjP+P8fcZvdF/kDZjBErdqBvkUIi2zMyXfCFlbz6a2bpRwBCJLdQcqJM6ui3L16u0AbrERAhquw+2KMJohglGIpuAO4PksoTQxjWJ5A/OAwXDBI+dch2hrCjpUsT/QYBUhpM6OQ6FIg1yUTXWVDwPE2/VQKWmZXguTlW2Aq0+14trnp1yg1p30Y+aPrAGIZYBc2n4d4FnOLqtbO5t0nSLOyyQWjpnMCbDYKEqohowFhniCqszKZZUPvNtTEN2qSg25PE0i1EEjAg+YS6SE8DkZg5Zc4xLUlFdfuT8d7Z/medU2uU/QiffISPuDCOlo6F5iR7SmQClYlI2xbX7htDlsx9ibma97taOb5XrMtqJIrc0c/NzmLjdCOUbO6zWHmhpXjE3dWG0jCqFUF9jukPpSQMWBolFsDCw2GwQLS6C5Dhru4DquPKiDDM+e07ZM2Eudk5/Xa5BjeuGqqQZcF1ZiQ93I79jdbU+FispNExmf3EFISOIwN6Wg7yDu3zj1m2rf0K9pnvhqFvNOubDk8c07XIOR8eOOiaTcxneGM2/te2FE9kC94Z0ch2yBywuZgYQMEhDVwYJElRMHkuiCCyHOJggGmO11Gb0HDZBtfXzu6cyN29emS0devioeeXGTL6gH4CTnyjOR3gXDdwryt42m3/nFOWH2ftnXvj3s0Rca62980HdMBmOgmcwHECi8roVSY539pk5QII7ELSTIKXOBA8IEHslS+j39TB19bCXMZWO40U25tz7w+5dv7OpizeDDEqTIckEkJdkBEKutJH/AU8xGnH//l0Eqr3vopnhtfT39/EmXNDdBCZII74e+A+cpAJkGzwD3kQGBUSmpZ/DSUcCux51IVRU2Z4ZnJvacetd5Q1YceOjfH62Xn4/HxSrsqYQgBYeUDulmxqLI4E1JRu1T/cFzJ9XvfoYIaqirlUjDEaMev3JnmudUfRVcJ1XSb+oEBqXDIBwktgcRZ5DoZjgRIFJqlAyLUp/aMbEKSRbG7mR3HlcZbgF4vCWc2jfmlvGe96LPxib72aDlreqnf1zGRiEtcxDmzdcHRCIAhkWPRNpSRlHZVyY17P6lq5soFZcnU/6REZ0fTjcNFA+D6homkdEBCx2aP6mfNFsdpBh3BxkMRMWIeQk8lwvJhXFCmsdvOLza7a0GlY5TUUhCVAG89spwiW7rf/VpptfpStrxycXaiPoW0fByk9qpwbRBEij/n4f3kUs8aOmhsnmTGnbdzeCo1zQ1YUo5kzwWWpua3Lk6tpihBzQPbUxhoXLnjzQ8JbHYDqFL9QaJbBY8RB+uLwi7LU1wMtybOqJlWBcW4ziUNUhEcCQhiAMRIv49KDzDE8pKQSR00W2x+OQyduKileKlf7yu3jJNbHJQYiQfiFxHQtbWg6W3TarfdSvtqVQvbKZpHDudW7rcPqDVvprprvca0jrBv5JMvCGE5qzgEnso2NyDPszoQVeMdctJUC7wqwvHhEfOa55nZfXhR8WROG86EhGKhcXQruSKYuE9qVsmydK5G020pCJqs9jEMn7G/cvlH9/aoL0VCGlQRC6IfcalSWp2d4ejB0vumdyw6z+bkRSGkw6t9NE50c3xIbGQePaMieh/tg16UGhhMgXuCk9qMlHGXmVXNwonWrRSSes9LxekD9MAQvfgmbI4FiZpIMS7221dVeVKfraro7mTwSDOU+Nu8LSNTK0sEd4ZnTJxCLwsebjzpEz5xg/Tz7+j4U8sFX+EFZchDlC9HO1sdegobkc7pQgUPzR58e4bCUQobE7Ji6OhL1u3GWktetYkuw/iS4+0LZHhamS3mVcKc5d4M3n+tnNTV70uHmq9KY4zBEv9wkMwO+DEtDijFcRZTzoxp0h4L39ybPiyOuit31aGXbeDej7S4oKHtnvH3jykLRV4o1iYMzoAHtqnOa93R5Zf6WabE2vtHsznsIsrvsk725azYCnq5gORdjvglkUjUviKHoRhuZ/BPaJ0GsXdvbs+/Jt6cZZnOrgvtfSMeXpQu8iO0pELV6PRCPgvBIKXO93m9ewbzheWkO51kb13zA9P0gVfD4eaLLEbkeCeBhKiDirgL8JyK3H27B3hv9eDm1hLi1PHGnLdtX7UQVSJ9bE/EZYHht92KtTwC0FulkdVqh/nuY2xx2Fi4yypnLie0E4q2fejbe53RCyTq3Y3C3/xNBnrBPBIUIKePoVOUThaEHYp0taoe7xz3r7ohA+QIddqBte6id0+9XteKbPcDL1H4NlLz7wQW03P2emYCIOk1YHDgszDN6m4OonfYv9Y/lybMoFdzLfxMPRSGKL10NjwH0LCV5eScQvgQWzTFpmMCt4dbGMhsiPXRl52wc4FboKBDEI1MtOsCYvD5mMwDukJ8/lksZvoLZ2xPjjitpt0Lh6gZG4Sh2kgdpS36FOUg0yRRt8lV6eX77pnpWIwOPULJa00ZbUNr2ctNqDGUtIV1QBin0KumWIWUmWUmMXhIXnDpktHPUa13K2LvzUxNb8Gi+7yDWeLkNJH4oDPbHIVnfPcmdfBe1ooaV8ErhQWRMAbdhHwsOUsxi5M3e192FzQ7f8Nu3TwNQzxPCfDQJz003F3jdWVswWGBOGdhAKhzYuMRQYBEG0HVlkLwPGxZfJxnVt3nLn9vt19ptDrdc+I7073cO3BUuE7q13G8Vv+vQz6LgAe5Spjjj1rSOu9y8hKk6tDHWb3bcbVvz8Y467VfMHhaRB5Pk6kJhaOoxjCG2DI+DQJxf8L+87uORf6sW9JLq6ZJDTrXj1oQGwBnoIGBHgc3ovBdqH6TrZN/lB+n80V/+MEYreL98bdPXv6+2FkrNylWIhcH1nZX48Nf7dY+O6LO/GkwaTH5UI0z9GLEs+qVHi1BHQYAF5icPmMbqk3Ta93nzfRadl6cbHpxE8Ft13l49pFlFHsVtiRcy0ZWLBvgdiirg4OxZkM9eWy3Quez+rM3KrZ/eKq+h1D4YStEv7gCIBI2fG8e8Lkg2M0B9lvw/WSnNTStxV7UA0auRptvKydDYMzXG3L0y4FX1xEBsOJJnASKwYP7wPQuQ/VRrL31Wz2F/lFdpWcnGKhUnNW9IN5f549elF217BnQuB9jMfV70f/YFmp5j8/IaNJbCZ5csAjUQaYrla1wKlGCRkevCdkHOLH2vAtCRksLRNmAM8sIhPULe2E0Vr0KjQwfrRMrmO3FLGvkLPKd967TGExORazV+XMS5boSc+04lyIek0LFk3EBj64lDhxwELaTPMEinlHKsnOsNr2zNE2mifyNwdVBrejUSsZIOgggXgjpCfUUNbBKtkuNZH9U05g96vhbD8zktVFQY+IHHh6dW3lHIq/G+rSyeWeiWVCNTVCia8ckLE3YXknppwEQGQZTkyDl+ZKZcCoqLhM2MTsJgDycm0wKTbK88QdRBU0H/jh+cDLTDUZ4B4PDEoMdT4H8Fa6nNc8zxXbTJ1et7Sfh5MLl/CDOL/z6VB350s4RXGmg9AOFYm03Ggh25ZOwqhUtNMKAsjlRlnFU/Is7FLMYOd0JK0JPMaLIQSwQHonTOhuMOefiamxocGQaJ7CUsnxQa+nI9K2agPAo04bNmzATNOlhwPptREGZSYMyvMTwoOwcfJ6qfCMiUsSZ2a6I6BOj15EFxkw6RsyOZz2UMiPpD7zTYSGoAKDo5wy4TcOyvhWnWvnlez60Y5cnZeuNvDfrE6kGvD9fqcFy76OqIReyRgQWXkLzRpzcUAvcvKcH0T4s8clNwuDYkNB3hDIhg+aEDoglQyVe5KdbWvLGD9zZd0IZH9wWhdGLTtAtmX2vQfEVyfeF0oko8sG6b7Tu+0oxckQU+yloGY6Xk6D6YLogpl2e/p12NMzhQfcQgRpwjlnbTLWsDfkvYIMxeHEtqd5n4fciUxa/O710Pk/E6aHYWOeXCSE9oddRBc2cs3IVc6uNoQJpzFxaAp7z1BoBribgbv/dKIa/hUCLXv0L5eUvPPNciJVXDH6ew8EmXmTB3KQhGgSYOA0uErpDHYm+eAue97OwJVoArq4ViJ8ok3GY+jqW3BTfkP9k6+YdXfo/agK0lg17GxBh0Grnm49GXr0Mb247GSnuxN87hxOrHuGIeIyhRw+mzDFgSfh4BQudPRtmy4dcS/9ng88+p5/zvgh695QpVWjvnc69NwDJdzzGeJAWGD4g8zGzLHvScfoeskBarjc5h5HFrCwRTA2UeyJp5T9hFd4bwnuDO9Vbp6xCkSHKVd0TCVroamTyfXv3gJbeAf2WYpwwgHKwgYotIhuCpzUIZ3Wo5kTYPREDjhmIHS4R0ymz/G8iPOOt+AU7YZ0rnE+o+iG+u9bBgSQKpIJba6e5yYA6H3jyNu+hOTCf4D7zh3EfRBn1IAFtWE4HFBF1JBjhMN22BIg+4FMskzEkT37I87jPTB4191rqR9FGRkkFej5eJXcU64Tnt8zCH4WTojJq+HGVMAnzByGx5BIxbuEIbai7+5vyH3CEkcA5AsQ359v/uqo14iu9Dlu0quYwQDlsABm2zRCzGrYfDr15Ha0o/LWSp/i5xqKnwk4qlBvGK4QLlIpOOyo9qFjnIjiLyspXhzUuuA9fAfAYdIAbmhH7x9Dwb8qWIN/VZA+6UonbYu4Og+sMwuDnwI+q8CYFMuTDemEsnsPOZT1mNQKmdL+tmVuxQGXJneboJZRBPRhNB4RgJlOOIDUatgUsH7vDfZ3h9/oK0Y+RHg8mqXMZOnOcK9/aJMGrgXtDh87fxixR/w7YufqYc1aFshsOwJ0kGF74m1JZ/O7E6N9xZKs++D9TOVa2Wzb43on/UhxMHFmRiD69a9YvVsH4GWNXL86H/sHqGIChS5XLfcdEED3/I7nvj9/Uu8k17gUDwMsug8E6idF0ADjwGxACHKvASoWPhcQKCBQQKCAQAGBAgIFBAoIFBAoIFBAoIBAAYECAgUECggUECggUECggMDHgMD/AjQbdSB4OFHHAAAAAElFTkSuQmCC"; @@ -60,11 +65,18 @@ impl RuntimeState { message_filters: self.data.message_filters.added_since(0), failed_sns_launches: self.data.failed_sns_launches.iter().copied().collect(), stable_memory_sizes: memory::memory_sizes(), + subnets: self.data.subnets.subnets().to_vec(), canister_ids: CanisterIds { + user_index: self.data.user_index_canister_id, + group_index: self.data.group_index_canister_id, + notifications_index: self.data.notifications_index_canister_id, + event_relay: self.data.event_relay_canister_id, proposals_bot: self.data.proposals_bot_canister_id, sns_wasm: self.data.sns_wasm_canister_id, escrow: self.data.escrow_canister_id, cycles_dispenser: self.data.cycles_dispenser_canister_id, + icp_ledger: self.data.icp_ledger_canister_id, + cmc: self.data.cycles_minting_canister_id, }, } } @@ -75,10 +87,20 @@ struct Data { governance_principals: HashSet, proposals_bot_canister_id: CanisterId, user_index_canister_id: CanisterId, + #[serde(default = "CanisterId::anonymous")] + group_index_canister_id: CanisterId, + #[serde(default = "CanisterId::anonymous")] + notifications_index_canister_id: CanisterId, + #[serde(default = "CanisterId::anonymous")] + event_relay_canister_id: CanisterId, sns_wasm_canister_id: CanisterId, #[serde(default = "CanisterId::anonymous")] escrow_canister_id: CanisterId, cycles_dispenser_canister_id: CanisterId, + #[serde(default = "CanisterId::anonymous")] + icp_ledger_canister_id: CanisterId, + #[serde(default = "CanisterId::anonymous")] + cycles_minting_canister_id: CanisterId, tokens: Tokens, nervous_systems: NervousSystems, failed_sns_launches: HashSet, @@ -87,27 +109,42 @@ struct Data { total_supply: Timestamped, circulating_supply: Timestamped, airdrop_config: Timestamped>, + #[serde(default)] + subnets: Subnets, + #[serde(default)] + timer_jobs: TimerJobs, rng_seed: [u8; 32], test_mode: bool, } impl Data { + #[allow(clippy::too_many_arguments)] pub fn new( governance_principals: HashSet, proposals_bot_canister_id: CanisterId, user_index_canister_id: CanisterId, + group_index_canister_id: CanisterId, + notifications_index_canister_id: CanisterId, + event_relay_canister_id: CanisterId, sns_wasm_canister_id: CanisterId, escrow_canister_id: CanisterId, cycles_dispenser_canister_id: CanisterId, + icp_ledger_canister_id: CanisterId, + cycles_minting_canister_id: CanisterId, test_mode: bool, ) -> Data { Data { governance_principals, proposals_bot_canister_id, user_index_canister_id, + group_index_canister_id, + notifications_index_canister_id, + event_relay_canister_id, sns_wasm_canister_id, escrow_canister_id, cycles_dispenser_canister_id, + icp_ledger_canister_id, + cycles_minting_canister_id, tokens: Tokens::default(), nervous_systems: NervousSystems::default(), failed_sns_launches: HashSet::new(), @@ -116,6 +153,8 @@ impl Data { total_supply: Timestamped::default(), circulating_supply: Timestamped::default(), airdrop_config: Timestamped::default(), + subnets: Subnets::default(), + timer_jobs: TimerJobs::default(), rng_seed: [0; 32], test_mode, } @@ -182,13 +221,20 @@ pub struct Metrics { pub message_filters: Vec, pub failed_sns_launches: Vec, pub stable_memory_sizes: BTreeMap, + pub subnets: Vec, pub canister_ids: CanisterIds, } #[derive(Serialize)] pub struct CanisterIds { + pub user_index: CanisterId, + pub group_index: CanisterId, + pub notifications_index: CanisterId, + pub event_relay: CanisterId, pub proposals_bot: CanisterId, pub sns_wasm: CanisterId, pub escrow: CanisterId, pub cycles_dispenser: CanisterId, + pub icp_ledger: CanisterId, + pub cmc: CanisterId, } diff --git a/backend/canisters/registry/impl/src/lifecycle/init.rs b/backend/canisters/registry/impl/src/lifecycle/init.rs index 84a5105316..b8da2909cf 100644 --- a/backend/canisters/registry/impl/src/lifecycle/init.rs +++ b/backend/canisters/registry/impl/src/lifecycle/init.rs @@ -18,9 +18,14 @@ fn init(args: Args) { args.governance_principals.into_iter().collect(), args.proposals_bot_canister_id, args.user_index_canister_id, + args.group_index_canister_id, + args.notifications_index_canister_id, + args.event_relay_canister_id, args.sns_wasm_canister_id, args.escrow_canister_id, args.cycles_dispenser_canister_id, + args.nns_ledger_canister_id, + args.cycles_minting_canister_id, args.test_mode, ); diff --git a/backend/canisters/registry/impl/src/lifecycle/post_upgrade.rs b/backend/canisters/registry/impl/src/lifecycle/post_upgrade.rs index 83d8c932f2..d2cb97c430 100644 --- a/backend/canisters/registry/impl/src/lifecycle/post_upgrade.rs +++ b/backend/canisters/registry/impl/src/lifecycle/post_upgrade.rs @@ -21,11 +21,31 @@ fn post_upgrade(args: Args) { msgpack::deserialize(reader).unwrap(); if data.test_mode { + data.group_index_canister_id = CanisterId::from_text("7kifq-3yaaa-aaaaf-ab2cq-cai").unwrap(); + data.notifications_index_canister_id = CanisterId::from_text("4glvk-ryaaa-aaaaf-aaaia-cai").unwrap(); + data.event_relay_canister_id = CanisterId::from_text("6jejw-xyaaa-aaaaf-biiba-cai").unwrap(); data.escrow_canister_id = CanisterId::from_text("tspqt-xaaaa-aaaal-qcnna-cai").unwrap(); } else { + data.group_index_canister_id = CanisterId::from_text("4ijyc-kiaaa-aaaaf-aaaja-cai").unwrap(); + data.notifications_index_canister_id = CanisterId::from_text("7ekiy-aiaaa-aaaaf-ab2dq-cai").unwrap(); + data.event_relay_canister_id = CanisterId::from_text("6ofpc-2aaaa-aaaaf-biibq-cai").unwrap(); data.escrow_canister_id = CanisterId::from_text("s4yi7-yiaaa-aaaar-qacpq-cai").unwrap(); } + // if data.test_mode { + // data.subnets + // .subnets + // .insert(Principal::from_text("eq6en-6jqla-fbu5s-daskr-h6hx2-376n5-iqabl-qgrng-gfqmv-n3yjr-mqe").unwrap()); + // } else { + // + // data.subnets + // .subnets + // .insert(Principal::from_text("eq6en-6jqla-fbu5s-daskr-h6hx2-376n5-iqabl-qgrng-gfqmv-n3yjr-mqe").unwrap()); + // data.subnets + // .subnets + // .insert(Principal::from_text("2fq7c-slacv-26cgz-vzbx2-2jrcs-5edph-i5s2j-tck77-c3rlz-iobzx-mqe").unwrap()); + // } + canister_logger::init_with_logs(data.test_mode, errors, logs, traces); let env = init_env(data.rng_seed); diff --git a/backend/canisters/registry/impl/src/model/mod.rs b/backend/canisters/registry/impl/src/model/mod.rs index ee2b9d7d4e..4b895290cd 100644 --- a/backend/canisters/registry/impl/src/model/mod.rs +++ b/backend/canisters/registry/impl/src/model/mod.rs @@ -1,3 +1,4 @@ pub mod message_filters; pub mod nervous_systems; +pub mod subnets; pub mod tokens; diff --git a/backend/canisters/registry/impl/src/model/subnets.rs b/backend/canisters/registry/impl/src/model/subnets.rs new file mode 100644 index 0000000000..b9b3ed3438 --- /dev/null +++ b/backend/canisters/registry/impl/src/model/subnets.rs @@ -0,0 +1,152 @@ +use candid::Principal; +use registry_canister::subnets::Subnet; +use serde::{Deserialize, Serialize}; +use tracing::info; +use types::{CanisterId, TimestampMillis}; + +#[derive(Serialize, Deserialize, Default)] +pub struct Subnets { + subnets: Vec, + in_progress: Option, +} + +impl Subnets { + pub fn subnets(&self) -> &[Subnet] { + &self.subnets + } + + pub fn start_new(&mut self, subnet_id: Principal, now: TimestampMillis) { + self.in_progress = Some(SubnetInProgress::new(subnet_id, now)); + } + + pub fn in_progress(&self) -> Option<&SubnetInProgress> { + self.in_progress.as_ref() + } + + pub fn update_in_progress(&mut self, f: F, now: TimestampMillis) -> Option { + let mut subnet = self.in_progress.take()?; + f(&mut subnet); + subnet.last_updated = now; + + let complete = subnet.is_complete(); + if complete { + let subnet_id = subnet.id; + self.subnets.push(subnet.try_into().unwrap()); + self.in_progress = None; + info!(%subnet_id, "Subnet added"); + } else { + self.in_progress = Some(subnet); + } + Some(complete) + } +} + +#[derive(Serialize, Deserialize)] +pub struct SubnetInProgress { + pub id: Principal, + pub local_user_index: Option, + pub local_group_index: Option, + pub notifications_canister: Option, + pub controllers_updated: bool, + pub event_relay_notified: bool, + pub notifications_index_notified: bool, + pub local_user_index_notified: bool, + pub local_group_index_notified: bool, + pub create_canister_block_index: Option, + last_updated: TimestampMillis, +} + +impl SubnetInProgress { + pub fn new(id: Principal, now: TimestampMillis) -> Self { + SubnetInProgress { + id, + last_updated: now, + local_user_index: None, + local_group_index: None, + notifications_canister: None, + controllers_updated: false, + event_relay_notified: false, + notifications_index_notified: false, + local_user_index_notified: false, + local_group_index_notified: false, + create_canister_block_index: None, + } + } + + pub fn next_step(&self) -> ExpandOntoNewSubnetStep { + let Some(local_user_index) = self.local_user_index else { + return ExpandOntoNewSubnetStep::CreateLocalUserIndex; + }; + let Some(local_group_index) = self.local_group_index else { + return ExpandOntoNewSubnetStep::CreateLocalGroupIndex; + }; + let Some(notifications_canister) = self.notifications_canister else { + return ExpandOntoNewSubnetStep::CreateNotificationsCanister; + }; + + let new_canister_ids = NewCanisterIds { + local_user_index, + local_group_index, + notifications_canister, + }; + if !self.controllers_updated { + ExpandOntoNewSubnetStep::UpdateControllers(new_canister_ids) + } else if !self.event_relay_notified { + ExpandOntoNewSubnetStep::NotifyEventRelay(new_canister_ids) + } else if !self.notifications_index_notified { + ExpandOntoNewSubnetStep::NotifyNotificationsIndex(new_canister_ids) + } else if !self.local_user_index_notified { + ExpandOntoNewSubnetStep::NotifyUserIndex(new_canister_ids) + } else if !self.local_group_index_notified { + ExpandOntoNewSubnetStep::NotifyGroupIndex(new_canister_ids) + } else { + ExpandOntoNewSubnetStep::Complete + } + } + + pub fn is_complete(&self) -> bool { + matches!(self.next_step(), ExpandOntoNewSubnetStep::Complete) + } +} + +impl TryFrom for Subnet { + type Error = (); + + fn try_from(value: SubnetInProgress) -> Result { + let (local_user_index, local_group_index, notifications_canister) = + match (value.local_user_index, value.local_group_index, value.notifications_canister) { + (Some(local_user_index), Some(local_group_index), Some(notifications_canister)) => { + (local_user_index, local_group_index, notifications_canister) + } + _ => return Err(()), + }; + + Ok(Subnet { + subnet_id: value.id, + local_user_index, + local_group_index, + notifications_canister, + }) + } +} + +#[derive(Debug)] +pub struct NewCanisterIds { + pub local_user_index: CanisterId, + pub local_group_index: CanisterId, + pub notifications_canister: CanisterId, +} + +#[derive(Debug)] +pub enum ExpandOntoNewSubnetStep { + CreateLocalUserIndex, + CreateLocalGroupIndex, + CreateNotificationsCanister, + UpdateControllers(NewCanisterIds), + NotifyCyclesDispenser(NewCanisterIds), + NotifyEventRelay(NewCanisterIds), + NotifyNotificationsIndex(NewCanisterIds), + NotifyUserIndex(NewCanisterIds), + NotifyGroupIndex(NewCanisterIds), + Complete, +} diff --git a/backend/canisters/registry/impl/src/queries/mod.rs b/backend/canisters/registry/impl/src/queries/mod.rs index d0894836af..bc8f49a5d8 100644 --- a/backend/canisters/registry/impl/src/queries/mod.rs +++ b/backend/canisters/registry/impl/src/queries/mod.rs @@ -1,3 +1,4 @@ mod c2c_nervous_systems; mod http_request; +mod subnets; mod updates; diff --git a/backend/canisters/registry/impl/src/queries/subnets.rs b/backend/canisters/registry/impl/src/queries/subnets.rs new file mode 100644 index 0000000000..8e83882414 --- /dev/null +++ b/backend/canisters/registry/impl/src/queries/subnets.rs @@ -0,0 +1,14 @@ +use crate::{read_state, RuntimeState}; +use canister_api_macros::query; +use canister_tracing_macros::trace; +use registry_canister::subnets::{Response::*, *}; + +#[query(msgpack = true)] +#[trace] +fn subnets(_args: Args) -> Response { + read_state(subnets_impl) +} + +fn subnets_impl(state: &RuntimeState) -> Response { + Success(state.data.subnets.subnets().to_vec()) +} diff --git a/backend/canisters/registry/impl/src/timer_job_types.rs b/backend/canisters/registry/impl/src/timer_job_types.rs new file mode 100644 index 0000000000..d101d7e273 --- /dev/null +++ b/backend/canisters/registry/impl/src/timer_job_types.rs @@ -0,0 +1,281 @@ +use crate::model::subnets::ExpandOntoNewSubnetStep; +use crate::{mutate_state, read_state}; +use candid::Principal; +use canister_timer_jobs::Job; +use constants::{MINUTE_IN_MS, NANOS_PER_MILLISECOND}; +use cycles_minting_canister::notify_create_canister::{Subnet, SubnetSelection}; +use ic_cdk::api::call::{CallResult, RejectionCode}; +use ic_cdk::api::management_canister::main::{CanisterSettings, UpdateSettingsArgument}; +use ic_ledger_types::{AccountIdentifier, Memo, Subaccount, Timestamp, Tokens, TransferArgs, DEFAULT_FEE}; +use serde::{Deserialize, Serialize}; +use types::{CanisterId, TimestampMillis}; + +const MEMO_CREATE_CANISTER: Memo = Memo(0x41455243); // == 'CREA' + +#[derive(Serialize, Deserialize, Clone)] +pub enum TimerJob { + ExpandOntoNewSubnet(ExpandOntoNewSubnetJob), +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct ExpandOntoNewSubnetJob { + pub subnet_id: Principal, + pub this_canister_id: CanisterId, + pub user_index: CanisterId, + pub group_index: CanisterId, + pub notifications_index: CanisterId, + pub event_relay: CanisterId, + pub cycles_dispenser: CanisterId, + pub ledger: CanisterId, + pub cmc: CanisterId, + pub create_canister_block_index: Option, +} + +impl Job for TimerJob { + fn execute(self) { + match self { + TimerJob::ExpandOntoNewSubnet(job) => job.execute(), + } + } +} + +impl Job for ExpandOntoNewSubnetJob { + fn execute(self) { + if let Some((next_step, now)) = + read_state(|state| state.data.subnets.in_progress().map(|s| (s.next_step(), state.env.now()))) + { + ic_cdk::spawn(self.process_step(next_step, now)); + } + } +} + +impl ExpandOntoNewSubnetJob { + async fn process_step(self, next_step: ExpandOntoNewSubnetStep, now: TimestampMillis) { + ic_cdk::println!("Expanding onto new subnet. Step: {next_step:?}"); + + let delay = match self.process_step_inner(next_step, now).await { + Ok(Some(false)) => 0, + Err(error) => { + ic_cdk::println!("{error:?}"); + MINUTE_IN_MS + } + Ok(Some(true)) | Ok(None) => return, + }; + + mutate_state(|state| { + let now = state.env.now(); + state + .data + .timer_jobs + .enqueue_job(TimerJob::ExpandOntoNewSubnet(self), now + delay, now); + }) + } + + async fn process_step_inner(&self, next_step: ExpandOntoNewSubnetStep, now: TimestampMillis) -> CallResult> { + let complete = match next_step { + ExpandOntoNewSubnetStep::CreateLocalUserIndex => { + let canister_id = create_canister( + self.ledger, + self.cmc, + self.subnet_id, + self.this_canister_id, + self.create_canister_block_index, + now, + ) + .await?; + + mutate_state(|state| { + state + .data + .subnets + .update_in_progress(|s| s.local_user_index = Some(canister_id), now) + }) + } + ExpandOntoNewSubnetStep::CreateLocalGroupIndex => { + let canister_id = create_canister( + self.ledger, + self.cmc, + self.subnet_id, + self.this_canister_id, + self.create_canister_block_index, + now, + ) + .await?; + + mutate_state(|state| { + state + .data + .subnets + .update_in_progress(|s| s.local_group_index = Some(canister_id), now) + }) + } + ExpandOntoNewSubnetStep::CreateNotificationsCanister => { + let canister_id = create_canister( + self.ledger, + self.cmc, + self.subnet_id, + self.this_canister_id, + self.create_canister_block_index, + now, + ) + .await?; + + mutate_state(|state| { + state + .data + .subnets + .update_in_progress(|s| s.notifications_canister = Some(canister_id), now) + }) + } + ExpandOntoNewSubnetStep::UpdateControllers(ids) => { + let futures: Vec<_> = [ + (ids.local_user_index, self.user_index), + (ids.local_group_index, self.group_index), + (ids.notifications_canister, self.notifications_index), + ] + .into_iter() + .map(|(canister_id, controller)| async move { + ic_cdk::api::management_canister::main::update_settings(UpdateSettingsArgument { + canister_id, + settings: CanisterSettings { + controllers: Some(vec![controller]), + ..Default::default() + }, + }) + .await + }) + .collect(); + + futures::future::try_join_all(futures).await?; + mutate_state(|state| state.data.subnets.update_in_progress(|s| s.controllers_updated = true, now)) + } + ExpandOntoNewSubnetStep::NotifyCyclesDispenser(ids) => { + let futures: Vec<_> = [ids.local_user_index, ids.local_group_index, ids.notifications_canister] + .into_iter() + .map(|canister_id| async move { + cycles_dispenser_canister_c2c_client::add_canister( + self.cycles_dispenser, + &cycles_dispenser_canister::add_canister::Args { canister_id }, + ) + .await + }) + .collect(); + + futures::future::try_join_all(futures).await?; + mutate_state(|state| state.data.subnets.update_in_progress(|s| s.event_relay_notified = true, now)) + } + ExpandOntoNewSubnetStep::NotifyEventRelay(ids) => { + event_relay_canister_c2c_client::authorize_principals( + self.event_relay, + &event_relay_canister::authorize_principals::Args { + principals: vec![ids.local_user_index, ids.local_group_index, ids.notifications_canister], + }, + ) + .await?; + + mutate_state(|state| state.data.subnets.update_in_progress(|s| s.event_relay_notified = true, now)) + } + ExpandOntoNewSubnetStep::NotifyNotificationsIndex(ids) => { + notifications_index_canister_c2c_client::add_notifications_canister( + self.notifications_index, + ¬ifications_index_canister::add_notifications_canister::Args { + canister_id: ids.notifications_canister, + authorizers: vec![ids.local_user_index, ids.local_group_index], + }, + ) + .await?; + + mutate_state(|state| { + state + .data + .subnets + .update_in_progress(|s| s.notifications_index_notified = true, now) + }) + } + ExpandOntoNewSubnetStep::NotifyUserIndex(ids) => { + user_index_canister_c2c_client::add_local_user_index_canister( + self.user_index, + &user_index_canister::add_local_user_index_canister::Args { + canister_id: ids.local_user_index, + notifications_canister_id: ids.notifications_canister, + }, + ) + .await?; + + mutate_state(|state| { + state + .data + .subnets + .update_in_progress(|s| s.local_user_index_notified = true, now) + }) + } + ExpandOntoNewSubnetStep::NotifyGroupIndex(ids) => { + group_index_canister_c2c_client::add_local_group_index_canister( + self.group_index, + &group_index_canister::add_local_group_index_canister::Args { + canister_id: ids.local_user_index, + local_user_index_canister_id: ids.local_user_index, + notifications_canister_id: ids.notifications_canister, + }, + ) + .await?; + + mutate_state(|state| { + state + .data + .subnets + .update_in_progress(|s| s.local_group_index_notified = true, now) + }) + } + ExpandOntoNewSubnetStep::Complete => Some(true), + }; + + Ok(complete) + } +} + +async fn create_canister( + ledger: CanisterId, + cmc: CanisterId, + subnet: Principal, + this_canister_id: Principal, + create_canister_block_index: Option, + now: TimestampMillis, +) -> CallResult { + let block_index = match create_canister_block_index { + Some(index) => index, + None => { + match icp_ledger_canister_c2c_client::transfer( + ledger, + &TransferArgs { + memo: MEMO_CREATE_CANISTER, + amount: Tokens::from_e8s(100_000_000), // 1 ICP + fee: DEFAULT_FEE, + from_subaccount: None, + to: AccountIdentifier::new(&cmc, &Subaccount::from(this_canister_id)), + created_at_time: Some(Timestamp { + timestamp_nanos: now * NANOS_PER_MILLISECOND, + }), + }, + ) + .await? + { + Ok(index) => index, + Err(error) => { + return Err((RejectionCode::Unknown, format!("{error:?}"))); + } + } + } + }; + + cycles_minting_canister_c2c_client::notify_create_canister( + cmc, + &cycles_minting_canister::notify_create_canister::Args { + block_index, + controller: this_canister_id, + subnet_selection: Some(SubnetSelection::Subnet(Subnet { subnet })), + }, + ) + .await? + .map_err(|error| (RejectionCode::Unknown, format!("{error:?}"))) +} diff --git a/backend/canisters/registry/impl/src/updates/expand_onto_subnet.rs b/backend/canisters/registry/impl/src/updates/expand_onto_subnet.rs new file mode 100644 index 0000000000..d735e629eb --- /dev/null +++ b/backend/canisters/registry/impl/src/updates/expand_onto_subnet.rs @@ -0,0 +1,42 @@ +use crate::guards::caller_is_governance_principal; +use crate::timer_job_types::ExpandOntoNewSubnetJob; +use crate::{mutate_state, RuntimeState}; +use canister_api_macros::proposal; +use canister_timer_jobs::Job; +use canister_tracing_macros::trace; +use registry_canister::expand_onto_subnet::{Response::*, *}; + +#[proposal(guard = "caller_is_governance_principal")] +#[trace] +fn expand_onto_subnet(args: Args) -> Response { + match mutate_state(|state| expand_onto_subnet_impl(args, state)) { + Ok(job) => { + job.execute(); + Success + } + Err(response) => response, + } +} + +fn expand_onto_subnet_impl(args: Args, state: &mut RuntimeState) -> Result { + if state.data.subnets.subnets().iter().any(|s| s.subnet_id == args.subnet_id) { + Err(AlreadyOnSubnet) + } else if state.data.subnets.in_progress().is_some() { + Err(AlreadyInProgress) + } else { + state.data.subnets.start_new(args.subnet_id, state.env.now()); + + Ok(ExpandOntoNewSubnetJob { + subnet_id: args.subnet_id, + this_canister_id: state.env.canister_id(), + user_index: state.data.user_index_canister_id, + group_index: state.data.group_index_canister_id, + notifications_index: state.data.notifications_index_canister_id, + event_relay: state.data.event_relay_canister_id, + cycles_dispenser: state.data.cycles_dispenser_canister_id, + ledger: state.data.icp_ledger_canister_id, + cmc: state.data.cycles_minting_canister_id, + create_canister_block_index: None, + }) + } +} diff --git a/backend/canisters/registry/impl/src/updates/mod.rs b/backend/canisters/registry/impl/src/updates/mod.rs index 2afec9e40d..d06c963288 100644 --- a/backend/canisters/registry/impl/src/updates/mod.rs +++ b/backend/canisters/registry/impl/src/updates/mod.rs @@ -2,6 +2,7 @@ pub mod add_message_filter; pub mod add_remove_swap_provider; pub mod add_token; pub mod c2c_set_submitting_proposals_enabled; +pub mod expand_onto_subnet; pub mod remove_message_filter; pub mod set_airdrop_config; pub mod set_token_enabled; diff --git a/backend/canisters/user_index/api/src/lifecycle/init.rs b/backend/canisters/user_index/api/src/lifecycle/init.rs index ffd59f1d17..e6f5827b8e 100644 --- a/backend/canisters/user_index/api/src/lifecycle/init.rs +++ b/backend/canisters/user_index/api/src/lifecycle/init.rs @@ -15,6 +15,7 @@ pub struct Args { pub storage_index_canister_id: CanisterId, pub escrow_canister_id: CanisterId, pub event_relay_canister_id: CanisterId, + pub registry_canister_id: CanisterId, pub nns_governance_canister_id: CanisterId, pub internet_identity_canister_id: CanisterId, pub translations_canister_id: CanisterId, diff --git a/backend/canisters/user_index/api/src/updates/add_local_user_index_canister.rs b/backend/canisters/user_index/api/src/updates/add_local_user_index_canister.rs index 97676ab1dc..a2e2ee3b5b 100644 --- a/backend/canisters/user_index/api/src/updates/add_local_user_index_canister.rs +++ b/backend/canisters/user_index/api/src/updates/add_local_user_index_canister.rs @@ -1,5 +1,4 @@ use candid::CandidType; -use human_readable::{HumanReadablePrincipal, ToHumanReadable}; use serde::{Deserialize, Serialize}; use types::CanisterId; @@ -15,20 +14,3 @@ pub enum Response { AlreadyAdded, InternalError(String), } - -#[derive(Serialize)] -pub struct HumanReadableArgs { - canister_id: HumanReadablePrincipal, - notifications_canister_id: HumanReadablePrincipal, -} - -impl ToHumanReadable for Args { - type Target = HumanReadableArgs; - - fn to_human_readable(&self) -> Self::Target { - HumanReadableArgs { - canister_id: self.canister_id.into(), - notifications_canister_id: self.notifications_canister_id.into(), - } - } -} diff --git a/backend/canisters/user_index/c2c_client/src/lib.rs b/backend/canisters/user_index/c2c_client/src/lib.rs index 4c19761f23..b2a51d9858 100644 --- a/backend/canisters/user_index/c2c_client/src/lib.rs +++ b/backend/canisters/user_index/c2c_client/src/lib.rs @@ -10,6 +10,7 @@ generate_c2c_call!(user); generate_candid_c2c_call!(users_chit); // Updates +generate_c2c_call!(add_local_user_index_canister); generate_c2c_call!(c2c_mark_user_canister_empty); generate_c2c_call!(c2c_report_message); generate_c2c_call!(c2c_notify_chit); diff --git a/backend/canisters/user_index/impl/src/guards.rs b/backend/canisters/user_index/impl/src/guards.rs index 4091239ee8..32c61edbd7 100644 --- a/backend/canisters/user_index/impl/src/guards.rs +++ b/backend/canisters/user_index/impl/src/guards.rs @@ -16,6 +16,14 @@ pub fn caller_is_governance_principal() -> Result<(), String> { } } +pub fn caller_is_registry_canister() -> Result<(), String> { + if read_state(|state| state.is_caller_registry_canister()) { + Ok(()) + } else { + Err("Caller is not the Registry canister".to_string()) + } +} + pub fn caller_is_local_user_index_canister() -> Result<(), String> { if read_state(|state| state.is_caller_local_user_index_canister()) { Ok(()) diff --git a/backend/canisters/user_index/impl/src/lib.rs b/backend/canisters/user_index/impl/src/lib.rs index b0f074d676..8e80a1ef12 100644 --- a/backend/canisters/user_index/impl/src/lib.rs +++ b/backend/canisters/user_index/impl/src/lib.rs @@ -85,6 +85,10 @@ impl RuntimeState { self.data.governance_principals.contains(&caller) } + pub fn is_caller_registry_canister(&self) -> bool { + self.env.caller() == self.data.registry_canister_id + } + pub fn is_caller_local_user_index_canister(&self) -> bool { let caller = self.env.caller(); self.data.local_index_map.get(&caller).is_some() @@ -292,6 +296,7 @@ impl RuntimeState { escrow: self.data.escrow_canister_id, translations: self.data.translations_canister_id, event_relay: event_relay_canister_id, + registry: self.data.registry_canister_id, internet_identity: self.data.internet_identity_canister_id, website: self.data.website_canister_id, }, @@ -345,6 +350,8 @@ struct Data { pub storage_index_canister_id: CanisterId, pub escrow_canister_id: CanisterId, pub translations_canister_id: CanisterId, + #[serde(default = "CanisterId::anonymous")] + pub registry_canister_id: CanisterId, pub event_store_client: EventStoreClient, pub storage_index_user_sync_queue: GroupedTimerJobQueue, pub user_index_event_sync_queue: CanisterEventSyncQueue, @@ -397,6 +404,7 @@ impl Data { storage_index_canister_id: CanisterId, escrow_canister_id: CanisterId, event_relay_canister_id: CanisterId, + registry_canister_id: CanisterId, nns_governance_canister_id: CanisterId, internet_identity_canister_id: CanisterId, translations_canister_id: CanisterId, @@ -422,6 +430,7 @@ impl Data { storage_index_canister_id, escrow_canister_id, translations_canister_id, + registry_canister_id, event_store_client: EventStoreClientBuilder::new(event_relay_canister_id, CdkRuntime::default()) .with_flush_delay(Duration::from_secs(60)) .build(), @@ -554,6 +563,7 @@ impl Default for Data { storage_index_canister_id: Principal::anonymous(), escrow_canister_id: Principal::anonymous(), translations_canister_id: Principal::anonymous(), + registry_canister_id: Principal::anonymous(), event_store_client: EventStoreClientBuilder::new(Principal::anonymous(), CdkRuntime::default()).build(), storage_index_user_sync_queue: GroupedTimerJobQueue::new(1, false), user_index_event_sync_queue: CanisterEventSyncQueue::default(), @@ -723,6 +733,7 @@ pub struct CanisterIds { pub escrow: CanisterId, pub translations: CanisterId, pub event_relay: CanisterId, + pub registry: CanisterId, pub internet_identity: CanisterId, pub website: CanisterId, } diff --git a/backend/canisters/user_index/impl/src/lifecycle/init.rs b/backend/canisters/user_index/impl/src/lifecycle/init.rs index b470465ddb..52a7c8b209 100644 --- a/backend/canisters/user_index/impl/src/lifecycle/init.rs +++ b/backend/canisters/user_index/impl/src/lifecycle/init.rs @@ -27,6 +27,7 @@ fn init(args: Args) { args.storage_index_canister_id, args.escrow_canister_id, args.event_relay_canister_id, + args.registry_canister_id, args.nns_governance_canister_id, args.internet_identity_canister_id, args.translations_canister_id, diff --git a/backend/canisters/user_index/impl/src/lifecycle/post_upgrade.rs b/backend/canisters/user_index/impl/src/lifecycle/post_upgrade.rs index d2761e052e..3add1718ce 100644 --- a/backend/canisters/user_index/impl/src/lifecycle/post_upgrade.rs +++ b/backend/canisters/user_index/impl/src/lifecycle/post_upgrade.rs @@ -6,6 +6,7 @@ use canister_tracing_macros::trace; use ic_cdk::post_upgrade; use stable_memory::get_reader; use tracing::info; +use types::CanisterId; use user_index_canister::post_upgrade::Args; use utils::cycles::init_cycles_dispenser_client; @@ -15,9 +16,15 @@ fn post_upgrade(args: Args) { let memory = get_upgrades_memory(); let reader = get_reader(&memory); - let (data, errors, logs, traces): (Data, Vec, Vec, Vec) = + let (mut data, errors, logs, traces): (Data, Vec, Vec, Vec) = msgpack::deserialize(reader).unwrap(); + if data.test_mode { + data.registry_canister_id = CanisterId::from_text("cglwi-oaaaa-aaaar-aqw4q-cai").unwrap(); + } else { + data.registry_canister_id = CanisterId::from_text("cpi5u-yiaaa-aaaar-aqw5a-cai").unwrap(); + } + canister_logger::init_with_logs(data.test_mode, errors, logs, traces); let env = init_env(data.rng_seed, data.oc_key_pair.is_initialised()); diff --git a/backend/canisters/user_index/impl/src/updates/add_local_user_index_canister.rs b/backend/canisters/user_index/impl/src/updates/add_local_user_index_canister.rs index f22a64ac71..50c3f6f485 100644 --- a/backend/canisters/user_index/impl/src/updates/add_local_user_index_canister.rs +++ b/backend/canisters/user_index/impl/src/updates/add_local_user_index_canister.rs @@ -1,6 +1,6 @@ -use crate::guards::caller_is_governance_principal; +use crate::guards::caller_is_registry_canister; use crate::{mutate_state, read_state, RuntimeState}; -use canister_api_macros::proposal; +use canister_api_macros::update; use canister_tracing_macros::trace; use local_user_index_canister::{UserIndexEvent, UserRegistered}; use tracing::info; @@ -9,7 +9,7 @@ use user_index_canister::add_local_user_index_canister::{Response::*, *}; use user_index_canister::ChildCanisterType; use utils::canister::{install_basic, set_controllers}; -#[proposal(guard = "caller_is_governance_principal")] +#[update(guard = "caller_is_registry_canister", msgpack = true)] #[trace] async fn add_local_user_index_canister(args: Args) -> Response { match read_state(|state| prepare(&args, state)) { diff --git a/backend/external_canisters/cmc/api/src/lib.rs b/backend/external_canisters/cmc/api/src/lib.rs index b7c6d39f7a..d4f48b7954 100644 --- a/backend/external_canisters/cmc/api/src/lib.rs +++ b/backend/external_canisters/cmc/api/src/lib.rs @@ -1,5 +1,24 @@ +use crate::notify_top_up::BlockIndex; +use candid::{CandidType, Deserialize}; +use serde::Serialize; + mod queries; mod updates; pub use queries::*; pub use updates::*; + +#[derive(CandidType, Serialize, Deserialize, Debug)] +pub enum NotifyError { + Refunded { + reason: String, + block_index: Option, + }, + InvalidTransaction(String), + TransactionTooOld(BlockIndex), + Processing, + Other { + error_code: u64, + error_message: String, + }, +} diff --git a/backend/external_canisters/cmc/api/src/updates/mod.rs b/backend/external_canisters/cmc/api/src/updates/mod.rs index 5b01cff189..6cbb48eabd 100644 --- a/backend/external_canisters/cmc/api/src/updates/mod.rs +++ b/backend/external_canisters/cmc/api/src/updates/mod.rs @@ -1 +1,3 @@ +pub mod notify_create_canister; pub mod notify_top_up; +pub mod set_authorized_subnetwork_list; diff --git a/backend/external_canisters/cmc/api/src/updates/notify_create_canister.rs b/backend/external_canisters/cmc/api/src/updates/notify_create_canister.rs new file mode 100644 index 0000000000..6b59c6f2dd --- /dev/null +++ b/backend/external_canisters/cmc/api/src/updates/notify_create_canister.rs @@ -0,0 +1,22 @@ +use crate::notify_top_up::CanisterId; +use crate::NotifyError; +use candid::{CandidType, Principal}; + +#[derive(CandidType)] +pub struct Args { + pub block_index: u64, + pub controller: Principal, + pub subnet_selection: Option, +} + +pub type Response = Result; + +#[derive(CandidType)] +pub enum SubnetSelection { + Subnet(Subnet), +} + +#[derive(CandidType)] +pub struct Subnet { + pub subnet: Principal, +} diff --git a/backend/external_canisters/cmc/api/src/updates/notify_top_up.rs b/backend/external_canisters/cmc/api/src/updates/notify_top_up.rs index 6811005e4b..60ff022c4e 100644 --- a/backend/external_canisters/cmc/api/src/updates/notify_top_up.rs +++ b/backend/external_canisters/cmc/api/src/updates/notify_top_up.rs @@ -1,5 +1,5 @@ +use crate::NotifyError; use candid::{CandidType, Principal}; -use serde::{Deserialize, Serialize}; pub type CanisterId = Principal; pub type Cycles = u128; @@ -12,18 +12,3 @@ pub struct Args { } pub type Response = Result; - -#[derive(CandidType, Serialize, Deserialize, Debug)] -pub enum NotifyError { - Refunded { - reason: String, - block_index: Option, - }, - InvalidTransaction(String), - TransactionTooOld(BlockIndex), - Processing, - Other { - error_code: u64, - error_message: String, - }, -} diff --git a/backend/external_canisters/cmc/api/src/updates/set_authorized_subnetwork_list.rs b/backend/external_canisters/cmc/api/src/updates/set_authorized_subnetwork_list.rs new file mode 100644 index 0000000000..e428e62301 --- /dev/null +++ b/backend/external_canisters/cmc/api/src/updates/set_authorized_subnetwork_list.rs @@ -0,0 +1,9 @@ +use candid::{CandidType, Principal}; + +#[derive(CandidType)] +pub struct Args { + pub who: Option, + pub subnets: Vec, +} + +pub type Response = (); diff --git a/backend/external_canisters/cmc/c2c_client/src/lib.rs b/backend/external_canisters/cmc/c2c_client/src/lib.rs index 39b23abd16..96d4e50f41 100644 --- a/backend/external_canisters/cmc/c2c_client/src/lib.rs +++ b/backend/external_canisters/cmc/c2c_client/src/lib.rs @@ -5,4 +5,6 @@ use cycles_minting_canister::*; generate_candid_c2c_call_no_args!(neuron_maturity_modulation); // Updates +generate_candid_c2c_call!(notify_create_canister); generate_candid_c2c_call!(notify_top_up); +generate_candid_c2c_call!(set_authorized_subnetwork_list); diff --git a/backend/integration_tests/Cargo.toml b/backend/integration_tests/Cargo.toml index c30102c50a..7ded1af309 100644 --- a/backend/integration_tests/Cargo.toml +++ b/backend/integration_tests/Cargo.toml @@ -11,6 +11,7 @@ candid = { workspace = true } community_canister = { path = "../canisters/community/api" } constants = { path = "../libraries/constants" } cycles_dispenser_canister = { path = "../canisters/cycles_dispenser/api" } +cycles_minting_canister = { path = "../external_canisters/cmc/api" } escrow_canister = { path = "../canisters/escrow/api" } event_relay_canister = { path = "../canisters/event_relay/api" } event_store_canister = { workspace = true } diff --git a/backend/integration_tests/src/client/cmc.rs b/backend/integration_tests/src/client/cmc.rs new file mode 100644 index 0000000000..1253a32dbd --- /dev/null +++ b/backend/integration_tests/src/client/cmc.rs @@ -0,0 +1,5 @@ +use crate::generate_update_call; +use cycles_minting_canister::*; + +// Updates +generate_update_call!(set_authorized_subnetwork_list); diff --git a/backend/integration_tests/src/client/mod.rs b/backend/integration_tests/src/client/mod.rs index c99fd72ebf..1893f8630e 100644 --- a/backend/integration_tests/src/client/mod.rs +++ b/backend/integration_tests/src/client/mod.rs @@ -13,6 +13,7 @@ use types::{CanisterId, CanisterWasm, DiamondMembershipPlanDuration, SignedDeleg mod macros; pub mod airdrop_bot; +pub mod cmc; pub mod community; pub mod cycles_dispenser; pub mod escrow; diff --git a/backend/integration_tests/src/client/registry.rs b/backend/integration_tests/src/client/registry.rs index 3dfa893b52..372a1ccf2c 100644 --- a/backend/integration_tests/src/client/registry.rs +++ b/backend/integration_tests/src/client/registry.rs @@ -2,9 +2,40 @@ use crate::{generate_msgpack_query_call, generate_msgpack_update_call, generate_ use registry_canister::*; // Queries +generate_msgpack_query_call!(subnets); generate_msgpack_query_call!(updates); // Updates generate_update_call!(add_token); +generate_update_call!(expand_onto_subnet); generate_update_call!(update_token); generate_msgpack_update_call!(set_token_enabled); + +pub mod happy_path { + use super::*; + use candid::Principal; + use pocket_ic::PocketIc; + use registry_canister::subnets::Subnet; + use std::time::Duration; + use types::{CanisterId, Empty}; + + pub fn expand_onto_subnet( + env: &mut PocketIc, + sender: Principal, + registry_canister_id: CanisterId, + subnet_id: Principal, + ) -> Subnet { + let response = super::expand_onto_subnet(env, sender, registry_canister_id, &expand_onto_subnet::Args { subnet_id }); + + assert!(matches!(response, expand_onto_subnet::Response::Success)); + + for _ in 0..50 { + env.advance_time(Duration::from_secs(1)); + env.tick(); + } + + let subnets::Response::Success(subnets) = super::subnets(env, sender, registry_canister_id, &Empty {}); + + subnets.last().unwrap().clone() + } +} diff --git a/backend/integration_tests/src/client/storage_index.rs b/backend/integration_tests/src/client/storage_index.rs index ed607d49a2..f8f6730922 100644 --- a/backend/integration_tests/src/client/storage_index.rs +++ b/backend/integration_tests/src/client/storage_index.rs @@ -76,7 +76,7 @@ pub mod happy_path { } } - pub fn upgrade_notifications_canister_wasm( + pub fn upgrade_bucket_canister_wasm( env: &mut PocketIc, sender: Principal, storage_index_canister_id: CanisterId, diff --git a/backend/integration_tests/src/setup.rs b/backend/integration_tests/src/setup.rs index eeec055108..c201106918 100644 --- a/backend/integration_tests/src/setup.rs +++ b/backend/integration_tests/src/setup.rs @@ -97,10 +97,6 @@ fn install_canisters(env: &mut PocketIc, controller: Principal) -> CanisterIds { let sign_in_with_email_canister_id = create_canister(env, controller); let website_canister_id = create_canister(env, controller); - let local_user_index_canister_id = create_canister(env, user_index_canister_id); - let local_group_index_canister_id = create_canister(env, group_index_canister_id); - let notifications_canister_id = create_canister(env, notifications_index_canister_id); - let community_canister_wasm = wasms::COMMUNITY.clone(); let cycles_dispenser_canister_wasm = wasms::CYCLES_DISPENSER.clone(); let cycles_minting_canister_wasm = wasms::CYCLES_MINTING_CANISTER.clone(); @@ -142,6 +138,7 @@ fn install_canisters(env: &mut PocketIc, controller: Principal) -> CanisterIds { storage_index_canister_id, escrow_canister_id, event_relay_canister_id, + registry_canister_id, nns_governance_canister_id, internet_identity_canister_id: NNS_INTERNET_IDENTITY_CANISTER_ID, translations_canister_id, @@ -166,6 +163,7 @@ fn install_canisters(env: &mut PocketIc, controller: Principal) -> CanisterIds { proposals_bot_user_id: proposals_bot_canister_id.into(), escrow_canister_id, event_relay_canister_id, + registry_canister_id, internet_identity_canister_id: NNS_INTERNET_IDENTITY_CANISTER_ID, video_call_operators: vec![VIDEO_CALL_OPERATOR], ic_root_key: env.root_key().unwrap(), @@ -184,6 +182,7 @@ fn install_canisters(env: &mut PocketIc, controller: Principal) -> CanisterIds { governance_principals: vec![controller], push_service_principals: vec![controller], user_index_canister_id, + registry_canister_id, authorizers: vec![user_index_canister_id, group_index_canister_id], cycles_dispenser_canister_id, notifications_canister_wasm: CanisterWasm::default(), @@ -265,22 +264,6 @@ fn install_canisters(env: &mut PocketIc, controller: Principal) -> CanisterIds { proposals_bot_init_args, ); - let airdrop_bot_init_args = airdrop_bot_canister::init::Args { - admins: vec![controller], - user_index_canister_id, - local_user_index_canister_id, - chat_ledger_canister_id, - wasm_version, - test_mode, - }; - install_canister( - env, - controller, - airdrop_bot_canister_id, - airdrop_bot_canister_wasm, - airdrop_bot_init_args, - ); - let storage_index_init_args = storage_index_canister::init::Args { governance_principals: vec![controller], user_controllers: vec![user_index_canister_id, group_index_canister_id], @@ -310,6 +293,7 @@ fn install_canisters(env: &mut PocketIc, controller: Principal) -> CanisterIds { proposals_bot_canister_id, storage_index_canister_id, ], + registry_canister_id, max_top_up_amount: 20 * T, min_interval: 5 * 60 * 1000, // 5 minutes min_cycles_balance: 200 * T, @@ -329,6 +313,9 @@ fn install_canisters(env: &mut PocketIc, controller: Principal) -> CanisterIds { let registry_init_args = registry_canister::init::Args { user_index_canister_id, + group_index_canister_id, + notifications_index_canister_id, + event_relay_canister_id, governance_principals: vec![controller], proposals_bot_canister_id, nns_ledger_canister_id, @@ -338,6 +325,7 @@ fn install_canisters(env: &mut PocketIc, controller: Principal) -> CanisterIds { sns_wasm_canister_id, escrow_canister_id, cycles_dispenser_canister_id, + cycles_minting_canister_id, wasm_version, test_mode, }; @@ -358,14 +346,10 @@ fn install_canisters(env: &mut PocketIc, controller: Principal) -> CanisterIds { install_canister(env, controller, escrow_canister_id, escrow_canister_wasm, escrow_init_args); let event_relay_init_args = event_relay_canister::init::Args { - push_events_whitelist: vec![ - user_index_canister_id, - online_users_canister_id, - local_user_index_canister_id, - local_group_index_canister_id, - ], + push_events_whitelist: vec![user_index_canister_id, online_users_canister_id], event_store_canister_id, cycles_dispenser_canister_id, + registry_canister_id, chat_ledger_canister_id, chat_governance_canister_id, wasm_version, @@ -408,13 +392,6 @@ fn install_canisters(env: &mut PocketIc, controller: Principal) -> CanisterIds { user_index_canister_id, local_user_index_canister_wasm, ); - client::user_index::happy_path::add_local_user_index_canister( - env, - controller, - user_index_canister_id, - local_user_index_canister_id, - notifications_canister_id, - ); client::group_index::happy_path::upgrade_group_canister_wasm(env, controller, group_index_canister_id, group_canister_wasm); client::group_index::happy_path::upgrade_community_canister_wasm( @@ -429,14 +406,6 @@ fn install_canisters(env: &mut PocketIc, controller: Principal) -> CanisterIds { group_index_canister_id, local_group_index_canister_wasm, ); - client::group_index::happy_path::add_local_group_index_canister( - env, - controller, - group_index_canister_id, - local_group_index_canister_id, - local_user_index_canister_id, - notifications_canister_id, - ); client::notifications_index::happy_path::upgrade_notifications_canister_wasm( env, @@ -444,16 +413,8 @@ fn install_canisters(env: &mut PocketIc, controller: Principal) -> CanisterIds { notifications_index_canister_id, notifications_canister_wasm, ); - client::notifications_index::happy_path::add_notifications_canister( - env, - controller, - notifications_index_canister_id, - notifications_canister_id, - local_user_index_canister_id, - local_group_index_canister_id, - ); - client::storage_index::happy_path::upgrade_notifications_canister_wasm( + client::storage_index::happy_path::upgrade_bucket_canister_wasm( env, controller, storage_index_canister_id, @@ -478,7 +439,7 @@ fn install_canisters(env: &mut PocketIc, controller: Principal) -> CanisterIds { let cycles_minting_canister_init_args = CyclesMintingCanisterInitPayload { ledger_canister_id: nns_ledger_canister_id, - governance_canister_id: CanisterId::anonymous(), + governance_canister_id: nns_governance_canister_id, minting_account_id: Some(minting_account.to_string()), last_purged_notification: Some(0), }; @@ -490,6 +451,18 @@ fn install_canisters(env: &mut PocketIc, controller: Principal) -> CanisterIds { cycles_minting_canister_init_args, ); + let application_subnet = env.topology().get_app_subnets().first().unwrap().clone(); + + client::cmc::set_authorized_subnetwork_list( + env, + nns_governance_canister_id, + cycles_minting_canister_id, + &cycles_minting_canister::set_authorized_subnetwork_list::Args { + who: None, + subnets: vec![application_subnet], + }, + ); + let sns_wasm_canister_init_args = SnsWasmCanisterInitPayload::default(); install_canister( env, @@ -499,6 +472,29 @@ fn install_canisters(env: &mut PocketIc, controller: Principal) -> CanisterIds { sns_wasm_canister_init_args, ); + client::ledger::happy_path::transfer(env, controller, nns_ledger_canister_id, registry_canister_id, 1_000_000_000); + + let subnet = client::registry::happy_path::expand_onto_subnet(env, controller, registry_canister_id, application_subnet); + let local_user_index_canister_id = subnet.local_user_index; + let local_group_index_canister_id = subnet.local_group_index; + let notifications_canister_id = subnet.notifications_canister; + + let airdrop_bot_init_args = airdrop_bot_canister::init::Args { + admins: vec![controller], + user_index_canister_id, + local_user_index_canister_id, + chat_ledger_canister_id, + wasm_version, + test_mode, + }; + install_canister( + env, + controller, + airdrop_bot_canister_id, + airdrop_bot_canister_wasm, + airdrop_bot_init_args, + ); + // Tick a load of times so that all the child canisters have time to get installed tick_many(env, 10); diff --git a/backend/tools/canister_installer/src/lib.rs b/backend/tools/canister_installer/src/lib.rs index 864b02af84..e843fe2e8e 100644 --- a/backend/tools/canister_installer/src/lib.rs +++ b/backend/tools/canister_installer/src/lib.rs @@ -80,6 +80,7 @@ async fn install_service_canisters_impl( cycles_dispenser_canister_id: canister_ids.cycles_dispenser, escrow_canister_id: canister_ids.escrow, event_relay_canister_id: canister_ids.event_relay, + registry_canister_id: canister_ids.registry, nns_governance_canister_id: canister_ids.nns_governance, internet_identity_canister_id: canister_ids.nns_internet_identity, translations_canister_id: canister_ids.translations, @@ -98,6 +99,7 @@ async fn install_service_canisters_impl( proposals_bot_user_id: canister_ids.proposals_bot.into(), escrow_canister_id: canister_ids.escrow, event_relay_canister_id: canister_ids.event_relay, + registry_canister_id: canister_ids.registry, internet_identity_canister_id: canister_ids.nns_internet_identity, video_call_operators: video_call_operators.clone(), ic_root_key: agent.read_root_key(), @@ -112,6 +114,7 @@ async fn install_service_canisters_impl( user_index_canister_id: canister_ids.user_index, authorizers: vec![canister_ids.user_index, canister_ids.group_index], cycles_dispenser_canister_id: canister_ids.cycles_dispenser, + registry_canister_id: canister_ids.registry, notifications_canister_wasm: CanisterWasm::default(), wasm_version: version, test_mode, @@ -199,6 +202,7 @@ async fn install_service_canisters_impl( canister_ids.proposals_bot, canister_ids.storage_index, ], + registry_canister_id: canister_ids.registry, max_top_up_amount: 20 * T, min_interval: 5 * 60 * 1000, // 5 minutes min_cycles_balance: 200 * T, @@ -212,6 +216,9 @@ async fn install_service_canisters_impl( let registry_canister_wasm = get_canister_wasm(CanisterName::Registry, version); let registry_init_args = registry_canister::init::Args { user_index_canister_id: canister_ids.user_index, + group_index_canister_id: canister_ids.group_index, + notifications_index_canister_id: canister_ids.notifications_index, + event_relay_canister_id: canister_ids.event_relay, governance_principals: vec![principal], proposals_bot_canister_id: canister_ids.proposals_bot, nns_ledger_canister_id: canister_ids.nns_ledger, @@ -221,6 +228,7 @@ async fn install_service_canisters_impl( nns_index_canister_id: canister_ids.nns_index, escrow_canister_id: canister_ids.escrow, cycles_dispenser_canister_id: canister_ids.cycles_dispenser, + cycles_minting_canister_id: canister_ids.nns_cmc, wasm_version: version, test_mode, }; @@ -264,6 +272,7 @@ async fn install_service_canisters_impl( ], event_store_canister_id: canister_ids.event_store, cycles_dispenser_canister_id: canister_ids.cycles_dispenser, + registry_canister_id: canister_ids.registry, chat_ledger_canister_id: SNS_LEDGER_CANISTER_ID, chat_governance_canister_id: SNS_GOVERNANCE_CANISTER_ID, wasm_version: version,