From 8e9d3e1e6f9eda40b31804dc6de29a1d14085a6f Mon Sep 17 00:00:00 2001 From: rupansh Date: Sun, 29 Sep 2024 08:10:34 +0530 Subject: [PATCH 01/20] feat(shared_utils): add utility for proof of participation --- Cargo.lock | 1 + src/lib/shared_utils/Cargo.toml | 2 + src/lib/shared_utils/src/common/mod.rs | 1 + .../src/common/participant_crypto/mod.rs | 186 ++++++++++++++++++ .../src/common/participant_crypto/types.rs | 41 ++++ 5 files changed, 231 insertions(+) create mode 100644 src/lib/shared_utils/src/common/participant_crypto/mod.rs create mode 100644 src/lib/shared_utils/src/common/participant_crypto/types.rs diff --git a/Cargo.lock b/Cargo.lock index 8a113cd5..432176c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5476,6 +5476,7 @@ version = "0.1.0" dependencies = [ "candid", "ciborium", + "ed25519-dalek", "futures", "ic-cdk 0.15.1", "ic-cdk-timers", diff --git a/src/lib/shared_utils/Cargo.toml b/src/lib/shared_utils/Cargo.toml index 80430800..5250a20c 100644 --- a/src/lib/shared_utils/Cargo.toml +++ b/src/lib/shared_utils/Cargo.toml @@ -17,6 +17,7 @@ ciborium = { workspace = true } serde_json_any_key = "2.0.0" serde_bytes = "0.11.14" icrc-ledger-types = { workspace = true } +ed25519-dalek = "2.1.1" [dev-dependencies] test_utils = { workspace = true } @@ -24,3 +25,4 @@ pprof = { version = "0.13", features = ["flamegraph"] } [features] mockdata = [] +local = [] diff --git a/src/lib/shared_utils/src/common/mod.rs b/src/lib/shared_utils/src/common/mod.rs index e4bd745f..e215e071 100644 --- a/src/lib/shared_utils/src/common/mod.rs +++ b/src/lib/shared_utils/src/common/mod.rs @@ -1,3 +1,4 @@ pub mod environment; pub mod types; pub mod utils; +pub mod participant_crypto; diff --git a/src/lib/shared_utils/src/common/participant_crypto/mod.rs b/src/lib/shared_utils/src/common/participant_crypto/mod.rs new file mode 100644 index 00000000..887376d4 --- /dev/null +++ b/src/lib/shared_utils/src/common/participant_crypto/mod.rs @@ -0,0 +1,186 @@ +//! Utilities for creating and verifying proof that a given canister is a part of YRAL Backend canisters +mod types; + +use std::{cell::RefCell, thread::LocalKey}; + +use candid::{CandidType, Principal}; +use ed25519_dalek::{Signature, VerifyingKey, Verifier}; +use ic_stable_structures::{StableBTreeMap, Memory}; +use types::{ManagementCanisterSchnorrPublicKeyReply, ManagementCanisterSchnorrPublicKeyRequest, ManagementCanisterSignatureReply, ManagementCanisterSignatureRequest, SchnorrAlgorithm, SchnorrKeyId}; +use serde::{Serialize, Deserialize}; + +pub(crate) const THRESHOLD_SCHNORR_KEY: &str = { + #[cfg(feature = "local")] + { + "dfx_test_key" + } + #[cfg(not(feature = "local"))] + { + "key_1" + } +}; + +pub(crate) type LocalPoPStore = LocalKey>; + +pub struct PubKeyCache(StableBTreeMap, M>); + +impl PubKeyCache { + pub fn init(memory: M) -> Self { + Self(StableBTreeMap::init(memory)) + } + + async fn get_or_init_public_key>(store: &'static LocalPoPStore, principal: Principal) -> Result { + let maybe_pk = store.with_borrow(|store| { + store.pubkey_cache().0.get(&principal) + }); + if let Some(pk) = maybe_pk { + return VerifyingKey::try_from(pk.as_slice()) + .map_err(|_| "invalid public key".to_string()) + } + + let derive_args = ManagementCanisterSchnorrPublicKeyRequest { + derivation_path: vec![], + canister_id: Some(principal), + key_id: SchnorrKeyId { + algorithm: SchnorrAlgorithm::Ed25519, + name: THRESHOLD_SCHNORR_KEY.to_string(), + } + }; + let (key_res,): (ManagementCanisterSchnorrPublicKeyReply,) = ic_cdk::call( + Principal::management_canister(), + "schnorr_public_key", + (derive_args,) + ) + .await + .map_err(|(_, msg)| { + format!("unable to get public key: {msg}") + })?; + + let key = key_res.public_key; + let vk = VerifyingKey::try_from(key.as_slice()) + .map_err(|_| "invalid public key".to_string())?; + store.with_borrow_mut(|store| { + store.pubkey_cache_mut().0.insert(principal, key.clone()); + }); + + Ok(vk) + } +} + +#[derive(Serialize)] +struct PoaMessage { + prefix: &'static [u8], + pub child: Principal, +} + +impl PoaMessage { + pub fn new(child: Principal) -> Self { + Self { + prefix: b"CHILD", + child, + } + } + + pub fn serialize_cbor(&self) -> Vec { + let mut bytes = vec![]; + ciborium::into_writer(self, &mut bytes) + .expect("PoaMessage should serialize succesfully"); + + bytes + } +} + +/// Proof that this canister id is a child of the parent canister +#[derive(Clone, CandidType, Serialize, Deserialize)] +struct ProofOfChild { + // Principal of the child + principal: Principal, + signature: Vec, +} + +impl ProofOfChild { + async fn new(child: Principal) -> Result { + let message = PoaMessage::new(child); + let sign_args = ManagementCanisterSignatureRequest { + message: message.serialize_cbor(), + derivation_path: vec![], + key_id: SchnorrKeyId { + algorithm: SchnorrAlgorithm::Ed25519, + name: THRESHOLD_SCHNORR_KEY.to_string() + }, + }; + + let (sig_res,): (ManagementCanisterSignatureReply,) = ic_cdk::api::call::call_with_payment( + Principal::management_canister(), + "sign_with_schnorr", + (sign_args,), + 25_000_000_000, + ) + .await + .map_err(|(_, msg)| format!("unable to sign: {msg}"))?; + + Ok(Self { + principal: child, + signature: sig_res.signature, + }) + } + + pub fn verify(&self, parent_key: &VerifyingKey) -> Result<(), String> { + let message = PoaMessage::new(self.principal); + let message_raw = message.serialize_cbor(); + + let sig = Signature::from_slice(&self.signature).map_err(|_| "invalid proof".to_string())?; + + parent_key.verify(&message_raw, &sig).map_err(|_| "invalid proof".to_string())?; + + Ok(()) + } +} + +#[derive(Clone, CandidType, Serialize, Deserialize)] +pub struct ProofOfParticipation { + chain: Vec, +} + +impl ProofOfParticipation { + /// New PoP for platform orchestrator + pub fn new_for_root() -> Self { + Self { + chain: vec![], + } + } + + pub async fn derive_for_child(self, child: Principal) -> Result { + let mut chain = self.chain; + let proof = ProofOfChild::new(child).await?; + chain.push(proof); + Ok(Self { + chain, + }) + } + + /// Verify that the caller is a YRAL canister + pub async fn verify_caller_is_participant>(&self, store: &'static LocalPoPStore) -> Result<(), String> { + let platform_orchestrator = store.with_borrow(|s| s.platform_orchestrator()); + let canister = ic_cdk::caller(); + + let mut parent = PubKeyCache::get_or_init_public_key(store, platform_orchestrator).await?; + for proof in &self.chain { + proof.verify(&parent)?; + if proof.principal == canister { + return Ok(()) + } + parent = PubKeyCache::get_or_init_public_key(store, proof.principal).await?; + } + + Err("invalid proof".to_string()) + } +} + +pub trait PoPStore { + fn pubkey_cache(&self) -> &PubKeyCache; + + fn pubkey_cache_mut(&mut self) -> &mut PubKeyCache; + + fn platform_orchestrator(&self) -> Principal; +} \ No newline at end of file diff --git a/src/lib/shared_utils/src/common/participant_crypto/types.rs b/src/lib/shared_utils/src/common/participant_crypto/types.rs new file mode 100644 index 00000000..41d2bd64 --- /dev/null +++ b/src/lib/shared_utils/src/common/participant_crypto/types.rs @@ -0,0 +1,41 @@ +use candid::{CandidType, Principal}; +use serde::{Deserialize, Serialize}; + +#[derive(CandidType, Serialize, Debug)] +pub struct ManagementCanisterSchnorrPublicKeyRequest { + pub canister_id: Option, + pub derivation_path: Vec>, + pub key_id: SchnorrKeyId, +} + +#[derive(CandidType, Deserialize, Debug)] +pub struct ManagementCanisterSchnorrPublicKeyReply { + pub public_key: Vec, + pub chain_code: Vec, +} + +#[derive(CandidType, Serialize, Deserialize, Debug, Copy, Clone)] +pub enum SchnorrAlgorithm { + #[serde(rename = "bip340secp256k1")] + Bip340Secp256k1, + #[serde(rename = "ed25519")] + Ed25519, +} + +#[derive(CandidType, Serialize, Debug, Clone)] +pub struct SchnorrKeyId { + pub algorithm: SchnorrAlgorithm, + pub name: String, +} + +#[derive(CandidType, Serialize, Debug)] +pub struct ManagementCanisterSignatureRequest { + pub message: Vec, + pub derivation_path: Vec>, + pub key_id: SchnorrKeyId, +} + +#[derive(CandidType, Deserialize, Debug)] +pub struct ManagementCanisterSignatureReply { + pub signature: Vec, +} \ No newline at end of file From 89778da5d6e11aef1e4545a2b3a2d643e174610e Mon Sep 17 00:00:00 2001 From: rupansh Date: Sun, 29 Sep 2024 09:06:28 +0530 Subject: [PATCH 02/20] feat: initial integration for proof of participation --- ..._pool_of_individual_canister_user_index.sh | 2 +- .../local_deploy/install_all_canisters.sh | 19 ++++++++---- .../individual_user_template/Cargo.toml | 3 ++ src/canister/individual_user_template/can.did | 3 ++ .../src/api/canister_lifecycle/init.rs | 4 +++ .../src/data_model/memory.rs | 6 ++++ .../src/data_model/mod.rs | 31 ++++++++++++++++--- src/canister/platform_orchestrator/Cargo.toml | 3 ++ .../provision_subnet_orchestrator.rs | 7 +++-- .../upgrade_canisters_in_network.rs | 8 +++-- src/canister/post_cache/Cargo.toml | 1 + src/canister/user_index/Cargo.toml | 3 ++ src/canister/user_index/can.did | 3 ++ .../src/api/canister_lifecycle/init.rs | 6 +++- ...upgrade_user_canisters_with_latest_wasm.rs | 7 +++++ ...dividual_user_canister_with_latest_wasm.rs | 6 ++++ src/canister/user_index/src/data_model/mod.rs | 4 +++ .../src/util/canister_management.rs | 10 ++++++ .../hot_or_not/bet_maker_canister_tests.rs | 1 + .../hot_or_not_timely_index_update_test.rs | 1 + .../hotornot_game_simulation_test.rs | 4 +++ .../tests/hot_or_not/reconcile_scores_test.rs | 8 +++++ .../tests/post/update_post_status_banned.rs | 1 + .../individual_user_template/types/arg.rs | 3 +- .../user_index/types/args.rs | 5 +-- .../src/common/participant_crypto/mod.rs | 31 ++++++++++++------- .../test_utils/src/setup/env/pocket_ic_env.rs | 1 + src/lib/test_utils/src/setup/env/v1.rs | 2 ++ 28 files changed, 153 insertions(+), 30 deletions(-) diff --git a/scripts/canisters/local_deploy/create_pool_of_individual_canister_user_index.sh b/scripts/canisters/local_deploy/create_pool_of_individual_canister_user_index.sh index 04ddfc79..86caf938 100755 --- a/scripts/canisters/local_deploy/create_pool_of_individual_canister_user_index.sh +++ b/scripts/canisters/local_deploy/create_pool_of_individual_canister_user_index.sh @@ -12,5 +12,5 @@ char_escaped=$(printf "%s" "$char" | sed 's/../\\&/g') # Create a shell script with the escaped hexadecimal string printf "(\"v1.0.0\", blob \"%s\")" "$char_escaped" > argument -dfx canister call user_index create_pool_of_individual_user_available_canisters --argument-file argument dfx ledger fabricate-cycles --cycles 20000000000000000 --canister user_index +dfx canister call user_index create_pool_of_individual_user_available_canisters --argument-file argument diff --git a/scripts/canisters/local_deploy/install_all_canisters.sh b/scripts/canisters/local_deploy/install_all_canisters.sh index 08ce0447..b89a0e6d 100755 --- a/scripts/canisters/local_deploy/install_all_canisters.sh +++ b/scripts/canisters/local_deploy/install_all_canisters.sh @@ -27,16 +27,20 @@ dfx canister create --no-wallet post_cache dfx canister create --no-wallet user_index dfx canister create --no-wallet platform_orchestrator -gzip_canister() { - gzip -f -1 ./target/wasm32-unknown-unknown/release/$1.wasm +# HACK: dfx doesn't support specifying feature flags.... +rebuild_canister() { + cargo build --target wasm32-unknown-unknown --release --features local -p $1 --locked + wasm-opt ./target/wasm32-unknown-unknown/release/$1.wasm -o ./target/wasm32-unknown-unknown/release/$1.wasm -Oz + gzip -fk -1 ./target/wasm32-unknown-unknown/release/$1.wasm + cp ./target/wasm32-unknown-unknown/release/$1.wasm.gz .dfx/local/canisters/$1/$1.wasm.gz } scripts/candid_generator.sh -gzip_canister individual_user_template -gzip_canister user_index -gzip_canister post_cache -gzip_canister platform_orchestrator +rebuild_canister individual_user_template +rebuild_canister user_index +rebuild_canister post_cache +rebuild_canister platform_orchestrator if [[ $skip_test != true ]] then @@ -90,6 +94,9 @@ dfx canister install user_index --argument "(record { vec { variant { CanisterAdmin }; variant { CanisterController }; } }; }; + proof_of_participation = opt record { + chain = vec {}; + }; version= \"v1.0.0\" })" diff --git a/src/canister/individual_user_template/Cargo.toml b/src/canister/individual_user_template/Cargo.toml index a4ea3db0..baad2d67 100644 --- a/src/canister/individual_user_template/Cargo.toml +++ b/src/canister/individual_user_template/Cargo.toml @@ -36,3 +36,6 @@ hex = "0.4.3" [dev-dependencies] test_utils = { workspace = true } + +[features] +local = ["shared_utils/local"] diff --git a/src/canister/individual_user_template/can.did b/src/canister/individual_user_template/can.did index 1166e0ad..a95fa68b 100644 --- a/src/canister/individual_user_template/can.did +++ b/src/canister/individual_user_template/can.did @@ -164,6 +164,7 @@ type IndividualUserCreatorDaoEntry = record { individual_profile_id : principal; }; type IndividualUserTemplateInitArgs = record { + proof_of_participation : opt ProofOfParticipation; known_principal_ids : opt vec record { KnownPrincipalType; principal }; version : text; url_to_send_canister_metrics_to : opt text; @@ -347,6 +348,8 @@ type PostViewStatistics = record { threshold_view_count : nat64; }; type Principals = record { principals : vec principal }; +type ProofOfChild = record { "principal" : principal; signature : blob }; +type ProofOfParticipation = record { chain : vec ProofOfChild }; type RejectionCode = variant { NoError; CanisterError; diff --git a/src/canister/individual_user_template/src/api/canister_lifecycle/init.rs b/src/canister/individual_user_template/src/api/canister_lifecycle/init.rs index 361bf9e4..9893fb00 100644 --- a/src/canister/individual_user_template/src/api/canister_lifecycle/init.rs +++ b/src/canister/individual_user_template/src/api/canister_lifecycle/init.rs @@ -26,6 +26,9 @@ fn init_impl(init_args: IndividualUserTemplateInitArgs, data: &mut CanisterData) data.version_details.version_number = init_args.upgrade_version_number.unwrap_or_default(); data.version_details.version = init_args.version; + if let Some(pop) = init_args.proof_of_participation { + data.proof_of_participation = Some(pop); + } } #[cfg(test)] @@ -60,6 +63,7 @@ mod test { "http://metrics-url.com/receive-metrics".to_string(), ), version: String::from("v1.0.0"), + proof_of_participation: None, }; let mut data = CanisterData::default(); diff --git a/src/canister/individual_user_template/src/data_model/memory.rs b/src/canister/individual_user_template/src/data_model/memory.rs index 8c7ff189..94d15035 100644 --- a/src/canister/individual_user_template/src/data_model/memory.rs +++ b/src/canister/individual_user_template/src/data_model/memory.rs @@ -19,6 +19,8 @@ const KV_STORAGE_NAMESPACE_KEY_VALUE_MEMORY: MemoryId = MemoryId::new(6); const WATCH_HISTORY_MEMORY: MemoryId = MemoryId::new(7); const SUCCESS_HISTORY_MEMORY: MemoryId = MemoryId::new(8); const TOKEN_LIST_MEMORY: MemoryId = MemoryId::new(9); +const PUBKEY_CACHE_MEMORY: MemoryId = MemoryId::new(10); + pub type Memory = VirtualMemory; thread_local! { @@ -68,6 +70,10 @@ pub fn get_token_list_memory() -> Memory { MEMORY_MANAGER.with(|m| m.borrow_mut().get(TOKEN_LIST_MEMORY)) } +pub fn get_pubkey_cache_memory() -> Memory { + MEMORY_MANAGER.with(|m| m.borrow_mut().get(PUBKEY_CACHE_MEMORY)) +} + pub fn init_memory_manager() { MEMORY_MANAGER.with(|m| { *m.borrow_mut() = MemoryManager::init_with_bucket_size(DefaultMemoryImpl::default(), 1); diff --git a/src/canister/individual_user_template/src/data_model/mod.rs b/src/canister/individual_user_template/src/data_model/mod.rs index 92685c6f..53ed21cf 100644 --- a/src/canister/individual_user_template/src/data_model/mod.rs +++ b/src/canister/individual_user_template/src/data_model/mod.rs @@ -5,7 +5,7 @@ use std::{ use candid::{Deserialize, Principal}; use ic_cdk::api::management_canister::provisional::CanisterId; -use memory::{get_success_history_memory, get_token_list_memory, get_watch_history_memory}; +use memory::{get_pubkey_cache_memory, get_success_history_memory, get_token_list_memory, get_watch_history_memory}; use serde::Serialize; use shared_utils::{ canister_specific::individual_user_template::types::{ @@ -26,12 +26,12 @@ use shared_utils::{ session::SessionType, token::TokenBalance, }, - common::types::{ + common::{participant_crypto::{PoPStore, ProofOfParticipation, PubKeyCache}, types::{ app_primitive_type::PostId, - known_principal::KnownPrincipalMap, + known_principal::{KnownPrincipalMap, KnownPrincipalType}, top_posts::{post_score_index::PostScoreIndex, post_score_index_item::PostStatus}, version_details::VersionDetails, - }, + }}, }; use self::memory::{ @@ -95,6 +95,9 @@ pub struct CanisterData { pub token_roots: ic_stable_structures::btreemap::BTreeMap, #[serde(default)] pub ml_data: MLData, + pub proof_of_participation: Option, + #[serde(skip, default = "_default_pubkey_cache")] + pub pubkey_cache: PubKeyCache, } pub fn _default_room_details( @@ -137,6 +140,10 @@ pub fn _default_success_history_v1( ic_stable_structures::btreemap::BTreeMap::init(get_success_history_memory()) } +pub fn _default_pubkey_cache() -> PubKeyCache { + PubKeyCache::init(get_pubkey_cache_memory()) +} + impl Default for CanisterData { fn default() -> Self { Self { @@ -168,6 +175,22 @@ impl Default for CanisterData { cdao_canisters: Vec::new(), token_roots: _default_token_list(), ml_data: MLData::default(), + proof_of_participation: None, + pubkey_cache: _default_pubkey_cache(), } } } + +impl PoPStore for CanisterData { + fn pubkey_cache(&self) -> &PubKeyCache { + &self.pubkey_cache + } + + fn pubkey_cache_mut(&mut self) -> &mut PubKeyCache { + &mut self.pubkey_cache + } + + fn platform_orchestrator(&self) -> Principal { + self.known_principal_ids[&KnownPrincipalType::CanisterIdPlatformOrchestrator] + } +} diff --git a/src/canister/platform_orchestrator/Cargo.toml b/src/canister/platform_orchestrator/Cargo.toml index f36f68ee..2aa00cf6 100644 --- a/src/canister/platform_orchestrator/Cargo.toml +++ b/src/canister/platform_orchestrator/Cargo.toml @@ -20,3 +20,6 @@ shared_utils = { workspace = true } [dev-dependencies] test_utils = { workspace = true } + +[features] +local = ["shared_utils/local"] diff --git a/src/canister/platform_orchestrator/src/api/canister_management/provision_subnet_orchestrator.rs b/src/canister/platform_orchestrator/src/api/canister_management/provision_subnet_orchestrator.rs index 1123be64..f7f9f4e2 100644 --- a/src/canister/platform_orchestrator/src/api/canister_management/provision_subnet_orchestrator.rs +++ b/src/canister/platform_orchestrator/src/api/canister_management/provision_subnet_orchestrator.rs @@ -15,10 +15,10 @@ use shared_utils::{ canister_specific::{ post_cache::types::arg::PostCacheInitArgs, user_index::types::args::UserIndexInitArgs, }, - common::types::{ + common::{participant_crypto::ProofOfParticipation, types::{ known_principal::{KnownPrincipalMap, KnownPrincipalType}, wasm::WasmType, - }, + }}, constant::{ GLOBAL_SUPER_ADMIN_USER_ID, NNS_CYCLE_MINTING_CANISTER, SUBNET_ORCHESTRATOR_CANISTER_INITIAL_CYCLES, YRAL_POST_CACHE_CANISTER_ID, @@ -116,11 +116,14 @@ pub async fn provision_subnet_orchestrator_canister( .insert(subnet_orchestrator_canister_id); }); + let mut proof_of_participation = ProofOfParticipation::new_for_root(); + proof_of_participation = proof_of_participation.derive_for_child(subnet_orchestrator_canister_id).await.unwrap(); let user_index_init_arg = UserIndexInitArgs { known_principal_ids: Some(known_principal_map.clone()), access_control_map: None, version: CANISTER_DATA .with_borrow(|canister_data| canister_data.version_detail.version.clone()), + proof_of_participation: Some(proof_of_participation), }; let subnet_orchestrator_install_code_arg = InstallCodeArgument { diff --git a/src/canister/platform_orchestrator/src/api/canister_management/upgrade_canisters_in_network.rs b/src/canister/platform_orchestrator/src/api/canister_management/upgrade_canisters_in_network.rs index f1661b56..72b92437 100644 --- a/src/canister/platform_orchestrator/src/api/canister_management/upgrade_canisters_in_network.rs +++ b/src/canister/platform_orchestrator/src/api/canister_management/upgrade_canisters_in_network.rs @@ -16,8 +16,7 @@ use shared_utils::{ post_cache::types::arg::PostCacheInitArgs, user_index::types::args::UserIndexInitArgs, }, common::{ - types::wasm::{CanisterWasm, WasmType}, - utils::{task::run_task_concurrently, upgrade_canister::upgrade_canister_util}, + participant_crypto::ProofOfParticipation, types::wasm::{CanisterWasm, WasmType}, utils::{task::run_task_concurrently, upgrade_canister::upgrade_canister_util} }, constant::{ POST_CACHE_CANISTER_CYCLES_RECHARGE_AMOUMT, POST_CACHE_CANISTER_CYCLES_THRESHOLD, @@ -255,6 +254,10 @@ async fn upgrade_subnet_orchestrator_canister( wasm: Vec, version: String, ) -> Result<(), String> { + // TODO: remove this when all subnet orchestrators are upgraded + let mut proof_of_participation = ProofOfParticipation::new_for_root(); + proof_of_participation = proof_of_participation.derive_for_child(canister_id).await?; + let install_code_arg = InstallCodeArgument { mode: CanisterInstallMode::Upgrade(None), canister_id, @@ -263,6 +266,7 @@ async fn upgrade_subnet_orchestrator_canister( known_principal_ids: None, access_control_map: None, version, + proof_of_participation: Some(proof_of_participation), }) .unwrap(), }; diff --git a/src/canister/post_cache/Cargo.toml b/src/canister/post_cache/Cargo.toml index 26edad01..68e0a018 100644 --- a/src/canister/post_cache/Cargo.toml +++ b/src/canister/post_cache/Cargo.toml @@ -27,3 +27,4 @@ features = ["mockdata"] [features] mockdata = [] +local = ["shared_utils/local"] diff --git a/src/canister/user_index/Cargo.toml b/src/canister/user_index/Cargo.toml index a62a0604..9030224d 100644 --- a/src/canister/user_index/Cargo.toml +++ b/src/canister/user_index/Cargo.toml @@ -22,3 +22,6 @@ futures = { workspace = true } [dev-dependencies] test_utils = { workspace = true } + +[features] +local = ["shared_utils/local"] diff --git a/src/canister/user_index/can.did b/src/canister/user_index/can.did index 8827eea5..6e5b0a75 100644 --- a/src/canister/user_index/can.did +++ b/src/canister/user_index/can.did @@ -63,6 +63,8 @@ type KnownPrincipalType = variant { UserIdGlobalSuperAdmin; }; type LogVisibility = variant { controllers; public }; +type ProofOfChild = record { "principal" : principal; signature : blob }; +type ProofOfParticipation = record { chain : vec ProofOfChild }; type QueryStats = record { response_payload_bytes_total : nat; num_instructions_total : nat; @@ -120,6 +122,7 @@ type UserAccessRole = variant { ProjectCanister; }; type UserIndexInitArgs = record { + proof_of_participation : opt ProofOfParticipation; known_principal_ids : opt vec record { KnownPrincipalType; principal }; version : text; access_control_map : opt vec record { principal; vec UserAccessRole }; diff --git a/src/canister/user_index/src/api/canister_lifecycle/init.rs b/src/canister/user_index/src/api/canister_lifecycle/init.rs index 2a4715d3..8f66256b 100644 --- a/src/canister/user_index/src/api/canister_lifecycle/init.rs +++ b/src/canister/user_index/src/api/canister_lifecycle/init.rs @@ -23,6 +23,9 @@ fn init_impl(init_args: UserIndexInitArgs, data: &mut CanisterData) { }); data.allow_upgrades_for_individual_canisters = true; data.last_run_upgrade_status.version = init_args.version; + if let Some(pop) = init_args.proof_of_participation { + data.proof_of_participation = Some(pop); + } } #[cfg(test)] @@ -31,7 +34,7 @@ mod test { use shared_utils::{ access_control::UserAccessRole, - common::types::known_principal::{KnownPrincipalMap, KnownPrincipalType}, + common::{participant_crypto::ProofOfParticipation, types::known_principal::{KnownPrincipalMap, KnownPrincipalType}}, }; use test_utils::setup::test_constants::{ get_global_super_admin_principal_id, @@ -72,6 +75,7 @@ mod test { known_principal_ids: Some(known_principal_ids), access_control_map: Some(access_control_map), version: String::from("v1.0.0"), + proof_of_participation: None, }; let mut data = CanisterData::default(); diff --git a/src/canister/user_index/src/api/upgrade_individual_user_template/update_user_index_upgrade_user_canisters_with_latest_wasm.rs b/src/canister/user_index/src/api/upgrade_individual_user_template/update_user_index_upgrade_user_canisters_with_latest_wasm.rs index b16d6b7a..20b87564 100644 --- a/src/canister/user_index/src/api/upgrade_individual_user_template/update_user_index_upgrade_user_canisters_with_latest_wasm.rs +++ b/src/canister/user_index/src/api/upgrade_individual_user_template/update_user_index_upgrade_user_canisters_with_latest_wasm.rs @@ -210,6 +210,12 @@ async fn upgrade_user_canister( version: String, individual_user_wasm: Vec, ) -> Result<(), String> { + // TODO: remove this after upgrade is executed on all individual canisters + let mut proof_of_participation = CANISTER_DATA.with_borrow(|cdata| cdata.proof_of_participation.clone()); + if let Some(pop) = proof_of_participation { + proof_of_participation = Some(pop.derive_for_child(canister_id).await?); + } + canister_management::upgrade_individual_user_canister( canister_id, CanisterInstallMode::Upgrade(None), @@ -219,6 +225,7 @@ async fn upgrade_user_canister( upgrade_version_number: None, url_to_send_canister_metrics_to: None, version, + proof_of_participation, }, individual_user_wasm, ) diff --git a/src/canister/user_index/src/api/upgrade_individual_user_template/upgrade_specific_individual_user_canister_with_latest_wasm.rs b/src/canister/user_index/src/api/upgrade_individual_user_template/upgrade_specific_individual_user_canister_with_latest_wasm.rs index 451a5f2d..af9d5965 100644 --- a/src/canister/user_index/src/api/upgrade_individual_user_template/upgrade_specific_individual_user_canister_with_latest_wasm.rs +++ b/src/canister/user_index/src/api/upgrade_individual_user_template/upgrade_specific_individual_user_canister_with_latest_wasm.rs @@ -51,6 +51,11 @@ async fn upgrade_specific_individual_user_canister_with_latest_wasm( recharge_canister_for_installing_wasm(user_canister_id).await?; + let mut proof_of_participation = CANISTER_DATA.with_borrow(|cdata| cdata.proof_of_participation.clone()); + if let Some(pop) = proof_of_participation { + proof_of_participation = Some(pop.derive_for_child(user_canister_id).await.unwrap()); + } + match canister_management::upgrade_individual_user_canister( user_canister_id, upgrade_mode.unwrap_or(CanisterInstallMode::Upgrade(None)), @@ -60,6 +65,7 @@ async fn upgrade_specific_individual_user_canister_with_latest_wasm( upgrade_version_number: Some(saved_upgrade_status.version_number + 1), url_to_send_canister_metrics_to: Some(configuration.url_to_send_canister_metrics_to), version: individual_canister_wasm.version, + proof_of_participation, }, individual_canister_wasm.wasm_blob, ) diff --git a/src/canister/user_index/src/data_model/mod.rs b/src/canister/user_index/src/data_model/mod.rs index ba9589c8..206ea0a9 100644 --- a/src/canister/user_index/src/data_model/mod.rs +++ b/src/canister/user_index/src/data_model/mod.rs @@ -6,6 +6,7 @@ use serde::Serialize; use shared_utils::canister_specific::user_index::types::{ BroadcastCallStatus, RecycleStatus, UpgradeStatus, }; +use shared_utils::common::participant_crypto::ProofOfParticipation; use shared_utils::common::types::wasm::{CanisterWasm, WasmType}; use self::memory::get_wasm_memory; @@ -38,6 +39,8 @@ pub struct CanisterData { pub recycle_status: RecycleStatus, #[serde(default)] pub last_broadcast_call_status: BroadcastCallStatus, + #[serde(default)] + pub proof_of_participation: Option, } impl Default for CanisterData { @@ -53,6 +56,7 @@ impl Default for CanisterData { backup_canister_pool: Default::default(), recycle_status: Default::default(), last_broadcast_call_status: Default::default(), + proof_of_participation: None, } } } diff --git a/src/canister/user_index/src/util/canister_management.rs b/src/canister/user_index/src/util/canister_management.rs index 316c718b..4f363a58 100644 --- a/src/canister/user_index/src/util/canister_management.rs +++ b/src/canister/user_index/src/util/canister_management.rs @@ -117,6 +117,10 @@ pub async fn install_canister_wasm( let configuration = CANISTER_DATA .with(|canister_data_ref_cell| canister_data_ref_cell.borrow().configuration.clone()); + let mut proof_of_participation = CANISTER_DATA.with_borrow(|cdata| cdata.proof_of_participation.clone()); + if let Some(pop) = proof_of_participation { + proof_of_participation = Some(pop.derive_for_child(canister_id).await.unwrap()); + } let individual_user_tempalate_init_args = IndividualUserTemplateInitArgs { profile_owner, known_principal_ids: Some(CANISTER_DATA.with(|canister_data_ref_cell| { @@ -129,6 +133,7 @@ pub async fn install_canister_wasm( upgrade_version_number: Some(0), version, url_to_send_canister_metrics_to: Some(configuration.url_to_send_canister_metrics_to), + proof_of_participation, }; // * encode argument for user canister init lifecycle method @@ -157,6 +162,10 @@ pub async fn reinstall_canister_wasm( let configuration = CANISTER_DATA .with(|canister_data_ref_cell| canister_data_ref_cell.borrow().configuration.clone()); + let mut proof_of_participation = CANISTER_DATA.with_borrow(|cdata| cdata.proof_of_participation.clone()); + if let Some(pop) = proof_of_participation { + proof_of_participation = Some(pop.derive_for_child(canister_id).await?); + } let individual_user_tempalate_init_args = IndividualUserTemplateInitArgs { profile_owner, known_principal_ids: Some(CANISTER_DATA.with(|canister_data_ref_cell| { @@ -169,6 +178,7 @@ pub async fn reinstall_canister_wasm( upgrade_version_number: Some(0), version, url_to_send_canister_metrics_to: Some(configuration.url_to_send_canister_metrics_to), + proof_of_participation, }; // * encode argument for user canister init lifecycle method diff --git a/src/lib/integration_tests/tests/hot_or_not/bet_maker_canister_tests.rs b/src/lib/integration_tests/tests/hot_or_not/bet_maker_canister_tests.rs index 0a89d015..9f694a3f 100644 --- a/src/lib/integration_tests/tests/hot_or_not/bet_maker_canister_tests.rs +++ b/src/lib/integration_tests/tests/hot_or_not/bet_maker_canister_tests.rs @@ -456,6 +456,7 @@ fn when_bet_maker_places_bet_on_a_post_it_is_assigned_a_slot_id_and_the_outcome_ known_principal_ids: None, profile_owner: None, url_to_send_canister_metrics_to: None, + proof_of_participation: None, }; pocket_ic diff --git a/src/lib/integration_tests/tests/hot_or_not/hot_or_not_timely_index_update_test.rs b/src/lib/integration_tests/tests/hot_or_not/hot_or_not_timely_index_update_test.rs index ab24672d..38f50e6a 100644 --- a/src/lib/integration_tests/tests/hot_or_not/hot_or_not_timely_index_update_test.rs +++ b/src/lib/integration_tests/tests/hot_or_not/hot_or_not_timely_index_update_test.rs @@ -79,6 +79,7 @@ fn hot_or_not_timely_update_test() { upgrade_version_number: None, url_to_send_canister_metrics_to: None, version: "1".to_string(), + proof_of_participation: None, }; let individual_template_args_bytes = encode_one(individual_template_args).unwrap(); diff --git a/src/lib/integration_tests/tests/hot_or_not/hotornot_game_simulation_test.rs b/src/lib/integration_tests/tests/hot_or_not/hotornot_game_simulation_test.rs index e81c983a..c5f241e4 100644 --- a/src/lib/integration_tests/tests/hot_or_not/hotornot_game_simulation_test.rs +++ b/src/lib/integration_tests/tests/hot_or_not/hotornot_game_simulation_test.rs @@ -90,6 +90,7 @@ fn hotornot_game_simulation_test() { upgrade_version_number: None, url_to_send_canister_metrics_to: None, version: "1".to_string(), + proof_of_participation: None, }; let individual_template_args_bytes = encode_one(individual_template_args).unwrap(); @@ -111,6 +112,7 @@ fn hotornot_game_simulation_test() { upgrade_version_number: None, url_to_send_canister_metrics_to: None, version: "1".to_string(), + proof_of_participation: None, }; let individual_template_args_bytes = encode_one(individual_template_args).unwrap(); @@ -132,6 +134,7 @@ fn hotornot_game_simulation_test() { upgrade_version_number: None, url_to_send_canister_metrics_to: None, version: "1".to_string(), + proof_of_participation: None, }; let individual_template_args_bytes = encode_one(individual_template_args).unwrap(); @@ -153,6 +156,7 @@ fn hotornot_game_simulation_test() { upgrade_version_number: None, url_to_send_canister_metrics_to: None, version: "1".to_string(), + proof_of_participation: None, }; let individual_template_args_bytes = encode_one(individual_template_args).unwrap(); diff --git a/src/lib/integration_tests/tests/hot_or_not/reconcile_scores_test.rs b/src/lib/integration_tests/tests/hot_or_not/reconcile_scores_test.rs index 3544dd64..98005334 100644 --- a/src/lib/integration_tests/tests/hot_or_not/reconcile_scores_test.rs +++ b/src/lib/integration_tests/tests/hot_or_not/reconcile_scores_test.rs @@ -91,6 +91,7 @@ fn reconcile_scores_test() { upgrade_version_number: None, url_to_send_canister_metrics_to: None, version: "1".to_string(), + proof_of_participation: None, }; let individual_template_args_bytes = encode_one(individual_template_args).unwrap(); @@ -112,6 +113,7 @@ fn reconcile_scores_test() { upgrade_version_number: None, url_to_send_canister_metrics_to: None, version: "1".to_string(), + proof_of_participation: None, }; let individual_template_args_bytes = encode_one(individual_template_args).unwrap(); @@ -133,6 +135,7 @@ fn reconcile_scores_test() { upgrade_version_number: None, url_to_send_canister_metrics_to: None, version: "1".to_string(), + proof_of_participation: None, }; let individual_template_args_bytes = encode_one(individual_template_args).unwrap(); @@ -154,6 +157,7 @@ fn reconcile_scores_test() { upgrade_version_number: None, url_to_send_canister_metrics_to: None, version: "1".to_string(), + proof_of_participation: None, }; let individual_template_args_bytes = encode_one(individual_template_args).unwrap(); @@ -963,6 +967,7 @@ fn reconcile_scores_test() { upgrade_version_number: None, url_to_send_canister_metrics_to: None, version: "1".to_string(), + proof_of_participation: None }; let individual_template_args_bytes = encode_one(individual_template_args).unwrap(); @@ -984,6 +989,7 @@ fn reconcile_scores_test() { upgrade_version_number: None, url_to_send_canister_metrics_to: None, version: "1".to_string(), + proof_of_participation: None }; let individual_template_args_bytes = encode_one(individual_template_args).unwrap(); @@ -1005,6 +1011,7 @@ fn reconcile_scores_test() { upgrade_version_number: None, url_to_send_canister_metrics_to: None, version: "1".to_string(), + proof_of_participation: None, }; let individual_template_args_bytes = encode_one(individual_template_args).unwrap(); @@ -1026,6 +1033,7 @@ fn reconcile_scores_test() { upgrade_version_number: None, url_to_send_canister_metrics_to: None, version: "1".to_string(), + proof_of_participation: None }; let individual_template_args_bytes = encode_one(individual_template_args).unwrap(); diff --git a/src/lib/integration_tests/tests/post/update_post_status_banned.rs b/src/lib/integration_tests/tests/post/update_post_status_banned.rs index b5a55b59..244f1b85 100644 --- a/src/lib/integration_tests/tests/post/update_post_status_banned.rs +++ b/src/lib/integration_tests/tests/post/update_post_status_banned.rs @@ -86,6 +86,7 @@ fn update_post_status_banned() { upgrade_version_number: None, url_to_send_canister_metrics_to: None, version: "1".to_string(), + proof_of_participation: None, }; let individual_template_args_bytes = encode_one(individual_template_args).unwrap(); diff --git a/src/lib/shared_utils/src/canister_specific/individual_user_template/types/arg.rs b/src/lib/shared_utils/src/canister_specific/individual_user_template/types/arg.rs index dd9f7aab..d5a69700 100644 --- a/src/lib/shared_utils/src/canister_specific/individual_user_template/types/arg.rs +++ b/src/lib/shared_utils/src/canister_specific/individual_user_template/types/arg.rs @@ -1,6 +1,6 @@ use candid::{CandidType, Deserialize, Principal}; -use crate::common::types::known_principal::KnownPrincipalMap; +use crate::common::{participant_crypto::ProofOfParticipation, types::known_principal::KnownPrincipalMap}; use super::hot_or_not::BetDirection; @@ -11,6 +11,7 @@ pub struct IndividualUserTemplateInitArgs { pub upgrade_version_number: Option, pub url_to_send_canister_metrics_to: Option, pub version: String, + pub proof_of_participation: Option, } #[derive(Deserialize, CandidType, Clone)] diff --git a/src/lib/shared_utils/src/canister_specific/user_index/types/args.rs b/src/lib/shared_utils/src/canister_specific/user_index/types/args.rs index 3a6182ea..12211696 100644 --- a/src/lib/shared_utils/src/canister_specific/user_index/types/args.rs +++ b/src/lib/shared_utils/src/canister_specific/user_index/types/args.rs @@ -2,11 +2,12 @@ use std::collections::HashMap; use candid::{CandidType, Deserialize, Principal}; -use crate::{access_control::UserAccessRole, common::types::known_principal::KnownPrincipalMap}; +use crate::{access_control::UserAccessRole, common::{participant_crypto::ProofOfParticipation, types::known_principal::KnownPrincipalMap}}; #[derive(Deserialize, CandidType, Default, Clone)] pub struct UserIndexInitArgs { pub known_principal_ids: Option, pub access_control_map: Option>>, - pub version: String + pub version: String, + pub proof_of_participation: Option, } diff --git a/src/lib/shared_utils/src/common/participant_crypto/mod.rs b/src/lib/shared_utils/src/common/participant_crypto/mod.rs index 887376d4..9a67ea67 100644 --- a/src/lib/shared_utils/src/common/participant_crypto/mod.rs +++ b/src/lib/shared_utils/src/common/participant_crypto/mod.rs @@ -161,19 +161,28 @@ impl ProofOfParticipation { /// Verify that the caller is a YRAL canister pub async fn verify_caller_is_participant>(&self, store: &'static LocalPoPStore) -> Result<(), String> { - let platform_orchestrator = store.with_borrow(|s| s.platform_orchestrator()); - let canister = ic_cdk::caller(); - - let mut parent = PubKeyCache::get_or_init_public_key(store, platform_orchestrator).await?; - for proof in &self.chain { - proof.verify(&parent)?; - if proof.principal == canister { - return Ok(()) - } - parent = PubKeyCache::get_or_init_public_key(store, proof.principal).await?; + #[cfg(feature = "local")] + { + // Hack: Always pass on local testing node + // a proper implementation requires deploying platform orchestrator locally + Ok(()) } + #[cfg(not(feature = "local"))] + { + let platform_orchestrator = store.with_borrow(|s| s.platform_orchestrator()); + let canister = ic_cdk::caller(); + + let mut parent = PubKeyCache::get_or_init_public_key(store, platform_orchestrator).await?; + for proof in &self.chain { + proof.verify(&parent)?; + if proof.principal == canister { + return Ok(()) + } + parent = PubKeyCache::get_or_init_public_key(store, proof.principal).await?; + } - Err("invalid proof".to_string()) + Err("invalid proof".to_string()) + } } } diff --git a/src/lib/test_utils/src/setup/env/pocket_ic_env.rs b/src/lib/test_utils/src/setup/env/pocket_ic_env.rs index 38ac23dc..8ff15066 100644 --- a/src/lib/test_utils/src/setup/env/pocket_ic_env.rs +++ b/src/lib/test_utils/src/setup/env/pocket_ic_env.rs @@ -42,6 +42,7 @@ struct AuthorizedSubnetWorks { pub fn get_new_pocket_ic_env() -> (PocketIc, KnownPrincipalMap) { let pocket_ic = PocketIcBuilder::new() .with_nns_subnet() + .with_ii_subnet() // enables tSchnorr .with_application_subnet() .with_application_subnet() .with_system_subnet() diff --git a/src/lib/test_utils/src/setup/env/v1.rs b/src/lib/test_utils/src/setup/env/v1.rs index e7bf3e45..2d3aed7a 100644 --- a/src/lib/test_utils/src/setup/env/v1.rs +++ b/src/lib/test_utils/src/setup/env/v1.rs @@ -121,6 +121,8 @@ pub fn get_initialized_env_with_provisioned_known_canisters( known_principal_ids: Some(known_principal_map_with_all_canisters.clone()), access_control_map: Some(user_index_access_control_map), version: String::from("v1.0.0"), + // V1 tests dont rely on PoP + proof_of_participation: None, }) .unwrap(), ); From 3580a3a38374e8f891161c0ca58ecc1c9cc7b8c0 Mon Sep 17 00:00:00 2001 From: rupansh Date: Sun, 29 Sep 2024 10:37:38 +0530 Subject: [PATCH 03/20] feat: add new referral flow with proof of participation --- src/canister/individual_user_template/can.did | 50 ++--- .../individual_user_template/src/api/mod.rs | 1 + .../src/api/referral/mod.rs | 94 +++++++++ .../api/token/get_rewarded_for_referral.rs | 36 +--- .../src/data_model/airdrop.rs | 15 ++ .../src/data_model/mod.rs | 5 + .../individual_user_template/src/lib.rs | 15 +- .../user_record/issue_rewards_for_referral.rs | 1 + ...five_hundred_tokens_as_referral_rewards.rs | 184 +++++++++++++++++- .../test_utils/src/setup/env/pocket_ic_env.rs | 66 ++++++- 10 files changed, 402 insertions(+), 65 deletions(-) create mode 100644 src/canister/individual_user_template/src/api/referral/mod.rs create mode 100644 src/canister/individual_user_template/src/data_model/airdrop.rs diff --git a/src/canister/individual_user_template/can.did b/src/canister/individual_user_template/can.did index a95fa68b..88dedfc6 100644 --- a/src/canister/individual_user_template/can.did +++ b/src/canister/individual_user_template/can.did @@ -379,17 +379,17 @@ type Result_18 = variant { Ok : vec text; Err : NamespaceErrors }; type Result_19 = variant { Ok : vec record { nat64; nat8 }; Err : text }; type Result_2 = variant { Ok : bool; Err : CdaoTokenError }; type Result_20 = variant { Ok; Err : MigrationErrors }; -type Result_21 = variant { Ok; Err : AirdropError }; -type Result_22 = variant { Ok : IndividualUserCreatorDaoEntry; Err : text }; -type Result_23 = variant { Committed : Committed; Aborted : record {} }; -type Result_24 = variant { Ok : Ok; Err : GovernanceError }; -type Result_25 = variant { Ok; Err : CdaoTokenError }; -type Result_26 = variant { Ok : text; Err : text }; -type Result_27 = variant { +type Result_21 = variant { Ok; Err : text }; +type Result_22 = variant { Ok; Err : AirdropError }; +type Result_23 = variant { Ok : IndividualUserCreatorDaoEntry; Err : text }; +type Result_24 = variant { Committed : Committed; Aborted : record {} }; +type Result_25 = variant { Ok : Ok; Err : GovernanceError }; +type Result_26 = variant { Ok; Err : CdaoTokenError }; +type Result_27 = variant { Ok : text; Err : text }; +type Result_28 = variant { Ok : UserProfileDetailsForFrontend; Err : UpdateProfileDetailsError; }; -type Result_28 = variant { Ok; Err : text }; type Result_29 = variant { Ok; Err : UpdateProfileSetUniqueUsernameError }; type Result_3 = variant { Ok : BettingStatus; @@ -411,10 +411,10 @@ type RoomDetails = record { }; type SessionType = variant { AnonymousSession; RegisteredSession }; type SettleNeuronsFundParticipationRequest = record { - result : opt Result_23; + result : opt Result_24; nns_proposal_id : opt nat64; }; -type SettleNeuronsFundParticipationResponse = record { result : opt Result_24 }; +type SettleNeuronsFundParticipationResponse = record { result : opt Result_25 }; type SlotDetails = record { room_details : vec record { nat64; RoomDetails } }; type SnsInitPayload = record { url : opt text; @@ -633,30 +633,34 @@ service : (IndividualUserTemplateInitArgs) -> { receive_bet_from_bet_makers_canister : (PlaceBetArg, principal) -> (Result_3); receive_bet_winnings_when_distributed : (nat64, BetOutcomeForBetMaker) -> (); receive_data_from_hotornot : (principal, nat64, vec Post) -> (Result_20); - request_airdrop : (principal, opt blob, nat, principal) -> (Result_21); + receive_reward_for_being_referred : () -> (Result_21); + receive_reward_for_referring : (ProofOfParticipation, principal) -> ( + Result_21, + ); + request_airdrop : (principal, opt blob, nat, principal) -> (Result_22); return_cycles_to_user_index_canister : (opt nat) -> (); save_snapshot_json : () -> (nat32); - send_creator_dao_stats_to_subnet_orchestrator : () -> (Result_22); + send_creator_dao_stats_to_subnet_orchestrator : () -> (Result_23); set_controller_as_subnet_orchestrator : (principal) -> (); settle_neurons_fund_participation : ( SettleNeuronsFundParticipationRequest, ) -> (SettleNeuronsFundParticipationResponse); transfer_token_to_user_canister : (principal, principal, opt blob, nat) -> ( - Result_25, + Result_26, ); transfer_tokens_and_posts : (principal, principal) -> (Result_20); - update_last_access_time : () -> (Result_26); + update_last_access_time : () -> (Result_27); update_last_canister_functionality_access_time : () -> (); - update_ml_feed_cache : (vec MLFeedCacheItem) -> (Result_26); + update_ml_feed_cache : (vec MLFeedCacheItem) -> (Result_27); update_post_add_view_details : (nat64, PostViewDetailsFromFrontend) -> (); update_post_as_ready_to_view : (nat64) -> (); update_post_increment_share_count : (nat64) -> (nat64); update_post_status : (nat64, PostStatus) -> (); update_post_toggle_like_status_by_caller : (nat64) -> (bool); update_profile_display_details : (UserProfileUpdateDetailsFromFrontend) -> ( - Result_27, + Result_28, ); - update_profile_owner : (opt principal) -> (Result_28); + update_profile_owner : (opt principal) -> (Result_21); update_profile_set_unique_username_once : (text) -> (Result_29); update_profiles_i_follow_toggle_list_with_specified_profile : ( FolloweeArg, @@ -664,13 +668,13 @@ service : (IndividualUserTemplateInitArgs) -> { update_profiles_that_follow_me_toggle_list_with_specified_profile : ( FollowerArg, ) -> (Result_8); - update_referrer_details : (UserCanisterDetails) -> (Result_26); - update_session_type : (SessionType) -> (Result_26); - update_success_history : (SuccessHistoryItemV1) -> (Result_26); - update_user_propensity : (float64) -> (Result_26); - update_watch_history : (WatchHistoryItem) -> (Result_26); + update_referrer_details : (UserCanisterDetails) -> (Result_27); + update_session_type : (SessionType) -> (Result_27); + update_success_history : (SuccessHistoryItemV1) -> (Result_27); + update_user_propensity : (float64) -> (Result_27); + update_watch_history : (WatchHistoryItem) -> (Result_27); update_well_known_principal : (KnownPrincipalType, principal) -> (); - upgrade_creator_dao_governance_canisters : (blob) -> (Result_28); + upgrade_creator_dao_governance_canisters : (blob) -> (Result_21); write_key_value_pair : (nat64, text, text) -> (Result_5); write_multiple_key_value_pairs : (nat64, vec record { text; text }) -> ( Result_6, diff --git a/src/canister/individual_user_template/src/api/mod.rs b/src/canister/individual_user_template/src/api/mod.rs index b750601a..6a39ad96 100644 --- a/src/canister/individual_user_template/src/api/mod.rs +++ b/src/canister/individual_user_template/src/api/mod.rs @@ -14,3 +14,4 @@ pub mod token; pub mod well_known_principal; pub mod cdao; pub mod device_id_management; +pub mod referral; diff --git a/src/canister/individual_user_template/src/api/referral/mod.rs b/src/canister/individual_user_template/src/api/referral/mod.rs new file mode 100644 index 00000000..a9d276d7 --- /dev/null +++ b/src/canister/individual_user_template/src/api/referral/mod.rs @@ -0,0 +1,94 @@ +use candid::Principal; +use ic_cdk::{notify, update}; +use shared_utils::{canister_specific::individual_user_template::types::session::SessionType, common::{participant_crypto::ProofOfParticipation, types::utility_token::token_event::{MintEvent, TokenEvent}, utils::system_time}}; + +use crate::{api::canister_management::update_last_access_time::update_last_canister_functionality_access_time, CANISTER_DATA, util::cycles::notify_to_recharge_canister}; + +pub(crate) fn coyn_token_reward_for_referral(referrer: Principal, referree: Principal) { + let current_time = system_time::get_current_system_time_from_ic(); + + CANISTER_DATA.with_borrow_mut(|cdata| { + let my_token_balance = &mut cdata.my_token_balance; + + let referral_reward_amount = TokenEvent::get_token_amount_for_token_event(&TokenEvent::Mint { + amount: 0, + details: MintEvent::Referral { + referee_user_principal_id: referree, + referrer_user_principal_id: referrer, + }, + timestamp: current_time, + }); + + my_token_balance.handle_token_event(TokenEvent::Mint { + amount: referral_reward_amount, + details: MintEvent::Referral { + referrer_user_principal_id: referrer, + referee_user_principal_id: referree, + }, + timestamp: current_time, + }); + }) +} + +#[update] +pub async fn receive_reward_for_being_referred() -> Result<(), String> { + notify_to_recharge_canister(); + let (pop, user_principal, referrer_details, session_type, has_parent) = CANISTER_DATA.with_borrow(|cdata| { + let profile = &cdata.profile; + ( + cdata.proof_of_participation.clone(), + profile.principal_id, + profile.referrer_details.clone(), + cdata.session_type, + cdata.airdrop.parent.is_some() + ) + }); + + let Some(pop) = pop else { + return Err("method is not available right now".into()); + }; + + let Some(user_principal) = user_principal else { + return Err("canister is not ready".into()); + }; + + let Some(referrer_details) = referrer_details else { + return Err("no referrer details found".into()); + }; + + if session_type != Some(SessionType::RegisteredSession) { + return Err("user not signed up".into()); + } + + if has_parent { + return Err("User has already claimed the reward".into()); + } + + update_last_canister_functionality_access_time(); + + coyn_token_reward_for_referral(referrer_details.profile_owner, user_principal); + + // Rollback if the notification fails + notify( + referrer_details.user_canister_id, + "receive_reward_for_referring", + (pop, user_principal) + ).map_err(|_| "failed to reward referrer".to_string()) + .unwrap(); + + Ok(()) +} + +#[update] +pub async fn receive_reward_for_referring(pop: ProofOfParticipation, referree_principal: Principal) -> Result<(), String> { + notify_to_recharge_canister(); + pop.verify_caller_is_participant(&CANISTER_DATA).await?; + + let Some(profile_owner) = CANISTER_DATA.with_borrow(|cdata| cdata.profile.principal_id) else { + return Err("canister is not ready".into()); + }; + + coyn_token_reward_for_referral(profile_owner, referree_principal); + + Ok(()) +} diff --git a/src/canister/individual_user_template/src/api/token/get_rewarded_for_referral.rs b/src/canister/individual_user_template/src/api/token/get_rewarded_for_referral.rs index 41432756..40bc9fc6 100644 --- a/src/canister/individual_user_template/src/api/token/get_rewarded_for_referral.rs +++ b/src/canister/individual_user_template/src/api/token/get_rewarded_for_referral.rs @@ -1,17 +1,12 @@ use crate::{ - api::canister_management::update_last_access_time::update_last_canister_functionality_access_time, + api::{canister_management::update_last_access_time::update_last_canister_functionality_access_time, referral::coyn_token_reward_for_referral}, util::cycles::notify_to_recharge_canister, CANISTER_DATA, }; use candid::Principal; use ic_cdk_macros::update; -use shared_utils::common::{ - types::{ - known_principal::KnownPrincipalType, - utility_token::token_event::{MintEvent, TokenEvent}, - }, - utils::system_time, -}; +use shared_utils::common::types::known_principal::KnownPrincipalType; +#[deprecated = "use new methods in crate::api::referral"] #[update] fn get_rewarded_for_referral(referrer: Principal, referree: Principal) { // * access control @@ -32,28 +27,5 @@ fn get_rewarded_for_referral(referrer: Principal, referree: Principal) { update_last_canister_functionality_access_time(); - let current_time = system_time::get_current_system_time_from_ic(); - - CANISTER_DATA.with(|canister_data_ref_cell| { - let my_token_balance = &mut canister_data_ref_cell.borrow_mut().my_token_balance; - - let referral_reward_amount = - TokenEvent::get_token_amount_for_token_event(&TokenEvent::Mint { - amount: 0, - details: MintEvent::Referral { - referrer_user_principal_id: referrer, - referee_user_principal_id: referree, - }, - timestamp: current_time, - }); - - my_token_balance.handle_token_event(TokenEvent::Mint { - amount: referral_reward_amount, - details: MintEvent::Referral { - referrer_user_principal_id: referrer, - referee_user_principal_id: referree, - }, - timestamp: current_time, - }); - }); + coyn_token_reward_for_referral(referrer, referree); } diff --git a/src/canister/individual_user_template/src/data_model/airdrop.rs b/src/canister/individual_user_template/src/data_model/airdrop.rs new file mode 100644 index 00000000..3460bb9e --- /dev/null +++ b/src/canister/individual_user_template/src/data_model/airdrop.rs @@ -0,0 +1,15 @@ +use candid::Principal; +use serde::{Serialize, Deserialize}; + +#[derive(Deserialize, Serialize)] +pub struct AirdropData { + pub parent: Option, +} + +impl Default for AirdropData { + fn default() -> Self { + Self { + parent: None, + } + } +} \ No newline at end of file diff --git a/src/canister/individual_user_template/src/data_model/mod.rs b/src/canister/individual_user_template/src/data_model/mod.rs index 53ed21cf..6e7b7063 100644 --- a/src/canister/individual_user_template/src/data_model/mod.rs +++ b/src/canister/individual_user_template/src/data_model/mod.rs @@ -3,6 +3,7 @@ use std::{ time::SystemTime, }; +use airdrop::AirdropData; use candid::{Deserialize, Principal}; use ic_cdk::api::management_canister::provisional::CanisterId; use memory::{get_pubkey_cache_memory, get_success_history_memory, get_token_list_memory, get_watch_history_memory}; @@ -43,6 +44,7 @@ use kv_storage::AppStorage; pub mod kv_storage; pub mod memory; +pub mod airdrop; #[derive(Deserialize, Serialize)] pub struct CanisterData { @@ -98,6 +100,8 @@ pub struct CanisterData { pub proof_of_participation: Option, #[serde(skip, default = "_default_pubkey_cache")] pub pubkey_cache: PubKeyCache, + #[serde(default)] + pub airdrop: AirdropData, } pub fn _default_room_details( @@ -177,6 +181,7 @@ impl Default for CanisterData { ml_data: MLData::default(), proof_of_participation: None, pubkey_cache: _default_pubkey_cache(), + airdrop: AirdropData::default(), } } } diff --git a/src/canister/individual_user_template/src/lib.rs b/src/canister/individual_user_template/src/lib.rs index 57c8637b..42833bd9 100644 --- a/src/canister/individual_user_template/src/lib.rs +++ b/src/canister/individual_user_template/src/lib.rs @@ -37,12 +37,15 @@ use shared_utils::{ }, session::SessionType, }, - common::types::{ - app_primitive_type::PostId, - http::{HttpRequest, HttpResponse}, - known_principal::KnownPrincipalType, - top_posts::post_score_index_item::PostStatus, - utility_token::token_event::TokenEvent, + common::{ + participant_crypto::ProofOfParticipation, + types::{ + app_primitive_type::PostId, + http::{HttpRequest, HttpResponse}, + known_principal::KnownPrincipalType, + top_posts::post_score_index_item::PostStatus, + utility_token::token_event::TokenEvent, + } }, pagination::PaginationError, types::canister_specific::individual_user_template::error_types::{ diff --git a/src/canister/user_index/src/api/user_record/issue_rewards_for_referral.rs b/src/canister/user_index/src/api/user_record/issue_rewards_for_referral.rs index ec2a874a..ef1a09bf 100644 --- a/src/canister/user_index/src/api/user_record/issue_rewards_for_referral.rs +++ b/src/canister/user_index/src/api/user_record/issue_rewards_for_referral.rs @@ -4,6 +4,7 @@ use ic_cdk_macros::update; use shared_utils::{canister_specific::individual_user_template::types::session::SessionType, common::utils::permissions::is_caller_global_admin}; +#[deprecated = "use new referral methods in individual user canister"] #[update(guard = "is_caller_global_admin")] pub async fn issue_rewards_for_referral(user_canister_id: Principal, referrer_principal: Principal, referee_principal: Principal) -> Result { diff --git a/src/lib/integration_tests/tests/authentication/when_a_new_user_signs_up_from_a_referral_then_the_new_user_is_given_a_thousand_utility_tokens_for_signing_up_and_the_referrer_and_referee_receive_five_hundred_tokens_as_referral_rewards.rs b/src/lib/integration_tests/tests/authentication/when_a_new_user_signs_up_from_a_referral_then_the_new_user_is_given_a_thousand_utility_tokens_for_signing_up_and_the_referrer_and_referee_receive_five_hundred_tokens_as_referral_rewards.rs index 0e26990f..0a25a7be 100644 --- a/src/lib/integration_tests/tests/authentication/when_a_new_user_signs_up_from_a_referral_then_the_new_user_is_given_a_thousand_utility_tokens_for_signing_up_and_the_referrer_and_referee_receive_five_hundred_tokens_as_referral_rewards.rs +++ b/src/lib/integration_tests/tests/authentication/when_a_new_user_signs_up_from_a_referral_then_the_new_user_is_given_a_thousand_utility_tokens_for_signing_up_and_the_referrer_and_referee_receive_five_hundred_tokens_as_referral_rewards.rs @@ -13,7 +13,7 @@ use shared_utils::{ types::canister_specific::individual_user_template::error_types::GetUserUtilityTokenTransactionHistoryError, }; use test_utils::setup::{ - env::v1::{get_initialized_env_with_provisioned_known_canisters, get_new_state_machine}, + env::{pocket_ic_env::{execute_query, execute_query_multi, execute_update, execute_update_no_res, get_new_pocket_ic_env}, v1::{get_initialized_env_with_provisioned_known_canisters, get_new_state_machine}}, test_constants::{get_mock_user_alice_principal_id, get_mock_user_bob_principal_id}, }; @@ -466,3 +466,185 @@ fn when_a_new_user_signs_up_from_a_referral_then_the_new_user_is_given_a_thousan alice_utility_token_transaction_history_after_referral ); } + +#[test] +fn when_a_new_user_signs_up_from_a_referral_then_the_new_user_is_given_a_thousand_utility_tokens_for_signing_up_and_the_referrer_and_referee_receive_five_hundred_tokens_as_referral_rewards_v2() { + let (pic, known_principals) = get_new_pocket_ic_env(); + let global_admin_principal = known_principals[&KnownPrincipalType::UserIdGlobalSuperAdmin]; + + let platform_orc = known_principals[&KnownPrincipalType::CanisterIdPlatformOrchestrator]; + let app_subnets = pic.topology().get_app_subnets(); + let user_index_res: Result = execute_update( + &pic, + global_admin_principal, + platform_orc, + "provision_subnet_orchestrator_canister", + &app_subnets[0] + ); + let user_index = user_index_res.unwrap(); + for _ in 0..30 { + pic.tick(); + } + + let alice_principal = get_mock_user_alice_principal_id(); + let alice_canister: Principal = execute_update( + &pic, + alice_principal, + user_index, + "get_requester_principals_canister_id_create_if_not_exists_and_optionally_allow_referrer", + &() + ); + + let alice_balance_after_signup: u64 = execute_query( + &pic, + Principal::anonymous(), + alice_canister, + "get_utility_token_balance", + &() + ); + assert_eq!(alice_balance_after_signup, 1000); + + let alice_txn_history_after_signup_res: Result, GetUserUtilityTokenTransactionHistoryError> = execute_query_multi( + &pic, + Principal::anonymous(), + alice_canister, + "get_user_utility_token_transaction_history_with_pagination", + (0u64, 10u64) + ); + let alice_txn_history_after_signup = alice_txn_history_after_signup_res.unwrap(); + + assert!(matches!( + alice_txn_history_after_signup.as_slice(), + [ + (_, TokenEvent::Mint { + details: MintEvent::NewUserSignup { + new_user_principal_id + }, + .. + }) + ] if *new_user_principal_id == alice_principal + )); + + let super_admin_user_id = Principal::from_text(GLOBAL_SUPER_ADMIN_USER_ID).unwrap(); + execute_update_no_res( + &pic, + super_admin_user_id, + alice_canister, + "update_session_type", + &SessionType::RegisteredSession + ); + + let bob_principal = get_mock_user_bob_principal_id(); + let bob_canister: Principal = execute_update( + &pic, + bob_principal, + user_index, + "get_requester_principals_canister_id_create_if_not_exists_and_optionally_allow_referrer", + &() + ); + + let ref_details = UserCanisterDetails { + profile_owner: alice_principal, + user_canister_id: alice_canister, + }; + execute_update_no_res( + &pic, + bob_principal, + bob_canister, + "update_referrer_details", + &ref_details + ); + + let bob_profile_details: UserProfileDetailsForFrontend = execute_query( + &pic, + Principal::anonymous(), + bob_canister, + "get_profile_details", + &() + ); + assert!(matches!( + bob_profile_details.referrer_details, + Some(details) if details == ref_details + )); + + execute_update_no_res( + &pic, + super_admin_user_id, + bob_canister, + "update_session_type", + &SessionType::RegisteredSession + ); + + execute_update_no_res( + &pic, + bob_principal, + bob_canister, + "receive_reward_for_being_referred", + &() + ); + + // wait for receive_reward to trigger on alice as well + for _ in 0..30 { + pic.tick(); + } + + let alice_balance_after_ref: u64 = execute_query( + &pic, + Principal::anonymous(), + alice_canister, + "get_utility_token_balance", + &() + ); + assert_eq!(alice_balance_after_ref, 1500); + + let bob_balance_after_ref: u64 = execute_query( + &pic, + Principal::anonymous(), + alice_canister, + "get_utility_token_balance", + &() + ); + assert_eq!(bob_balance_after_ref, 1500); + + let alice_txn_history_after_ref_res: Result, GetUserUtilityTokenTransactionHistoryError> = execute_query_multi( + &pic, + Principal::anonymous(), + alice_canister, + "get_user_utility_token_transaction_history_with_pagination", + (0u64, 10u64) + ); + let alice_txn_history_after_ref = alice_txn_history_after_ref_res.unwrap(); + assert_eq!(alice_txn_history_after_ref.len(), 2); + + assert!(matches!( + alice_txn_history_after_ref[0].1, + TokenEvent::Mint { + details: MintEvent::Referral { + referee_user_principal_id, + referrer_user_principal_id + }, + .. + } if referrer_user_principal_id == alice_principal && referee_user_principal_id == bob_principal + )); + + let bob_txn_history_after_ref_res: Result, GetUserUtilityTokenTransactionHistoryError> = execute_query_multi( + &pic, + Principal::anonymous(), + bob_canister, + "get_user_utility_token_transaction_history_with_pagination", + (0u64, 10u64) + ); + let bob_txn_history_after_ref = bob_txn_history_after_ref_res.unwrap(); + assert_eq!(bob_txn_history_after_ref.len(), 2); + + assert!(matches!( + bob_txn_history_after_ref[0].1, + TokenEvent::Mint { + details: MintEvent::Referral { + referee_user_principal_id, + referrer_user_principal_id + }, + .. + } if referrer_user_principal_id == alice_principal && referee_user_principal_id == bob_principal + )); +} diff --git a/src/lib/test_utils/src/setup/env/pocket_ic_env.rs b/src/lib/test_utils/src/setup/env/pocket_ic_env.rs index 8ff15066..6397ba63 100644 --- a/src/lib/test_utils/src/setup/env/pocket_ic_env.rs +++ b/src/lib/test_utils/src/setup/env/pocket_ic_env.rs @@ -1,9 +1,9 @@ use std::collections::{HashMap, HashSet}; -use candid::{CandidType, Principal}; -use ic_cdk::api::management_canister::main::{CanisterId, LogVisibility}; +use candid::{utils::ArgumentEncoder, CandidType, Deserialize, Principal}; +use ic_cdk::api::management_canister::main::CanisterId; use ic_ledger_types::{AccountIdentifier, BlockIndex, Tokens, DEFAULT_SUBACCOUNT}; -use pocket_ic::{management_canister::CanisterSettings, PocketIc, PocketIcBuilder}; +use pocket_ic::{management_canister::CanisterSettings, PocketIc, PocketIcBuilder, UserError, WasmResult}; use shared_utils::{ canister_specific::platform_orchestrator::types::args::PlatformOrchestratorInitArgs, common::types::{ @@ -193,3 +193,63 @@ pub fn get_new_pocket_ic_env() -> (PocketIc, KnownPrincipalMap) { (pocket_ic, known_principal) } + +pub fn execute_query Deserialize<'x>>( + pic: &PocketIc, + sender: Principal, + canister_id: CanisterId, + method_name: &str, + payload: &P, +) -> R { + unwrap_res(pic.query_call(canister_id, sender, method_name, candid::encode_one(payload).unwrap())) +} + +pub fn execute_query_multi Deserialize<'x>>( + pic: &PocketIc, + sender: Principal, + canister_id: CanisterId, + method_name: &str, + payload: P, +) -> R { + unwrap_res(pic.query_call(canister_id, sender, method_name, candid::encode_args(payload).unwrap())) +} + +pub fn execute_update Deserialize<'x>>( + pic: &PocketIc, + sender: Principal, + canister_id: CanisterId, + method_name: &str, + payload: &P, +) -> R { + unwrap_res(pic.update_call(canister_id, sender, method_name, candid::encode_one(payload).unwrap())) +} + +pub fn execute_update_multi Deserialize<'x>>( + pic: &PocketIc, + sender: Principal, + canister_id: CanisterId, + method: &str, + payload: P +) -> R { + unwrap_res(pic.update_call(canister_id, sender, method, candid::encode_args(payload).unwrap())) +} + +pub fn execute_update_no_res( + pic: &PocketIc, + sender: Principal, + canister_id: CanisterId, + method: &str, + payload: &P, +) { + let res = pic.update_call(canister_id, sender, method, candid::encode_one(payload).unwrap()); + if let WasmResult::Reject(error) = res.unwrap() { + panic!("{error}"); + } +} + +fn unwrap_res Deserialize<'x>>(response: Result) -> R { + match response.unwrap() { + WasmResult::Reply(bytes) => candid::decode_one(&bytes).unwrap(), + WasmResult::Reject(error) => panic!("{error}"), + } +} From 17c20b26afe8bc4a3575db4bd33e4a41030d72d8 Mon Sep 17 00:00:00 2001 From: rupansh Date: Wed, 23 Oct 2024 21:35:59 +0530 Subject: [PATCH 04/20] chore: rename Proof of Participant related identifiers --- .../individual_user_template/src/data_model/mod.rs | 4 ++-- .../src/common/participant_crypto/mod.rs | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/canister/individual_user_template/src/data_model/mod.rs b/src/canister/individual_user_template/src/data_model/mod.rs index 6e7b7063..5d1b827e 100644 --- a/src/canister/individual_user_template/src/data_model/mod.rs +++ b/src/canister/individual_user_template/src/data_model/mod.rs @@ -27,7 +27,7 @@ use shared_utils::{ session::SessionType, token::TokenBalance, }, - common::{participant_crypto::{PoPStore, ProofOfParticipation, PubKeyCache}, types::{ + common::{participant_crypto::{ProofOfParticipationStore, ProofOfParticipation, PubKeyCache}, types::{ app_primitive_type::PostId, known_principal::{KnownPrincipalMap, KnownPrincipalType}, top_posts::{post_score_index::PostScoreIndex, post_score_index_item::PostStatus}, @@ -186,7 +186,7 @@ impl Default for CanisterData { } } -impl PoPStore for CanisterData { +impl ProofOfParticipationStore for CanisterData { fn pubkey_cache(&self) -> &PubKeyCache { &self.pubkey_cache } diff --git a/src/lib/shared_utils/src/common/participant_crypto/mod.rs b/src/lib/shared_utils/src/common/participant_crypto/mod.rs index 9a67ea67..14f075b0 100644 --- a/src/lib/shared_utils/src/common/participant_crypto/mod.rs +++ b/src/lib/shared_utils/src/common/participant_crypto/mod.rs @@ -29,7 +29,7 @@ impl PubKeyCache { Self(StableBTreeMap::init(memory)) } - async fn get_or_init_public_key>(store: &'static LocalPoPStore, principal: Principal) -> Result { + async fn get_or_init_public_key>(store: &'static LocalPoPStore, principal: Principal) -> Result { let maybe_pk = store.with_borrow(|store| { store.pubkey_cache().0.get(&principal) }); @@ -68,12 +68,12 @@ impl PubKeyCache { } #[derive(Serialize)] -struct PoaMessage { +struct ProofOfAuthorityMsg { prefix: &'static [u8], pub child: Principal, } -impl PoaMessage { +impl ProofOfAuthorityMsg { pub fn new(child: Principal) -> Self { Self { prefix: b"CHILD", @@ -100,7 +100,7 @@ struct ProofOfChild { impl ProofOfChild { async fn new(child: Principal) -> Result { - let message = PoaMessage::new(child); + let message = ProofOfAuthorityMsg::new(child); let sign_args = ManagementCanisterSignatureRequest { message: message.serialize_cbor(), derivation_path: vec![], @@ -126,7 +126,7 @@ impl ProofOfChild { } pub fn verify(&self, parent_key: &VerifyingKey) -> Result<(), String> { - let message = PoaMessage::new(self.principal); + let message = ProofOfAuthorityMsg::new(self.principal); let message_raw = message.serialize_cbor(); let sig = Signature::from_slice(&self.signature).map_err(|_| "invalid proof".to_string())?; @@ -160,7 +160,7 @@ impl ProofOfParticipation { } /// Verify that the caller is a YRAL canister - pub async fn verify_caller_is_participant>(&self, store: &'static LocalPoPStore) -> Result<(), String> { + pub async fn verify_caller_is_participant>(&self, store: &'static LocalPoPStore) -> Result<(), String> { #[cfg(feature = "local")] { // Hack: Always pass on local testing node @@ -186,7 +186,7 @@ impl ProofOfParticipation { } } -pub trait PoPStore { +pub trait ProofOfParticipationStore { fn pubkey_cache(&self) -> &PubKeyCache; fn pubkey_cache_mut(&mut self) -> &mut PubKeyCache; From 5745f1f2ba273d7f0295cbbb549e13d0d7c5ea6e Mon Sep 17 00:00:00 2001 From: rupansh Date: Wed, 27 Nov 2024 12:26:51 +0530 Subject: [PATCH 05/20] refactor: move PoP data to heap --- .../src/data_model/memory.rs | 5 ---- .../src/data_model/mod.rs | 18 ++++++-------- .../src/common/participant_crypto/mod.rs | 24 ++++++++----------- 3 files changed, 17 insertions(+), 30 deletions(-) diff --git a/src/canister/individual_user_template/src/data_model/memory.rs b/src/canister/individual_user_template/src/data_model/memory.rs index 94d15035..ad4bb736 100644 --- a/src/canister/individual_user_template/src/data_model/memory.rs +++ b/src/canister/individual_user_template/src/data_model/memory.rs @@ -19,7 +19,6 @@ const KV_STORAGE_NAMESPACE_KEY_VALUE_MEMORY: MemoryId = MemoryId::new(6); const WATCH_HISTORY_MEMORY: MemoryId = MemoryId::new(7); const SUCCESS_HISTORY_MEMORY: MemoryId = MemoryId::new(8); const TOKEN_LIST_MEMORY: MemoryId = MemoryId::new(9); -const PUBKEY_CACHE_MEMORY: MemoryId = MemoryId::new(10); pub type Memory = VirtualMemory; @@ -70,10 +69,6 @@ pub fn get_token_list_memory() -> Memory { MEMORY_MANAGER.with(|m| m.borrow_mut().get(TOKEN_LIST_MEMORY)) } -pub fn get_pubkey_cache_memory() -> Memory { - MEMORY_MANAGER.with(|m| m.borrow_mut().get(PUBKEY_CACHE_MEMORY)) -} - pub fn init_memory_manager() { MEMORY_MANAGER.with(|m| { *m.borrow_mut() = MemoryManager::init_with_bucket_size(DefaultMemoryImpl::default(), 1); diff --git a/src/canister/individual_user_template/src/data_model/mod.rs b/src/canister/individual_user_template/src/data_model/mod.rs index 5d1b827e..3c2bf566 100644 --- a/src/canister/individual_user_template/src/data_model/mod.rs +++ b/src/canister/individual_user_template/src/data_model/mod.rs @@ -6,7 +6,7 @@ use std::{ use airdrop::AirdropData; use candid::{Deserialize, Principal}; use ic_cdk::api::management_canister::provisional::CanisterId; -use memory::{get_pubkey_cache_memory, get_success_history_memory, get_token_list_memory, get_watch_history_memory}; +use memory::{get_success_history_memory, get_token_list_memory, get_watch_history_memory}; use serde::Serialize; use shared_utils::{ canister_specific::individual_user_template::types::{ @@ -98,8 +98,8 @@ pub struct CanisterData { #[serde(default)] pub ml_data: MLData, pub proof_of_participation: Option, - #[serde(skip, default = "_default_pubkey_cache")] - pub pubkey_cache: PubKeyCache, + #[serde(skip, default)] + pub pubkey_cache: PubKeyCache, #[serde(default)] pub airdrop: AirdropData, } @@ -144,10 +144,6 @@ pub fn _default_success_history_v1( ic_stable_structures::btreemap::BTreeMap::init(get_success_history_memory()) } -pub fn _default_pubkey_cache() -> PubKeyCache { - PubKeyCache::init(get_pubkey_cache_memory()) -} - impl Default for CanisterData { fn default() -> Self { Self { @@ -180,18 +176,18 @@ impl Default for CanisterData { token_roots: _default_token_list(), ml_data: MLData::default(), proof_of_participation: None, - pubkey_cache: _default_pubkey_cache(), + pubkey_cache: PubKeyCache::default(), airdrop: AirdropData::default(), } } } -impl ProofOfParticipationStore for CanisterData { - fn pubkey_cache(&self) -> &PubKeyCache { +impl ProofOfParticipationStore for CanisterData { + fn pubkey_cache(&self) -> &PubKeyCache { &self.pubkey_cache } - fn pubkey_cache_mut(&mut self) -> &mut PubKeyCache { + fn pubkey_cache_mut(&mut self) -> &mut PubKeyCache { &mut self.pubkey_cache } diff --git a/src/lib/shared_utils/src/common/participant_crypto/mod.rs b/src/lib/shared_utils/src/common/participant_crypto/mod.rs index 14f075b0..3976cdaa 100644 --- a/src/lib/shared_utils/src/common/participant_crypto/mod.rs +++ b/src/lib/shared_utils/src/common/participant_crypto/mod.rs @@ -1,11 +1,10 @@ //! Utilities for creating and verifying proof that a given canister is a part of YRAL Backend canisters mod types; -use std::{cell::RefCell, thread::LocalKey}; +use std::{cell::RefCell, collections::HashMap, thread::LocalKey}; use candid::{CandidType, Principal}; use ed25519_dalek::{Signature, VerifyingKey, Verifier}; -use ic_stable_structures::{StableBTreeMap, Memory}; use types::{ManagementCanisterSchnorrPublicKeyReply, ManagementCanisterSchnorrPublicKeyRequest, ManagementCanisterSignatureReply, ManagementCanisterSignatureRequest, SchnorrAlgorithm, SchnorrKeyId}; use serde::{Serialize, Deserialize}; @@ -22,16 +21,13 @@ pub(crate) const THRESHOLD_SCHNORR_KEY: &str = { pub(crate) type LocalPoPStore = LocalKey>; -pub struct PubKeyCache(StableBTreeMap, M>); +#[derive(Default, Serialize, Deserialize)] +pub struct PubKeyCache(HashMap>); -impl PubKeyCache { - pub fn init(memory: M) -> Self { - Self(StableBTreeMap::init(memory)) - } - - async fn get_or_init_public_key>(store: &'static LocalPoPStore, principal: Principal) -> Result { +impl PubKeyCache { + async fn get_or_init_public_key(store: &'static LocalPoPStore, principal: Principal) -> Result { let maybe_pk = store.with_borrow(|store| { - store.pubkey_cache().0.get(&principal) + store.pubkey_cache().0.get(&principal).cloned() }); if let Some(pk) = maybe_pk { return VerifyingKey::try_from(pk.as_slice()) @@ -160,7 +156,7 @@ impl ProofOfParticipation { } /// Verify that the caller is a YRAL canister - pub async fn verify_caller_is_participant>(&self, store: &'static LocalPoPStore) -> Result<(), String> { + pub async fn verify_caller_is_participant(&self, store: &'static LocalPoPStore) -> Result<(), String> { #[cfg(feature = "local")] { // Hack: Always pass on local testing node @@ -186,10 +182,10 @@ impl ProofOfParticipation { } } -pub trait ProofOfParticipationStore { - fn pubkey_cache(&self) -> &PubKeyCache; +pub trait ProofOfParticipationStore { + fn pubkey_cache(&self) -> &PubKeyCache; - fn pubkey_cache_mut(&mut self) -> &mut PubKeyCache; + fn pubkey_cache_mut(&mut self) -> &mut PubKeyCache; fn platform_orchestrator(&self) -> Principal; } \ No newline at end of file From 7f03139ea29e69b5e1c750b0b2dc59a0f64c3b25 Mon Sep 17 00:00:00 2001 From: rupansh Date: Thu, 24 Oct 2024 13:31:09 +0530 Subject: [PATCH 06/20] fix: update post_upgrade hooks for individual user and user index --- .../src/api/canister_lifecycle/post_upgrade.rs | 6 +++++- .../src/api/canister_lifecycle/post_upgrade.rs | 9 ++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/canister/individual_user_template/src/api/canister_lifecycle/post_upgrade.rs b/src/canister/individual_user_template/src/api/canister_lifecycle/post_upgrade.rs index f0943231..7197f3bf 100644 --- a/src/canister/individual_user_template/src/api/canister_lifecycle/post_upgrade.rs +++ b/src/canister/individual_user_template/src/api/canister_lifecycle/post_upgrade.rs @@ -66,6 +66,10 @@ fn save_upgrade_args_to_memory() { .configuration .url_to_send_canister_metrics_to = Some(url_to_send_canister_metrics_to); } + + if let Some(pop) = upgrade_args.proof_of_participation { + canister_data_ref_cell.proof_of_participation = Some(pop); + } }); } @@ -76,4 +80,4 @@ fn migrate_excessive_tokens(){ canister_data_ref_cell.my_token_balance.utility_token_balance = 1000; } }); -} \ No newline at end of file +} diff --git a/src/canister/user_index/src/api/canister_lifecycle/post_upgrade.rs b/src/canister/user_index/src/api/canister_lifecycle/post_upgrade.rs index 02a9fc63..9d6ee35b 100644 --- a/src/canister/user_index/src/api/canister_lifecycle/post_upgrade.rs +++ b/src/canister/user_index/src/api/canister_lifecycle/post_upgrade.rs @@ -19,8 +19,8 @@ fn post_upgrade() { fn update_version_from_args() { let (upgrade_args,) = ic_cdk::api::call::arg_data::<(UserIndexInitArgs,)>(ArgDecoderConfig::default()); - CANISTER_DATA.with(|canister_data_ref| { - let last_upgrade_status = canister_data_ref.borrow().last_run_upgrade_status.clone(); + CANISTER_DATA.with_borrow_mut(|canister_data_ref| { + let last_upgrade_status = canister_data_ref.last_run_upgrade_status.clone(); let upgrade_status = UpgradeStatus { last_run_on: system_time::get_current_system_time_from_ic(), failed_canister_ids: vec![], @@ -28,7 +28,10 @@ fn update_version_from_args() { successful_upgrade_count: 0, version: upgrade_args.version, }; - canister_data_ref.borrow_mut().last_run_upgrade_status = upgrade_status; + canister_data_ref.last_run_upgrade_status = upgrade_status; + if let Some(pop) = upgrade_args.proof_of_participation { + canister_data_ref.proof_of_participation = Some(pop); + } }) } From 8f48fda0fe78667cd3a3277e4e109524ab863d3e Mon Sep 17 00:00:00 2001 From: rupansh Date: Thu, 24 Oct 2024 13:31:16 +0530 Subject: [PATCH 07/20] refactor: use `DFX_NETWORK` instead of feature flag for local network detection --- .../all_canisters_test_suite_on_any_push.yml | 8 +-- .../individual_user_template/Cargo.toml | 2 - src/canister/platform_orchestrator/Cargo.toml | 2 - src/canister/post_cache/Cargo.toml | 1 - src/canister/user_index/Cargo.toml | 2 - src/lib/shared_utils/Cargo.toml | 1 - .../src/common/participant_crypto/mod.rs | 56 ++++++++++--------- 7 files changed, 34 insertions(+), 38 deletions(-) diff --git a/.github/workflows/all_canisters_test_suite_on_any_push.yml b/.github/workflows/all_canisters_test_suite_on_any_push.yml index c5ffd7ca..5e3e26da 100644 --- a/.github/workflows/all_canisters_test_suite_on_any_push.yml +++ b/.github/workflows/all_canisters_test_suite_on_any_push.yml @@ -49,19 +49,19 @@ jobs: run: nix-shell --run "dfx stop" - name: Build platform_orchestrator canister run: | - nix-shell --run "dfx build platform_orchestrator" + nix-shell --run "dfx build platform_orchestrator --ic" gzip -f -1 ./target/wasm32-unknown-unknown/release/platform_orchestrator.wasm - name: Build individual_user_template canister run: | - nix-shell --run "dfx build individual_user_template" + nix-shell --run "dfx build individual_user_template --ic" gzip -f -1 ./target/wasm32-unknown-unknown/release/individual_user_template.wasm - name: Build user_index canister run: | - nix-shell --run "dfx build user_index" + nix-shell --run "dfx build user_index --ic" gzip -f -1 ./target/wasm32-unknown-unknown/release/user_index.wasm - name: Build post_cache canister run: | - nix-shell --run "dfx build post_cache" + nix-shell --run "dfx build post_cache --ic" gzip -f -1 ./target/wasm32-unknown-unknown/release/post_cache.wasm - name: Run canister test suite env: diff --git a/src/canister/individual_user_template/Cargo.toml b/src/canister/individual_user_template/Cargo.toml index baad2d67..a7ae276f 100644 --- a/src/canister/individual_user_template/Cargo.toml +++ b/src/canister/individual_user_template/Cargo.toml @@ -37,5 +37,3 @@ hex = "0.4.3" [dev-dependencies] test_utils = { workspace = true } -[features] -local = ["shared_utils/local"] diff --git a/src/canister/platform_orchestrator/Cargo.toml b/src/canister/platform_orchestrator/Cargo.toml index 2aa00cf6..eccfa50d 100644 --- a/src/canister/platform_orchestrator/Cargo.toml +++ b/src/canister/platform_orchestrator/Cargo.toml @@ -21,5 +21,3 @@ shared_utils = { workspace = true } [dev-dependencies] test_utils = { workspace = true } -[features] -local = ["shared_utils/local"] diff --git a/src/canister/post_cache/Cargo.toml b/src/canister/post_cache/Cargo.toml index 68e0a018..26edad01 100644 --- a/src/canister/post_cache/Cargo.toml +++ b/src/canister/post_cache/Cargo.toml @@ -27,4 +27,3 @@ features = ["mockdata"] [features] mockdata = [] -local = ["shared_utils/local"] diff --git a/src/canister/user_index/Cargo.toml b/src/canister/user_index/Cargo.toml index 9030224d..d1bd5d66 100644 --- a/src/canister/user_index/Cargo.toml +++ b/src/canister/user_index/Cargo.toml @@ -23,5 +23,3 @@ futures = { workspace = true } [dev-dependencies] test_utils = { workspace = true } -[features] -local = ["shared_utils/local"] diff --git a/src/lib/shared_utils/Cargo.toml b/src/lib/shared_utils/Cargo.toml index 5250a20c..065ec574 100644 --- a/src/lib/shared_utils/Cargo.toml +++ b/src/lib/shared_utils/Cargo.toml @@ -25,4 +25,3 @@ pprof = { version = "0.13", features = ["flamegraph"] } [features] mockdata = [] -local = [] diff --git a/src/lib/shared_utils/src/common/participant_crypto/mod.rs b/src/lib/shared_utils/src/common/participant_crypto/mod.rs index 3976cdaa..bda08a4b 100644 --- a/src/lib/shared_utils/src/common/participant_crypto/mod.rs +++ b/src/lib/shared_utils/src/common/participant_crypto/mod.rs @@ -8,15 +8,22 @@ use ed25519_dalek::{Signature, VerifyingKey, Verifier}; use types::{ManagementCanisterSchnorrPublicKeyReply, ManagementCanisterSchnorrPublicKeyRequest, ManagementCanisterSignatureReply, ManagementCanisterSignatureRequest, SchnorrAlgorithm, SchnorrKeyId}; use serde::{Serialize, Deserialize}; -pub(crate) const THRESHOLD_SCHNORR_KEY: &str = { - #[cfg(feature = "local")] - { - "dfx_test_key" - } - #[cfg(not(feature = "local"))] - { - "key_1" +const fn is_local() -> bool { + let Some(network) = std::option_env!("DFX_NETWORK") else { + return true; + }; + + match network.as_bytes() { + b"ic" => false, + b"local" => true, + _ => panic!("unknown `DFX_NETWORK`"), } +} + +pub(crate) const THRESHOLD_SCHNORR_KEY: &str = if is_local() { + "dfx_test_key" +} else { + "key_1" }; pub(crate) type LocalPoPStore = LocalKey>; @@ -157,28 +164,25 @@ impl ProofOfParticipation { /// Verify that the caller is a YRAL canister pub async fn verify_caller_is_participant(&self, store: &'static LocalPoPStore) -> Result<(), String> { - #[cfg(feature = "local")] - { + if is_local() { // Hack: Always pass on local testing node // a proper implementation requires deploying platform orchestrator locally - Ok(()) + return Ok(()) } - #[cfg(not(feature = "local"))] - { - let platform_orchestrator = store.with_borrow(|s| s.platform_orchestrator()); - let canister = ic_cdk::caller(); - - let mut parent = PubKeyCache::get_or_init_public_key(store, platform_orchestrator).await?; - for proof in &self.chain { - proof.verify(&parent)?; - if proof.principal == canister { - return Ok(()) - } - parent = PubKeyCache::get_or_init_public_key(store, proof.principal).await?; - } - Err("invalid proof".to_string()) + let platform_orchestrator = store.with_borrow(|s| s.platform_orchestrator()); + let canister = ic_cdk::caller(); + + let mut parent = PubKeyCache::get_or_init_public_key(store, platform_orchestrator).await?; + for proof in &self.chain { + proof.verify(&parent)?; + if proof.principal == canister { + return Ok(()) + } + parent = PubKeyCache::get_or_init_public_key(store, proof.principal).await?; } + + Err("invalid proof".to_string()) } } @@ -188,4 +192,4 @@ pub trait ProofOfParticipationStore { fn pubkey_cache_mut(&mut self) -> &mut PubKeyCache; fn platform_orchestrator(&self) -> Principal; -} \ No newline at end of file +} From b1626a6d17c5cf930abe2060f1bc42623a70c762 Mon Sep 17 00:00:00 2001 From: rupansh Date: Thu, 24 Oct 2024 20:56:23 +0530 Subject: [PATCH 08/20] chore(scripts/local_deploy/install_all_canisters): remove unnecessary rebuild steps --- .../canisters/local_deploy/install_all_canisters.sh | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/scripts/canisters/local_deploy/install_all_canisters.sh b/scripts/canisters/local_deploy/install_all_canisters.sh index b89a0e6d..db0069d7 100755 --- a/scripts/canisters/local_deploy/install_all_canisters.sh +++ b/scripts/canisters/local_deploy/install_all_canisters.sh @@ -27,21 +27,8 @@ dfx canister create --no-wallet post_cache dfx canister create --no-wallet user_index dfx canister create --no-wallet platform_orchestrator -# HACK: dfx doesn't support specifying feature flags.... -rebuild_canister() { - cargo build --target wasm32-unknown-unknown --release --features local -p $1 --locked - wasm-opt ./target/wasm32-unknown-unknown/release/$1.wasm -o ./target/wasm32-unknown-unknown/release/$1.wasm -Oz - gzip -fk -1 ./target/wasm32-unknown-unknown/release/$1.wasm - cp ./target/wasm32-unknown-unknown/release/$1.wasm.gz .dfx/local/canisters/$1/$1.wasm.gz -} - scripts/candid_generator.sh -rebuild_canister individual_user_template -rebuild_canister user_index -rebuild_canister post_cache -rebuild_canister platform_orchestrator - if [[ $skip_test != true ]] then cargo test From bc8334f644e329d999d6e262a8ea450d87178cf2 Mon Sep 17 00:00:00 2001 From: rupansh Date: Thu, 24 Oct 2024 21:10:25 +0530 Subject: [PATCH 09/20] chore: update nix env --- default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/default.nix b/default.nix index ac65b3a9..100542d4 100644 --- a/default.nix +++ b/default.nix @@ -3,7 +3,7 @@ let rev = "1c3a28d84f970e7774af04372ade06399add182e"; nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/archive/${rev}.tar.gz"; pkgs = import nixpkgs { }; - dfx-env = import (builtins.fetchTarball "https://github.com/ninegua/ic-nix/releases/download/20240610/dfx-env.tar.gz") { version = "20240610"; inherit pkgs; }; + dfx-env = import (builtins.fetchTarball "https://github.com/ninegua/ic-nix/releases/download/20240924/dfx-env.tar.gz") { version = "20240924"; inherit pkgs; }; in dfx-env.overrideAttrs (old: { nativeBuildInputs = with pkgs; old.nativeBuildInputs ++ From ef9d96eb79932398609e47f38d1f389a87fee77c Mon Sep 17 00:00:00 2001 From: rupansh Date: Sun, 3 Nov 2024 05:51:24 +0530 Subject: [PATCH 10/20] feat: add proof of participation merkleization --- Cargo.lock | 199 +++-- src/canister/individual_user_template/can.did | 7 +- .../api/canister_lifecycle/post_upgrade.rs | 13 +- .../deregister_subnet_orchestrator.rs | 10 +- .../get_all_subnet_orchestrators.rs | 2 +- .../canister_management/known_principal.rs | 2 +- ...pgrade_creator_dao_governance_canisters.rs | 2 +- ...populate_known_principal_for_all_subnet.rs | 2 +- .../provision_subnet_orchestrator.rs | 15 +- .../register_new_subnet_orhestrator.rs | 15 +- ..._upgrades_for_individual_user_canisters.rs | 2 +- .../update_canisters_last_access_time.rs | 2 +- ...date_profile_owner_for_individual_users.rs | 2 +- .../update_timers_for_hon_game.rs | 2 +- .../upgrade_canisters_in_network.rs | 8 +- .../upgrade_specific_individual_canister.rs | 2 +- ...aiming_cycles_from_individual_canisters.rs | 2 +- ...ycles_from_subnet_orchestrator_canister.rs | 2 +- ...ollect_creator_dao_stats_in_the_network.rs | 2 +- .../src/data_model/mod.rs | 57 +- .../utils/registered_subnet_orchestrator.rs | 2 +- src/canister/user_index/can.did | 7 +- .../create_pool_of_available_canisters.rs | 81 +- .../get_subnet_backup_capacity.rs | 3 +- ...upgrade_user_canisters_with_latest_wasm.rs | 9 +- ...dividual_user_canister_with_latest_wasm.rs | 9 +- ...ot_exists_and_optionally_allow_referrer.rs | 12 +- .../api/user_signup/are_signups_enabled.rs | 8 +- src/canister/user_index/src/data_model/mod.rs | 46 +- .../src/util/canister_management.rs | 17 +- .../util/types/individual_user_canister.rs | 6 +- src/lib/shared_utils/Cargo.toml | 11 +- .../participant_crypto/merkle/backing.rs | 113 +++ .../participant_crypto/merkle/layout.rs | 708 ++++++++++++++++++ .../common/participant_crypto/merkle/mod.rs | 103 +++ .../src/common/participant_crypto/mod.rs | 107 ++- 36 files changed, 1399 insertions(+), 191 deletions(-) create mode 100644 src/lib/shared_utils/src/common/participant_crypto/merkle/backing.rs create mode 100644 src/lib/shared_utils/src/common/participant_crypto/merkle/layout.rs create mode 100644 src/lib/shared_utils/src/common/participant_crypto/merkle/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 432176c3..da16a733 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -132,6 +132,12 @@ version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10f00e1f6e58a40e807377c75c6a7f97bf9044fab57816f2414e6f5f4499d7b8" +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + [[package]] name = "arrayvec" version = "0.5.2" @@ -168,7 +174,7 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", "synstructure", ] @@ -180,7 +186,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -191,7 +197,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -318,6 +324,19 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake3" +version = "1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8ee0c1824c4dea5b5f81736aff91bae041d2c07ee1192bec91054e10e3e601e" +dependencies = [ + "arrayref", + "arrayvec 0.7.6", + "cc", + "cfg-if", + "constant_time_eq", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -347,7 +366,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", "syn_derive", ] @@ -422,6 +441,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + [[package]] name = "byte-unit" version = "4.0.19" @@ -525,7 +550,7 @@ dependencies = [ "lazy_static", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -672,7 +697,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -738,6 +763,12 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + [[package]] name = "convert_case" version = "0.4.0" @@ -895,7 +926,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -984,7 +1015,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -1006,7 +1037,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core 0.20.10", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -1074,7 +1105,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -1158,7 +1189,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -1197,6 +1228,12 @@ dependencies = [ "signature", ] +[[package]] +name = "ed25519-compact" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9b3460f44bea8cd47f45a0c70892f1eff856d97cd55358b2f73f663789f6190" + [[package]] name = "ed25519-dalek" version = "2.1.1" @@ -1417,7 +1454,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -1541,6 +1578,21 @@ dependencies = [ "crunchy", ] +[[package]] +name = "hash-db" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e7d7786361d7425ae2fe4f9e407eb0efaa0840f5212d109cc018c40c35c6ab4" + +[[package]] +name = "hash256-std-hasher" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" +dependencies = [ + "crunchy", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1947,7 +1999,7 @@ dependencies = [ "quote", "serde", "serde_tokenstream 0.2.2", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -1961,7 +2013,7 @@ dependencies = [ "quote", "serde", "serde_tokenstream 0.2.2", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -3604,7 +3656,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -3650,6 +3702,17 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -3965,6 +4028,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memory-db" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808b50db46293432a45e63bc15ea51e0ab4c0a1647b8eb114e31a3e698dd6fbe" +dependencies = [ + "hash-db", +] + [[package]] name = "merlin" version = "3.0.0" @@ -4043,7 +4115,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -4225,6 +4297,18 @@ dependencies = [ "group", ] +[[package]] +name = "parity-scale-codec" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be4817d39f3272f69c59fe05d0535ae6456c2dc2fa1ba02910296c7e0a5c590" +dependencies = [ + "arrayvec 0.7.6", + "byte-slice-cast", + "impl-trait-for-tuples", + "rustversion", +] + [[package]] name = "parking_lot" version = "0.12.3" @@ -4309,7 +4393,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -4361,7 +4445,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -4538,7 +4622,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" dependencies = [ "proc-macro2", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -4601,9 +4685,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -4647,7 +4731,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.77", + "syn 2.0.89", "tempfile", ] @@ -4661,7 +4745,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -5217,7 +5301,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -5320,7 +5404,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -5331,7 +5415,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -5364,7 +5448,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -5387,7 +5471,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -5474,20 +5558,27 @@ dependencies = [ name = "shared_utils" version = "0.1.0" dependencies = [ + "blake3", "candid", "ciborium", - "ed25519-dalek", + "ed25519-compact", "futures", + "hash-db", + "hash256-std-hasher", "ic-cdk 0.15.1", "ic-cdk-timers", "ic-stable-structures", "icrc-ledger-types 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "memory-db", + "parity-scale-codec", "pprof", "rmp-serde", "serde", "serde_bytes", "serde_json_any_key", "test_utils", + "trie-db", + "trie-root", ] [[package]] @@ -5648,7 +5739,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -5693,9 +5784,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" dependencies = [ "proc-macro2", "quote", @@ -5711,7 +5802,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -5731,7 +5822,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -5803,7 +5894,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -5904,7 +5995,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -6018,7 +6109,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -6073,6 +6164,26 @@ dependencies = [ "tracing-serde", ] +[[package]] +name = "trie-db" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c992b4f40c234a074d48a757efeabb1a6be88af84c0c23f7ca158950cb0ae7f" +dependencies = [ + "hash-db", + "log", + "smallvec", +] + +[[package]] +name = "trie-root" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4ed310ef5ab98f5fa467900ed906cb9232dd5376597e00fd4cba2a449d06c0b" +dependencies = [ + "hash-db", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -6284,7 +6395,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", "wasm-bindgen-shared", ] @@ -6318,7 +6429,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6624,7 +6735,7 @@ checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", "synstructure", ] @@ -6646,7 +6757,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -6666,7 +6777,7 @@ checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", "synstructure", ] @@ -6687,7 +6798,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -6709,5 +6820,5 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] diff --git a/src/canister/individual_user_template/can.did b/src/canister/individual_user_template/can.did index 88dedfc6..dac5b6ca 100644 --- a/src/canister/individual_user_template/can.did +++ b/src/canister/individual_user_template/can.did @@ -348,7 +348,12 @@ type PostViewStatistics = record { threshold_view_count : nat64; }; type Principals = record { principals : vec principal }; -type ProofOfChild = record { "principal" : principal; signature : blob }; +type ProofOfChild = record { + proof_of_inclusion : vec blob; + "principal" : principal; + children_proof : ProofOfChildren; +}; +type ProofOfChildren = record { signature : blob; merkle_root : blob }; type ProofOfParticipation = record { chain : vec ProofOfChild }; type RejectionCode = variant { NoError; diff --git a/src/canister/platform_orchestrator/src/api/canister_lifecycle/post_upgrade.rs b/src/canister/platform_orchestrator/src/api/canister_lifecycle/post_upgrade.rs index 804c1302..bff21a44 100644 --- a/src/canister/platform_orchestrator/src/api/canister_lifecycle/post_upgrade.rs +++ b/src/canister/platform_orchestrator/src/api/canister_lifecycle/post_upgrade.rs @@ -3,7 +3,7 @@ use ic_cdk::api::call::ArgDecoderConfig; use ic_cdk_macros::post_upgrade; use ic_stable_structures::Memory; use shared_utils::{ - canister_specific::platform_orchestrator::types::args::PlatformOrchestratorInitArgs, + canister_specific::{platform_orchestrator::types::args::PlatformOrchestratorInitArgs, post_cache}, common::utils::system_time, }; @@ -13,6 +13,7 @@ use crate::{data_model::memory, CANISTER_DATA}; pub fn post_upgrade() { restore_data_from_stable_memory(); update_version_from_args(); + initialize_children_merkle(); } fn restore_data_from_stable_memory() { @@ -38,3 +39,13 @@ fn update_version_from_args() { canister_data.version_detail.last_update_on = system_time::get_current_system_time(); }) } + +// TODO: remove this once the upgrade is complete +fn initialize_children_merkle() { + CANISTER_DATA.with_borrow_mut(|cdata| { + let mut children: Vec<_> = cdata.subnet_orchestrators().iter().copied().collect(); + children.extend(cdata.post_cache_orchestrators()); + + cdata.children_merkle.insert_children(children) + }); +} diff --git a/src/canister/platform_orchestrator/src/api/canister_management/deregister_subnet_orchestrator.rs b/src/canister/platform_orchestrator/src/api/canister_management/deregister_subnet_orchestrator.rs index a9b7343a..c10b2f87 100644 --- a/src/canister/platform_orchestrator/src/api/canister_management/deregister_subnet_orchestrator.rs +++ b/src/canister/platform_orchestrator/src/api/canister_management/deregister_subnet_orchestrator.rs @@ -6,14 +6,6 @@ use crate::{guard::is_caller::is_caller_global_admin_or_controller, CANISTER_DAT #[update(guard = "is_caller_global_admin_or_controller")] fn deregister_subnet_orchestrator(canister_id: Principal, remove_it_completely: bool) { CANISTER_DATA.with_borrow_mut(|canister_data| { - canister_data - .subet_orchestrator_with_capacity_left - .remove(&canister_id); - - if remove_it_completely { - canister_data - .all_subnet_orchestrator_canisters_list - .remove(&canister_id); - } + canister_data.remove_subnet_orchestrator(canister_id, remove_it_completely); }); } diff --git a/src/canister/platform_orchestrator/src/api/canister_management/get_all_subnet_orchestrators.rs b/src/canister/platform_orchestrator/src/api/canister_management/get_all_subnet_orchestrators.rs index 0b1a61fd..d289022d 100644 --- a/src/canister/platform_orchestrator/src/api/canister_management/get_all_subnet_orchestrators.rs +++ b/src/canister/platform_orchestrator/src/api/canister_management/get_all_subnet_orchestrators.rs @@ -8,7 +8,7 @@ use crate::CANISTER_DATA; #[query] fn get_all_subnet_orchestrators() -> Vec { CANISTER_DATA.with_borrow(|canister_data| { - let canisters = canister_data.all_subnet_orchestrator_canisters_list.iter().map(|canister_id| {*canister_id}).collect::>(); + let canisters = canister_data.subnet_orchestrators().iter().map(|canister_id| {*canister_id}).collect::>(); canisters }) } \ No newline at end of file diff --git a/src/canister/platform_orchestrator/src/api/canister_management/known_principal.rs b/src/canister/platform_orchestrator/src/api/canister_management/known_principal.rs index f055dd95..f5d7a0f5 100644 --- a/src/canister/platform_orchestrator/src/api/canister_management/known_principal.rs +++ b/src/canister/platform_orchestrator/src/api/canister_management/known_principal.rs @@ -75,7 +75,7 @@ async fn issue_update_known_principal_for_all_subnet( ) { let subnet_list: Vec = CANISTER_DATA.with_borrow(|canister_data| { canister_data - .all_subnet_orchestrator_canisters_list + .subnet_orchestrators() .iter() .copied() .collect() diff --git a/src/canister/platform_orchestrator/src/api/canister_management/notify_all_individual_canisters_to_upgrade_creator_dao_governance_canisters.rs b/src/canister/platform_orchestrator/src/api/canister_management/notify_all_individual_canisters_to_upgrade_creator_dao_governance_canisters.rs index 4849934c..e221bd53 100644 --- a/src/canister/platform_orchestrator/src/api/canister_management/notify_all_individual_canisters_to_upgrade_creator_dao_governance_canisters.rs +++ b/src/canister/platform_orchestrator/src/api/canister_management/notify_all_individual_canisters_to_upgrade_creator_dao_governance_canisters.rs @@ -7,7 +7,7 @@ pub fn notify_all_individual_canisters_to_upgrade_creator_dao_governance_caniste wasm_module: Vec, ) -> Result<(), String> { CANISTER_DATA.with_borrow(|canister_data| { - let subnet_orchestrators = canister_data.all_subnet_orchestrator_canisters_list.iter(); + let subnet_orchestrators = canister_data.subnet_orchestrators().iter(); for canister_id in subnet_orchestrators { ic_cdk::notify::<_>( diff --git a/src/canister/platform_orchestrator/src/api/canister_management/populate_known_principal_for_all_subnet.rs b/src/canister/platform_orchestrator/src/api/canister_management/populate_known_principal_for_all_subnet.rs index 4646a511..b1c41f59 100644 --- a/src/canister/platform_orchestrator/src/api/canister_management/populate_known_principal_for_all_subnet.rs +++ b/src/canister/platform_orchestrator/src/api/canister_management/populate_known_principal_for_all_subnet.rs @@ -10,7 +10,7 @@ use crate::{guard::is_caller::is_caller_global_admin_or_controller, CANISTER_DAT #[update(guard = "is_caller_global_admin_or_controller")] async fn populate_known_principal_for_all_subnet() { let subnet_orchestrators: Vec = CANISTER_DATA.with_borrow(|canister_data| { - canister_data.all_subnet_orchestrator_canisters_list.iter().copied().collect() + canister_data.subnet_orchestrators().iter().copied().collect() }); for subnet_id in subnet_orchestrators { diff --git a/src/canister/platform_orchestrator/src/api/canister_management/provision_subnet_orchestrator.rs b/src/canister/platform_orchestrator/src/api/canister_management/provision_subnet_orchestrator.rs index f7f9f4e2..b1cc86f7 100644 --- a/src/canister/platform_orchestrator/src/api/canister_management/provision_subnet_orchestrator.rs +++ b/src/canister/platform_orchestrator/src/api/canister_management/provision_subnet_orchestrator.rs @@ -105,19 +105,14 @@ pub async fn provision_subnet_orchestrator_canister( ); CANISTER_DATA.with_borrow_mut(|canister_data| { - canister_data - .all_post_cache_orchestrator_list - .insert(post_cache_canister_id); - canister_data - .all_subnet_orchestrator_canisters_list - .insert(subnet_orchestrator_canister_id); - canister_data - .subet_orchestrator_with_capacity_left - .insert(subnet_orchestrator_canister_id); + canister_data.insert_subnet_orchestrator_and_post_cache( + subnet_orchestrator_canister_id, + post_cache_canister_id + ); }); let mut proof_of_participation = ProofOfParticipation::new_for_root(); - proof_of_participation = proof_of_participation.derive_for_child(subnet_orchestrator_canister_id).await.unwrap(); + proof_of_participation = proof_of_participation.derive_for_child(&CANISTER_DATA, subnet_orchestrator_canister_id).await.unwrap(); let user_index_init_arg = UserIndexInitArgs { known_principal_ids: Some(known_principal_map.clone()), access_control_map: None, diff --git a/src/canister/platform_orchestrator/src/api/canister_management/register_new_subnet_orhestrator.rs b/src/canister/platform_orchestrator/src/api/canister_management/register_new_subnet_orhestrator.rs index 3bad50e9..37e5708c 100644 --- a/src/canister/platform_orchestrator/src/api/canister_management/register_new_subnet_orhestrator.rs +++ b/src/canister/platform_orchestrator/src/api/canister_management/register_new_subnet_orhestrator.rs @@ -33,7 +33,7 @@ async fn register_new_subnet_orchestrator( if let Some(first_subnet_orchestrator) = CANISTER_DATA.with_borrow(|canister_data| { canister_data - .all_subnet_orchestrator_canisters_list + .subnet_orchestrators() .iter() .next() .copied() @@ -59,15 +59,10 @@ async fn register_new_subnet_orchestrator( } CANISTER_DATA.with_borrow_mut(|canister_data| { - canister_data - .all_subnet_orchestrator_canisters_list - .insert(new_subnet_orchestrator_caniter_id); - - if subnet_is_available_for_provisioning_individual_canister { - canister_data - .subet_orchestrator_with_capacity_left - .insert(new_subnet_orchestrator_caniter_id); - } + canister_data.insert_subnet_orchestrator( + new_subnet_orchestrator_caniter_id, + subnet_is_available_for_provisioning_individual_canister, + ); Ok(()) }) } diff --git a/src/canister/platform_orchestrator/src/api/canister_management/stop_upgrades_for_individual_user_canisters.rs b/src/canister/platform_orchestrator/src/api/canister_management/stop_upgrades_for_individual_user_canisters.rs index 9aed5630..bb2c7643 100644 --- a/src/canister/platform_orchestrator/src/api/canister_management/stop_upgrades_for_individual_user_canisters.rs +++ b/src/canister/platform_orchestrator/src/api/canister_management/stop_upgrades_for_individual_user_canisters.rs @@ -7,7 +7,7 @@ use crate::{guard::is_caller::is_caller_global_admin_or_controller, CANISTER_DAT async fn stop_upgrades_for_individual_user_canisters() -> Result { let subnet_orchestrator_list = CANISTER_DATA.with_borrow(|canister_data| { - canister_data.all_subnet_orchestrator_canisters_list.clone() + canister_data.subnet_orchestrators().clone() }); for subnet_orchestrator in subnet_orchestrator_list { diff --git a/src/canister/platform_orchestrator/src/api/canister_management/update_canisters_last_access_time.rs b/src/canister/platform_orchestrator/src/api/canister_management/update_canisters_last_access_time.rs index 6e9ed630..22c4ec9a 100644 --- a/src/canister/platform_orchestrator/src/api/canister_management/update_canisters_last_access_time.rs +++ b/src/canister/platform_orchestrator/src/api/canister_management/update_canisters_last_access_time.rs @@ -6,7 +6,7 @@ use crate::{guard::is_caller::is_caller_global_admin_or_controller, CANISTER_DAT #[update(guard = "is_caller_global_admin_or_controller")] async fn update_canisters_last_functionality_access_time() -> Result { let subnet_orchestrator_list = CANISTER_DATA - .with_borrow(|canister_data| canister_data.all_subnet_orchestrator_canisters_list.clone()); + .with_borrow(|canister_data| canister_data.subnet_orchestrators().clone()); for subnet_orchestrator in subnet_orchestrator_list { let result: CallResult<()> = call( diff --git a/src/canister/platform_orchestrator/src/api/canister_management/update_profile_owner_for_individual_users.rs b/src/canister/platform_orchestrator/src/api/canister_management/update_profile_owner_for_individual_users.rs index b931d98c..7c7d9856 100644 --- a/src/canister/platform_orchestrator/src/api/canister_management/update_profile_owner_for_individual_users.rs +++ b/src/canister/platform_orchestrator/src/api/canister_management/update_profile_owner_for_individual_users.rs @@ -5,7 +5,7 @@ use crate::CANISTER_DATA; #[update] fn update_profile_owner_for_individual_canisters() { CANISTER_DATA.with_borrow(|canister_data| { - canister_data.all_subnet_orchestrator_canisters_list.iter().for_each(|subnet_canster_id| { + canister_data.subnet_orchestrators().iter().for_each(|subnet_canster_id| { let _ = ic_cdk::notify(*subnet_canster_id, "update_profile_owner_for_individual_canisters", ()); }) }) diff --git a/src/canister/platform_orchestrator/src/api/canister_management/update_timers_for_hon_game.rs b/src/canister/platform_orchestrator/src/api/canister_management/update_timers_for_hon_game.rs index 3a5127b9..c8b8a1fe 100644 --- a/src/canister/platform_orchestrator/src/api/canister_management/update_timers_for_hon_game.rs +++ b/src/canister/platform_orchestrator/src/api/canister_management/update_timers_for_hon_game.rs @@ -6,7 +6,7 @@ use crate::{guard::is_caller::is_caller_global_admin_or_controller, CANISTER_DAT #[update(guard = "is_caller_global_admin_or_controller")] async fn update_restart_timers_hon_game() -> Result { let subnet_orchestrator_list = CANISTER_DATA - .with_borrow(|canister_data| canister_data.all_subnet_orchestrator_canisters_list.clone()); + .with_borrow(|canister_data| canister_data.subnet_orchestrators().clone()); for subnet_orchestrator in subnet_orchestrator_list { let result: CallResult<()> = call( diff --git a/src/canister/platform_orchestrator/src/api/canister_management/upgrade_canisters_in_network.rs b/src/canister/platform_orchestrator/src/api/canister_management/upgrade_canisters_in_network.rs index 72b92437..3f4664de 100644 --- a/src/canister/platform_orchestrator/src/api/canister_management/upgrade_canisters_in_network.rs +++ b/src/canister/platform_orchestrator/src/api/canister_management/upgrade_canisters_in_network.rs @@ -62,7 +62,7 @@ async fn upgrade_individual_canisters(upgrade_arg: UpgradeCanisterArg) { canister_data.last_subnet_canister_upgrade_status.count = 0; }); let subnet_orchestrator_canisters = CANISTER_DATA - .with_borrow(|canister_data| canister_data.all_subnet_orchestrator_canisters_list.clone()); + .with_borrow(|canister_data| canister_data.subnet_orchestrators().clone()); for subnet_orchestrator in subnet_orchestrator_canisters.iter() { match recharge_subnet_orchestrator_if_needed(*subnet_orchestrator).await { @@ -118,9 +118,9 @@ async fn upgrade_subnet_canisters(upgrade_arg: UpgradeCanisterArg) { let canister_list = CANISTER_DATA.with_borrow(|canister_data| { match upgrade_arg.canister { - WasmType::PostCacheWasm => Ok(canister_data.all_post_cache_orchestrator_list.clone()), + WasmType::PostCacheWasm => Ok(canister_data.post_cache_orchestrators().clone()), WasmType::SubnetOrchestratorWasm => { - Ok(canister_data.all_subnet_orchestrator_canisters_list.clone()) + Ok(canister_data.subnet_orchestrators().clone()) } _ => Err(()), } @@ -256,7 +256,7 @@ async fn upgrade_subnet_orchestrator_canister( ) -> Result<(), String> { // TODO: remove this when all subnet orchestrators are upgraded let mut proof_of_participation = ProofOfParticipation::new_for_root(); - proof_of_participation = proof_of_participation.derive_for_child(canister_id).await?; + proof_of_participation = proof_of_participation.derive_for_child(&CANISTER_DATA, canister_id).await?; let install_code_arg = InstallCodeArgument { mode: CanisterInstallMode::Upgrade(None), diff --git a/src/canister/platform_orchestrator/src/api/canister_management/upgrade_specific_individual_canister.rs b/src/canister/platform_orchestrator/src/api/canister_management/upgrade_specific_individual_canister.rs index 668f0857..89588546 100644 --- a/src/canister/platform_orchestrator/src/api/canister_management/upgrade_specific_individual_canister.rs +++ b/src/canister/platform_orchestrator/src/api/canister_management/upgrade_specific_individual_canister.rs @@ -6,7 +6,7 @@ use crate::{guard::is_caller::is_caller_global_admin_or_controller, CANISTER_DAT #[update(guard = "is_caller_global_admin_or_controller")] fn upgrade_specific_individual_canister(individual_canister_id: Principal) { CANISTER_DATA.with_borrow(|canister_data| { - canister_data.all_subnet_orchestrator_canisters_list.iter().for_each(|subnet_id| { + canister_data.subnet_orchestrators().iter().for_each(|subnet_id| { let _ = ic_cdk::notify(*subnet_id, "upgrade_specific_individual_user_canister_with_latest_wasm", (individual_canister_id, )); }) }) diff --git a/src/canister/platform_orchestrator/src/api/cycle_management/start_reclaiming_cycles_from_individual_canisters.rs b/src/canister/platform_orchestrator/src/api/cycle_management/start_reclaiming_cycles_from_individual_canisters.rs index 1d9eb259..5bd3c0fb 100644 --- a/src/canister/platform_orchestrator/src/api/cycle_management/start_reclaiming_cycles_from_individual_canisters.rs +++ b/src/canister/platform_orchestrator/src/api/cycle_management/start_reclaiming_cycles_from_individual_canisters.rs @@ -6,7 +6,7 @@ use crate::{guard::is_caller::is_caller_global_admin_or_controller, CANISTER_DAT fn start_reclaiming_cycles_from_individual_canisters() -> Result { CANISTER_DATA.with_borrow(|canister_data| { canister_data - .all_subnet_orchestrator_canisters_list + .subnet_orchestrators() .iter() .for_each(|subnet_orchestrator_id| { ic_cdk::notify( diff --git a/src/canister/platform_orchestrator/src/api/cycle_management/start_reclaiming_cycles_from_subnet_orchestrator_canister.rs b/src/canister/platform_orchestrator/src/api/cycle_management/start_reclaiming_cycles_from_subnet_orchestrator_canister.rs index 94a76be7..e2645f3d 100644 --- a/src/canister/platform_orchestrator/src/api/cycle_management/start_reclaiming_cycles_from_subnet_orchestrator_canister.rs +++ b/src/canister/platform_orchestrator/src/api/cycle_management/start_reclaiming_cycles_from_subnet_orchestrator_canister.rs @@ -7,7 +7,7 @@ use crate::{guard::is_caller::is_caller_global_admin_or_controller, CANISTER_DAT #[update(guard = "is_caller_global_admin_or_controller")] async fn start_reclaiming_cycles_from_subnet_orchestrator_canister() -> String { CANISTER_DATA.with_borrow(|canister_data| { - canister_data.all_subnet_orchestrator_canisters_list.iter().for_each(|subnet_orchestrator_id| { + canister_data.subnet_orchestrators().iter().for_each(|subnet_orchestrator_id| { ic_cdk::notify(*subnet_orchestrator_id, "return_cycles_to_platform_orchestrator_canister", ()).unwrap(); }); }); diff --git a/src/canister/platform_orchestrator/src/api/stats/collect_creator_dao_stats_in_the_network.rs b/src/canister/platform_orchestrator/src/api/stats/collect_creator_dao_stats_in_the_network.rs index e6366d3f..18ec709a 100644 --- a/src/canister/platform_orchestrator/src/api/stats/collect_creator_dao_stats_in_the_network.rs +++ b/src/canister/platform_orchestrator/src/api/stats/collect_creator_dao_stats_in_the_network.rs @@ -13,7 +13,7 @@ use crate::{guard::is_caller::is_caller_global_admin_or_controller, CANISTER_DAT #[update(guard = "is_caller_global_admin_or_controller")] pub fn collect_creator_dao_stats_in_the_network() { let subnet_orchestrators = CANISTER_DATA - .with_borrow(|canister_data| canister_data.all_subnet_orchestrator_canisters_list.clone()); + .with_borrow(|canister_data| canister_data.subnet_orchestrators().clone()); let creator_dao_stats_task = subnet_orchestrators diff --git a/src/canister/platform_orchestrator/src/data_model/mod.rs b/src/canister/platform_orchestrator/src/data_model/mod.rs index a5387e33..abdf9329 100644 --- a/src/canister/platform_orchestrator/src/data_model/mod.rs +++ b/src/canister/platform_orchestrator/src/data_model/mod.rs @@ -16,8 +16,10 @@ use shared_utils::{ SubnetUpgradeReport, }, }, - common::types::wasm::{CanisterWasm, WasmType}, - types::creator_dao_stats::CreatorDaoTokenStats, + common::{ + participant_crypto::{merkle::ChildrenMerkle, ProofOfParticipationDeriverStore}, + types::wasm::{CanisterWasm, WasmType}, + }, types::creator_dao_stats::CreatorDaoTokenStats, }; use self::memory::{ @@ -34,8 +36,8 @@ pub struct StateGuard { #[derive(Serialize, Deserialize)] pub struct CanisterData { - pub all_subnet_orchestrator_canisters_list: HashSet, - pub all_post_cache_orchestrator_list: HashSet, + all_subnet_orchestrator_canisters_list: HashSet, + all_post_cache_orchestrator_list: HashSet, pub subet_orchestrator_with_capacity_left: HashSet, pub version_detail: VersionDetails, #[serde(skip, default = "_default_wasms")] @@ -53,6 +55,8 @@ pub struct CanisterData { pub state_guard: StateGuard, #[serde(default)] pub creator_dao_stats: CreatorDaoTokenStats, + #[serde(default)] + pub children_merkle: ChildrenMerkle, } fn _default_wasms() -> StableBTreeMap { @@ -82,8 +86,53 @@ impl Default for CanisterData { subnets_upgrade_report: SubnetUpgradeReport::default(), state_guard: StateGuard::default(), creator_dao_stats: CreatorDaoTokenStats::default(), + children_merkle: ChildrenMerkle::default(), + } + } +} + +impl CanisterData { + pub fn insert_subnet_orchestrator_and_post_cache(&mut self, subnet_orchestrator: Principal, post_cache: Principal) { + self.all_subnet_orchestrator_canisters_list.insert(subnet_orchestrator); + self.all_post_cache_orchestrator_list.insert(post_cache); + self.subet_orchestrator_with_capacity_left.insert(subnet_orchestrator); + self.children_merkle.insert_children([subnet_orchestrator, post_cache]); + } + + pub fn insert_subnet_orchestrator(&mut self, subnet_orchestrator: Principal, provisioning_available: bool) { + self.all_subnet_orchestrator_canisters_list.insert(subnet_orchestrator); + if provisioning_available { + self.subet_orchestrator_with_capacity_left.insert(subnet_orchestrator); + } + self.children_merkle.insert_children([subnet_orchestrator]); + } + + pub fn remove_subnet_orchestrator(&mut self, subnet_orchestrator: Principal, remove_it_completely: bool) { + self.subet_orchestrator_with_capacity_left.remove(&subnet_orchestrator); + if remove_it_completely { + self.all_subnet_orchestrator_canisters_list.remove(&subnet_orchestrator); + // WARN: does not revoke their Proof of Participation + self.children_merkle.remove_child(subnet_orchestrator); } } + + pub fn subnet_orchestrators(&self) -> &HashSet { + &self.all_subnet_orchestrator_canisters_list + } + + pub fn post_cache_orchestrators(&self) -> &HashSet { + &self.all_post_cache_orchestrator_list + } +} + +impl ProofOfParticipationDeriverStore for CanisterData { + fn children_merkle(&self) -> &ChildrenMerkle { + &self.children_merkle + } + + fn children_merkle_mut(&mut self) -> &mut ChildrenMerkle { + &mut self.children_merkle + } } #[derive(Serialize, Deserialize, CandidType, Clone)] diff --git a/src/canister/platform_orchestrator/src/utils/registered_subnet_orchestrator.rs b/src/canister/platform_orchestrator/src/utils/registered_subnet_orchestrator.rs index 6de37f92..3dde04c8 100644 --- a/src/canister/platform_orchestrator/src/utils/registered_subnet_orchestrator.rs +++ b/src/canister/platform_orchestrator/src/utils/registered_subnet_orchestrator.rs @@ -63,7 +63,7 @@ impl RegisteredSubnetOrchestrator { pub fn new(canister_id: Principal) -> Result { let contains = CANISTER_DATA.with_borrow(|canister_data| { canister_data - .all_subnet_orchestrator_canisters_list + .subnet_orchestrators() .contains(&canister_id) }); diff --git a/src/canister/user_index/can.did b/src/canister/user_index/can.did index 6e5b0a75..7770e728 100644 --- a/src/canister/user_index/can.did +++ b/src/canister/user_index/can.did @@ -63,7 +63,12 @@ type KnownPrincipalType = variant { UserIdGlobalSuperAdmin; }; type LogVisibility = variant { controllers; public }; -type ProofOfChild = record { "principal" : principal; signature : blob }; +type ProofOfChild = record { + proof_of_inclusion : vec blob; + "principal" : principal; + children_proof : ProofOfChildren; +}; +type ProofOfChildren = record { signature : blob; merkle_root : blob }; type ProofOfParticipation = record { chain : vec ProofOfChild }; type QueryStats = record { response_payload_bytes_total : nat; diff --git a/src/canister/user_index/src/api/canister_management/create_pool_of_available_canisters.rs b/src/canister/user_index/src/api/canister_management/create_pool_of_available_canisters.rs index b3fdb53b..dfd2bd60 100644 --- a/src/canister/user_index/src/api/canister_management/create_pool_of_available_canisters.rs +++ b/src/canister/user_index/src/api/canister_management/create_pool_of_available_canisters.rs @@ -1,16 +1,10 @@ -use std::pin::Pin; - -use futures::Future; +use futures::StreamExt; use ic_cdk::{api::is_controller, caller}; -use shared_utils::{common::{types::wasm::{CanisterWasm, WasmType}, utils::task::run_task_concurrently}, constant::{get_backup_individual_user_canister_batch_size, get_backup_individual_user_canister_threshold, get_individual_user_canister_subnet_batch_size}}; +use shared_utils::{common::types::wasm::{CanisterWasm, WasmType}, constant::{get_backup_individual_user_canister_batch_size, get_backup_individual_user_canister_threshold, get_individual_user_canister_subnet_batch_size}}; use ic_cdk_macros::update; -use crate::{util::canister_management::{create_empty_user_canister, create_users_canister}, CANISTER_DATA}; +use crate::{util::canister_management::{create_empty_user_canister, install_canister_wasm, recharge_canister_for_installing_wasm}, CANISTER_DATA}; -enum CanisterCodeState { - Empty, - WasmInstalled -} #[update] pub fn create_pool_of_individual_user_available_canisters(version: String, individual_user_wasm: Vec) -> Result { @@ -35,34 +29,53 @@ pub fn create_pool_of_individual_user_available_canisters(version: String, indiv pub async fn impl_create_pool_of_individual_user_available_canisters(version: String, individual_user_wasm: Vec) { let backup_individual_user_canister_batch_size = get_backup_individual_user_canister_batch_size(); + let individual_user_canister_subnet_batch_size = get_individual_user_canister_subnet_batch_size(); + let total_cnt = backup_individual_user_canister_batch_size + individual_user_canister_subnet_batch_size; //empty canister for backup - let create_empty_canister_futures = (0..backup_individual_user_canister_batch_size) - .map(|_| Box::pin(async { - let canister_id = create_empty_user_canister().await; - (canister_id, CanisterCodeState::Empty) - }) as Pin>>); - - //canisters with installed wasm for available pool - let individual_user_canister_subnet_batch_size = get_individual_user_canister_subnet_batch_size(); - let create_canister_with_wasm_futures = (0..individual_user_canister_subnet_batch_size) - .map(|_| Box::pin(async { - let canister_id = create_users_canister(None, version.clone(), individual_user_wasm.clone()).await; - (canister_id, CanisterCodeState::WasmInstalled) - }) as Pin>>); + let create_empty_canister_futures = (0..total_cnt) + .map(|_| create_empty_user_canister()); + let mut cans_stream = futures::stream::iter(create_empty_canister_futures).buffer_unordered(10); - let combined_create_canister_futures = create_canister_with_wasm_futures.chain(create_empty_canister_futures); + let to_install: Vec<_> = cans_stream.by_ref().take(individual_user_canister_subnet_batch_size as usize).collect().await; + CANISTER_DATA.with_borrow_mut(|cdata| { + cdata.children_merkle.insert_children(to_install.clone()); + }); - run_task_concurrently(combined_create_canister_futures, - 10, - |(canister_id, canister_code_state)| { - CANISTER_DATA.with_borrow_mut(|canister_data| { - match canister_code_state { - CanisterCodeState::Empty => canister_data.backup_canister_pool.insert(canister_id), - CanisterCodeState::WasmInstalled => canister_data.available_canisters.insert(canister_id) - }; - }); - }, - || false).await; + //canisters with installed wasm for available pool + let install_wasm_futs = to_install.into_iter().map(move |canister_id| { + let version = version.clone(); + let individual_user_wasm = individual_user_wasm.clone(); + async move { + recharge_canister_for_installing_wasm(canister_id).await.map_err(|e| (canister_id, e))?; + install_canister_wasm( + canister_id, + None, + version, + individual_user_wasm, + ).await + } + }); + let mut install_wasm_stream = futures::stream::iter(install_wasm_futs).buffer_unordered(10); + while let Some(res) = install_wasm_stream.next().await { + match res { + Ok(canister_id) => { + CANISTER_DATA.with_borrow_mut(|cdata| { + cdata.available_canisters.insert(canister_id); + }) + } + Err((canister_id, e)) => { + ic_cdk::println!("Failed to install wasm on canister: {:?}, error: {:?}", canister_id, e); + CANISTER_DATA.with_borrow_mut(|cdata| { + cdata.insert_backup_canister(canister_id); + }) + } + } + } + while let Some(canister_id) = cans_stream.next().await { + CANISTER_DATA.with_borrow_mut(|cdata| { + cdata.insert_backup_canister(canister_id); + }) + } } \ No newline at end of file diff --git a/src/canister/user_index/src/api/canister_management/get_subnet_backup_capacity.rs b/src/canister/user_index/src/api/canister_management/get_subnet_backup_capacity.rs index 5d33afa6..dba6de49 100644 --- a/src/canister/user_index/src/api/canister_management/get_subnet_backup_capacity.rs +++ b/src/canister/user_index/src/api/canister_management/get_subnet_backup_capacity.rs @@ -1,4 +1,3 @@ -use std::borrow::Borrow; use ic_cdk_macros::query; use crate::CANISTER_DATA; @@ -7,6 +6,6 @@ use crate::CANISTER_DATA; #[query] pub fn get_subnet_backup_capacity() -> u64 { CANISTER_DATA.with_borrow(|canister_data| { - canister_data.borrow().backup_canister_pool.len() as u64 + canister_data.backup_canisters().len() as u64 }) } \ No newline at end of file diff --git a/src/canister/user_index/src/api/upgrade_individual_user_template/update_user_index_upgrade_user_canisters_with_latest_wasm.rs b/src/canister/user_index/src/api/upgrade_individual_user_template/update_user_index_upgrade_user_canisters_with_latest_wasm.rs index 20b87564..256a65dd 100644 --- a/src/canister/user_index/src/api/upgrade_individual_user_template/update_user_index_upgrade_user_canisters_with_latest_wasm.rs +++ b/src/canister/user_index/src/api/upgrade_individual_user_template/update_user_index_upgrade_user_canisters_with_latest_wasm.rs @@ -51,6 +51,13 @@ pub async fn upgrade_user_canisters_with_latest_wasm( }) }); + // TODO: remove this after upgrade is executed on all individual canisters + CANISTER_DATA.with_borrow_mut(|cdata| { + cdata.children_merkle.insert_children( + user_principal_id_to_canister_id_vec.iter().map(|(_, canister_id)| *canister_id) + ); + }); + let saved_upgrade_status = CANISTER_DATA.with(|canister_data_ref_cell| { canister_data_ref_cell .borrow() @@ -213,7 +220,7 @@ async fn upgrade_user_canister( // TODO: remove this after upgrade is executed on all individual canisters let mut proof_of_participation = CANISTER_DATA.with_borrow(|cdata| cdata.proof_of_participation.clone()); if let Some(pop) = proof_of_participation { - proof_of_participation = Some(pop.derive_for_child(canister_id).await?); + proof_of_participation = Some(pop.derive_for_child(&CANISTER_DATA, canister_id).await?); } canister_management::upgrade_individual_user_canister( diff --git a/src/canister/user_index/src/api/upgrade_individual_user_template/upgrade_specific_individual_user_canister_with_latest_wasm.rs b/src/canister/user_index/src/api/upgrade_individual_user_template/upgrade_specific_individual_user_canister_with_latest_wasm.rs index af9d5965..4b77548f 100644 --- a/src/canister/user_index/src/api/upgrade_individual_user_template/upgrade_specific_individual_user_canister_with_latest_wasm.rs +++ b/src/canister/user_index/src/api/upgrade_individual_user_template/upgrade_specific_individual_user_canister_with_latest_wasm.rs @@ -51,9 +51,14 @@ async fn upgrade_specific_individual_user_canister_with_latest_wasm( recharge_canister_for_installing_wasm(user_canister_id).await?; - let mut proof_of_participation = CANISTER_DATA.with_borrow(|cdata| cdata.proof_of_participation.clone()); + // TODO: remove this after upgrade is executed on all individual canisters + let mut proof_of_participation = CANISTER_DATA.with_borrow_mut(|cdata| { + cdata.children_merkle.insert_children([user_canister_id]); + + cdata.proof_of_participation.clone() + }); if let Some(pop) = proof_of_participation { - proof_of_participation = Some(pop.derive_for_child(user_canister_id).await.unwrap()); + proof_of_participation = Some(pop.derive_for_child(&CANISTER_DATA, user_canister_id).await.unwrap()); } match canister_management::upgrade_individual_user_canister( diff --git a/src/canister/user_index/src/api/user_record/get_requester_principals_canister_id_create_if_not_exists_and_optionally_allow_referrer.rs b/src/canister/user_index/src/api/user_record/get_requester_principals_canister_id_create_if_not_exists_and_optionally_allow_referrer.rs index 81710b81..8865358a 100644 --- a/src/canister/user_index/src/api/user_record/get_requester_principals_canister_id_create_if_not_exists_and_optionally_allow_referrer.rs +++ b/src/canister/user_index/src/api/user_record/get_requester_principals_canister_id_create_if_not_exists_and_optionally_allow_referrer.rs @@ -152,7 +152,7 @@ async fn new_user_signup(user_id: Principal) -> Result { let available_individual_user_canisters_cnt = CANISTER_DATA.with_borrow(|canister_data| canister_data.available_canisters.len() as u64); let backup_individual_user_canister_cnt = - CANISTER_DATA.with_borrow(|canister_data| canister_data.backup_canister_pool.len() as u64); + CANISTER_DATA.with_borrow(|canister_data| canister_data.backup_canisters().len() as u64); // notify platform_orchestrator that this subnet has reached maximum capacity. if available_individual_user_canisters_cnt == 0 && backup_individual_user_canister_cnt == 0 { @@ -190,7 +190,7 @@ async fn new_user_signup(user_id: Principal) -> Result { async fn provision_new_available_canisters(individual_user_template_canister_wasm: CanisterWasm) { let backup_pool_canister_count = - CANISTER_DATA.with_borrow(|canister_data| canister_data.backup_canister_pool.len() as u64); + CANISTER_DATA.with_borrow(|canister_data| canister_data.backup_canisters().len() as u64); let individual_canister_batch_size = get_individual_user_canister_subnet_batch_size(); let canister_count = individual_canister_batch_size.min(backup_pool_canister_count); let available_canister_count = @@ -198,7 +198,7 @@ async fn provision_new_available_canisters(individual_user_template_canister_was let max_canister_count = available_canister_count + canister_count; let install_canister_wasm_futures = CANISTER_DATA.with_borrow(|canister_data| { - let mut backup_pool_canister = canister_data.backup_canister_pool.clone().into_iter(); + let mut backup_pool_canister = canister_data.backup_canisters().clone().into_iter(); (0..canister_count).map(move |_| { let individual_user_template_canister_wasm_version = individual_user_template_canister_wasm.version.clone(); @@ -210,7 +210,7 @@ async fn provision_new_available_canisters(individual_user_template_canister_was // Remove the canister id from backup pool so no one else access it CANISTER_DATA.with_borrow_mut(|canister_data| { - canister_data.backup_canister_pool.remove(&canister_id) + canister_data.remove_backup_canister(&canister_id) }); async move { @@ -236,7 +236,7 @@ async fn provision_new_available_canisters(individual_user_template_canister_was canister_data.available_canisters.insert(canister_id); } Err(e) => { - canister_data.backup_canister_pool.insert(e.0); + canister_data.reinsert_backup_canister_due_to_failure(e.0); ic_cdk::println!("Error installing wasm for canister {} {}", e.0, e.1); } }) @@ -260,7 +260,7 @@ async fn provision_new_available_canisters(individual_user_template_canister_was async fn provision_new_backup_canisters(canister_count: u64) { let breaking_condition = || { CANISTER_DATA.with_borrow(|canister_data| { - canister_data.backup_canister_pool.len() as u64 + canister_data.backup_canisters().len() as u64 > get_backup_individual_user_canister_batch_size() }) }; diff --git a/src/canister/user_index/src/api/user_signup/are_signups_enabled.rs b/src/canister/user_index/src/api/user_signup/are_signups_enabled.rs index 4e6aad73..eb391238 100644 --- a/src/canister/user_index/src/api/user_signup/are_signups_enabled.rs +++ b/src/canister/user_index/src/api/user_signup/are_signups_enabled.rs @@ -25,15 +25,13 @@ mod test { #[test] fn test_are_signups_enabled_impl() { - let mut canister_data = CanisterData { - configuration: Configuration { + let mut canister_data = CanisterData::default(); + canister_data.configuration = Configuration { known_principal_ids: HashMap::default(), signups_open_on_this_subnet: true, url_to_send_canister_metrics_to: String::from("http://example.com") - }, - ..Default::default() }; - + assert!(are_signups_enabled_impl(&canister_data)); canister_data.configuration.signups_open_on_this_subnet = false; diff --git a/src/canister/user_index/src/data_model/mod.rs b/src/canister/user_index/src/data_model/mod.rs index 206ea0a9..3b0ceaa3 100644 --- a/src/canister/user_index/src/data_model/mod.rs +++ b/src/canister/user_index/src/data_model/mod.rs @@ -6,7 +6,8 @@ use serde::Serialize; use shared_utils::canister_specific::user_index::types::{ BroadcastCallStatus, RecycleStatus, UpgradeStatus, }; -use shared_utils::common::participant_crypto::ProofOfParticipation; +use shared_utils::common::participant_crypto::merkle::ChildrenMerkle; +use shared_utils::common::participant_crypto::{ProofOfParticipation, ProofOfParticipationDeriverStore}; use shared_utils::common::types::wasm::{CanisterWasm, WasmType}; use self::memory::get_wasm_memory; @@ -30,7 +31,7 @@ pub struct CanisterData { pub allow_upgrades_for_individual_canisters: bool, pub available_canisters: HashSet, #[serde(default)] - pub backup_canister_pool: HashSet, + backup_canister_pool: HashSet, pub user_principal_id_to_canister_id_map: BTreeMap, pub unique_user_name_to_user_principal_id_map: BTreeMap, #[serde(skip, default = "_empty_wasms")] @@ -41,6 +42,8 @@ pub struct CanisterData { pub last_broadcast_call_status: BroadcastCallStatus, #[serde(default)] pub proof_of_participation: Option, + #[serde(default)] + pub children_merkle: ChildrenMerkle, } impl Default for CanisterData { @@ -57,7 +60,46 @@ impl Default for CanisterData { recycle_status: Default::default(), last_broadcast_call_status: Default::default(), proof_of_participation: None, + children_merkle: ChildrenMerkle::default(), + } + } +} + +impl CanisterData { + pub fn insert_backup_canister(&mut self, canister_id: Principal) -> bool { + let inserted = self.backup_canister_pool.insert(canister_id); + if inserted { + self.children_merkle.insert_children([canister_id]); } + inserted + } + + pub fn remove_backup_canister(&mut self, canister_id: &Principal) { + self.backup_canister_pool.remove(&canister_id); + // removal from backup pool does not mean its not part of our fleet + // an individual canister might be installed on it instead, for example + // so we don't remove it from children_merkle + } + + // reinsert a previously removed backup canister + // caller MUST be careful and only call this function if the canister was previously removed + // USE [`CanisterData::insert_backup_canister`] if you are not sure + pub fn reinsert_backup_canister_due_to_failure(&mut self, canister_id: Principal) { + self.backup_canister_pool.insert(canister_id); + } + + pub fn backup_canisters(&self) -> &HashSet { + &self.backup_canister_pool + } +} + +impl ProofOfParticipationDeriverStore for CanisterData { + fn children_merkle(&self) -> &ChildrenMerkle { + &self.children_merkle + } + + fn children_merkle_mut(&mut self) -> &mut ChildrenMerkle { + &mut self.children_merkle } } diff --git a/src/canister/user_index/src/util/canister_management.rs b/src/canister/user_index/src/util/canister_management.rs index 4f363a58..224ae617 100644 --- a/src/canister/user_index/src/util/canister_management.rs +++ b/src/canister/user_index/src/util/canister_management.rs @@ -49,17 +49,6 @@ struct CustomInstallCodeArgument { pub unsafe_drop_stable_memory: Option, } -pub async fn create_users_canister( - profile_owner: Option, - version: String, - individual_user_wasm: Vec, -) -> Principal { - let canister_id = create_empty_user_canister().await; - recharge_canister_for_installing_wasm(canister_id).await; - install_canister_wasm(canister_id, profile_owner, version, individual_user_wasm).await; - canister_id -} - pub async fn create_empty_user_canister() -> Principal { // * config for provisioning canister let arg = CreateCanisterArgument { @@ -95,7 +84,7 @@ pub async fn provision_number_of_empty_canisters( let result_callback = |canister_id: Principal| { CANISTER_DATA.with_borrow_mut(|canister_data| { - canister_data.backup_canister_pool.insert(canister_id) + canister_data.insert_backup_canister(canister_id) }); }; @@ -119,7 +108,7 @@ pub async fn install_canister_wasm( let mut proof_of_participation = CANISTER_DATA.with_borrow(|cdata| cdata.proof_of_participation.clone()); if let Some(pop) = proof_of_participation { - proof_of_participation = Some(pop.derive_for_child(canister_id).await.unwrap()); + proof_of_participation = Some(pop.derive_for_child(&CANISTER_DATA, canister_id).await.unwrap()); } let individual_user_tempalate_init_args = IndividualUserTemplateInitArgs { profile_owner, @@ -164,7 +153,7 @@ pub async fn reinstall_canister_wasm( let mut proof_of_participation = CANISTER_DATA.with_borrow(|cdata| cdata.proof_of_participation.clone()); if let Some(pop) = proof_of_participation { - proof_of_participation = Some(pop.derive_for_child(canister_id).await?); + proof_of_participation = Some(pop.derive_for_child(&CANISTER_DATA, canister_id).await?); } let individual_user_tempalate_init_args = IndividualUserTemplateInitArgs { profile_owner, diff --git a/src/canister/user_index/src/util/types/individual_user_canister.rs b/src/canister/user_index/src/util/types/individual_user_canister.rs index 0bb389b2..0cbaa067 100644 --- a/src/canister/user_index/src/util/types/individual_user_canister.rs +++ b/src/canister/user_index/src/util/types/individual_user_canister.rs @@ -97,12 +97,12 @@ impl IndividualUserCanister { pub async fn allot_empty_canister(&self) -> Result { let alloted_canister_id_res = CANISTER_DATA.with_borrow_mut(|canister_data| { - let Some(new_canister_id) = canister_data.backup_canister_pool.iter().next().copied() + let Some(new_canister_id) = canister_data.backup_canisters().iter().next().copied() else { return Err("No Backup Canisters Available".into()); }; - canister_data.backup_canister_pool.remove(&new_canister_id); + canister_data.remove_backup_canister(&new_canister_id); Ok(new_canister_id) }); @@ -119,7 +119,7 @@ impl IndividualUserCanister { .await .inspect_err(|_| { CANISTER_DATA.with_borrow_mut(|canister_data| { - canister_data.backup_canister_pool.insert(canister_id) + canister_data.reinsert_backup_canister_due_to_failure(canister_id) }); }) .map_err(|e| e.1)?; diff --git a/src/lib/shared_utils/Cargo.toml b/src/lib/shared_utils/Cargo.toml index 065ec574..89bb3ea9 100644 --- a/src/lib/shared_utils/Cargo.toml +++ b/src/lib/shared_utils/Cargo.toml @@ -17,7 +17,16 @@ ciborium = { workspace = true } serde_json_any_key = "2.0.0" serde_bytes = "0.11.14" icrc-ledger-types = { workspace = true } -ed25519-dalek = "2.1.1" +ed25519-compact = { version = "2.1.1", default-features = false, features = ["std"] } +trie-db = { version = "0.29.1", default-features = false } +hash-db = { version = "0.16.0", default-features = false } +blake3 = "1.5.4" +hash256-std-hasher = { version = "0.15.2", default-features = false } +parity-scale-codec = { version = "3.6.12", default-features = false } +memory-db = { version = "0.32.0", default-features = false } +trie-root = { version = "0.18.0", default-features = false } +# monotree = { git = "https://github.com/rupansh-sekar-yral/monotree.git", rev = "1504007dae91a909fbb29bfebb36181ef3333cb8" } +# hashbrown = { version = "0.7", features = ["serde"] } [dev-dependencies] test_utils = { workspace = true } diff --git a/src/lib/shared_utils/src/common/participant_crypto/merkle/backing.rs b/src/lib/shared_utils/src/common/participant_crypto/merkle/backing.rs new file mode 100644 index 00000000..1b087bf4 --- /dev/null +++ b/src/lib/shared_utils/src/common/participant_crypto/merkle/backing.rs @@ -0,0 +1,113 @@ +use std::collections::{btree_map::Entry, BTreeMap}; + +use hash_db::{AsHashDB, HashDB, HashDBRef, Hasher, Prefix}; +use memory_db::{HashKey, KeyFunction}; +use serde::{Deserialize, Serialize}; +use trie_db::{TrieLayout, NodeCodec}; + +use super::{Blake3Hasher, ChildreenTreeLayout, Hash}; + +type ChildrenHK = HashKey; + +// https://docs.rs/memory-db/0.32.0/src/memory_db/lib.rs.html#83-92 modified for our uses +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ChildrenBacking { + data: BTreeMap, i32)>, + hashed_null_node: Hash, + null_node_data: Vec, +} + +impl Default for ChildrenBacking { + fn default() -> Self { + Self { + data: BTreeMap::new(), + hashed_null_node: ::Codec::hashed_null_node(), + null_node_data: ::Codec::empty_node().to_vec(), + } + } +} + +impl HashDB> for ChildrenBacking { + fn get(&self, key: &Hash, prefix: Prefix) -> Option> { + if key == &self.hashed_null_node { + return Some(self.null_node_data.clone()); + } + + let key = ChildrenHK::key(key, prefix); + match self.data.get(&key) { + Some(&(ref v, rc)) if rc > 0 => Some(v.clone()), + _ => None, + } + } + + fn contains(&self, key: &Hash, prefix: Prefix) -> bool { + if key == &self.hashed_null_node { + return true; + } + + let key = ChildrenHK::key(key, prefix); + self.data.get(&key).map(|&(_, rc)| rc > 0).unwrap_or_default() + } + + fn emplace(&mut self, key: Hash, prefix: Prefix, value: Vec) { + if value == self.null_node_data { + return; + } + + let key = ChildrenHK::key(&key, prefix); + match self.data.entry(key) { + Entry::Occupied(mut entry) => { + let &mut (ref mut old_value, ref mut rc) = entry.get_mut(); + if *rc <= 0 { + *old_value = value; + } + *rc += 1; + }, + Entry::Vacant(entry) => { + entry.insert((value, 1)); + } + } + } + + fn insert(&mut self, prefix: Prefix, value: &[u8]) -> Hash { + if value == self.null_node_data { + return self.hashed_null_node; + } + + let key = Blake3Hasher::hash(value); + self.emplace(key, prefix, value.to_vec()); + key + } + + fn remove(&mut self, key: &Hash, prefix: Prefix) { + if key == &self.hashed_null_node { + return; + } + + let key = ChildrenHK::key(key, prefix); + self.data.entry(key) + .and_modify(|&mut (_, ref mut rc)| *rc -= 1) + .or_insert_with(|| (vec![], -1)); + } +} + +impl AsHashDB> for ChildrenBacking { + fn as_hash_db(&self) -> &dyn HashDB> { + self + } + + fn as_hash_db_mut<'a>(&'a mut self) -> &'a mut (dyn HashDB> + 'a) { + self + } +} + +impl HashDBRef> for ChildrenBacking { + fn get(&self, key: &Hash, prefix: Prefix) -> Option> { + HashDB::get(self, key, prefix) + } + + fn contains(&self, key: &Hash, prefix: Prefix) -> bool { + HashDB::contains(self, key, prefix) + } +} + diff --git a/src/lib/shared_utils/src/common/participant_crypto/merkle/layout.rs b/src/lib/shared_utils/src/common/participant_crypto/merkle/layout.rs new file mode 100644 index 00000000..41734d0e --- /dev/null +++ b/src/lib/shared_utils/src/common/participant_crypto/merkle/layout.rs @@ -0,0 +1,708 @@ +// Taken directly from https://github.com/paritytech/trie/blob/master/test-support/reference-trie/src/substrate.rs + +use core::{borrow::Borrow, iter::once, marker::PhantomData, ops::Range}; +use hash_db::Hasher; +use parity_scale_codec as codec; +use parity_scale_codec::{Compact, Decode, Encode, Input, Output}; +use trie_db::{ + nibble_ops, + node::{NibbleSlicePlan, NodeHandlePlan, NodePlan, Value, ValuePlan}, + ChildReference, NodeCodec as NodeCodecT, TrieConfiguration, TrieLayout, +}; + +/// Constants used into trie simplification codec. +mod trie_constants { + const FIRST_PREFIX: u8 = 0b_00 << 6; + pub const LEAF_PREFIX_MASK: u8 = 0b_01 << 6; + pub const BRANCH_WITHOUT_MASK: u8 = 0b_10 << 6; + pub const BRANCH_WITH_MASK: u8 = 0b_11 << 6; + pub const EMPTY_TRIE: u8 = FIRST_PREFIX /*| (0b_00 << 4)*/; + pub const ALT_HASHING_LEAF_PREFIX_MASK: u8 = FIRST_PREFIX | (0b_1 << 5); + pub const ALT_HASHING_BRANCH_WITH_MASK: u8 = FIRST_PREFIX | (0b_01 << 4); + pub const ESCAPE_COMPACT_HEADER: u8 = EMPTY_TRIE | 0b_00_01; +} + +pub const TRIE_VALUE_NODE_THRESHOLD: u32 = 33; + +/// Codec-flavored TrieStream. +#[derive(Default, Clone)] +pub struct TrieStream { + /// Current node buffer. + buffer: Vec, +} + +fn branch_node_bit_mask(has_children: impl Iterator) -> (u8, u8) { + let mut bitmap: u16 = 0; + let mut cursor: u16 = 1; + for v in has_children { + if v { + bitmap |= cursor + } + cursor <<= 1; + } + ((bitmap % 256) as u8, (bitmap / 256) as u8) +} + +/// Create a leaf/branch node, encoding a number of nibbles. +fn fuse_nibbles_node(nibbles: &[u8], kind: NodeKind) -> impl Iterator + '_ { + let size = nibbles.len(); + let iter_start = match kind { + NodeKind::Leaf => size_and_prefix_iterator(size, trie_constants::LEAF_PREFIX_MASK, 2), + NodeKind::BranchNoValue => + size_and_prefix_iterator(size, trie_constants::BRANCH_WITHOUT_MASK, 2), + NodeKind::BranchWithValue => + size_and_prefix_iterator(size, trie_constants::BRANCH_WITH_MASK, 2), + NodeKind::HashedValueLeaf => + size_and_prefix_iterator(size, trie_constants::ALT_HASHING_LEAF_PREFIX_MASK, 3), + NodeKind::HashedValueBranch => + size_and_prefix_iterator(size, trie_constants::ALT_HASHING_BRANCH_WITH_MASK, 4), + }; + iter_start + .chain(if nibbles.len() % 2 == 1 { Some(nibbles[0]) } else { None }) + .chain(nibbles[nibbles.len() % 2..].chunks(2).map(|ch| ch[0] << 4 | ch[1])) +} + +use trie_root::Value as TrieStreamValue; +impl trie_root::TrieStream for TrieStream { + fn new() -> Self { + Self { buffer: Vec::new() } + } + + fn append_empty_data(&mut self) { + self.buffer.push(trie_constants::EMPTY_TRIE); + } + + fn append_leaf(&mut self, key: &[u8], value: TrieStreamValue) { + let kind = match &value { + TrieStreamValue::Inline(..) => NodeKind::Leaf, + TrieStreamValue::Node(..) => NodeKind::HashedValueLeaf, + }; + self.buffer.extend(fuse_nibbles_node(key, kind)); + match &value { + TrieStreamValue::Inline(value) => { + Compact(value.len() as u32).encode_to(&mut self.buffer); + self.buffer.extend_from_slice(value); + }, + TrieStreamValue::Node(hash) => { + self.buffer.extend_from_slice(hash.as_slice()); + }, + }; + } + + fn begin_branch( + &mut self, + maybe_partial: Option<&[u8]>, + maybe_value: Option, + has_children: impl Iterator, + ) { + if let Some(partial) = maybe_partial { + let kind = match &maybe_value { + None => NodeKind::BranchNoValue, + Some(TrieStreamValue::Inline(..)) => NodeKind::BranchWithValue, + Some(TrieStreamValue::Node(..)) => NodeKind::HashedValueBranch, + }; + + self.buffer.extend(fuse_nibbles_node(partial, kind)); + let bm = branch_node_bit_mask(has_children); + self.buffer.extend([bm.0, bm.1].iter()); + } else { + unreachable!("trie stream codec only for no extension trie"); + } + match maybe_value { + None => (), + Some(TrieStreamValue::Inline(value)) => { + Compact(value.len() as u32).encode_to(&mut self.buffer); + self.buffer.extend_from_slice(value); + }, + Some(TrieStreamValue::Node(hash)) => { + self.buffer.extend_from_slice(hash.as_slice()); + }, + } + } + + fn append_extension(&mut self, _key: &[u8]) { + debug_assert!(false, "trie stream codec only for no extension trie"); + } + + fn append_substream(&mut self, other: Self) { + let data = other.out(); + match data.len() { + 0..=31 => data.encode_to(&mut self.buffer), + _ => H::hash(&data).as_ref().encode_to(&mut self.buffer), + } + } + + fn out(self) -> Vec { + self.buffer + } +} + +/// Helper struct for trie node decoder. This implements `codec::Input` on a byte slice, while +/// tracking the absolute position. This is similar to `std::io::Cursor` but does not implement +/// `Read` and `io` is not in `sp-std`. +struct ByteSliceInput<'a> { + data: &'a [u8], + offset: usize, +} + +impl<'a> ByteSliceInput<'a> { + fn new(data: &'a [u8]) -> Self { + ByteSliceInput { data, offset: 0 } + } + + fn take(&mut self, count: usize) -> Result, codec::Error> { + if self.offset + count > self.data.len() { + return Err("out of data".into()) + } + + let range = self.offset..(self.offset + count); + self.offset += count; + Ok(range) + } +} + +impl<'a> Input for ByteSliceInput<'a> { + fn remaining_len(&mut self) -> Result, codec::Error> { + Ok(Some(self.data.len().saturating_sub(self.offset))) + } + + fn read(&mut self, into: &mut [u8]) -> Result<(), codec::Error> { + let range = self.take(into.len())?; + into.copy_from_slice(&self.data[range]); + Ok(()) + } + + fn read_byte(&mut self) -> Result { + if self.offset + 1 > self.data.len() { + return Err("out of data".into()) + } + + let byte = self.data[self.offset]; + self.offset += 1; + Ok(byte) + } +} + +/// Concrete implementation of a [`NodeCodecT`] with SCALE encoding. +/// +/// It is generic over `H` the [`Hasher`]. +#[derive(Default, Clone)] +pub struct NodeCodec(PhantomData); + +impl NodeCodecT for NodeCodec +where + H: Hasher, +{ + const ESCAPE_HEADER: Option = Some(trie_constants::ESCAPE_COMPACT_HEADER); + type Error = Error; + type HashOut = H::Out; + + fn hashed_null_node() -> ::Out { + H::hash(::empty_node()) + } + + fn decode_plan(data: &[u8]) -> Result { + let mut input = ByteSliceInput::new(data); + + let header = NodeHeader::decode(&mut input)?; + let contains_hash = header.contains_hash_of_value(); + + let branch_has_value = if let NodeHeader::Branch(has_value, _) = &header { + *has_value + } else { + // hashed_value_branch + true + }; + + match header { + NodeHeader::Null => Ok(NodePlan::Empty), + NodeHeader::HashedValueBranch(nibble_count) | NodeHeader::Branch(_, nibble_count) => { + let padding = nibble_count % nibble_ops::NIBBLE_PER_BYTE != 0; + // check that the padding is valid (if any) + if padding && nibble_ops::pad_left(data[input.offset]) != 0 { + return Err(Error::BadFormat) + } + let partial = input.take( + (nibble_count + (nibble_ops::NIBBLE_PER_BYTE - 1)) / + nibble_ops::NIBBLE_PER_BYTE, + )?; + let partial_padding = nibble_ops::number_padding(nibble_count); + let bitmap_range = input.take(BITMAP_LENGTH)?; + let bitmap = Bitmap::decode(&data[bitmap_range])?; + let value = if branch_has_value { + Some(if contains_hash { + ValuePlan::Node(input.take(H::LENGTH)?) + } else { + let count = >::decode(&mut input)?.0 as usize; + ValuePlan::Inline(input.take(count)?) + }) + } else { + None + }; + let mut children = [ + None, None, None, None, None, None, None, None, None, None, None, None, None, + None, None, None, + ]; + for (i, child) in children.iter_mut().enumerate().take(nibble_ops::NIBBLE_LENGTH) { + if bitmap.value_at(i) { + let count = >::decode(&mut input)?.0 as usize; + let range = input.take(count)?; + *child = Some(if count == H::LENGTH { + NodeHandlePlan::Hash(range) + } else { + NodeHandlePlan::Inline(range) + }); + } + } + Ok(NodePlan::NibbledBranch { + partial: NibbleSlicePlan::new(partial, partial_padding), + value, + children, + }) + }, + NodeHeader::HashedValueLeaf(nibble_count) | NodeHeader::Leaf(nibble_count) => { + let padding = nibble_count % nibble_ops::NIBBLE_PER_BYTE != 0; + // check that the padding is valid (if any) + if padding && nibble_ops::pad_left(data[input.offset]) != 0 { + return Err(Error::BadFormat) + } + let partial = input.take( + (nibble_count + (nibble_ops::NIBBLE_PER_BYTE - 1)) / + nibble_ops::NIBBLE_PER_BYTE, + )?; + let partial_padding = nibble_ops::number_padding(nibble_count); + let value = if contains_hash { + ValuePlan::Node(input.take(H::LENGTH)?) + } else { + let count = >::decode(&mut input)?.0 as usize; + ValuePlan::Inline(input.take(count)?) + }; + + Ok(NodePlan::Leaf { + partial: NibbleSlicePlan::new(partial, partial_padding), + value, + }) + }, + } + } + + fn is_empty_node(data: &[u8]) -> bool { + data == ::empty_node() + } + + fn empty_node() -> &'static [u8] { + &[trie_constants::EMPTY_TRIE] + } + + fn leaf_node(partial: impl Iterator, number_nibble: usize, value: Value) -> Vec { + let contains_hash = matches!(&value, Value::Node(..)); + let mut output = if contains_hash { + partial_from_iterator_encode(partial, number_nibble, NodeKind::HashedValueLeaf) + } else { + partial_from_iterator_encode(partial, number_nibble, NodeKind::Leaf) + }; + match value { + Value::Inline(value) => { + Compact(value.len() as u32).encode_to(&mut output); + output.extend_from_slice(value); + }, + Value::Node(hash) => { + debug_assert!(hash.len() == H::LENGTH); + output.extend_from_slice(hash); + }, + } + output + } + + fn extension_node( + _partial: impl Iterator, + _nbnibble: usize, + _child: ChildReference<::Out>, + ) -> Vec { + unreachable!("No extension codec.") + } + + fn branch_node( + _children: impl Iterator::Out>>>>, + _maybe_value: Option, + ) -> Vec { + unreachable!("No extension codec.") + } + + fn branch_node_nibbled( + partial: impl Iterator, + number_nibble: usize, + children: impl Iterator::Out>>>>, + value: Option, + ) -> Vec { + let contains_hash = matches!(&value, Some(Value::Node(..))); + let mut output = match (&value, contains_hash) { + (&None, _) => + partial_from_iterator_encode(partial, number_nibble, NodeKind::BranchNoValue), + (_, false) => + partial_from_iterator_encode(partial, number_nibble, NodeKind::BranchWithValue), + (_, true) => + partial_from_iterator_encode(partial, number_nibble, NodeKind::HashedValueBranch), + }; + + let bitmap_index = output.len(); + let mut bitmap: [u8; BITMAP_LENGTH] = [0; BITMAP_LENGTH]; + (0..BITMAP_LENGTH).for_each(|_| output.push(0)); + match value { + Some(Value::Inline(value)) => { + Compact(value.len() as u32).encode_to(&mut output); + output.extend_from_slice(value); + }, + Some(Value::Node(hash)) => { + debug_assert!(hash.len() == H::LENGTH); + output.extend_from_slice(hash); + }, + None => (), + } + Bitmap::encode( + children.map(|maybe_child| match maybe_child.borrow() { + Some(ChildReference::Hash(h)) => { + h.as_ref().encode_to(&mut output); + true + }, + &Some(ChildReference::Inline(inline_data, len)) => { + inline_data.as_ref()[..len].encode_to(&mut output); + true + }, + None => false, + }), + bitmap.as_mut(), + ); + output[bitmap_index..bitmap_index + BITMAP_LENGTH] + .copy_from_slice(&bitmap[..BITMAP_LENGTH]); + output + } +} + +// utils + +/// Encode and allocate node type header (type and size), and partial value. +/// It uses an iterator over encoded partial bytes as input. +fn partial_from_iterator_encode>( + partial: I, + nibble_count: usize, + node_kind: NodeKind, +) -> Vec { + let mut output = Vec::with_capacity(4 + (nibble_count / nibble_ops::NIBBLE_PER_BYTE)); + match node_kind { + NodeKind::Leaf => NodeHeader::Leaf(nibble_count).encode_to(&mut output), + NodeKind::BranchWithValue => NodeHeader::Branch(true, nibble_count).encode_to(&mut output), + NodeKind::BranchNoValue => NodeHeader::Branch(false, nibble_count).encode_to(&mut output), + NodeKind::HashedValueLeaf => + NodeHeader::HashedValueLeaf(nibble_count).encode_to(&mut output), + NodeKind::HashedValueBranch => + NodeHeader::HashedValueBranch(nibble_count).encode_to(&mut output), + }; + output.extend(partial); + output +} + +const BITMAP_LENGTH: usize = 2; + +/// Radix 16 trie, bitmap encoding implementation, +/// it contains children mapping information for a branch +/// (children presence only), it encodes into +/// a compact bitmap encoding representation. +pub(crate) struct Bitmap(u16); + +impl Bitmap { + pub fn decode(data: &[u8]) -> Result { + let value = u16::decode(&mut &data[..])?; + if value == 0 { + Err("Bitmap without a child.".into()) + } else { + Ok(Bitmap(value)) + } + } + + pub fn value_at(&self, i: usize) -> bool { + self.0 & (1u16 << i) != 0 + } + + pub fn encode>(has_children: I, dest: &mut [u8]) { + let mut bitmap: u16 = 0; + let mut cursor: u16 = 1; + for v in has_children { + if v { + bitmap |= cursor + } + cursor <<= 1; + } + dest[0] = (bitmap % 256) as u8; + dest[1] = (bitmap / 256) as u8; + } +} + +/// substrate trie layout +pub struct LayoutV0(PhantomData); + +/// substrate trie layout, with external value nodes. +pub struct LayoutV1(PhantomData); + +impl TrieLayout for LayoutV0 +where + H: Hasher + core::fmt::Debug, +{ + const USE_EXTENSION: bool = false; + const ALLOW_EMPTY: bool = true; + const MAX_INLINE_VALUE: Option = None; + + type Hash = H; + type Codec = NodeCodec; +} + +impl TrieConfiguration for LayoutV0 +where + H: Hasher + core::fmt::Debug, +{ + fn trie_root(input: I) -> ::Out + where + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]>, + { + trie_root::trie_root_no_extension::(input, Self::MAX_INLINE_VALUE) + } + + fn trie_root_unhashed(input: I) -> Vec + where + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]>, + { + trie_root::unhashed_trie_no_extension::( + input, + Self::MAX_INLINE_VALUE, + ) + } + + fn encode_index(input: u32) -> Vec { + codec::Encode::encode(&codec::Compact(input)) + } +} + +impl TrieLayout for LayoutV1 +where + H: Hasher + core::fmt::Debug, +{ + const USE_EXTENSION: bool = false; + const ALLOW_EMPTY: bool = true; + const MAX_INLINE_VALUE: Option = Some(TRIE_VALUE_NODE_THRESHOLD); + + type Hash = H; + type Codec = NodeCodec; +} + +impl TrieConfiguration for LayoutV1 +where + H: Hasher + core::fmt::Debug, +{ + fn trie_root(input: I) -> ::Out + where + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]>, + { + trie_root::trie_root_no_extension::(input, Self::MAX_INLINE_VALUE) + } + + fn trie_root_unhashed(input: I) -> Vec + where + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]>, + { + trie_root::unhashed_trie_no_extension::( + input, + Self::MAX_INLINE_VALUE, + ) + } + + fn encode_index(input: u32) -> Vec { + codec::Encode::encode(&codec::Compact(input)) + } +} + +/// A node header +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub(crate) enum NodeHeader { + Null, + // contains wether there is a value and nibble count + Branch(bool, usize), + // contains nibble count + Leaf(usize), + // contains nibble count. + HashedValueBranch(usize), + // contains nibble count. + HashedValueLeaf(usize), +} + +impl NodeHeader { + pub(crate) fn contains_hash_of_value(&self) -> bool { + matches!(self, NodeHeader::HashedValueBranch(_) | NodeHeader::HashedValueLeaf(_)) + } +} + +/// NodeHeader without content +pub(crate) enum NodeKind { + Leaf, + BranchNoValue, + BranchWithValue, + HashedValueLeaf, + HashedValueBranch, +} + +impl Encode for NodeHeader { + fn encode_to(&self, output: &mut T) { + match self { + NodeHeader::Null => output.push_byte(trie_constants::EMPTY_TRIE), + NodeHeader::Branch(true, nibble_count) => + encode_size_and_prefix(*nibble_count, trie_constants::BRANCH_WITH_MASK, 2, output), + NodeHeader::Branch(false, nibble_count) => encode_size_and_prefix( + *nibble_count, + trie_constants::BRANCH_WITHOUT_MASK, + 2, + output, + ), + NodeHeader::Leaf(nibble_count) => + encode_size_and_prefix(*nibble_count, trie_constants::LEAF_PREFIX_MASK, 2, output), + NodeHeader::HashedValueBranch(nibble_count) => encode_size_and_prefix( + *nibble_count, + trie_constants::ALT_HASHING_BRANCH_WITH_MASK, + 4, + output, + ), + NodeHeader::HashedValueLeaf(nibble_count) => encode_size_and_prefix( + *nibble_count, + trie_constants::ALT_HASHING_LEAF_PREFIX_MASK, + 3, + output, + ), + } + } +} + +impl codec::EncodeLike for NodeHeader {} + +impl Decode for NodeHeader { + fn decode(input: &mut I) -> Result { + let i = input.read_byte()?; + if i == trie_constants::EMPTY_TRIE { + return Ok(NodeHeader::Null) + } + match i & (0b11 << 6) { + trie_constants::LEAF_PREFIX_MASK => Ok(NodeHeader::Leaf(decode_size(i, input, 2)?)), + trie_constants::BRANCH_WITH_MASK => + Ok(NodeHeader::Branch(true, decode_size(i, input, 2)?)), + trie_constants::BRANCH_WITHOUT_MASK => + Ok(NodeHeader::Branch(false, decode_size(i, input, 2)?)), + trie_constants::EMPTY_TRIE => { + if i & (0b111 << 5) == trie_constants::ALT_HASHING_LEAF_PREFIX_MASK { + Ok(NodeHeader::HashedValueLeaf(decode_size(i, input, 3)?)) + } else if i & (0b1111 << 4) == trie_constants::ALT_HASHING_BRANCH_WITH_MASK { + Ok(NodeHeader::HashedValueBranch(decode_size(i, input, 4)?)) + } else { + // do not allow any special encoding + Err("Unallowed encoding".into()) + } + }, + _ => unreachable!(), + } + } +} + +/// Returns an iterator over encoded bytes for node header and size. +/// Size encoding allows unlimited, length inefficient, representation, but +/// is bounded to 16 bit maximum value to avoid possible DOS. +pub(crate) fn size_and_prefix_iterator( + size: usize, + prefix: u8, + prefix_mask: usize, +) -> impl Iterator { + let max_value = 255u8 >> prefix_mask; + let l1 = core::cmp::min((max_value as usize).saturating_sub(1), size); + let (first_byte, mut rem) = if size == l1 { + (once(prefix + l1 as u8), 0) + } else { + (once(prefix + max_value), size - l1) + }; + let next_bytes = move || { + if rem > 0 { + if rem < 256 { + let result = rem - 1; + rem = 0; + Some(result as u8) + } else { + rem = rem.saturating_sub(255); + Some(255) + } + } else { + None + } + }; + first_byte.chain(core::iter::from_fn(next_bytes)) +} + +/// Encodes size and prefix to a stream output. +fn encode_size_and_prefix(size: usize, prefix: u8, prefix_mask: usize, out: &mut W) +where + W: Output + ?Sized, +{ + for b in size_and_prefix_iterator(size, prefix, prefix_mask) { + out.push_byte(b) + } +} + +/// Decode size only from stream input and header byte. +fn decode_size( + first: u8, + input: &mut impl Input, + prefix_mask: usize, +) -> Result { + let max_value = 255u8 >> prefix_mask; + let mut result = (first & max_value) as usize; + if result < max_value as usize { + return Ok(result) + } + result -= 1; + loop { + let n = input.read_byte()? as usize; + if n < 255 { + return Ok(result + n + 1) + } + result += 255; + } +} + +/// Error type used for trie related errors. +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum Error { + BadFormat, + Decode(codec::Error), + Trie(Box>), +} + +impl core::fmt::Display for Error { + fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { + fmt.write_str("Error") + } +} + +impl std::error::Error for Error where H: core::fmt::Debug {} + +impl From for Error { + fn from(x: codec::Error) -> Self { + Error::Decode(x) + } +} + +impl From>> for Error { + fn from(x: Box>) -> Self { + Error::Trie(x) + } +} \ No newline at end of file diff --git a/src/lib/shared_utils/src/common/participant_crypto/merkle/mod.rs b/src/lib/shared_utils/src/common/participant_crypto/merkle/mod.rs new file mode 100644 index 00000000..87412a15 --- /dev/null +++ b/src/lib/shared_utils/src/common/participant_crypto/merkle/mod.rs @@ -0,0 +1,103 @@ + +use backing::ChildrenBacking; +use candid::Principal; +use hash256_std_hasher::Hash256StdHasher; +use hash_db::Hasher; +use serde::{Deserialize, Serialize}; +use trie_db::{proof::{generate_proof, verify_proof}, NodeCodec, TrieDBMut, TrieDBMutBuilder, TrieLayout, TrieMut}; + +use super::ProofOfChildren; + +mod layout; +mod backing; + +pub(super) type Hash = [u8; 32]; +pub(super) type ProofOfInclusion = Vec>; + +#[derive(Default, Debug, Clone, PartialEq)] +pub struct Blake3Hasher; + +impl Hasher for Blake3Hasher { + type Out = Hash; + + type StdHasher = Hash256StdHasher; + + const LENGTH: usize = 32; + + fn hash(x: &[u8]) -> Hash { + let mut hasher = blake3::Hasher::new(); + hasher.update(x); + let hs: [u8; 32] = hasher.finalize().into(); + hs + } +} + +pub(super) type ChildreenTreeLayout = layout::LayoutV1; + +#[derive(Serialize, Deserialize)] +pub struct ChildrenMerkle { + db: ChildrenBacking, + pub(super) root: Hash, + pub(super) proof_of_children: Option, +} + +impl Default for ChildrenMerkle { + fn default() -> Self { + Self { + db: ChildrenBacking::default(), + root: ::Codec::hashed_null_node(), + proof_of_children: None, + } + } +} + +impl ChildrenMerkle { + fn trie_mut(&mut self) -> TrieDBMut<'_, ChildreenTreeLayout> { + TrieDBMutBuilder::from_existing( + &mut self.db, + &mut self.root, + ).build() + } + + pub fn insert_children(&mut self, children: impl IntoIterator) { + let prev_root = self.root; + let mut trie = self.trie_mut(); + for child in children { + let key = Blake3Hasher::hash(child.as_slice()); + trie.insert(&key, b"_").expect("insertion should not fail"); + } + std::mem::drop(trie); + if self.root != prev_root { + // mark proof of children as stale + self.proof_of_children = None; + } + } + + pub fn remove_child(&mut self, child: Principal) { + let mut trie = self.trie_mut(); + let key = Blake3Hasher::hash(child.as_slice()); + trie.remove(&key).expect("removal should not fail"); + std::mem::drop(trie); + + // mark proof of children as stale + self.proof_of_children = None; + } + + pub fn proof_of_inclusion(&self, child: Principal) -> Result { + let key = Blake3Hasher::hash(child.as_slice()); + generate_proof::<_, ChildreenTreeLayout, _, _>( + &self.db, + &self.root, + [&key], + ).map_err(|e| format!("failed to generate proof of inclusion {e:?}")) + } + + pub fn verify_proof_of_inclusion(root: Hash, proof_of_inclusion: &[Vec], child: Principal) -> Result<(), String> { + let key = Blake3Hasher::hash(child.as_slice()); + verify_proof::( + &root, + proof_of_inclusion, + [&(key, Some(b"_"))] + ).map_err(|_| "invalid proof of inclusion".to_string()) + } +} diff --git a/src/lib/shared_utils/src/common/participant_crypto/mod.rs b/src/lib/shared_utils/src/common/participant_crypto/mod.rs index bda08a4b..2b105192 100644 --- a/src/lib/shared_utils/src/common/participant_crypto/mod.rs +++ b/src/lib/shared_utils/src/common/participant_crypto/mod.rs @@ -1,10 +1,12 @@ //! Utilities for creating and verifying proof that a given canister is a part of YRAL Backend canisters mod types; +pub mod merkle; use std::{cell::RefCell, collections::HashMap, thread::LocalKey}; use candid::{CandidType, Principal}; -use ed25519_dalek::{Signature, VerifyingKey, Verifier}; +use ed25519_compact::{Signature, PublicKey}; +use merkle::{ChildrenMerkle, ProofOfInclusion}; use types::{ManagementCanisterSchnorrPublicKeyReply, ManagementCanisterSchnorrPublicKeyRequest, ManagementCanisterSignatureReply, ManagementCanisterSignatureRequest, SchnorrAlgorithm, SchnorrKeyId}; use serde::{Serialize, Deserialize}; @@ -32,12 +34,12 @@ pub(crate) type LocalPoPStore = LocalKey>; pub struct PubKeyCache(HashMap>); impl PubKeyCache { - async fn get_or_init_public_key(store: &'static LocalPoPStore, principal: Principal) -> Result { + async fn get_or_init_public_key(store: &'static LocalPoPStore, principal: Principal) -> Result { let maybe_pk = store.with_borrow(|store| { store.pubkey_cache().0.get(&principal).cloned() }); if let Some(pk) = maybe_pk { - return VerifyingKey::try_from(pk.as_slice()) + return PublicKey::from_slice(pk.as_slice()) .map_err(|_| "invalid public key".to_string()) } @@ -60,7 +62,7 @@ impl PubKeyCache { })?; let key = key_res.public_key; - let vk = VerifyingKey::try_from(key.as_slice()) + let vk = PublicKey::from_slice(key.as_slice()) .map_err(|_| "invalid public key".to_string())?; store.with_borrow_mut(|store| { store.pubkey_cache_mut().0.insert(principal, key.clone()); @@ -73,14 +75,14 @@ impl PubKeyCache { #[derive(Serialize)] struct ProofOfAuthorityMsg { prefix: &'static [u8], - pub child: Principal, + pub merkle_root: [u8; 32], } impl ProofOfAuthorityMsg { - pub fn new(child: Principal) -> Self { + pub fn new(merkle_root: [u8; 32]) -> Self { Self { - prefix: b"CHILD", - child, + prefix: b"CHILDREN", + merkle_root, } } @@ -93,17 +95,16 @@ impl ProofOfAuthorityMsg { } } -/// Proof that this canister id is a child of the parent canister +/// Proof that a given merkle tree contains children of the parent canister #[derive(Clone, CandidType, Serialize, Deserialize)] -struct ProofOfChild { - // Principal of the child - principal: Principal, +struct ProofOfChildren { + merkle_root: [u8; 32], signature: Vec, } -impl ProofOfChild { - async fn new(child: Principal) -> Result { - let message = ProofOfAuthorityMsg::new(child); +impl ProofOfChildren { + async fn new(merkle_root: [u8; 32]) -> Result { + let message = ProofOfAuthorityMsg::new(merkle_root); let sign_args = ManagementCanisterSignatureRequest { message: message.serialize_cbor(), derivation_path: vec![], @@ -123,13 +124,13 @@ impl ProofOfChild { .map_err(|(_, msg)| format!("unable to sign: {msg}"))?; Ok(Self { - principal: child, + merkle_root, signature: sig_res.signature, }) } - pub fn verify(&self, parent_key: &VerifyingKey) -> Result<(), String> { - let message = ProofOfAuthorityMsg::new(self.principal); + pub fn verify(&self, parent_key: &PublicKey) -> Result<(), String> { + let message = ProofOfAuthorityMsg::new(self.merkle_root); let message_raw = message.serialize_cbor(); let sig = Signature::from_slice(&self.signature).map_err(|_| "invalid proof".to_string())?; @@ -140,6 +141,36 @@ impl ProofOfChild { } } +// Proof that given canister id exists in the merkle tree containing the children of the parent canister +#[derive(Clone, CandidType, Serialize, Deserialize)] +struct ProofOfChild { + principal: Principal, + children_proof: ProofOfChildren, + proof_of_inclusion: ProofOfInclusion, +} + +impl ProofOfChild { + pub fn new(children_proof: ProofOfChildren, principal: Principal, proof_of_inclusion: ProofOfInclusion) -> Self { + Self { + principal, + children_proof, + proof_of_inclusion, + } + } + + pub fn verify(&self, parent_key: &PublicKey) -> Result<(), String> { + self.children_proof.verify(parent_key)?; + + ChildrenMerkle::verify_proof_of_inclusion( + self.children_proof.merkle_root, + &self.proof_of_inclusion, + self.principal, + )?; + + Ok(()) + } +} + #[derive(Clone, CandidType, Serialize, Deserialize)] pub struct ProofOfParticipation { chain: Vec, @@ -153,12 +184,34 @@ impl ProofOfParticipation { } } - pub async fn derive_for_child(self, child: Principal) -> Result { - let mut chain = self.chain; - let proof = ProofOfChild::new(child).await?; - chain.push(proof); - Ok(Self { - chain, + pub async fn derive_for_child(&self, store: &'static LocalPoPStore, child: Principal) -> Result { + let (proof_of_inclusion, maybe_poc) = store.with_borrow(|s| { + let children_merkle = s.children_merkle(); + children_merkle.proof_of_inclusion(child) + .map(|poi| { + (poi, children_merkle.proof_of_children.clone()) + }) + })?; + let poc = if let Some(poc) = maybe_poc { + poc + } else { + let root = store.with_borrow(|s| s.children_merkle().root); + let poc = ProofOfChildren::new(root).await?; + store.with_borrow_mut(|s| { + s.children_merkle_mut().proof_of_children = Some(poc.clone()); + }); + poc + }; + + let mut chain = self.chain.clone(); + chain.push(ProofOfChild::new( + poc, + child, + proof_of_inclusion, + )); + + Ok(ProofOfParticipation { + chain }) } @@ -193,3 +246,9 @@ pub trait ProofOfParticipationStore { fn platform_orchestrator(&self) -> Principal; } + +pub trait ProofOfParticipationDeriverStore { + fn children_merkle(&self) -> &merkle::ChildrenMerkle; + + fn children_merkle_mut(&mut self) -> &mut merkle::ChildrenMerkle; +} From 24cf650d91b695a823375652458fd2bf3c66015b Mon Sep 17 00:00:00 2001 From: rupansh Date: Wed, 27 Nov 2024 13:49:32 +0530 Subject: [PATCH 11/20] chore(integration_tests/authentication/referral): increase waiting time after provisioning subnet orchestrator --- ...d_referee_receive_five_hundred_tokens_as_referral_rewards.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/integration_tests/tests/authentication/when_a_new_user_signs_up_from_a_referral_then_the_new_user_is_given_a_thousand_utility_tokens_for_signing_up_and_the_referrer_and_referee_receive_five_hundred_tokens_as_referral_rewards.rs b/src/lib/integration_tests/tests/authentication/when_a_new_user_signs_up_from_a_referral_then_the_new_user_is_given_a_thousand_utility_tokens_for_signing_up_and_the_referrer_and_referee_receive_five_hundred_tokens_as_referral_rewards.rs index 0a25a7be..d1cb77c5 100644 --- a/src/lib/integration_tests/tests/authentication/when_a_new_user_signs_up_from_a_referral_then_the_new_user_is_given_a_thousand_utility_tokens_for_signing_up_and_the_referrer_and_referee_receive_five_hundred_tokens_as_referral_rewards.rs +++ b/src/lib/integration_tests/tests/authentication/when_a_new_user_signs_up_from_a_referral_then_the_new_user_is_given_a_thousand_utility_tokens_for_signing_up_and_the_referrer_and_referee_receive_five_hundred_tokens_as_referral_rewards.rs @@ -482,7 +482,7 @@ fn when_a_new_user_signs_up_from_a_referral_then_the_new_user_is_given_a_thousan &app_subnets[0] ); let user_index = user_index_res.unwrap(); - for _ in 0..30 { + for _ in 0..1000 { pic.tick(); } From 86dcaac21360f4a3cea9f2e949d867906e7691ee Mon Sep 17 00:00:00 2001 From: rupansh Date: Wed, 27 Nov 2024 19:57:53 +0530 Subject: [PATCH 12/20] Revert "chore(integration_tests/authentication/referral): increase waiting time after provisioning subnet orchestrator" This reverts commit 24cf650d91b695a823375652458fd2bf3c66015b. --- ...d_referee_receive_five_hundred_tokens_as_referral_rewards.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/integration_tests/tests/authentication/when_a_new_user_signs_up_from_a_referral_then_the_new_user_is_given_a_thousand_utility_tokens_for_signing_up_and_the_referrer_and_referee_receive_five_hundred_tokens_as_referral_rewards.rs b/src/lib/integration_tests/tests/authentication/when_a_new_user_signs_up_from_a_referral_then_the_new_user_is_given_a_thousand_utility_tokens_for_signing_up_and_the_referrer_and_referee_receive_five_hundred_tokens_as_referral_rewards.rs index d1cb77c5..0a25a7be 100644 --- a/src/lib/integration_tests/tests/authentication/when_a_new_user_signs_up_from_a_referral_then_the_new_user_is_given_a_thousand_utility_tokens_for_signing_up_and_the_referrer_and_referee_receive_five_hundred_tokens_as_referral_rewards.rs +++ b/src/lib/integration_tests/tests/authentication/when_a_new_user_signs_up_from_a_referral_then_the_new_user_is_given_a_thousand_utility_tokens_for_signing_up_and_the_referrer_and_referee_receive_five_hundred_tokens_as_referral_rewards.rs @@ -482,7 +482,7 @@ fn when_a_new_user_signs_up_from_a_referral_then_the_new_user_is_given_a_thousan &app_subnets[0] ); let user_index = user_index_res.unwrap(); - for _ in 0..1000 { + for _ in 0..30 { pic.tick(); } From 3018f1661e602735b8bd8703108f4ca262004949 Mon Sep 17 00:00:00 2001 From: rupansh Date: Wed, 27 Nov 2024 19:58:28 +0530 Subject: [PATCH 13/20] fix(scripts/local_deploy/install_all_canisters): add back gzip --- scripts/canisters/local_deploy/install_all_canisters.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/scripts/canisters/local_deploy/install_all_canisters.sh b/scripts/canisters/local_deploy/install_all_canisters.sh index db0069d7..89029c86 100755 --- a/scripts/canisters/local_deploy/install_all_canisters.sh +++ b/scripts/canisters/local_deploy/install_all_canisters.sh @@ -27,8 +27,17 @@ dfx canister create --no-wallet post_cache dfx canister create --no-wallet user_index dfx canister create --no-wallet platform_orchestrator +gzip_canister() { + gzip -f -1 ./target/wasm32-unknown-unknown/release/$1.wasm +} + scripts/candid_generator.sh +gzip_canister individual_user_template +gzip_canister user_index +gzip_canister post_cache +gzip_canister platform_orchestrator + if [[ $skip_test != true ]] then cargo test From 97003fc0a4619b71d65990e089c01047170085d9 Mon Sep 17 00:00:00 2001 From: rupansh Date: Wed, 27 Nov 2024 19:59:48 +0530 Subject: [PATCH 14/20] fix+refactor(shared_utils/participant_crypto): use GenericArray instead of [u8; 32] for Hashes --- Cargo.lock | 21 +++++++--- src/lib/shared_utils/Cargo.toml | 1 + .../common/participant_crypto/merkle/mod.rs | 39 ++++++++++++++++--- .../src/common/participant_crypto/mod.rs | 8 ++-- 4 files changed, 55 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index da16a733..7864e69d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -343,7 +343,7 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -863,7 +863,7 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ - "generic-array", + "generic-array 0.14.7", "rand_core", "subtle", "zeroize", @@ -875,7 +875,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array", + "generic-array 0.14.7", "typenum", ] @@ -1267,7 +1267,7 @@ dependencies = [ "crypto-bigint", "digest", "ff", - "generic-array", + "generic-array 0.14.7", "group", "pem-rfc7468", "pkcs8", @@ -1498,6 +1498,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "generic-array" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cb8bc4c28d15ade99c7e90b219f30da4be5c88e586277e8cbe886beeb868ab2" +dependencies = [ + "serde", + "typenum", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -5330,7 +5340,7 @@ checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct", "der", - "generic-array", + "generic-array 0.14.7", "pkcs8", "subtle", "zeroize", @@ -5563,6 +5573,7 @@ dependencies = [ "ciborium", "ed25519-compact", "futures", + "generic-array 1.1.1", "hash-db", "hash256-std-hasher", "ic-cdk 0.15.1", diff --git a/src/lib/shared_utils/Cargo.toml b/src/lib/shared_utils/Cargo.toml index 89bb3ea9..37a56d88 100644 --- a/src/lib/shared_utils/Cargo.toml +++ b/src/lib/shared_utils/Cargo.toml @@ -27,6 +27,7 @@ memory-db = { version = "0.32.0", default-features = false } trie-root = { version = "0.18.0", default-features = false } # monotree = { git = "https://github.com/rupansh-sekar-yral/monotree.git", rev = "1504007dae91a909fbb29bfebb36181ef3333cb8" } # hashbrown = { version = "0.7", features = ["serde"] } +generic-array = { version = "1.1.1", features = ["serde"] } [dev-dependencies] test_utils = { workspace = true } diff --git a/src/lib/shared_utils/src/common/participant_crypto/merkle/mod.rs b/src/lib/shared_utils/src/common/participant_crypto/merkle/mod.rs index 87412a15..e3c5a575 100644 --- a/src/lib/shared_utils/src/common/participant_crypto/merkle/mod.rs +++ b/src/lib/shared_utils/src/common/participant_crypto/merkle/mod.rs @@ -1,6 +1,7 @@ use backing::ChildrenBacking; -use candid::Principal; +use candid::{CandidType, Principal}; +use generic_array::{typenum::U32, GenericArray}; use hash256_std_hasher::Hash256StdHasher; use hash_db::Hasher; use serde::{Deserialize, Serialize}; @@ -11,7 +12,35 @@ use super::ProofOfChildren; mod layout; mod backing; -pub(super) type Hash = [u8; 32]; +// We use GenericArray instead of [u8; 32] because serde::Serialize generates an implementation +// that is too complex for IC to run... +#[derive(Serialize, Deserialize, Clone, Copy, Default, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)] +pub struct Hash(GenericArray); + +impl AsRef<[u8]> for Hash { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl AsMut<[u8]> for Hash { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +impl CandidType for Hash { + fn _ty() -> candid::types::Type { + <[u8; 32]>::_ty() + } + + fn idl_serialize(&self, serializer: S) -> Result<(), S::Error> + where + S: candid::types::Serializer { + self.0.idl_serialize(serializer) + } +} + pub(super) type ProofOfInclusion = Vec>; #[derive(Default, Debug, Clone, PartialEq)] @@ -28,7 +57,7 @@ impl Hasher for Blake3Hasher { let mut hasher = blake3::Hasher::new(); hasher.update(x); let hs: [u8; 32] = hasher.finalize().into(); - hs + Hash(hs.into()) } } @@ -64,7 +93,7 @@ impl ChildrenMerkle { let mut trie = self.trie_mut(); for child in children { let key = Blake3Hasher::hash(child.as_slice()); - trie.insert(&key, b"_").expect("insertion should not fail"); + trie.insert(key.as_ref(), b"_").expect("insertion should not fail"); } std::mem::drop(trie); if self.root != prev_root { @@ -76,7 +105,7 @@ impl ChildrenMerkle { pub fn remove_child(&mut self, child: Principal) { let mut trie = self.trie_mut(); let key = Blake3Hasher::hash(child.as_slice()); - trie.remove(&key).expect("removal should not fail"); + trie.remove(key.as_ref()).expect("removal should not fail"); std::mem::drop(trie); // mark proof of children as stale diff --git a/src/lib/shared_utils/src/common/participant_crypto/mod.rs b/src/lib/shared_utils/src/common/participant_crypto/mod.rs index 2b105192..865a7427 100644 --- a/src/lib/shared_utils/src/common/participant_crypto/mod.rs +++ b/src/lib/shared_utils/src/common/participant_crypto/mod.rs @@ -75,11 +75,11 @@ impl PubKeyCache { #[derive(Serialize)] struct ProofOfAuthorityMsg { prefix: &'static [u8], - pub merkle_root: [u8; 32], + pub merkle_root: merkle::Hash, } impl ProofOfAuthorityMsg { - pub fn new(merkle_root: [u8; 32]) -> Self { + pub fn new(merkle_root: merkle::Hash) -> Self { Self { prefix: b"CHILDREN", merkle_root, @@ -98,12 +98,12 @@ impl ProofOfAuthorityMsg { /// Proof that a given merkle tree contains children of the parent canister #[derive(Clone, CandidType, Serialize, Deserialize)] struct ProofOfChildren { - merkle_root: [u8; 32], + merkle_root: merkle::Hash, signature: Vec, } impl ProofOfChildren { - async fn new(merkle_root: [u8; 32]) -> Result { + async fn new(merkle_root: merkle::Hash) -> Result { let message = ProofOfAuthorityMsg::new(merkle_root); let sign_args = ManagementCanisterSignatureRequest { message: message.serialize_cbor(), From d5c60357ca3373c676c069580055913146cdd128 Mon Sep 17 00:00:00 2001 From: rupansh Date: Fri, 29 Nov 2024 22:31:12 +0530 Subject: [PATCH 15/20] refactor: switch from ciborium to minicbor-serde TODO: check compatibility between the two --- Cargo.lock | 38 +++++++++++++++++-- Cargo.toml | 2 +- .../individual_user_template/Cargo.toml | 2 +- .../api/canister_lifecycle/post_upgrade.rs | 3 +- .../src/api/canister_lifecycle/pre_upgrade.rs | 6 +-- .../src/data_model/kv_storage.rs | 10 ++--- src/canister/platform_orchestrator/Cargo.toml | 2 +- .../api/canister_lifecycle/post_upgrade.rs | 3 +- .../src/api/canister_lifecycle/pre_upgrade.rs | 6 +-- .../src/data_model/mod.rs | 6 +-- src/canister/user_index/Cargo.toml | 2 +- .../api/canister_lifecycle/post_upgrade.rs | 4 +- .../src/api/canister_lifecycle/pre_upgrade.rs | 6 +-- .../create_pool_of_available_canisters.rs | 4 +- src/lib/shared_utils/Cargo.toml | 2 +- .../src/common/participant_crypto/mod.rs | 7 +--- src/lib/shared_utils/src/common/types/wasm.rs | 11 ++---- 17 files changed, 62 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7864e69d..53c04d50 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3749,7 +3749,6 @@ name = "individual_user_template" version = "0.1.0" dependencies = [ "candid", - "ciborium", "futures", "hex", "ic-base-types", @@ -3767,6 +3766,7 @@ dependencies = [ "ic-sns-wasm", "ic-stable-structures", "icrc-ledger-types 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "minicbor-serde", "serde", "serde_bytes", "serde_json", @@ -4075,6 +4075,36 @@ dependencies = [ "unicase", ] +[[package]] +name = "minicbor" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0452a60c1863c1f50b5f77cd295e8d2786849f35883f0b9e18e7e6e1b5691b0" +dependencies = [ + "minicbor-derive", +] + +[[package]] +name = "minicbor-derive" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd2209fff77f705b00c737016a48e73733d7fbccb8b007194db148f03561fb70" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "minicbor-serde" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "becf18ac384ecf6f53b2db3b1549eebff664c67ecf259ae99be5912193291686" +dependencies = [ + "minicbor", + "serde", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -4491,10 +4521,10 @@ name = "platform_orchestrator" version = "0.1.0" dependencies = [ "candid", - "ciborium", "ic-cdk 0.15.1", "ic-cdk-macros 0.16.0", "ic-stable-structures", + "minicbor-serde", "serde", "shared_utils", "test_utils", @@ -5570,7 +5600,6 @@ version = "0.1.0" dependencies = [ "blake3", "candid", - "ciborium", "ed25519-compact", "futures", "generic-array 1.1.1", @@ -5581,6 +5610,7 @@ dependencies = [ "ic-stable-structures", "icrc-ledger-types 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "memory-db", + "minicbor-serde", "parity-scale-codec", "pprof", "rmp-serde", @@ -6287,12 +6317,12 @@ name = "user_index" version = "0.1.0" dependencies = [ "candid", - "ciborium", "futures", "ic-cdk 0.15.1", "ic-cdk-macros 0.16.0", "ic-cdk-timers", "ic-stable-structures", + "minicbor-serde", "serde", "serde_json", "shared_utils", diff --git a/Cargo.toml b/Cargo.toml index ee2f9747..98dab6a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ members = [ [workspace.dependencies] candid = "0.10.2" -ciborium = "0.2.1" +minicbor-serde = { version = "0.3.2", features = ["std"] } pocket-ic = "6.0.0" ic-cdk = "0.15.1" ic-cdk-timers = "0.7.0" diff --git a/src/canister/individual_user_template/Cargo.toml b/src/canister/individual_user_template/Cargo.toml index a7ae276f..daa9ad3f 100644 --- a/src/canister/individual_user_template/Cargo.toml +++ b/src/canister/individual_user_template/Cargo.toml @@ -10,7 +10,7 @@ crate-type = ["cdylib"] [dependencies] candid = { workspace = true } -ciborium = { workspace = true } +minicbor-serde = { workspace = true } ic-stable-structures = { workspace = true } ic-cdk = { workspace = true } ic-cdk-timers = { workspace = true } diff --git a/src/canister/individual_user_template/src/api/canister_lifecycle/post_upgrade.rs b/src/canister/individual_user_template/src/api/canister_lifecycle/post_upgrade.rs index 7197f3bf..282e952b 100644 --- a/src/canister/individual_user_template/src/api/canister_lifecycle/post_upgrade.rs +++ b/src/canister/individual_user_template/src/api/canister_lifecycle/post_upgrade.rs @@ -1,4 +1,3 @@ -use ciborium::de; use ic_cdk::api::call::ArgDecoderConfig; use ic_cdk_macros::post_upgrade; use ic_stable_structures::Memory; @@ -31,7 +30,7 @@ fn restore_data_from_stable_memory() { heap_data.read(4, &mut canister_data_bytes); let canister_data = - de::from_reader(&*canister_data_bytes).expect("Failed to deserialize heap data"); + minicbor_serde::from_slice(&canister_data_bytes).expect("Failed to deserialize heap data"); CANISTER_DATA.with(|canister_data_ref_cell| { *canister_data_ref_cell.borrow_mut() = canister_data; }); diff --git a/src/canister/individual_user_template/src/api/canister_lifecycle/pre_upgrade.rs b/src/canister/individual_user_template/src/api/canister_lifecycle/pre_upgrade.rs index 85226274..ba7d0f6c 100644 --- a/src/canister/individual_user_template/src/api/canister_lifecycle/pre_upgrade.rs +++ b/src/canister/individual_user_template/src/api/canister_lifecycle/pre_upgrade.rs @@ -1,4 +1,3 @@ -use ciborium::ser; use ic_cdk::api::stable; use ic_cdk_macros::pre_upgrade; use ic_stable_structures::writer::Writer; @@ -9,9 +8,8 @@ use crate::CANISTER_DATA; #[pre_upgrade] fn pre_upgrade() { - let mut state_bytes = vec![]; - CANISTER_DATA.with(|canister_data_ref_cell| { - ser::into_writer(&*canister_data_ref_cell.borrow(), &mut state_bytes) + let state_bytes = CANISTER_DATA.with(|canister_data_ref_cell| { + minicbor_serde::to_vec(&*canister_data_ref_cell.borrow()) }) .expect("failed to encode state"); diff --git a/src/canister/individual_user_template/src/data_model/kv_storage.rs b/src/canister/individual_user_template/src/data_model/kv_storage.rs index 12546545..5c49649f 100644 --- a/src/canister/individual_user_template/src/data_model/kv_storage.rs +++ b/src/canister/individual_user_template/src/data_model/kv_storage.rs @@ -37,13 +37,12 @@ impl From for NamespaceForFrontend { impl Storable for Namespace { fn to_bytes(&self) -> std::borrow::Cow<[u8]> { - let mut bytes = vec![]; - ciborium::ser::into_writer(self, &mut bytes).unwrap(); + let bytes = minicbor_serde::to_vec(self).unwrap(); Cow::Owned(bytes) } fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Self { - let namespace: Self = ciborium::de::from_reader(bytes.as_ref()).unwrap(); + let namespace: Self = minicbor_serde::from_slice(bytes.as_ref()).unwrap(); namespace } @@ -188,13 +187,12 @@ pub struct NameSpaceKey { impl Storable for NameSpaceKey { fn to_bytes(&self) -> std::borrow::Cow<[u8]> { - let mut bytes = vec![]; - ciborium::ser::into_writer(self, &mut bytes).unwrap(); + let bytes = minicbor_serde::to_vec(self).unwrap(); Cow::Owned(bytes) } fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Self { - let namespace: Self = ciborium::de::from_reader(bytes.as_ref()).unwrap(); + let namespace: Self = minicbor_serde::from_slice(bytes.as_ref()).unwrap(); namespace } diff --git a/src/canister/platform_orchestrator/Cargo.toml b/src/canister/platform_orchestrator/Cargo.toml index eccfa50d..97f33c44 100644 --- a/src/canister/platform_orchestrator/Cargo.toml +++ b/src/canister/platform_orchestrator/Cargo.toml @@ -15,7 +15,7 @@ ic-cdk = { workspace = true } serde = { workspace = true } ic-cdk-macros = { workspace = true } ic-stable-structures = { workspace = true } -ciborium = { workspace = true } +minicbor-serde = { workspace = true } shared_utils = { workspace = true } [dev-dependencies] diff --git a/src/canister/platform_orchestrator/src/api/canister_lifecycle/post_upgrade.rs b/src/canister/platform_orchestrator/src/api/canister_lifecycle/post_upgrade.rs index bff21a44..c2a5f0fc 100644 --- a/src/canister/platform_orchestrator/src/api/canister_lifecycle/post_upgrade.rs +++ b/src/canister/platform_orchestrator/src/api/canister_lifecycle/post_upgrade.rs @@ -1,4 +1,3 @@ -use ciborium::de; use ic_cdk::api::call::ArgDecoderConfig; use ic_cdk_macros::post_upgrade; use ic_stable_structures::Memory; @@ -25,7 +24,7 @@ fn restore_data_from_stable_memory() { let mut canister_data_bytes = vec![0; heap_data_len]; heap_data.read(4, &mut canister_data_bytes); let canister_data = - de::from_reader(&*canister_data_bytes).expect("Failed to deserialize heap data"); + minicbor_serde::from_slice(&canister_data_bytes).expect("Failed to deserialize heap data"); CANISTER_DATA.with_borrow_mut(|cd| { *cd = canister_data; }) diff --git a/src/canister/platform_orchestrator/src/api/canister_lifecycle/pre_upgrade.rs b/src/canister/platform_orchestrator/src/api/canister_lifecycle/pre_upgrade.rs index 7a20bd3a..7d12952e 100644 --- a/src/canister/platform_orchestrator/src/api/canister_lifecycle/pre_upgrade.rs +++ b/src/canister/platform_orchestrator/src/api/canister_lifecycle/pre_upgrade.rs @@ -1,15 +1,13 @@ use ic_cdk_macros::pre_upgrade; use ic_stable_structures::writer::Writer; -use ciborium::ser; use crate::{data_model::memory, CANISTER_DATA}; #[pre_upgrade] fn pre_upgrade() { - let mut state_bytes = vec![]; - CANISTER_DATA.with_borrow(|canister_data| - ser::into_writer(&*canister_data, &mut state_bytes) + let state_bytes = CANISTER_DATA.with_borrow(|canister_data| + minicbor_serde::to_vec(canister_data) ) .expect("failed to encode state"); let len = state_bytes.len() as u32; diff --git a/src/canister/platform_orchestrator/src/data_model/mod.rs b/src/canister/platform_orchestrator/src/data_model/mod.rs index abdf9329..f033388e 100644 --- a/src/canister/platform_orchestrator/src/data_model/mod.rs +++ b/src/canister/platform_orchestrator/src/data_model/mod.rs @@ -1,4 +1,3 @@ -use ciborium::de; use ic_stable_structures::{storable::Bound, StableBTreeMap, StableLog, Storable}; use std::{ borrow::Cow, @@ -161,13 +160,12 @@ pub struct CanisterUpgradeStatus { impl Storable for CanisterUpgradeStatus { fn to_bytes(&self) -> std::borrow::Cow<[u8]> { - let mut bytes = vec![]; - ciborium::ser::into_writer(self, &mut bytes).unwrap(); + let bytes = minicbor_serde::to_vec(self).unwrap(); Cow::Owned(bytes) } fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Self { - let canister_upgrade_log: CanisterUpgradeStatus = de::from_reader(bytes.as_ref()).unwrap(); + let canister_upgrade_log: CanisterUpgradeStatus = minicbor_serde::from_slice(bytes.as_ref()).unwrap(); canister_upgrade_log } diff --git a/src/canister/user_index/Cargo.toml b/src/canister/user_index/Cargo.toml index d1bd5d66..52efaf5c 100644 --- a/src/canister/user_index/Cargo.toml +++ b/src/canister/user_index/Cargo.toml @@ -17,7 +17,7 @@ shared_utils = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } ic-stable-structures = { workspace = true } -ciborium = { workspace = true } +minicbor-serde = { workspace = true } futures = { workspace = true } [dev-dependencies] diff --git a/src/canister/user_index/src/api/canister_lifecycle/post_upgrade.rs b/src/canister/user_index/src/api/canister_lifecycle/post_upgrade.rs index 9d6ee35b..ebe44103 100644 --- a/src/canister/user_index/src/api/canister_lifecycle/post_upgrade.rs +++ b/src/canister/user_index/src/api/canister_lifecycle/post_upgrade.rs @@ -1,5 +1,3 @@ -use ciborium::de; - use ic_cdk::api::call::ArgDecoderConfig; use ic_cdk_macros::post_upgrade; use ic_stable_structures::Memory; @@ -44,7 +42,7 @@ fn restore_data_from_stable_memory() { let mut canister_data_bytes = vec![0; heap_data_len]; heap_data.read(4, &mut canister_data_bytes); let canister_data = - de::from_reader(&*canister_data_bytes).expect("Failed to deserialize heap data"); + minicbor_serde::from_slice(&canister_data_bytes).expect("Failed to deserialize heap data"); CANISTER_DATA.with_borrow_mut(|cd| { *cd = canister_data; }) diff --git a/src/canister/user_index/src/api/canister_lifecycle/pre_upgrade.rs b/src/canister/user_index/src/api/canister_lifecycle/pre_upgrade.rs index c7e22557..1e1e4f93 100644 --- a/src/canister/user_index/src/api/canister_lifecycle/pre_upgrade.rs +++ b/src/canister/user_index/src/api/canister_lifecycle/pre_upgrade.rs @@ -1,14 +1,12 @@ use ic_cdk_macros::pre_upgrade; use ic_stable_structures::writer::Writer; -use ciborium::ser; use crate::{data_model::memory, CANISTER_DATA}; #[pre_upgrade] fn pre_upgrade() { - let mut state_bytes = vec![]; - CANISTER_DATA.with_borrow(|canister_data| - ser::into_writer(&*canister_data, &mut state_bytes) + let state_bytes = CANISTER_DATA.with_borrow(|canister_data| + minicbor_serde::to_vec(canister_data) ) .expect("failed to encode state"); let len = state_bytes.len() as u32; diff --git a/src/canister/user_index/src/api/canister_management/create_pool_of_available_canisters.rs b/src/canister/user_index/src/api/canister_management/create_pool_of_available_canisters.rs index dfd2bd60..b7051a08 100644 --- a/src/canister/user_index/src/api/canister_management/create_pool_of_available_canisters.rs +++ b/src/canister/user_index/src/api/canister_management/create_pool_of_available_canisters.rs @@ -47,7 +47,7 @@ pub async fn impl_create_pool_of_individual_user_available_canisters(version: St let version = version.clone(); let individual_user_wasm = individual_user_wasm.clone(); async move { - recharge_canister_for_installing_wasm(canister_id).await.map_err(|e| (canister_id, e))?; + recharge_canister_for_installing_wasm(canister_id).await.map_err(|e| (canister_id, format!("recharge error {e}")))?; install_canister_wasm( canister_id, None, @@ -65,7 +65,7 @@ pub async fn impl_create_pool_of_individual_user_available_canisters(version: St }) } Err((canister_id, e)) => { - ic_cdk::println!("Failed to install wasm on canister: {:?}, error: {:?}", canister_id, e); + ic_cdk::println!("Failed to install wasm on canister: {}, error: {:?}", canister_id, e); CANISTER_DATA.with_borrow_mut(|cdata| { cdata.insert_backup_canister(canister_id); }) diff --git a/src/lib/shared_utils/Cargo.toml b/src/lib/shared_utils/Cargo.toml index 37a56d88..bd5cc060 100644 --- a/src/lib/shared_utils/Cargo.toml +++ b/src/lib/shared_utils/Cargo.toml @@ -13,7 +13,7 @@ ic-stable-structures = { workspace = true } rmp-serde = { workspace = true } futures = { workspace = true } serde = { workspace = true } -ciborium = { workspace = true } +minicbor-serde = { workspace = true } serde_json_any_key = "2.0.0" serde_bytes = "0.11.14" icrc-ledger-types = { workspace = true } diff --git a/src/lib/shared_utils/src/common/participant_crypto/mod.rs b/src/lib/shared_utils/src/common/participant_crypto/mod.rs index 865a7427..cff355a3 100644 --- a/src/lib/shared_utils/src/common/participant_crypto/mod.rs +++ b/src/lib/shared_utils/src/common/participant_crypto/mod.rs @@ -87,11 +87,8 @@ impl ProofOfAuthorityMsg { } pub fn serialize_cbor(&self) -> Vec { - let mut bytes = vec![]; - ciborium::into_writer(self, &mut bytes) - .expect("PoaMessage should serialize succesfully"); - - bytes + minicbor_serde::to_vec(self) + .expect("PoaMessage should serialize succesfully") } } diff --git a/src/lib/shared_utils/src/common/types/wasm.rs b/src/lib/shared_utils/src/common/types/wasm.rs index 3b525fd5..b52f0312 100644 --- a/src/lib/shared_utils/src/common/types/wasm.rs +++ b/src/lib/shared_utils/src/common/types/wasm.rs @@ -1,7 +1,6 @@ use std::borrow::Cow; use candid::CandidType; -use ciborium::de; use serde::{Deserialize, Serialize}; use ic_stable_structures::{storable::Bound, Storable}; @@ -15,13 +14,12 @@ pub enum WasmType { impl Storable for WasmType { fn to_bytes(&self) -> std::borrow::Cow<[u8]> { - let mut bytes = vec![]; - ciborium::ser::into_writer(self, &mut bytes).unwrap(); + let bytes = minicbor_serde::to_vec(self).unwrap(); Cow::Owned(bytes) } fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Self { - let wasm_type: WasmType = de::from_reader(bytes.as_ref()).unwrap(); + let wasm_type: WasmType = minicbor_serde::from_slice(bytes.as_ref()).unwrap(); wasm_type } @@ -37,13 +35,12 @@ pub struct CanisterWasm { impl Storable for CanisterWasm { fn to_bytes(&self) -> std::borrow::Cow<[u8]> { - let mut bytes = vec![]; - ciborium::ser::into_writer(self, &mut bytes).unwrap(); + let bytes = minicbor_serde::to_vec(self).unwrap(); Cow::Owned(bytes) } fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Self { - let canister_wasm: CanisterWasm = de::from_reader(bytes.as_ref()).unwrap(); + let canister_wasm: CanisterWasm = minicbor_serde::from_slice(bytes.as_ref()).unwrap(); canister_wasm } From 4a1cd56d4550a3a734bf2d29aaf65f1b3a467dab Mon Sep 17 00:00:00 2001 From: rupansh Date: Sat, 30 Nov 2024 00:21:34 +0530 Subject: [PATCH 16/20] refactor(integration_tests): update wait time for provision_subnet_orchestrator --- ...five_hundred_tokens_as_referral_rewards.rs | 19 +--- .../tests/creator_dao/main.rs | 30 ++---- .../test_number_of_creator_tokens.rs | 31 ++----- .../hot_or_not/bet_maker_canister_tests.rs | 47 ++-------- .../hotornot_game_simulation_test.rs | 39 ++------ ...ke_individual_canister_logs_public_test.rs | 27 ++---- .../tests/kv_store/test_namespace.rs | 29 ++---- .../update_global_known_principal_test.rs | 47 ++-------- .../update_subnet_known_principal_test.rs | 29 ++---- ...vision_empty_canisters_in_a_subnet_test.rs | 27 ++---- .../provision_subnet_orchestrator_test.rs | 29 ++---- ...deregister_new_subnet_orchestrator_test.rs | 50 +++------- .../reset_ml_feed_cache_test.rs | 27 ++---- .../update_canisters_last_access_time_test.rs | 29 ++---- .../migrate_hotornot_user_to_yral_test.rs | 92 ++++--------------- ..._if_needed_and_will_install_latest_wasm.rs | 28 ++---- ..._backup_canister_on_signups_if_required.rs | 30 ++---- ...arge_individual_canister_when_requested.rs | 57 ++---------- ..._empty_canisters_to_individual_canister.rs | 29 ++---- ..._out_once_backup_canisters_are_consumed.rs | 29 ++---- ..._before_upgrading_invidividual_canister.rs | 28 ++---- .../test_utils/src/setup/env/pocket_ic_env.rs | 53 +++++++++++ src/lib/test_utils/src/setup/env/v1.rs | 2 +- 23 files changed, 223 insertions(+), 585 deletions(-) diff --git a/src/lib/integration_tests/tests/authentication/when_a_new_user_signs_up_from_a_referral_then_the_new_user_is_given_a_thousand_utility_tokens_for_signing_up_and_the_referrer_and_referee_receive_five_hundred_tokens_as_referral_rewards.rs b/src/lib/integration_tests/tests/authentication/when_a_new_user_signs_up_from_a_referral_then_the_new_user_is_given_a_thousand_utility_tokens_for_signing_up_and_the_referrer_and_referee_receive_five_hundred_tokens_as_referral_rewards.rs index 0a25a7be..d7e28ac7 100644 --- a/src/lib/integration_tests/tests/authentication/when_a_new_user_signs_up_from_a_referral_then_the_new_user_is_given_a_thousand_utility_tokens_for_signing_up_and_the_referrer_and_referee_receive_five_hundred_tokens_as_referral_rewards.rs +++ b/src/lib/integration_tests/tests/authentication/when_a_new_user_signs_up_from_a_referral_then_the_new_user_is_given_a_thousand_utility_tokens_for_signing_up_and_the_referrer_and_referee_receive_five_hundred_tokens_as_referral_rewards.rs @@ -13,7 +13,7 @@ use shared_utils::{ types::canister_specific::individual_user_template::error_types::GetUserUtilityTokenTransactionHistoryError, }; use test_utils::setup::{ - env::{pocket_ic_env::{execute_query, execute_query_multi, execute_update, execute_update_no_res, get_new_pocket_ic_env}, v1::{get_initialized_env_with_provisioned_known_canisters, get_new_state_machine}}, + env::{pocket_ic_env::{execute_query, execute_query_multi, execute_update, execute_update_no_res, get_new_pocket_ic_env, provision_subnet_orchestrator_canister}, v1::{get_initialized_env_with_provisioned_known_canisters, get_new_state_machine}}, test_constants::{get_mock_user_alice_principal_id, get_mock_user_bob_principal_id}, }; @@ -470,21 +470,12 @@ fn when_a_new_user_signs_up_from_a_referral_then_the_new_user_is_given_a_thousan #[test] fn when_a_new_user_signs_up_from_a_referral_then_the_new_user_is_given_a_thousand_utility_tokens_for_signing_up_and_the_referrer_and_referee_receive_five_hundred_tokens_as_referral_rewards_v2() { let (pic, known_principals) = get_new_pocket_ic_env(); - let global_admin_principal = known_principals[&KnownPrincipalType::UserIdGlobalSuperAdmin]; - - let platform_orc = known_principals[&KnownPrincipalType::CanisterIdPlatformOrchestrator]; - let app_subnets = pic.topology().get_app_subnets(); - let user_index_res: Result = execute_update( + let user_index = provision_subnet_orchestrator_canister( &pic, - global_admin_principal, - platform_orc, - "provision_subnet_orchestrator_canister", - &app_subnets[0] + &known_principals, + 0, + None ); - let user_index = user_index_res.unwrap(); - for _ in 0..30 { - pic.tick(); - } let alice_principal = get_mock_user_alice_principal_id(); let alice_canister: Principal = execute_update( diff --git a/src/lib/integration_tests/tests/creator_dao/main.rs b/src/lib/integration_tests/tests/creator_dao/main.rs index d3165152..99ca6d5b 100644 --- a/src/lib/integration_tests/tests/creator_dao/main.rs +++ b/src/lib/integration_tests/tests/creator_dao/main.rs @@ -25,6 +25,7 @@ use shared_utils::constant::{ SNS_TOKEN_LEDGER_MODULE_HASH, SNS_TOKEN_ROOT_MODULE_HASH, SNS_TOKEN_SWAP_MODULE_HASH, }; use shared_utils::types::creator_dao_stats::CreatorDaoTokenStats; +use test_utils::setup::env::pocket_ic_env::provision_subnet_orchestrator_canister; use std::time::{Duration, UNIX_EPOCH}; use std::{collections::HashMap, fmt::Debug, str::FromStr, time::SystemTime, vec}; use test_utils::setup::test_constants::get_mock_user_bob_principal_id; @@ -118,7 +119,7 @@ fn add_wasm(wasm_file: &[u8], canister_type: u32) -> AddWasmPayload { #[test] fn creator_dao_tests() { - let (pocket_ic, known_principal) = get_new_pocket_ic_env(); + let (pocket_ic, mut known_principal) = get_new_pocket_ic_env(); let platform_canister_id = known_principal .get(&KnownPrincipalType::CanisterIdPlatformOrchestrator) .cloned() @@ -126,8 +127,6 @@ fn creator_dao_tests() { let super_admin = get_global_super_admin_principal_id(); - let application_subnets = pocket_ic.topology().get_app_subnets(); - let charlie_global_admin = get_mock_user_charlie_principal_id(); pocket_ic @@ -152,25 +151,12 @@ fn creator_dao_tests() { ) .unwrap(); - let subnet_orchestrator_canister_id: Principal = pocket_ic - .update_call( - platform_canister_id, - charlie_global_admin, - "provision_subnet_orchestrator_canister", - candid::encode_one(application_subnets[1]).unwrap(), - ) - .map(|res| { - let canister_id_result: Result = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("Canister call failed"), - }; - canister_id_result.unwrap() - }) - .unwrap(); - - for i in 0..50 { - pocket_ic.tick(); - } + let subnet_orchestrator_canister_id = provision_subnet_orchestrator_canister( + &pocket_ic, + &known_principal, + 1, + Some(charlie_global_admin), + ); let alice_principal = get_mock_user_alice_principal_id(); let alice_canister_id: Principal = pocket_ic diff --git a/src/lib/integration_tests/tests/creator_dao/test_number_of_creator_tokens.rs b/src/lib/integration_tests/tests/creator_dao/test_number_of_creator_tokens.rs index 1f182875..c33ef972 100644 --- a/src/lib/integration_tests/tests/creator_dao/test_number_of_creator_tokens.rs +++ b/src/lib/integration_tests/tests/creator_dao/test_number_of_creator_tokens.rs @@ -21,7 +21,7 @@ use shared_utils::{ constant::{MAX_LIMIT_FOR_CREATOR_DAO_SNS_TOKEN, SNS_WASM_W_PRINCIPAL_ID}, }; use test_utils::setup::{ - env::pocket_ic_env::get_new_pocket_ic_env, + env::pocket_ic_env::{get_new_pocket_ic_env, provision_subnet_orchestrator_canister}, test_constants::{ get_global_super_admin_principal_id, get_mock_user_alice_principal_id, get_mock_user_charlie_principal_id, @@ -32,7 +32,7 @@ use crate::utils::setup_sns_w_canister_for_creator_dao; #[test] pub fn test_number_of_creator_tokens() { - let (pocket_ic, known_principal) = get_new_pocket_ic_env(); + let (pocket_ic, mut known_principal) = get_new_pocket_ic_env(); let platform_canister_id = known_principal .get(&KnownPrincipalType::CanisterIdPlatformOrchestrator) .cloned() @@ -40,8 +40,6 @@ pub fn test_number_of_creator_tokens() { let super_admin = get_global_super_admin_principal_id(); - let application_subnets = pocket_ic.topology().get_app_subnets(); - let charlie_global_admin = get_mock_user_charlie_principal_id(); pocket_ic @@ -66,25 +64,12 @@ pub fn test_number_of_creator_tokens() { ) .unwrap(); - let subnet_orchestrator_canister_id: Principal = pocket_ic - .update_call( - platform_canister_id, - charlie_global_admin, - "provision_subnet_orchestrator_canister", - candid::encode_one(application_subnets[1]).unwrap(), - ) - .map(|res| { - let canister_id_result: Result = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("Canister call failed"), - }; - canister_id_result.unwrap() - }) - .unwrap(); - - for i in 0..50 { - pocket_ic.tick(); - } + let subnet_orchestrator_canister_id = provision_subnet_orchestrator_canister( + &pocket_ic, + &known_principal, + 1, + Some(charlie_global_admin), + ); let alice_principal = get_mock_user_alice_principal_id(); let alice_canister_id: Principal = pocket_ic diff --git a/src/lib/integration_tests/tests/hot_or_not/bet_maker_canister_tests.rs b/src/lib/integration_tests/tests/hot_or_not/bet_maker_canister_tests.rs index 9f694a3f..b07800c7 100644 --- a/src/lib/integration_tests/tests/hot_or_not/bet_maker_canister_tests.rs +++ b/src/lib/integration_tests/tests/hot_or_not/bet_maker_canister_tests.rs @@ -12,7 +12,7 @@ use shared_utils::{ common::types::known_principal::KnownPrincipalType, }; use test_utils::setup::{ - env::pocket_ic_env::get_new_pocket_ic_env, + env::pocket_ic_env::{get_new_pocket_ic_env, provision_n_subnet_orchestrator_canisters}, test_constants::{ get_mock_user_alice_principal_id, get_mock_user_bob_principal_id, get_mock_user_charlie_principal_id, get_mock_user_dan_principal_id, @@ -47,43 +47,14 @@ fn when_bet_maker_places_bet_on_a_post_it_is_assigned_a_slot_id_and_the_outcome_ .copied() .unwrap(); - let application_subnets = pocket_ic.topology().get_app_subnets(); - - let subnet_orchestrator_canister_id_0 = pocket_ic - .update_call( - platform_orchestrator_canister_id, - global_admin_principal, - "provision_subnet_orchestrator_canister", - candid::encode_one(application_subnets[0]).unwrap(), - ) - .map(|res| { - let canister_id_result: Result = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("Canister call failed"), - }; - canister_id_result.unwrap() - }) - .unwrap(); - - let subnet_orchestrator_canister_id_1 = pocket_ic - .update_call( - platform_orchestrator_canister_id, - global_admin_principal, - "provision_subnet_orchestrator_canister", - candid::encode_one(application_subnets[1]).unwrap(), - ) - .map(|res| { - let canister_id_result: Result = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("Canister call failed"), - }; - canister_id_result.unwrap() - }) - .unwrap(); - - for i in 0..50 { - pocket_ic.tick(); - } + let subnet_orchestrators = provision_n_subnet_orchestrator_canisters( + &pocket_ic, + &known_principal_map, + 2, + None, + ); + let subnet_orchestrator_canister_id_0 = subnet_orchestrators[0]; + let subnet_orchestrator_canister_id_1 = subnet_orchestrators[1]; //Post Creator Canister let alice_canister_id = pocket_ic diff --git a/src/lib/integration_tests/tests/hot_or_not/hotornot_game_simulation_test.rs b/src/lib/integration_tests/tests/hot_or_not/hotornot_game_simulation_test.rs index c5f241e4..06c4da2d 100644 --- a/src/lib/integration_tests/tests/hot_or_not/hotornot_game_simulation_test.rs +++ b/src/lib/integration_tests/tests/hot_or_not/hotornot_game_simulation_test.rs @@ -19,7 +19,7 @@ use shared_utils::{ common::types::known_principal::{self, KnownPrincipalType}, }; use test_utils::setup::{ - env::pocket_ic_env::get_new_pocket_ic_env, + env::pocket_ic_env::{get_new_pocket_ic_env, provision_subnet_orchestrator_canister}, test_constants::{ get_mock_user_alice_principal_id, get_mock_user_bob_principal_id, get_mock_user_charlie_principal_id, get_mock_user_dan_principal_id, @@ -963,37 +963,12 @@ fn hotornot_game_simulation_test() { fn hotornot_game_simulation_test_2() { let (pic, known_principals) = get_new_pocket_ic_env(); - let platform_canister_id = known_principals - .get(&KnownPrincipalType::CanisterIdPlatformOrchestrator) - .cloned() - .unwrap(); - - let global_admin = known_principals - .get(&KnownPrincipalType::UserIdGlobalSuperAdmin) - .cloned() - .unwrap(); - - let application_subnets = pic.topology().get_app_subnets(); - - let subnet_orchestrator_canister_id = pic - .update_call( - platform_canister_id, - global_admin, - "provision_subnet_orchestrator_canister", - candid::encode_one(application_subnets[0]).unwrap(), - ) - .map(|res| { - let canister_id_result: Result = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("Canister call failed"), - }; - canister_id_result.unwrap() - }) - .unwrap(); - - for _ in 0..50 { - pic.tick() - } + let subnet_orchestrator_canister_id = provision_subnet_orchestrator_canister( + &pic, + &known_principals, + 0, + None, + ); // Init N canisters let mut individual_template_canister_ids = vec![]; diff --git a/src/lib/integration_tests/tests/individual_canister/platform_orchestrator_on_receiving_call_from_global_admin_can_make_individual_canister_logs_public_test.rs b/src/lib/integration_tests/tests/individual_canister/platform_orchestrator_on_receiving_call_from_global_admin_can_make_individual_canister_logs_public_test.rs index 12f9e9ea..088fad21 100644 --- a/src/lib/integration_tests/tests/individual_canister/platform_orchestrator_on_receiving_call_from_global_admin_can_make_individual_canister_logs_public_test.rs +++ b/src/lib/integration_tests/tests/individual_canister/platform_orchestrator_on_receiving_call_from_global_admin_can_make_individual_canister_logs_public_test.rs @@ -2,7 +2,7 @@ use candid::Principal; use pocket_ic::WasmResult; use shared_utils::common::types::known_principal::KnownPrincipalType; use test_utils::setup::{ - env::pocket_ic_env::get_new_pocket_ic_env, + env::pocket_ic_env::{get_new_pocket_ic_env, provision_subnet_orchestrator_canister}, test_constants::{ get_global_super_admin_principal_id, get_mock_user_alice_principal_id, get_mock_user_charlie_canister_id, @@ -41,25 +41,12 @@ pub fn platform_orchestrator_on_receiving_call_from_global_admin_can_make_indivi }) .unwrap(); - let subnet_orchestrator_canister_id = pocket_ic - .update_call( - platform_orchestrator_canister_id, - charlie_global_admin_principal, - "provision_subnet_orchestrator_canister", - candid::encode_one(application_subnets[0]).unwrap(), - ) - .map(|res| { - let canister_id_result: Result = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("Canister call failed"), - }; - canister_id_result.unwrap() - }) - .unwrap(); - - for _ in 0..50 { - pocket_ic.tick() - } + let subnet_orchestrator_canister_id = provision_subnet_orchestrator_canister( + &pocket_ic, + &known_principal_map, + 0, + Some(charlie_global_admin_principal), + ); let alice_principal = get_mock_user_alice_principal_id(); diff --git a/src/lib/integration_tests/tests/kv_store/test_namespace.rs b/src/lib/integration_tests/tests/kv_store/test_namespace.rs index 60cf2a8f..94a03e3b 100644 --- a/src/lib/integration_tests/tests/kv_store/test_namespace.rs +++ b/src/lib/integration_tests/tests/kv_store/test_namespace.rs @@ -9,7 +9,7 @@ use shared_utils::{ common::types::known_principal::KnownPrincipalType, }; use test_utils::setup::{ - env::pocket_ic_env::get_new_pocket_ic_env, + env::pocket_ic_env::{get_new_pocket_ic_env, provision_subnet_orchestrator_canister}, test_constants::{get_mock_user_alice_principal_id, get_mock_user_charlie_principal_id}, }; @@ -27,8 +27,6 @@ fn create_new_namespace() { .cloned() .unwrap(); - let application_subnets = pocket_ic.topology().get_app_subnets(); - let charlie_global_admin = get_mock_user_charlie_principal_id(); pocket_ic @@ -40,25 +38,12 @@ fn create_new_namespace() { ) .unwrap(); - let subnet_orchestrator_canister_id: Principal = pocket_ic - .update_call( - platform_canister_id, - charlie_global_admin, - "provision_subnet_orchestrator_canister", - candid::encode_one(application_subnets[1]).unwrap(), - ) - .map(|res| { - let canister_id_result: Result = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("Canister call failed"), - }; - canister_id_result.unwrap() - }) - .unwrap(); - - for i in 0..50 { - pocket_ic.tick(); - } + let subnet_orchestrator_canister_id = provision_subnet_orchestrator_canister( + &pocket_ic, + &known_principal, + 1, + Some(charlie_global_admin), + ); let alice_principal_id = get_mock_user_alice_principal_id(); diff --git a/src/lib/integration_tests/tests/platform_orchestrator/known_principal/update_global_known_principal_test.rs b/src/lib/integration_tests/tests/platform_orchestrator/known_principal/update_global_known_principal_test.rs index cbc58a25..b7181bcc 100644 --- a/src/lib/integration_tests/tests/platform_orchestrator/known_principal/update_global_known_principal_test.rs +++ b/src/lib/integration_tests/tests/platform_orchestrator/known_principal/update_global_known_principal_test.rs @@ -24,7 +24,7 @@ use std::{ time::SystemTime, }; use test_utils::setup::{ - env::pocket_ic_env::get_new_pocket_ic_env, + env::pocket_ic_env::{get_new_pocket_ic_env, provision_n_subnet_orchestrator_canisters}, test_constants::{ get_global_super_admin_principal_id, get_mock_user_alice_canister_id, get_mock_user_alice_principal_id, get_mock_user_bob_canister_id, @@ -107,43 +107,14 @@ fn when_global_known_principal_is_updated_it_is_reflected_in_all_canisters() { .cloned() .unwrap(); - let application_subnets = pocket_ic.topology().get_app_subnets(); - - let first_subnet_orchestrator_canister_id: Principal = pocket_ic - .update_call( - platform_canister_id, - super_admin, - "provision_subnet_orchestrator_canister", - candid::encode_one(application_subnets[1]).unwrap(), - ) - .map(|res| { - let canister_id_result: Result = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("Canister call failed"), - }; - canister_id_result.unwrap() - }) - .unwrap(); - - let second_subnet_orchestrator_canister_id: Principal = pocket_ic - .update_call( - platform_canister_id, - super_admin, - "provision_subnet_orchestrator_canister", - candid::encode_one(application_subnets[1]).unwrap(), - ) - .map(|res| { - let canister_id_result: Result = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("Canister call failed"), - }; - canister_id_result.unwrap() - }) - .unwrap(); - - for _ in 0..30 { - pocket_ic.tick(); - } + let subnet_orchestrators = provision_n_subnet_orchestrator_canisters( + &pocket_ic, + &known_principal, + 2, + None + ); + let first_subnet_orchestrator_canister_id = subnet_orchestrators[0]; + let second_subnet_orchestrator_canister_id = subnet_orchestrators[1]; let governance_canister_id = Principal::from_text(GOVERNANCE_CANISTER_ID).unwrap(); diff --git a/src/lib/integration_tests/tests/platform_orchestrator/known_principal/update_subnet_known_principal_test.rs b/src/lib/integration_tests/tests/platform_orchestrator/known_principal/update_subnet_known_principal_test.rs index 47027ac1..dcf60bca 100644 --- a/src/lib/integration_tests/tests/platform_orchestrator/known_principal/update_subnet_known_principal_test.rs +++ b/src/lib/integration_tests/tests/platform_orchestrator/known_principal/update_subnet_known_principal_test.rs @@ -23,7 +23,7 @@ use std::{ time::SystemTime, }; use test_utils::setup::{ - env::pocket_ic_env::get_new_pocket_ic_env, + env::pocket_ic_env::{get_new_pocket_ic_env, provision_subnet_orchestrator_canister}, test_constants::{ get_global_super_admin_principal_id, get_mock_user_alice_canister_id, get_mock_user_alice_principal_id, v1::CANISTER_INITIAL_CYCLES_FOR_SPAWNING_CANISTERS, @@ -115,27 +115,12 @@ fn when_subnet_known_principal_is_updated_it_is_reflected_in_individual_canister .cloned() .unwrap(); - let application_subnets = pocket_ic.topology().get_app_subnets(); - - let subnet_orchestrator_canister_id: Principal = pocket_ic - .update_call( - platform_canister_id, - super_admin, - "provision_subnet_orchestrator_canister", - candid::encode_one(application_subnets[1]).unwrap(), - ) - .map(|res| { - let canister_id_result: Result = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("Canister call failed"), - }; - canister_id_result.unwrap() - }) - .unwrap(); - - for i in 0..50 { - pocket_ic.tick(); - } + let subnet_orchestrator_canister_id = provision_subnet_orchestrator_canister( + &pocket_ic, + &known_principal, + 1, + None, + ); let post_cache_canister_id = Principal::anonymous(); diff --git a/src/lib/integration_tests/tests/platform_orchestrator/provision_empty_canisters_in_a_subnet_test.rs b/src/lib/integration_tests/tests/platform_orchestrator/provision_empty_canisters_in_a_subnet_test.rs index c92da21f..86a5da5b 100644 --- a/src/lib/integration_tests/tests/platform_orchestrator/provision_empty_canisters_in_a_subnet_test.rs +++ b/src/lib/integration_tests/tests/platform_orchestrator/provision_empty_canisters_in_a_subnet_test.rs @@ -5,7 +5,7 @@ use shared_utils::{ constant::TEST_BACKUP_INDIVIDUAL_USER_CANISTER_BATCH_SIZE, }; use test_utils::setup::{ - env::pocket_ic_env::get_new_pocket_ic_env, test_constants::get_mock_user_charlie_principal_id, + env::pocket_ic_env::{get_new_pocket_ic_env, provision_subnet_orchestrator_canister}, test_constants::get_mock_user_charlie_principal_id, }; #[test] @@ -34,25 +34,12 @@ fn provision_empty_canisters_in_a_subnet_test() { ) .unwrap(); - let subnet_orchestrator_canister_id: Principal = pocket_ic - .update_call( - platform_canister_id, - charlie_global_admin, - "provision_subnet_orchestrator_canister", - candid::encode_one(application_subnets[0]).unwrap(), - ) - .map(|res| { - let canister_id_result: Result = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("Canister call failed"), - }; - canister_id_result.unwrap() - }) - .unwrap(); - - for i in 0..130 { - pocket_ic.tick(); - } + let subnet_orchestrator_canister_id = provision_subnet_orchestrator_canister( + &pocket_ic, + &known_principal, + 0, + Some(charlie_global_admin), + ); let empty_canisters_cnt = pocket_ic .query_call( diff --git a/src/lib/integration_tests/tests/platform_orchestrator/provision_subnet_orchestrator_test.rs b/src/lib/integration_tests/tests/platform_orchestrator/provision_subnet_orchestrator_test.rs index 96319398..d4dda775 100644 --- a/src/lib/integration_tests/tests/platform_orchestrator/provision_subnet_orchestrator_test.rs +++ b/src/lib/integration_tests/tests/platform_orchestrator/provision_subnet_orchestrator_test.rs @@ -8,7 +8,7 @@ use shared_utils::{ common::types::{known_principal::KnownPrincipalType, wasm::WasmType}, }; use test_utils::setup::{ - env::pocket_ic_env::get_new_pocket_ic_env, test_constants::get_mock_user_charlie_principal_id, + env::pocket_ic_env::{self, get_new_pocket_ic_env}, test_constants::get_mock_user_charlie_principal_id, }; pub type CanisterId = Principal; @@ -26,8 +26,6 @@ fn provision_subnet_orchestrator_canister() { .cloned() .unwrap(); - let application_subnets = pocket_ic.topology().get_app_subnets(); - let charlie_global_admin = get_mock_user_charlie_principal_id(); pocket_ic @@ -39,25 +37,12 @@ fn provision_subnet_orchestrator_canister() { ) .unwrap(); - let subnet_orchestrator_canister_id: Principal = pocket_ic - .update_call( - platform_canister_id, - charlie_global_admin, - "provision_subnet_orchestrator_canister", - candid::encode_one(application_subnets[1]).unwrap(), - ) - .map(|res| { - let canister_id_result: Result = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("Canister call failed"), - }; - canister_id_result.unwrap() - }) - .unwrap(); - - for i in 0..110 { - pocket_ic.tick(); - } + let subnet_orchestrator_canister_id = pocket_ic_env::provision_subnet_orchestrator_canister( + &pocket_ic, + &known_principal, + 1, + Some(charlie_global_admin), + ); //Check version Installed let last_upgrade_status: UpgradeStatus = pocket_ic diff --git a/src/lib/integration_tests/tests/platform_orchestrator/register_and_deregister_new_subnet_orchestrator_test.rs b/src/lib/integration_tests/tests/platform_orchestrator/register_and_deregister_new_subnet_orchestrator_test.rs index 2cd25342..5093a147 100644 --- a/src/lib/integration_tests/tests/platform_orchestrator/register_and_deregister_new_subnet_orchestrator_test.rs +++ b/src/lib/integration_tests/tests/platform_orchestrator/register_and_deregister_new_subnet_orchestrator_test.rs @@ -5,14 +5,13 @@ use shared_utils::{ common::types::known_principal::KnownPrincipalType, }; use test_utils::setup::{ - env::pocket_ic_env::get_new_pocket_ic_env, test_constants::get_mock_user_charlie_principal_id, + env::pocket_ic_env::{get_new_pocket_ic_env, provision_subnet_orchestrator_canister, provision_subnet_orchestrator_canister_no_wait}, test_constants::get_mock_user_charlie_principal_id, }; #[test] fn register_subnet_orchestrator_with_platform_orchestrator_test() { let (pocket_ic, known_principal) = get_new_pocket_ic_env(); - let application_subnets = pocket_ic.topology().get_app_subnets(); let platform_canister_id = known_principal .get(&KnownPrincipalType::CanisterIdPlatformOrchestrator) .cloned() @@ -34,25 +33,12 @@ fn register_subnet_orchestrator_with_platform_orchestrator_test() { ) .unwrap(); - let subnet_orchestrator_canister_id: Principal = pocket_ic - .update_call( - platform_canister_id, - charlie_global_admin, - "provision_subnet_orchestrator_canister", - candid::encode_one(application_subnets[1]).unwrap(), - ) - .map(|res| { - let canister_id_result: Result = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("Canister call failed"), - }; - canister_id_result.unwrap() - }) - .unwrap(); - - for i in 0..110 { - pocket_ic.tick(); - } + let subnet_orchestrator_canister_id = provision_subnet_orchestrator_canister( + &pocket_ic, + &known_principal, + 1, + Some(charlie_global_admin), + ); let new_subnet_orchestrator_canister = pocket_ic.create_canister(); pocket_ic.add_cycles(new_subnet_orchestrator_canister, 1_000_000_000_000_000); @@ -222,7 +208,6 @@ fn register_subnet_orchestrator_with_platform_orchestrator_test() { fn deregister_subnet_orchestrator_from_platform_orchestrator() { let (pocket_ic, known_principal) = get_new_pocket_ic_env(); - let application_subnets = pocket_ic.topology().get_app_subnets(); let platform_canister_id = known_principal .get(&KnownPrincipalType::CanisterIdPlatformOrchestrator) .cloned() @@ -244,21 +229,12 @@ fn deregister_subnet_orchestrator_from_platform_orchestrator() { ) .unwrap(); - let subnet_orchestrator_canister_id: Principal = pocket_ic - .update_call( - platform_canister_id, - charlie_global_admin, - "provision_subnet_orchestrator_canister", - candid::encode_one(application_subnets[1]).unwrap(), - ) - .map(|res| { - let canister_id_result: Result = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("Canister call failed"), - }; - canister_id_result.unwrap() - }) - .unwrap(); + let subnet_orchestrator_canister_id = provision_subnet_orchestrator_canister_no_wait( + &pocket_ic, + &known_principal, + 1, + Some(charlie_global_admin), + ); let deregister_new_subnet_orchestrator_res = pocket_ic .update_call( diff --git a/src/lib/integration_tests/tests/platform_orchestrator/reset_ml_feed_cache_test.rs b/src/lib/integration_tests/tests/platform_orchestrator/reset_ml_feed_cache_test.rs index 0dddf794..a821a6b0 100644 --- a/src/lib/integration_tests/tests/platform_orchestrator/reset_ml_feed_cache_test.rs +++ b/src/lib/integration_tests/tests/platform_orchestrator/reset_ml_feed_cache_test.rs @@ -25,7 +25,7 @@ use shared_utils::{ constant::{NNS_CYCLE_MINTING_CANISTER, NNS_LEDGER_CANISTER_ID}, }; use test_utils::setup::{ - env::pocket_ic_env::get_new_pocket_ic_env, + env::pocket_ic_env::{get_new_pocket_ic_env, provision_subnet_orchestrator_canister}, test_constants::{ get_mock_user_alice_principal_id, get_mock_user_bob_principal_id, get_mock_user_charlie_principal_id, get_mock_user_dan_principal_id, @@ -89,25 +89,12 @@ fn reset_ml_feed_cache_test() { ) .unwrap(); - let user_index_canister_id: Principal = pocket_ic - .update_call( - platform_canister_id, - super_admin, - "provision_subnet_orchestrator_canister", - candid::encode_one(application_subnets[1]).unwrap(), - ) - .map(|res| { - let canister_id_result: Result = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("Canister call failed"), - }; - canister_id_result.unwrap() - }) - .unwrap(); - - for i in 0..50 { - pocket_ic.tick(); - } + let user_index_canister_id = provision_subnet_orchestrator_canister( + &pocket_ic, + &known_principal, + 1, + None, + ); // User Index available details - call get_subnet_available_capacity diff --git a/src/lib/integration_tests/tests/platform_orchestrator/update_canisters_last_access_time_test.rs b/src/lib/integration_tests/tests/platform_orchestrator/update_canisters_last_access_time_test.rs index b8c79014..31e0a19b 100644 --- a/src/lib/integration_tests/tests/platform_orchestrator/update_canisters_last_access_time_test.rs +++ b/src/lib/integration_tests/tests/platform_orchestrator/update_canisters_last_access_time_test.rs @@ -25,7 +25,7 @@ use shared_utils::{ constant::{NNS_CYCLE_MINTING_CANISTER, NNS_LEDGER_CANISTER_ID}, }; use test_utils::setup::{ - env::pocket_ic_env::get_new_pocket_ic_env, + env::pocket_ic_env::{get_new_pocket_ic_env, provision_subnet_orchestrator_canister}, test_constants::{ get_mock_user_alice_principal_id, get_mock_user_bob_principal_id, get_mock_user_charlie_principal_id, get_mock_user_dan_principal_id, @@ -96,8 +96,6 @@ fn update_canisters_last_access_time_test() { .cloned() .unwrap(); - let application_subnets = pocket_ic.topology().get_app_subnets(); - let charlie_global_admin = get_mock_user_charlie_principal_id(); let alice_principal_id = get_mock_user_alice_principal_id(); let bob_principal_id = get_mock_user_bob_principal_id(); @@ -112,25 +110,12 @@ fn update_canisters_last_access_time_test() { ) .unwrap(); - let user_index_canister_id: Principal = pocket_ic - .update_call( - platform_canister_id, - super_admin, - "provision_subnet_orchestrator_canister", - candid::encode_one(application_subnets[1]).unwrap(), - ) - .map(|res| { - let canister_id_result: Result = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("Canister call failed"), - }; - canister_id_result.unwrap() - }) - .unwrap(); - - for i in 0..50 { - pocket_ic.tick(); - } + let user_index_canister_id = provision_subnet_orchestrator_canister( + &pocket_ic, + &known_principal, + 1, + None + ); // upgrade pf_orch diff --git a/src/lib/integration_tests/tests/profile/migrate_hotornot_user_to_yral_test.rs b/src/lib/integration_tests/tests/profile/migrate_hotornot_user_to_yral_test.rs index 36450804..cc615858 100644 --- a/src/lib/integration_tests/tests/profile/migrate_hotornot_user_to_yral_test.rs +++ b/src/lib/integration_tests/tests/profile/migrate_hotornot_user_to_yral_test.rs @@ -15,7 +15,7 @@ use std::{ time::Duration, }; use test_utils::setup::{ - env::pocket_ic_env::get_new_pocket_ic_env, + env::pocket_ic_env::{get_new_pocket_ic_env, provision_n_subnet_orchestrator_canisters}, test_constants::{ get_mock_user_alice_principal_id, get_mock_user_bob_principal_id, get_mock_user_charlie_principal_id, get_mock_user_dan_principal_id, @@ -37,43 +37,14 @@ fn test_transfer_token_can_heppen_only_once_from_hot_or_not_canister_to_yral_can .cloned() .unwrap(); - let application_subnets = pocket_ic.topology().get_app_subnets(); - - let hot_or_not_subnet_orchestrator_canister_id: Principal = pocket_ic - .update_call( - platform_canister_id, - super_admin, - "provision_subnet_orchestrator_canister", - candid::encode_one(application_subnets[0]).unwrap(), - ) - .map(|res| { - let canister_id_result: Result = match res { - PocketICWasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("Canister call failed"), - }; - canister_id_result.unwrap() - }) - .unwrap(); - - let yral_subnet_orchestrator_canister_id: Principal = pocket_ic - .update_call( - platform_canister_id, - super_admin, - "provision_subnet_orchestrator_canister", - candid::encode_one(application_subnets[1]).unwrap(), - ) - .map(|res| { - let canister_id_result: Result = match res { - PocketICWasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("Canister call failed"), - }; - canister_id_result.unwrap() - }) - .unwrap(); - - for _ in 0..30 { - pocket_ic.tick(); - } + let subnet_orchestrators = provision_n_subnet_orchestrator_canisters( + &pocket_ic, + &known_principal, + 2, + None, + ); + let hot_or_not_subnet_orchestrator_canister_id = subnet_orchestrators[0]; + let yral_subnet_orchestrator_canister_id = subnet_orchestrators[1]; let post_cache_canister_id = Principal::anonymous(); @@ -540,43 +511,14 @@ fn test_when_user_tries_to_misuse_to_recieve_tokens_and_posts() { .cloned() .unwrap(); - let application_subnets = pocket_ic.topology().get_app_subnets(); - - let hot_or_not_subnet_orchestrator_canister_id: Principal = pocket_ic - .update_call( - platform_canister_id, - super_admin, - "provision_subnet_orchestrator_canister", - candid::encode_one(application_subnets[0]).unwrap(), - ) - .map(|res| { - let canister_id_result: Result = match res { - PocketICWasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("Canister call failed"), - }; - canister_id_result.unwrap() - }) - .unwrap(); - - let yral_subnet_orchestrator_canister_id: Principal = pocket_ic - .update_call( - platform_canister_id, - super_admin, - "provision_subnet_orchestrator_canister", - candid::encode_one(application_subnets[1]).unwrap(), - ) - .map(|res| { - let canister_id_result: Result = match res { - PocketICWasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("Canister call failed"), - }; - canister_id_result.unwrap() - }) - .unwrap(); - - for _ in 0..30 { - pocket_ic.tick(); - } + let subnet_orchestrators = provision_n_subnet_orchestrator_canisters( + &pocket_ic, + &known_principal, + 2, + None, + ); + let hot_or_not_subnet_orchestrator_canister_id = subnet_orchestrators[0]; + let yral_subnet_orchestrator_canister_id = subnet_orchestrators[1]; let post_cache_canister_id = Principal::anonymous(); diff --git a/src/lib/integration_tests/tests/reset_individual_user_canister/reset_individual_canister_can_called_from_governance_canister_and_resetting_would_recharge_the_canister_if_needed_and_will_install_latest_wasm.rs b/src/lib/integration_tests/tests/reset_individual_user_canister/reset_individual_canister_can_called_from_governance_canister_and_resetting_would_recharge_the_canister_if_needed_and_will_install_latest_wasm.rs index 127f6f18..91068155 100644 --- a/src/lib/integration_tests/tests/reset_individual_user_canister/reset_individual_canister_can_called_from_governance_canister_and_resetting_would_recharge_the_canister_if_needed_and_will_install_latest_wasm.rs +++ b/src/lib/integration_tests/tests/reset_individual_user_canister/reset_individual_canister_can_called_from_governance_canister_and_resetting_would_recharge_the_canister_if_needed_and_will_install_latest_wasm.rs @@ -24,7 +24,7 @@ use shared_utils::{ constant::{NNS_CYCLE_MINTING_CANISTER, NNS_LEDGER_CANISTER_ID}, }; use test_utils::setup::{ - env::pocket_ic_env::get_new_pocket_ic_env, + env::pocket_ic_env::{get_new_pocket_ic_env, provision_subnet_orchestrator_canister}, test_constants::{ get_mock_user_alice_principal_id, get_mock_user_bob_principal_id, get_mock_user_charlie_principal_id, get_mock_user_dan_principal_id, @@ -110,26 +110,12 @@ fn reset_individual_canister_test() { ) .unwrap(); - let user_index_canister_id: Principal = pocket_ic - .update_call( - platform_canister_id, - admin_principal_id, - "provision_subnet_orchestrator_canister", - candid::encode_one(application_subnets[1]).unwrap(), - ) - .map(|res| { - let canister_id_result: Result = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("Canister call failed"), - }; - canister_id_result.unwrap() - }) - .unwrap(); - - for i in 0..50 { - pocket_ic.tick(); - } - + let user_index_canister_id = provision_subnet_orchestrator_canister( + &pocket_ic, + &known_principal, + 1, + Some(admin_principal_id), + ); // let user_index_canister_id = pocket_ic.create_canister_with_settings(Some(admin_principal_id), None); // pocket_ic.add_cycles(user_index_canister_id, 2_000_000_000_000_000); // let user_index_wasm = user_index_canister_wasm(); diff --git a/src/lib/integration_tests/tests/subnet_orchestrator/provision_new_available_and_backup_canister_on_signups_if_required.rs b/src/lib/integration_tests/tests/subnet_orchestrator/provision_new_available_and_backup_canister_on_signups_if_required.rs index 8b7f4481..1ce4c13f 100644 --- a/src/lib/integration_tests/tests/subnet_orchestrator/provision_new_available_and_backup_canister_on_signups_if_required.rs +++ b/src/lib/integration_tests/tests/subnet_orchestrator/provision_new_available_and_backup_canister_on_signups_if_required.rs @@ -8,7 +8,7 @@ use shared_utils::{ }, }; use test_utils::setup::{ - env::pocket_ic_env::get_new_pocket_ic_env, test_constants::get_mock_user_charlie_principal_id, + env::pocket_ic_env::{get_new_pocket_ic_env, provision_subnet_orchestrator_canister}, test_constants::get_mock_user_charlie_principal_id, }; #[ignore = "we are not provisioning new backup canisters anymore"] @@ -25,8 +25,6 @@ pub fn provision_new_available_and_backup_canisters_on_signup_if_required() { .cloned() .unwrap(); - let application_subnets = pocket_ic.topology().get_app_subnets(); - let charlie_global_admin = get_mock_user_charlie_principal_id(); pocket_ic @@ -38,26 +36,12 @@ pub fn provision_new_available_and_backup_canisters_on_signup_if_required() { ) .unwrap(); - let subnet_orchestrator_canister_id: Principal = pocket_ic - .update_call( - platform_canister_id, - charlie_global_admin, - "provision_subnet_orchestrator_canister", - candid::encode_one(application_subnets[1]).unwrap(), - ) - .map(|res| { - let canister_id_result: Result = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("Canister call failed"), - }; - canister_id_result.unwrap() - }) - .unwrap(); - - for i in 0..130 { - pocket_ic.tick(); - } - + let subnet_orchestrator_canister_id = provision_subnet_orchestrator_canister( + &pocket_ic, + &known_principal, + 1, + Some(charlie_global_admin), + ); let subnet_available_canister_cnt = pocket_ic .query_call( subnet_orchestrator_canister_id, diff --git a/src/lib/integration_tests/tests/subnet_orchestrator/recharge_individual_canister_when_requested.rs b/src/lib/integration_tests/tests/subnet_orchestrator/recharge_individual_canister_when_requested.rs index cdf1f574..a437bfab 100644 --- a/src/lib/integration_tests/tests/subnet_orchestrator/recharge_individual_canister_when_requested.rs +++ b/src/lib/integration_tests/tests/subnet_orchestrator/recharge_individual_canister_when_requested.rs @@ -2,7 +2,7 @@ use candid::Principal; use pocket_ic::WasmResult; use shared_utils::common::types::known_principal::KnownPrincipalType; use test_utils::setup::{ - env::pocket_ic_env::get_new_pocket_ic_env, + env::pocket_ic_env::{get_new_pocket_ic_env, provision_n_subnet_orchestrator_canisters}, test_constants::{get_mock_user_alice_principal_id, get_mock_user_bob_principal_id}, }; @@ -18,54 +18,15 @@ fn recharge_individual_canister_when_requested() { let mut lucy_winnings = 0_u64; let mut tom_winnigns = 0_u64; - let platform_orchestrator_canister_id = known_principal_map - .get(&KnownPrincipalType::CanisterIdPlatformOrchestrator) - .copied() - .unwrap(); - - let global_admin_principal = known_principal_map - .get(&KnownPrincipalType::UserIdGlobalSuperAdmin) - .copied() - .unwrap(); - - let application_subnets = pocket_ic.topology().get_app_subnets(); - - let subnet_orchestrator_canister_id_0 = pocket_ic - .update_call( - platform_orchestrator_canister_id, - global_admin_principal, - "provision_subnet_orchestrator_canister", - candid::encode_one(application_subnets[0]).unwrap(), - ) - .map(|res| { - let canister_id_result: Result = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("Canister call failed"), - }; - canister_id_result.unwrap() - }) - .unwrap(); - - let subnet_orchestrator_canister_id_1 = pocket_ic - .update_call( - platform_orchestrator_canister_id, - global_admin_principal, - "provision_subnet_orchestrator_canister", - candid::encode_one(application_subnets[1]).unwrap(), - ) - .map(|res| { - let canister_id_result: Result = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("Canister call failed"), - }; - canister_id_result.unwrap() - }) - .unwrap(); - - for i in 0..50 { - pocket_ic.tick(); - } + let subnet_orchestrators = provision_n_subnet_orchestrator_canisters( + &pocket_ic, + &known_principal_map, + 2, + None, + ); + let subnet_orchestrator_canister_id_0 = subnet_orchestrators[0]; + let subnet_orchestrator_canister_id_1 = subnet_orchestrators[1]; //Post Creator Canister let alice_canister_id = pocket_ic .update_call( diff --git a/src/lib/integration_tests/tests/subnet_orchestrator/test_allot_empty_canisters_to_individual_canister.rs b/src/lib/integration_tests/tests/subnet_orchestrator/test_allot_empty_canisters_to_individual_canister.rs index 09df6f38..1d2cedeb 100644 --- a/src/lib/integration_tests/tests/subnet_orchestrator/test_allot_empty_canisters_to_individual_canister.rs +++ b/src/lib/integration_tests/tests/subnet_orchestrator/test_allot_empty_canisters_to_individual_canister.rs @@ -2,7 +2,7 @@ use candid::Principal; use pocket_ic::WasmResult; use shared_utils::common::types::known_principal::KnownPrincipalType; use test_utils::setup::{ - env::pocket_ic_env::get_new_pocket_ic_env, + env::pocket_ic_env::{get_new_pocket_ic_env, provision_subnet_orchestrator_canister}, test_constants::{get_mock_user_alice_principal_id, get_mock_user_charlie_principal_id}, }; @@ -19,8 +19,6 @@ fn test_allot_empty_canisters_to_individual_canister() { .cloned() .unwrap(); - let application_subnets = pocket_ic.topology().get_app_subnets(); - let charlie_global_admin = get_mock_user_charlie_principal_id(); pocket_ic @@ -32,25 +30,12 @@ fn test_allot_empty_canisters_to_individual_canister() { ) .unwrap(); - let subnet_orchestrator_canister_id: Principal = pocket_ic - .update_call( - platform_canister_id, - charlie_global_admin, - "provision_subnet_orchestrator_canister", - candid::encode_one(application_subnets[1]).unwrap(), - ) - .map(|res| { - let canister_id_result: Result = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("Canister call failed"), - }; - canister_id_result.unwrap() - }) - .unwrap(); - - for i in 0..120 { - pocket_ic.tick(); - } + let subnet_orchestrator_canister_id = provision_subnet_orchestrator_canister( + &pocket_ic, + &known_principal, + 1, + Some(charlie_global_admin), + ); let alice_yral_principal_id = get_mock_user_alice_principal_id(); let alice_yral_canister_id = pocket_ic diff --git a/src/lib/integration_tests/tests/subnet_orchestrator/test_mark_subnet_orchestrator_as_maxed_out_once_backup_canisters_are_consumed.rs b/src/lib/integration_tests/tests/subnet_orchestrator/test_mark_subnet_orchestrator_as_maxed_out_once_backup_canisters_are_consumed.rs index 4fce7686..440958a4 100644 --- a/src/lib/integration_tests/tests/subnet_orchestrator/test_mark_subnet_orchestrator_as_maxed_out_once_backup_canisters_are_consumed.rs +++ b/src/lib/integration_tests/tests/subnet_orchestrator/test_mark_subnet_orchestrator_as_maxed_out_once_backup_canisters_are_consumed.rs @@ -8,7 +8,7 @@ use shared_utils::{ }, }; use test_utils::setup::{ - env::pocket_ic_env::get_new_pocket_ic_env, test_constants::get_mock_user_charlie_principal_id, + env::pocket_ic_env::{get_new_pocket_ic_env, provision_subnet_orchestrator_canister}, test_constants::get_mock_user_charlie_principal_id, }; #[test] @@ -24,8 +24,6 @@ fn test_mark_subnet_orchestrator_as_maxed_out_once_backup_canisters_are_consumed .cloned() .unwrap(); - let application_subnets = pocket_ic.topology().get_app_subnets(); - let charlie_global_admin = get_mock_user_charlie_principal_id(); pocket_ic @@ -37,25 +35,12 @@ fn test_mark_subnet_orchestrator_as_maxed_out_once_backup_canisters_are_consumed ) .unwrap(); - let subnet_orchestrator_canister_id: Principal = pocket_ic - .update_call( - platform_canister_id, - charlie_global_admin, - "provision_subnet_orchestrator_canister", - candid::encode_one(application_subnets[1]).unwrap(), - ) - .map(|res| { - let canister_id_result: Result = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("Canister call failed"), - }; - canister_id_result.unwrap() - }) - .unwrap(); - - for i in 0..130 { - pocket_ic.tick(); - } + let subnet_orchestrator_canister_id = provision_subnet_orchestrator_canister( + &pocket_ic, + &known_principal, + 1, + Some(charlie_global_admin), + ); let subnet_available_canister_cnt = pocket_ic .query_call( diff --git a/src/lib/integration_tests/tests/subnet_orchestrator/test_updating_controller_before_upgrading_invidividual_canister.rs b/src/lib/integration_tests/tests/subnet_orchestrator/test_updating_controller_before_upgrading_invidividual_canister.rs index 3f9a489c..8413aa78 100644 --- a/src/lib/integration_tests/tests/subnet_orchestrator/test_updating_controller_before_upgrading_invidividual_canister.rs +++ b/src/lib/integration_tests/tests/subnet_orchestrator/test_updating_controller_before_upgrading_invidividual_canister.rs @@ -5,7 +5,7 @@ use shared_utils::{ common::types::known_principal::KnownPrincipalType, }; use test_utils::setup::{ - env::pocket_ic_env::get_new_pocket_ic_env, + env::pocket_ic_env::{get_new_pocket_ic_env, provision_subnet_orchestrator_canister}, test_constants::{get_mock_user_alice_canister_id, get_mock_user_charlie_principal_id}, }; @@ -35,26 +35,12 @@ fn test_updating_controller_before_upgrading_invidividual_canister() { ) .unwrap(); - let subnet_orchestrator_canister_id: Principal = pocket_ic - .update_call( - platform_canister_id, - charlie_global_admin, - "provision_subnet_orchestrator_canister", - candid::encode_one(application_subnets[1]).unwrap(), - ) - .map(|res| { - let canister_id_result: Result = match res { - WasmResult::Reply(payload) => candid::decode_one(&payload).unwrap(), - _ => panic!("Canister call failed"), - }; - canister_id_result.unwrap() - }) - .unwrap(); - - for _ in 0..110 { - pocket_ic.tick() - } - + let subnet_orchestrator_canister_id = provision_subnet_orchestrator_canister( + &pocket_ic, + &known_principal, + 1, + Some(charlie_global_admin), + ); let alice_yral_principal_id = get_mock_user_alice_canister_id(); let alice_yral_canister_id = pocket_ic diff --git a/src/lib/test_utils/src/setup/env/pocket_ic_env.rs b/src/lib/test_utils/src/setup/env/pocket_ic_env.rs index 6397ba63..d87ea547 100644 --- a/src/lib/test_utils/src/setup/env/pocket_ic_env.rs +++ b/src/lib/test_utils/src/setup/env/pocket_ic_env.rs @@ -194,6 +194,59 @@ pub fn get_new_pocket_ic_env() -> (PocketIc, KnownPrincipalMap) { (pocket_ic, known_principal) } +/// Provision a subnet canister +/// `app_subnet_idx` is the index of the application subnet to use. Set to 0 if you're unsure +/// optionally provide a `caller` for the call, else it uses the global super admin +pub fn provision_subnet_orchestrator_canister(pic: &PocketIc, known_principals: &KnownPrincipalMap, app_subnet_idx: usize, caller: Option) -> Principal { + let user_index = provision_subnet_orchestrator_canister_no_wait(pic, known_principals, app_subnet_idx, caller); + for _ in 0..1000 { + pic.tick(); + } + + user_index +} + +/// use [`provision_subnet_orchestrator_canister`] if you don't know what you're doing +pub fn provision_subnet_orchestrator_canister_no_wait(pic: &PocketIc, known_principals: &KnownPrincipalMap, app_subnet_idx: usize, caller: Option) -> Principal { + let caller = caller.unwrap_or_else(|| known_principals[&KnownPrincipalType::UserIdGlobalSuperAdmin]); + let platform_orc = known_principals[&KnownPrincipalType::CanisterIdPlatformOrchestrator]; + let app_subnets = pic.topology().get_app_subnets(); + let user_index_res: Result = execute_update( + pic, + caller, + platform_orc, + "provision_subnet_orchestrator_canister", + &app_subnets[app_subnet_idx] + ); + let user_index = user_index_res.unwrap(); + user_index +} + +pub fn provision_n_subnet_orchestrator_canisters(pic: &PocketIc, known_principals: &KnownPrincipalMap, n: usize, caller: Option) -> Vec { + let mut out = vec![]; + + let caller = caller.unwrap_or_else(|| known_principals[&KnownPrincipalType::CanisterIdPlatformOrchestrator]); + let platform_orc = known_principals[&KnownPrincipalType::CanisterIdPlatformOrchestrator]; + let app_subnets = pic.topology().get_app_subnets(); + + for subnet in app_subnets.into_iter().take(n) { + let user_index_res: Result = execute_update( + pic, + caller, + platform_orc, + "provision_subnet_orchestrator_canister", + &subnet + ); + out.push(user_index_res.unwrap()); + } + + for _ in 0..(1000 + 10 * n) { + pic.tick() + } + + out +} + pub fn execute_query Deserialize<'x>>( pic: &PocketIc, sender: Principal, diff --git a/src/lib/test_utils/src/setup/env/v1.rs b/src/lib/test_utils/src/setup/env/v1.rs index 2d3aed7a..87aef85a 100644 --- a/src/lib/test_utils/src/setup/env/v1.rs +++ b/src/lib/test_utils/src/setup/env/v1.rs @@ -153,7 +153,7 @@ pub fn provision_individual_user_canisters( ) .unwrap(); - for _ in 0..100 { + for _ in 0..1000 { state_machine.tick(); } } From c8701156b61c518622fcd8ac79a6af733d2e01c8 Mon Sep 17 00:00:00 2001 From: rupansh Date: Sat, 30 Nov 2024 01:44:00 +0530 Subject: [PATCH 17/20] fix(user_index/create_pool_of_available_canisters): decrease concurrency --- .../create_pool_of_available_canisters.rs | 15 +++++++-------- src/canister/user_index/src/data_model/mod.rs | 5 +++++ src/lib/test_utils/src/setup/env/pocket_ic_env.rs | 4 ++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/canister/user_index/src/api/canister_management/create_pool_of_available_canisters.rs b/src/canister/user_index/src/api/canister_management/create_pool_of_available_canisters.rs index b7051a08..f5bdb268 100644 --- a/src/canister/user_index/src/api/canister_management/create_pool_of_available_canisters.rs +++ b/src/canister/user_index/src/api/canister_management/create_pool_of_available_canisters.rs @@ -35,12 +35,17 @@ pub async fn impl_create_pool_of_individual_user_available_canisters(version: St //empty canister for backup let create_empty_canister_futures = (0..total_cnt) .map(|_| create_empty_user_canister()); - let mut cans_stream = futures::stream::iter(create_empty_canister_futures).buffer_unordered(10); + let cans_stream = futures::stream::iter(create_empty_canister_futures).buffer_unordered(10); - let to_install: Vec<_> = cans_stream.by_ref().take(individual_user_canister_subnet_batch_size as usize).collect().await; + // Adding to children merkle is not done concurrently to benefit as much as possible from merkleization + let mut empty_canisters: Vec<_> = cans_stream.collect().await; + let to_install: Vec<_> = empty_canisters.drain(0..individual_user_canister_subnet_batch_size as usize).collect(); CANISTER_DATA.with_borrow_mut(|cdata| { cdata.children_merkle.insert_children(to_install.clone()); }); + CANISTER_DATA.with_borrow_mut(|cdata| { + cdata.insert_backup_canisters(empty_canisters); + }); //canisters with installed wasm for available pool let install_wasm_futs = to_install.into_iter().map(move |canister_id| { @@ -72,10 +77,4 @@ pub async fn impl_create_pool_of_individual_user_available_canisters(version: St } } } - - while let Some(canister_id) = cans_stream.next().await { - CANISTER_DATA.with_borrow_mut(|cdata| { - cdata.insert_backup_canister(canister_id); - }) - } } \ No newline at end of file diff --git a/src/canister/user_index/src/data_model/mod.rs b/src/canister/user_index/src/data_model/mod.rs index 3b0ceaa3..40544c34 100644 --- a/src/canister/user_index/src/data_model/mod.rs +++ b/src/canister/user_index/src/data_model/mod.rs @@ -74,6 +74,11 @@ impl CanisterData { inserted } + pub fn insert_backup_canisters(&mut self, canister_ids: impl IntoIterator + Clone) { + self.backup_canister_pool.extend(canister_ids.clone()); + self.children_merkle.insert_children(canister_ids); + } + pub fn remove_backup_canister(&mut self, canister_id: &Principal) { self.backup_canister_pool.remove(&canister_id); // removal from backup pool does not mean its not part of our fleet diff --git a/src/lib/test_utils/src/setup/env/pocket_ic_env.rs b/src/lib/test_utils/src/setup/env/pocket_ic_env.rs index d87ea547..d0dff740 100644 --- a/src/lib/test_utils/src/setup/env/pocket_ic_env.rs +++ b/src/lib/test_utils/src/setup/env/pocket_ic_env.rs @@ -199,7 +199,7 @@ pub fn get_new_pocket_ic_env() -> (PocketIc, KnownPrincipalMap) { /// optionally provide a `caller` for the call, else it uses the global super admin pub fn provision_subnet_orchestrator_canister(pic: &PocketIc, known_principals: &KnownPrincipalMap, app_subnet_idx: usize, caller: Option) -> Principal { let user_index = provision_subnet_orchestrator_canister_no_wait(pic, known_principals, app_subnet_idx, caller); - for _ in 0..1000 { + for _ in 0..10000 { pic.tick(); } @@ -240,7 +240,7 @@ pub fn provision_n_subnet_orchestrator_canisters(pic: &PocketIc, known_principal out.push(user_index_res.unwrap()); } - for _ in 0..(1000 + 10 * n) { + for _ in 0..(10000 + 100 * n) { pic.tick() } From c1e983c586b78b75408b53b35227514e70dcafa6 Mon Sep 17 00:00:00 2001 From: rupansh Date: Tue, 3 Dec 2024 03:05:52 +0530 Subject: [PATCH 18/20] fix: fixes for integration tests --- .../workflows/all_canisters_test_suite_on_any_push.yml | 8 ++++---- src/lib/shared_utils/src/common/participant_crypto/mod.rs | 6 ------ src/lib/test_utils/src/setup/env/pocket_ic_env.rs | 7 +++---- src/lib/test_utils/src/setup/env/v1.rs | 2 +- 4 files changed, 8 insertions(+), 15 deletions(-) diff --git a/.github/workflows/all_canisters_test_suite_on_any_push.yml b/.github/workflows/all_canisters_test_suite_on_any_push.yml index 5e3e26da..c5ffd7ca 100644 --- a/.github/workflows/all_canisters_test_suite_on_any_push.yml +++ b/.github/workflows/all_canisters_test_suite_on_any_push.yml @@ -49,19 +49,19 @@ jobs: run: nix-shell --run "dfx stop" - name: Build platform_orchestrator canister run: | - nix-shell --run "dfx build platform_orchestrator --ic" + nix-shell --run "dfx build platform_orchestrator" gzip -f -1 ./target/wasm32-unknown-unknown/release/platform_orchestrator.wasm - name: Build individual_user_template canister run: | - nix-shell --run "dfx build individual_user_template --ic" + nix-shell --run "dfx build individual_user_template" gzip -f -1 ./target/wasm32-unknown-unknown/release/individual_user_template.wasm - name: Build user_index canister run: | - nix-shell --run "dfx build user_index --ic" + nix-shell --run "dfx build user_index" gzip -f -1 ./target/wasm32-unknown-unknown/release/user_index.wasm - name: Build post_cache canister run: | - nix-shell --run "dfx build post_cache --ic" + nix-shell --run "dfx build post_cache" gzip -f -1 ./target/wasm32-unknown-unknown/release/post_cache.wasm - name: Run canister test suite env: diff --git a/src/lib/shared_utils/src/common/participant_crypto/mod.rs b/src/lib/shared_utils/src/common/participant_crypto/mod.rs index cff355a3..44b92bbf 100644 --- a/src/lib/shared_utils/src/common/participant_crypto/mod.rs +++ b/src/lib/shared_utils/src/common/participant_crypto/mod.rs @@ -214,12 +214,6 @@ impl ProofOfParticipation { /// Verify that the caller is a YRAL canister pub async fn verify_caller_is_participant(&self, store: &'static LocalPoPStore) -> Result<(), String> { - if is_local() { - // Hack: Always pass on local testing node - // a proper implementation requires deploying platform orchestrator locally - return Ok(()) - } - let platform_orchestrator = store.with_borrow(|s| s.platform_orchestrator()); let canister = ic_cdk::caller(); diff --git a/src/lib/test_utils/src/setup/env/pocket_ic_env.rs b/src/lib/test_utils/src/setup/env/pocket_ic_env.rs index d0dff740..e98a12a8 100644 --- a/src/lib/test_utils/src/setup/env/pocket_ic_env.rs +++ b/src/lib/test_utils/src/setup/env/pocket_ic_env.rs @@ -199,7 +199,7 @@ pub fn get_new_pocket_ic_env() -> (PocketIc, KnownPrincipalMap) { /// optionally provide a `caller` for the call, else it uses the global super admin pub fn provision_subnet_orchestrator_canister(pic: &PocketIc, known_principals: &KnownPrincipalMap, app_subnet_idx: usize, caller: Option) -> Principal { let user_index = provision_subnet_orchestrator_canister_no_wait(pic, known_principals, app_subnet_idx, caller); - for _ in 0..10000 { + for _ in 0..50 { pic.tick(); } @@ -218,8 +218,7 @@ pub fn provision_subnet_orchestrator_canister_no_wait(pic: &PocketIc, known_prin "provision_subnet_orchestrator_canister", &app_subnets[app_subnet_idx] ); - let user_index = user_index_res.unwrap(); - user_index + user_index_res.unwrap() } pub fn provision_n_subnet_orchestrator_canisters(pic: &PocketIc, known_principals: &KnownPrincipalMap, n: usize, caller: Option) -> Vec { @@ -240,7 +239,7 @@ pub fn provision_n_subnet_orchestrator_canisters(pic: &PocketIc, known_principal out.push(user_index_res.unwrap()); } - for _ in 0..(10000 + 100 * n) { + for _ in 0..(50 + 10 * n) { pic.tick() } diff --git a/src/lib/test_utils/src/setup/env/v1.rs b/src/lib/test_utils/src/setup/env/v1.rs index 87aef85a..4a693fb3 100644 --- a/src/lib/test_utils/src/setup/env/v1.rs +++ b/src/lib/test_utils/src/setup/env/v1.rs @@ -153,7 +153,7 @@ pub fn provision_individual_user_canisters( ) .unwrap(); - for _ in 0..1000 { + for _ in 0..50 { state_machine.tick(); } } From 68c42739fbed1532f081f83014e9b93dea79314f Mon Sep 17 00:00:00 2001 From: rupansh Date: Tue, 3 Dec 2024 03:29:56 +0530 Subject: [PATCH 19/20] fix(test_utils/pocket_ic_env): fix provision_n_subnet_orchestrator_canisters --- src/lib/test_utils/src/setup/env/pocket_ic_env.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/test_utils/src/setup/env/pocket_ic_env.rs b/src/lib/test_utils/src/setup/env/pocket_ic_env.rs index e98a12a8..4c4e0234 100644 --- a/src/lib/test_utils/src/setup/env/pocket_ic_env.rs +++ b/src/lib/test_utils/src/setup/env/pocket_ic_env.rs @@ -224,7 +224,7 @@ pub fn provision_subnet_orchestrator_canister_no_wait(pic: &PocketIc, known_prin pub fn provision_n_subnet_orchestrator_canisters(pic: &PocketIc, known_principals: &KnownPrincipalMap, n: usize, caller: Option) -> Vec { let mut out = vec![]; - let caller = caller.unwrap_or_else(|| known_principals[&KnownPrincipalType::CanisterIdPlatformOrchestrator]); + let caller = caller.unwrap_or_else(|| known_principals[&KnownPrincipalType::UserIdGlobalSuperAdmin]); let platform_orc = known_principals[&KnownPrincipalType::CanisterIdPlatformOrchestrator]; let app_subnets = pic.topology().get_app_subnets(); From 442f1f590fa820e3fd622d7bc874fc4521c7ef91 Mon Sep 17 00:00:00 2001 From: rupansh Date: Tue, 3 Dec 2024 19:26:55 +0530 Subject: [PATCH 20/20] fix: fixes for subnet orchestrator tests --- .../create_pool_of_available_canisters.rs | 12 +++++++++--- ...s_maxed_out_once_backup_canisters_are_consumed.rs | 4 ++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/canister/user_index/src/api/canister_management/create_pool_of_available_canisters.rs b/src/canister/user_index/src/api/canister_management/create_pool_of_available_canisters.rs index f5bdb268..b0efcb30 100644 --- a/src/canister/user_index/src/api/canister_management/create_pool_of_available_canisters.rs +++ b/src/canister/user_index/src/api/canister_management/create_pool_of_available_canisters.rs @@ -61,7 +61,11 @@ pub async fn impl_create_pool_of_individual_user_available_canisters(version: St ).await } }); + let mut install_wasm_stream = futures::stream::iter(install_wasm_futs).buffer_unordered(10); + + let mut backup_canisters = Vec::new(); + while let Some(res) = install_wasm_stream.next().await { match res { Ok(canister_id) => { @@ -71,10 +75,12 @@ pub async fn impl_create_pool_of_individual_user_available_canisters(version: St } Err((canister_id, e)) => { ic_cdk::println!("Failed to install wasm on canister: {}, error: {:?}", canister_id, e); - CANISTER_DATA.with_borrow_mut(|cdata| { - cdata.insert_backup_canister(canister_id); - }) + backup_canisters.push(canister_id); } } } + + CANISTER_DATA.with_borrow_mut(|cdata| { + cdata.insert_backup_canisters(backup_canisters); + }) } \ No newline at end of file diff --git a/src/lib/integration_tests/tests/subnet_orchestrator/test_mark_subnet_orchestrator_as_maxed_out_once_backup_canisters_are_consumed.rs b/src/lib/integration_tests/tests/subnet_orchestrator/test_mark_subnet_orchestrator_as_maxed_out_once_backup_canisters_are_consumed.rs index 440958a4..d80befc1 100644 --- a/src/lib/integration_tests/tests/subnet_orchestrator/test_mark_subnet_orchestrator_as_maxed_out_once_backup_canisters_are_consumed.rs +++ b/src/lib/integration_tests/tests/subnet_orchestrator/test_mark_subnet_orchestrator_as_maxed_out_once_backup_canisters_are_consumed.rs @@ -42,6 +42,10 @@ fn test_mark_subnet_orchestrator_as_maxed_out_once_backup_canisters_are_consumed Some(charlie_global_admin), ); + for _ in 0..150 { + pocket_ic.tick(); + } + let subnet_available_canister_cnt = pocket_ic .query_call( subnet_orchestrator_canister_id,