Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 2 additions & 20 deletions crates/icp-canister-interfaces/src/cycles_ledger.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use candid::{CandidType, Nat, Principal};
use serde::Deserialize;

use crate::management_canister::CanisterSettingsArg;

/// 100m cycles
pub const CYCLES_LEDGER_BLOCK_FEE: u128 = 100_000_000;
pub const CYCLES_LEDGER_DECIMALS: i64 = 12;
Expand All @@ -9,26 +11,6 @@ pub const CYCLES_LEDGER_CID: &str = "um5iw-rqaaa-aaaaq-qaaba-cai";
pub const CYCLES_LEDGER_PRINCIPAL: Principal =
Principal::from_slice(&[0, 0, 0, 0, 2, 16, 0, 2, 1, 1]);

/// Log visibility setting for a canister.
/// Matches the cycles ledger's LogVisibility variant type.
#[derive(Clone, Debug, CandidType, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum LogVisibility {
Controllers,
Public,
AllowedViewers(Vec<Principal>),
}

#[derive(Clone, Debug, CandidType, Deserialize)]
pub struct CanisterSettingsArg {
pub freezing_threshold: Option<Nat>,
pub controllers: Option<Vec<Principal>>,
pub reserved_cycles_limit: Option<Nat>,
pub log_visibility: Option<LogVisibility>,
pub memory_allocation: Option<Nat>,
pub compute_allocation: Option<Nat>,
}

#[derive(Clone, Debug, CandidType, Deserialize)]
pub enum SubnetSelectionArg {
Filter { subnet_type: Option<String> },
Expand Down
1 change: 1 addition & 0 deletions crates/icp-canister-interfaces/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod cycles_minting_canister;
pub mod governance;
pub mod icp_ledger;
pub mod internet_identity;
pub mod management_canister;
pub mod nns_migration;
pub mod nns_root;
pub mod proxy;
Expand Down
33 changes: 33 additions & 0 deletions crates/icp-canister-interfaces/src/management_canister.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use candid::{CandidType, Nat, Principal};
use serde::Deserialize;

#[derive(Clone, Debug, CandidType, Deserialize)]
pub struct MgmtCreateCanisterArgs {
pub settings: Option<CanisterSettingsArg>,
pub sender_canister_version: Option<u64>,
}

#[derive(Clone, Debug, CandidType, Deserialize)]
pub struct MgmtCreateCanisterResponse {
pub canister_id: Principal,
}

#[derive(Clone, Debug, CandidType, Deserialize)]
pub struct CanisterSettingsArg {
pub freezing_threshold: Option<Nat>,
pub controllers: Option<Vec<Principal>>,
pub reserved_cycles_limit: Option<Nat>,
pub log_visibility: Option<LogVisibility>,
pub memory_allocation: Option<Nat>,
pub compute_allocation: Option<Nat>,
}

/// Log visibility setting for a canister.
/// Matches the cycles ledger's LogVisibility variant type.
#[derive(Clone, Debug, CandidType, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum LogVisibility {
Controllers,
Public,
AllowedViewers(Vec<Principal>),
}
2 changes: 1 addition & 1 deletion crates/icp-cli/src/commands/canister/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use clap::{ArgGroup, Args, Parser};
use icp::context::Context;
use icp::parsers::{CyclesAmount, DurationAmount, MemoryAmount};
use icp::{Canister, context::CanisterSelection, prelude::*};
use icp_canister_interfaces::cycles_ledger::CanisterSettingsArg;
use icp_canister_interfaces::management_canister::CanisterSettingsArg;

use crate::{commands::args, operations::create::CreateOperation};

Expand Down
109 changes: 72 additions & 37 deletions crates/icp-cli/src/operations/create.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
use candid::{Decode, Encode, Nat, Principal};
use ic_agent::{Agent, AgentError};
use ic_agent::{
Agent, AgentError,
agent::{Subnet, SubnetType},
};
use icp_canister_interfaces::{
cycles_ledger::{
CYCLES_LEDGER_PRINCIPAL, CanisterSettingsArg, CreateCanisterArgs, CreateCanisterResponse,
CreationArgs, SubnetSelectionArg,
CYCLES_LEDGER_PRINCIPAL, CreateCanisterArgs, CreateCanisterResponse, CreationArgs,
SubnetSelectionArg,
},
cycles_minting_canister::CYCLES_MINTING_CANISTER_PRINCIPAL,
registry::{GetSubnetForCanisterRequest, GetSubnetForCanisterResult, REGISTRY_PRINCIPAL},
management_canister::{
CanisterSettingsArg, MgmtCreateCanisterArgs, MgmtCreateCanisterResponse,
},
};
use rand::seq::IndexedRandom;
use snafu::{ResultExt, Snafu};
use snafu::{OptionExt, ResultExt, Snafu};
use std::sync::Arc;
use tokio::sync::OnceCell;

