1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
//! The canister interface for the IC management canister. See the [specification][spec] for full documentation of the interface.
//!
//! [spec]: https://smartcontracts.org/docs/interface-spec/index.html#ic-management-canister
use crate::{call::AsyncCall, Canister};
use candid::{CandidType, Deserialize, Nat};
use ic_agent::{export::Principal, Agent};
use std::{convert::AsRef, fmt::Debug, ops::Deref};
use strum_macros::{AsRefStr, EnumString};
pub mod attributes;
pub mod builders;
pub use builders::{CreateCanisterBuilder, InstallCodeBuilder, UpdateCanisterBuilder};
/// The IC management canister.
#[derive(Debug, Clone)]
pub struct ManagementCanister<'agent>(Canister<'agent>);
impl<'agent> Deref for ManagementCanister<'agent> {
type Target = Canister<'agent>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
/// All the known methods of the management canister.
#[derive(AsRefStr, Debug, EnumString)]
#[strum(serialize_all = "snake_case")]
pub enum MgmtMethod {
// FIXME when rust-lang/rust#85960 is resolved, update these to links.
/// See `Canister::<ManagementCanister>::create_canister`.
CreateCanister,
/// See `Canister::<ManagementCanister>::install_code`.
InstallCode,
/// See `Canister::<ManagementCanister>::start_canister`.
StartCanister,
/// See `Canister::<ManagementCanister>::stop_canister`.
StopCanister,
/// See `Canister::<ManagementCanister>::canister_status`.
CanisterStatus,
/// See `Canister::<ManagementCanister>::delete_canister`.
DeleteCanister,
/// See `Canister::<ManagementCanister>::deposit_cycles`.
DepositCycles,
/// See `Canister::<ManagementCanister>::raw_rand`.
RawRand,
/// See `Canister::<ManagementCanister>::provisional_create_canister_with_cycles`.
ProvisionalCreateCanisterWithCycles,
/// See `Canister::<ManagementCanister>::provisional_top_up_canister`.
ProvisionalTopUpCanister,
/// See `Canister::<ManagementCanister>::uninstall_code`.
UninstallCode,
/// See `Canister::<ManagementCanister>::update_settings`.
UpdateSettings,
}
impl<'agent> ManagementCanister<'agent> {
/// Create an instance of a `ManagementCanister` interface pointing to the specified Canister ID.
pub fn create(agent: &'agent Agent) -> Self {
Self(
Canister::builder()
.with_agent(agent)
.with_canister_id(Principal::management_canister())
.build()
.unwrap(),
)
}
/// Create a `ManagementCanister` interface from an existing canister object.
pub fn from_canister(canister: Canister<'agent>) -> Self {
Self(canister)
}
}
/// The complete canister status information of a canister. This includes
/// the CanisterStatus, a hash of the module installed on the canister (None if nothing installed),
/// the contoller of the canister, the canisters memory size, and its balance in cycles.
#[derive(Clone, Debug, Deserialize, CandidType)]
pub struct StatusCallResult {
/// The status of the canister.
pub status: CanisterStatus,
/// The canister's settings.
pub settings: DefiniteCanisterSettings,
/// The SHA-256 hash of the canister's installed code, if any.
pub module_hash: Option<Vec<u8>>,
/// The total size, in bytes, of the memory the canister is using.
pub memory_size: Nat,
/// The canister's cycle balance.
pub cycles: Nat,
}
/// The concrete settings of a canister.
#[derive(Clone, Debug, Deserialize, CandidType)]
pub struct DefiniteCanisterSettings {
/// The set of canister controllers. Controllers can update the canister via the management canister.
pub controllers: Vec<Principal>,
/// The allocation percentage (between 0 and 100 inclusive) for *guaranteed* compute capacity.
pub compute_allocation: Nat,
/// The allocation, in bytes (up to 256 TiB) that the canister is allowed to use for storage.
pub memory_allocation: Nat,
/// The IC will freeze a canister protectively if it will likely run out of cycles before this amount of time, in seconds (up to `u64::MAX`), has passed.
pub freezing_threshold: Nat,
}
impl std::fmt::Display for StatusCallResult {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(self, f)
}
}
/// The status of a Canister, whether it's running, in the process of stopping, or
/// stopped.
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, CandidType)]
pub enum CanisterStatus {
/// The canister is currently running.
#[serde(rename = "running")]
Running,
/// The canister is in the process of stopping.
#[serde(rename = "stopping")]
Stopping,
/// The canister is stopped.
#[serde(rename = "stopped")]
Stopped,
}
impl std::fmt::Display for CanisterStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(self, f)
}
}
impl<'agent> ManagementCanister<'agent> {
/// Get the status of a canister.
pub fn canister_status<'canister: 'agent>(
&'canister self,
canister_id: &Principal,
) -> impl 'agent + AsyncCall<(StatusCallResult,)> {
#[derive(CandidType)]
struct In {
canister_id: Principal,
}
self.update_(MgmtMethod::CanisterStatus.as_ref())
.with_arg(In {
canister_id: *canister_id,
})
.with_effective_canister_id(canister_id.to_owned())
.build()
.map(|result: (StatusCallResult,)| (result.0,))
}
/// Create a canister.
pub fn create_canister<'canister: 'agent>(
&'canister self,
) -> CreateCanisterBuilder<'agent, 'canister> {
CreateCanisterBuilder::builder(self)
}
/// This method deposits the cycles included in this call into the specified canister.
/// Only the controller of the canister can deposit cycles.
pub fn deposit_cycles<'canister: 'agent>(
&'canister self,
canister_id: &Principal,
) -> impl 'agent + AsyncCall<()> {
#[derive(CandidType)]
struct Argument {
canister_id: Principal,
}
self.update_(MgmtMethod::DepositCycles.as_ref())
.with_arg(Argument {
canister_id: *canister_id,
})
.with_effective_canister_id(canister_id.to_owned())
.build()
}
/// Deletes a canister.
pub fn delete_canister<'canister: 'agent>(
&'canister self,
canister_id: &Principal,
) -> impl 'agent + AsyncCall<()> {
#[derive(CandidType)]
struct Argument {
canister_id: Principal,
}
self.update_(MgmtMethod::DeleteCanister.as_ref())
.with_arg(Argument {
canister_id: *canister_id,
})
.with_effective_canister_id(canister_id.to_owned())
.build()
}
/// Until developers can convert real ICP tokens to a top up an existing canister,
/// the system provides the provisional_top_up_canister method.
/// It adds amount cycles to the balance of canister identified by amount
/// (implicitly capping it at MAX_CANISTER_BALANCE).
pub fn provisional_top_up_canister<'canister: 'agent>(
&'canister self,
canister_id: &Principal,
amount: u64,
) -> impl 'agent + AsyncCall<()> {
#[derive(CandidType)]
struct Argument {
canister_id: Principal,
amount: u64,
}
self.update_(MgmtMethod::ProvisionalTopUpCanister.as_ref())
.with_arg(Argument {
canister_id: *canister_id,
amount,
})
.with_effective_canister_id(canister_id.to_owned())
.build()
}
/// This method takes no input and returns 32 pseudo-random bytes to the caller.
/// The return value is unknown to any part of the IC at time of the submission of this call.
/// A new return value is generated for each call to this method.
pub fn raw_rand<'canister: 'agent>(&'canister self) -> impl 'agent + AsyncCall<(Vec<u8>,)> {
self.update_(MgmtMethod::RawRand.as_ref())
.build()
.map(|result: (Vec<u8>,)| (result.0,))
}
/// Starts a canister.
pub fn start_canister<'canister: 'agent>(
&'canister self,
canister_id: &Principal,
) -> impl 'agent + AsyncCall<()> {
#[derive(CandidType)]
struct Argument {
canister_id: Principal,
}
self.update_(MgmtMethod::StartCanister.as_ref())
.with_arg(Argument {
canister_id: *canister_id,
})
.with_effective_canister_id(canister_id.to_owned())
.build()
}
/// Stop a canister.
pub fn stop_canister<'canister: 'agent>(
&'canister self,
canister_id: &Principal,
) -> impl 'agent + AsyncCall<()> {
#[derive(CandidType)]
struct Argument {
canister_id: Principal,
}
self.update_(MgmtMethod::StopCanister.as_ref())
.with_arg(Argument {
canister_id: *canister_id,
})
.with_effective_canister_id(canister_id.to_owned())
.build()
}
/// This method removes a canister’s code and state, making the canister empty again.
/// Only the controller of the canister can uninstall code.
/// Uninstalling a canister’s code will reject all calls that the canister has not yet responded to,
/// and drop the canister’s code and state.
/// Outstanding responses to the canister will not be processed, even if they arrive after code has been installed again.
/// The canister is now empty. In particular, any incoming or queued calls will be rejected.
//// A canister after uninstalling retains its cycles balance, controller, status, and allocations.
pub fn uninstall_code<'canister: 'agent>(
&'canister self,
canister_id: &Principal,
) -> impl 'agent + AsyncCall<()> {
#[derive(CandidType)]
struct Argument {
canister_id: Principal,
}
self.update_(MgmtMethod::UninstallCode.as_ref())
.with_arg(Argument {
canister_id: *canister_id,
})
.with_effective_canister_id(canister_id.to_owned())
.build()
}
/// Install a canister, with all the arguments necessary for creating the canister.
pub fn install_code<'canister: 'agent>(
&'canister self,
canister_id: &Principal,
wasm: &'canister [u8],
) -> InstallCodeBuilder<'agent, 'canister> {
InstallCodeBuilder::builder(self, canister_id, wasm)
}
/// Update one or more of a canisters settings (i.e its controller, compute allocation, or memory allocation.)
pub fn update_settings<'canister: 'agent>(
&'canister self,
canister_id: &Principal,
) -> UpdateCanisterBuilder<'agent, 'canister> {
UpdateCanisterBuilder::builder(self, canister_id)
}
}