Expand Down Expand Up @@ -91,13 +96,35 @@ impl CreateOperation {
pub async fn create(
&self,
settings: &CanisterSettingsArg,
) -> Result<Principal, CreateOperationError> {
let selected_subnet = self
.get_subnet()
.await
.map_err(|e| CreateOperationError::SubnetResolution { message: e })?;
let subnet_info = self
.inner
.agent
.get_subnet_by_id(&selected_subnet)
.await
.context(GetSubnetSnafu)?;
let cid = if let Some(SubnetType::Unknown(kind)) = subnet_info.subnet_type()
&& kind == "cloud_engine"
{
self.create_mgmt(settings, &subnet_info).await?
} else {
self.create_ledger(settings, selected_subnet).await?
};
Ok(cid)
}

async fn create_ledger(
&self,
settings: &CanisterSettingsArg,
selected_subnet: Principal,
) -> Result<Principal, CreateOperationError> {
let creation_args = CreationArgs {
subnet_selection: Some(SubnetSelectionArg::Subnet {
subnet: self
.get_subnet()
.await
.map_err(|e| CreateOperationError::SubnetResolution { message: e })?,
subnet: selected_subnet,
}),
settings: Some(settings.clone()),
};
Expand Down Expand Up @@ -128,10 +155,40 @@ impl CreateOperation {
.fail();
}
};

Ok(cid)
}

async fn create_mgmt(
&self,
settings: &CanisterSettingsArg,
selected_subnet: &Subnet,
) -> Result<Principal, CreateOperationError> {
let arg = MgmtCreateCanisterArgs {
settings: Some(settings.clone()),
sender_canister_version: None,
};

// Call management canister create_canister
let resp = self
.inner
.agent
.update(&Principal::management_canister(), "create_canister")
.with_arg(Encode!(&arg).context(CandidEncodeSnafu)?)
.with_effective_canister_id(
*selected_subnet
.iter_canister_ranges()
.next()
.context(CreateCanisterSnafu {
message: "subnet did not contain canister ranges",
})?
.start(),
)
.await
.context(AgentSnafu)?;
let resp = Decode!(&resp, MgmtCreateCanisterResponse).context(CandidDecodeSnafu)?;
Ok(resp.canister_id)
}

/// 1. If a subnet is explicitly provided, use it
/// 2. If no canisters exist yet, pick a random available subnet
/// 3. If canisters exist, use the same subnet as the first existing canister
Expand All @@ -148,10 +205,13 @@ impl CreateOperation {
}

if let Some(canister) = self.inner.existing_canisters.first() {
let subnet = get_canister_subnet(&self.inner.agent, *canister)
let subnet = &self
.inner
.agent
.get_subnet_by_canister(canister)
.await
.map_err(|e| e.to_string())?;
Ok(subnet)
Ok(subnet.id())
} else {
// If no canisters exist, pick a random available subnet
let subnets = get_available_subnets(&self.inner.agent)
Expand All @@ -170,31 +230,6 @@ impl CreateOperation {
}
}

async fn get_canister_subnet(
agent: &Agent,
canister: Principal,
) -> Result<Principal, CreateOperationError> {
let args = &GetSubnetForCanisterRequest {
principal: Some(canister),
};

let bs = agent
.query(&REGISTRY_PRINCIPAL, "get_subnet_for_canister")
.with_arg(Encode!(args).context(CandidEncodeSnafu)?)
.call()
.await
.context(GetSubnetSnafu)?;

let resp = Decode!(&bs, GetSubnetForCanisterResult).context(CandidDecodeSnafu)?;

let out = resp
.map_err(|err| CreateOperationError::Registry { message: err })?
.subnet_id
.ok_or(CreateOperationError::MissingSubnetId)?;

Ok(out)
}

async fn get_available_subnets(agent: &Agent) -> Result<Vec<Principal>, CreateOperationError> {
let bs = agent
.query(&CYCLES_MINTING_CANISTER_PRINCIPAL, "get_default_subnets")
Expand Down
6 changes: 3 additions & 3 deletions crates/icp/src/canister/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::collections::HashMap;

use candid::{Nat, Principal};
use icp_canister_interfaces::cycles_ledger::CanisterSettingsArg;
use icp_canister_interfaces::management_canister::CanisterSettingsArg;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -187,9 +187,9 @@ impl From<LogVisibility> for ic_management_canister_types::LogVisibility {
}
}

impl From<LogVisibility> for icp_canister_interfaces::cycles_ledger::LogVisibility {
impl From<LogVisibility> for icp_canister_interfaces::management_canister::LogVisibility {
fn from(value: LogVisibility) -> Self {
use icp_canister_interfaces::cycles_ledger::LogVisibility as CyclesLedgerLogVisibility;
use icp_canister_interfaces::management_canister::LogVisibility as CyclesLedgerLogVisibility;
match value {
LogVisibility::Controllers => CyclesLedgerLogVisibility::Controllers,
LogVisibility::Public => CyclesLedgerLogVisibility::Public,
Expand Down
3 changes: 2 additions & 1 deletion crates/icp/src/network/managed/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ use ic_ledger_types::{AccountIdentifier, Memo, Subaccount, Tokens, TransferArgs,
use ic_utils::interfaces::management_canister::builders::CanisterInstallMode;
use icp_canister_interfaces::{
cycles_ledger::{
CYCLES_LEDGER_BLOCK_FEE, CYCLES_LEDGER_PRINCIPAL, CanisterSettingsArg, CreateCanisterArgs,
CYCLES_LEDGER_BLOCK_FEE, CYCLES_LEDGER_PRINCIPAL, CreateCanisterArgs,
CreateCanisterResponse, CreationArgs,
},
cycles_minting_canister::{
CYCLES_MINTING_CANISTER_PRINCIPAL, ConversionRateResponse, MEMO_MINT_CYCLES,
NotifyMintArgs, NotifyMintResponse,
},
icp_ledger::{ICP_LEDGER_BLOCK_FEE_E8S, ICP_LEDGER_PRINCIPAL},
management_canister::CanisterSettingsArg,
};
use icrc_ledger_types::icrc1::{
account::Account,
Expand Down