ic_utils/interfaces/
management_canister.rs

1//! The canister interface for the IC management canister. See the [specification][spec] for full documentation of the interface.
2//!
3//! [spec]: https://internetcomputer.org/docs/current/references/ic-interface-spec#ic-management-canister
4
5use crate::{
6    call::{AsyncCall, SyncCall},
7    Canister,
8};
9use ic_agent::{export::Principal, Agent};
10use ic_management_canister_types::{
11    CanisterIdRecord, DeleteCanisterSnapshotArgs, LoadCanisterSnapshotArgs,
12    ProvisionalTopUpCanisterArgs, ReadCanisterSnapshotDataArgs, ReadCanisterSnapshotMetadataArgs,
13    TakeCanisterSnapshotArgs, UploadCanisterSnapshotDataArgs, UploadCanisterSnapshotMetadataArgs,
14    UploadChunkArgs,
15};
16// Re-export the types that are used be defined in this file.
17pub use ic_management_canister_types::{
18    CanisterLogRecord, CanisterStatusResult, CanisterStatusType, CanisterTimer, ChunkHash,
19    DefiniteCanisterSettings, FetchCanisterLogsResult, LogVisibility, OnLowWasmMemoryHookStatus,
20    QueryStats, ReadCanisterSnapshotDataResult, ReadCanisterSnapshotMetadataResult, Snapshot,
21    SnapshotDataKind, SnapshotDataOffset, SnapshotMetadataGlobal, SnapshotSource,
22    StoredChunksResult, UploadCanisterSnapshotMetadataResult, UploadChunkResult,
23};
24use std::{convert::AsRef, ops::Deref};
25use strum_macros::{AsRefStr, Display, EnumString};
26
27pub mod attributes;
28pub mod builders;
29
30#[doc(inline)]
31pub use builders::{
32    CreateCanisterBuilder, InstallBuilder, InstallChunkedCodeBuilder, InstallCodeBuilder,
33    UpdateCanisterBuilder,
34};
35
36/// The IC management canister.
37#[derive(Debug, Clone)]
38pub struct ManagementCanister<'agent>(Canister<'agent>);
39
40impl<'agent> Deref for ManagementCanister<'agent> {
41    type Target = Canister<'agent>;
42    fn deref(&self) -> &Self::Target {
43        &self.0
44    }
45}
46
47/// All the known methods of the management canister.
48#[derive(AsRefStr, Debug, EnumString, Display)]
49#[strum(serialize_all = "snake_case")]
50pub enum MgmtMethod {
51    /// See [`ManagementCanister::create_canister`].
52    CreateCanister,
53    /// See [`ManagementCanister::install_code`].
54    InstallCode,
55    /// See [`ManagementCanister::start_canister`].
56    StartCanister,
57    /// See [`ManagementCanister::stop_canister`].
58    StopCanister,
59    /// See [`ManagementCanister::canister_status`].
60    CanisterStatus,
61    /// See [`ManagementCanister::delete_canister`].
62    DeleteCanister,
63    /// See [`ManagementCanister::deposit_cycles`].
64    DepositCycles,
65    /// See [`ManagementCanister::raw_rand`].
66    RawRand,
67    /// See [`CreateCanisterBuilder::as_provisional_create_with_amount`].
68    ProvisionalCreateCanisterWithCycles,
69    /// See [`ManagementCanister::provisional_top_up_canister`].
70    ProvisionalTopUpCanister,
71    /// See [`ManagementCanister::uninstall_code`].
72    UninstallCode,
73    /// See [`ManagementCanister::update_settings`].
74    UpdateSettings,
75    /// See [`ManagementCanister::upload_chunk`].
76    UploadChunk,
77    /// See [`ManagementCanister::clear_chunk_store`].
78    ClearChunkStore,
79    /// See [`ManagementCanister::stored_chunks`].
80    StoredChunks,
81    /// See [`ManagementCanister::install_chunked_code`].
82    InstallChunkedCode,
83    /// See [`ManagementCanister::fetch_canister_logs`].
84    FetchCanisterLogs,
85    /// See [`ManagementCanister::take_canister_snapshot`].
86    TakeCanisterSnapshot,
87    /// See [`ManagementCanister::load_canister_snapshot`].
88    LoadCanisterSnapshot,
89    /// See [`ManagementCanister::list_canister_snapshots`].
90    ListCanisterSnapshots,
91    /// See [`ManagementCanister::delete_canister_snapshot`].
92    DeleteCanisterSnapshot,
93    /// See [`ManagementCanister::read_canister_snapshot_metadata`].
94    ReadCanisterSnapshotMetadata,
95    /// See [`ManagementCanister::read_canister_snapshot_data`].
96    ReadCanisterSnapshotData,
97    /// See [`ManagementCanister::upload_canister_snapshot_metadata`].
98    UploadCanisterSnapshotMetadata,
99    /// See [`ManagementCanister::upload_canister_snapshot_data`].
100    UploadCanisterSnapshotData,
101    /// There is no corresponding agent function as only canisters can call it.
102    EcdsaPublicKey,
103    /// There is no corresponding agent function as only canisters can call it.
104    SignWithEcdsa,
105    /// There is no corresponding agent function as only canisters can call it. Use [`BitcoinCanister`](super::BitcoinCanister) instead.
106    BitcoinGetBalance,
107    /// There is no corresponding agent function as only canisters can call it. Use [`BitcoinCanister`](super::BitcoinCanister) instead.
108    BitcoinGetUtxos,
109    /// There is no corresponding agent function as only canisters can call it. Use [`BitcoinCanister`](super::BitcoinCanister) instead.
110    BitcoinSendTransaction,
111    /// There is no corresponding agent function as only canisters can call it. Use [`BitcoinCanister`](super::BitcoinCanister) instead.
112    BitcoinGetCurrentFeePercentiles,
113    /// There is no corresponding agent function as only canisters can call it. Use [`BitcoinCanister`](super::BitcoinCanister) instead.
114    BitcoinGetBlockHeaders,
115    /// There is no corresponding agent function as only canisters can call it.
116    NodeMetricsHistory,
117    /// There is no corresponding agent function as only canisters can call it.
118    CanisterInfo,
119}
120
121impl<'agent> ManagementCanister<'agent> {
122    /// Create an instance of a `ManagementCanister` interface pointing to the specified Canister ID.
123    pub fn create(agent: &'agent Agent) -> Self {
124        Self(
125            Canister::builder()
126                .with_agent(agent)
127                .with_canister_id(Principal::management_canister())
128                .build()
129                .unwrap(),
130        )
131    }
132
133    /// Create a `ManagementCanister` interface from an existing canister object.
134    pub fn from_canister(canister: Canister<'agent>) -> Self {
135        Self(canister)
136    }
137}
138
139#[doc(hidden)]
140#[deprecated(since = "0.42.0", note = "Please use CanisterStatusResult instead")]
141pub type StatusCallResult = CanisterStatusResult;
142
143#[doc(hidden)]
144#[deprecated(since = "0.42.0", note = "Please use CanisterStatusType instead")]
145pub type CanisterStatus = CanisterStatusType;
146
147#[doc(hidden)]
148#[deprecated(since = "0.42.0", note = "Please use FetchCanisterLogsResult instead")]
149pub type FetchCanisterLogsResponse = FetchCanisterLogsResult;
150
151#[doc(hidden)]
152#[deprecated(since = "0.42.0", note = "Please use StoredChunksResult instead")]
153pub type StoreChunksResult = StoredChunksResult;
154
155#[doc(hidden)]
156#[deprecated(
157    since = "0.42.0",
158    note = "Please use ReadCanisterSnapshotMetadataResult instead"
159)]
160pub type SnapshotMetadata = ReadCanisterSnapshotMetadataResult;
161
162#[doc(hidden)]
163#[deprecated(
164    since = "0.42.0",
165    note = "Please use ReadCanisterSnapshotDataResult instead"
166)]
167pub type SnapshotDataResult = ReadCanisterSnapshotDataResult;
168
169#[doc(hidden)]
170#[deprecated(
171    since = "0.42.0",
172    note = "Please use UploadCanisterSnapshotMetadataResult instead"
173)]
174pub type CanisterSnapshotId = UploadCanisterSnapshotMetadataResult;
175
176impl<'agent> ManagementCanister<'agent> {
177    /// Get the status of a canister.
178    pub fn canister_status(
179        &self,
180        canister_id: &Principal,
181    ) -> impl 'agent + AsyncCall<Value = (CanisterStatusResult,)> {
182        self.update(MgmtMethod::CanisterStatus.as_ref())
183            .with_arg(CanisterIdRecord {
184                canister_id: *canister_id,
185            })
186            .with_effective_canister_id(canister_id.to_owned())
187            .build()
188            .map(|result: (CanisterStatusResult,)| (result.0,))
189    }
190
191    /// Create a canister.
192    pub fn create_canister<'canister>(&'canister self) -> CreateCanisterBuilder<'agent, 'canister> {
193        CreateCanisterBuilder::builder(self)
194    }
195
196    /// This method deposits the cycles included in this call into the specified canister.
197    /// Only the controller of the canister can deposit cycles.
198    pub fn deposit_cycles(&self, canister_id: &Principal) -> impl 'agent + AsyncCall<Value = ()> {
199        self.update(MgmtMethod::DepositCycles.as_ref())
200            .with_arg(CanisterIdRecord {
201                canister_id: *canister_id,
202            })
203            .with_effective_canister_id(canister_id.to_owned())
204            .build()
205    }
206
207    /// Deletes a canister.
208    pub fn delete_canister(&self, canister_id: &Principal) -> impl 'agent + AsyncCall<Value = ()> {
209        self.update(MgmtMethod::DeleteCanister.as_ref())
210            .with_arg(CanisterIdRecord {
211                canister_id: *canister_id,
212            })
213            .with_effective_canister_id(canister_id.to_owned())
214            .build()
215    }
216
217    /// Until developers can convert real ICP tokens to a top up an existing canister,
218    /// the system provides the `provisional_top_up_canister` method.
219    /// It adds amount cycles to the balance of canister identified by amount
220    /// (implicitly capping it at `MAX_CANISTER_BALANCE`).
221    pub fn provisional_top_up_canister(
222        &self,
223        canister_id: &Principal,
224        top_up_args: &ProvisionalTopUpCanisterArgs,
225    ) -> impl 'agent + AsyncCall<Value = ()> {
226        self.update(MgmtMethod::ProvisionalTopUpCanister.as_ref())
227            .with_arg(top_up_args)
228            .with_effective_canister_id(canister_id.to_owned())
229            .build()
230    }
231
232    /// This method takes no input and returns 32 pseudo-random bytes to the caller.
233    /// The return value is unknown to any part of the IC at time of the submission of this call.
234    /// A new return value is generated for each call to this method.
235    pub fn raw_rand(&self) -> impl 'agent + AsyncCall<Value = (Vec<u8>,)> {
236        self.update(MgmtMethod::RawRand.as_ref())
237            .build()
238            .map(|result: (Vec<u8>,)| (result.0,))
239    }
240
241    /// Starts a canister.
242    pub fn start_canister(&self, canister_id: &Principal) -> impl 'agent + AsyncCall<Value = ()> {
243        self.update(MgmtMethod::StartCanister.as_ref())
244            .with_arg(CanisterIdRecord {
245                canister_id: *canister_id,
246            })
247            .with_effective_canister_id(canister_id.to_owned())
248            .build()
249    }
250
251    /// Stop a canister.
252    pub fn stop_canister(&self, canister_id: &Principal) -> impl 'agent + AsyncCall<Value = ()> {
253        self.update(MgmtMethod::StopCanister.as_ref())
254            .with_arg(CanisterIdRecord {
255                canister_id: *canister_id,
256            })
257            .with_effective_canister_id(canister_id.to_owned())
258            .build()
259    }
260
261    /// This method removes a canister’s code and state, making the canister empty again.
262    /// Only the controller of the canister can uninstall code.
263    /// Uninstalling a canister’s code will reject all calls that the canister has not yet responded to,
264    /// and drop the canister’s code and state.
265    /// Outstanding responses to the canister will not be processed, even if they arrive after code has been installed again.
266    /// The canister is now empty. In particular, any incoming or queued calls will be rejected.
267    //// A canister after uninstalling retains its cycles balance, controller, status, and allocations.
268    pub fn uninstall_code(&self, canister_id: &Principal) -> impl 'agent + AsyncCall<Value = ()> {
269        self.update(MgmtMethod::UninstallCode.as_ref())
270            .with_arg(CanisterIdRecord {
271                canister_id: *canister_id,
272            })
273            .with_effective_canister_id(canister_id.to_owned())
274            .build()
275    }
276
277    /// Install a canister, with all the arguments necessary for creating the canister.
278    pub fn install_code<'canister>(
279        &'canister self,
280        canister_id: &Principal,
281        wasm: &'canister [u8],
282    ) -> InstallCodeBuilder<'agent, 'canister> {
283        InstallCodeBuilder::builder(self, canister_id, wasm)
284    }
285
286    /// Update one or more of a canisters settings (i.e its controller, compute allocation, or memory allocation.)
287    pub fn update_settings<'canister>(
288        &'canister self,
289        canister_id: &Principal,
290    ) -> UpdateCanisterBuilder<'agent, 'canister> {
291        UpdateCanisterBuilder::builder(self, canister_id)
292    }
293
294    /// Upload a chunk of a WASM module to a canister's chunked WASM storage.
295    pub fn upload_chunk(
296        &self,
297        canister_id: &Principal,
298        upload_chunk_args: &UploadChunkArgs,
299    ) -> impl 'agent + AsyncCall<Value = (UploadChunkResult,)> {
300        self.update(MgmtMethod::UploadChunk.as_ref())
301            .with_arg(upload_chunk_args)
302            .with_effective_canister_id(*canister_id)
303            .build()
304    }
305
306    /// Clear a canister's chunked WASM storage.
307    pub fn clear_chunk_store(
308        &self,
309        canister_id: &Principal,
310    ) -> impl 'agent + AsyncCall<Value = ()> {
311        self.update(MgmtMethod::ClearChunkStore.as_ref())
312            .with_arg(CanisterIdRecord {
313                canister_id: *canister_id,
314            })
315            .with_effective_canister_id(*canister_id)
316            .build()
317    }
318
319    /// Get a list of the hashes of a canister's stored WASM chunks
320    pub fn stored_chunks(
321        &self,
322        canister_id: &Principal,
323    ) -> impl 'agent + AsyncCall<Value = (StoredChunksResult,)> {
324        self.update(MgmtMethod::StoredChunks.as_ref())
325            .with_arg(CanisterIdRecord {
326                canister_id: *canister_id,
327            })
328            .with_effective_canister_id(*canister_id)
329            .build()
330    }
331
332    /// Install a canister module previously uploaded in chunks via [`upload_chunk`](Self::upload_chunk).
333    pub fn install_chunked_code<'canister>(
334        &'canister self,
335        canister_id: &Principal,
336        wasm_module_hash: &[u8],
337    ) -> InstallChunkedCodeBuilder<'agent, 'canister> {
338        InstallChunkedCodeBuilder::builder(self, *canister_id, wasm_module_hash)
339    }
340
341    /// Install a canister module, automatically selecting one-shot installation or chunked installation depending on module size.
342    ///
343    /// # Warnings
344    ///
345    /// This will clear chunked code storage if chunked installation is used. Do not use with canisters that you are manually uploading chunked code to.
346    pub fn install<'canister: 'builder, 'builder>(
347        &'canister self,
348        canister_id: &Principal,
349        wasm: &'builder [u8],
350    ) -> InstallBuilder<'agent, 'canister, 'builder> {
351        InstallBuilder::builder(self, canister_id, wasm)
352    }
353
354    /// Fetch the logs of a canister.
355    pub fn fetch_canister_logs(
356        &self,
357        canister_id: &Principal,
358    ) -> impl 'agent + SyncCall<Value = (FetchCanisterLogsResult,)> {
359        // `fetch_canister_logs` is only supported in non-replicated mode.
360        self.query(MgmtMethod::FetchCanisterLogs.as_ref())
361            .with_arg(CanisterIdRecord {
362                canister_id: *canister_id,
363            })
364            .with_effective_canister_id(*canister_id)
365            .build()
366    }
367
368    /// Creates a canister snapshot, optionally replacing an existing snapshot.
369    ///  
370    /// <div class="warning">Canisters should be stopped before running this method!</div>
371    pub fn take_canister_snapshot(
372        &self,
373        canister_id: &Principal,
374        take_args: &TakeCanisterSnapshotArgs,
375    ) -> impl 'agent + AsyncCall<Value = (Snapshot,)> {
376        self.update(MgmtMethod::TakeCanisterSnapshot.as_ref())
377            .with_arg(take_args)
378            .with_effective_canister_id(*canister_id)
379            .build()
380    }
381
382    /// Loads a canister snapshot by ID, replacing the canister's state with its state at the time the snapshot was taken.
383    ///
384    /// <div class="warning">Canisters should be stopped before running this method!</div>
385    pub fn load_canister_snapshot(
386        &self,
387        canister_id: &Principal,
388        load_args: &LoadCanisterSnapshotArgs,
389    ) -> impl 'agent + AsyncCall<Value = ()> {
390        self.update(MgmtMethod::LoadCanisterSnapshot.as_ref())
391            .with_arg(load_args)
392            .with_effective_canister_id(*canister_id)
393            .build()
394    }
395
396    /// List a canister's recorded snapshots.
397    pub fn list_canister_snapshots(
398        &self,
399        canister_id: &Principal,
400    ) -> impl 'agent + AsyncCall<Value = (Vec<Snapshot>,)> {
401        self.update(MgmtMethod::ListCanisterSnapshots.as_ref())
402            .with_arg(CanisterIdRecord {
403                canister_id: *canister_id,
404            })
405            .with_effective_canister_id(*canister_id)
406            .build()
407    }
408
409    /// Deletes a recorded canister snapshot by ID.
410    pub fn delete_canister_snapshot(
411        &self,
412        canister_id: &Principal,
413        delete_args: &DeleteCanisterSnapshotArgs,
414    ) -> impl 'agent + AsyncCall<Value = ()> {
415        self.update(MgmtMethod::DeleteCanisterSnapshot.as_ref())
416            .with_arg(delete_args)
417            .with_effective_canister_id(*canister_id)
418            .build()
419    }
420
421    /// Reads the metadata of a recorded canister snapshot by canister ID and snapshot ID.
422    pub fn read_canister_snapshot_metadata(
423        &self,
424        canister_id: &Principal,
425        metadata_args: &ReadCanisterSnapshotMetadataArgs,
426    ) -> impl 'agent + AsyncCall<Value = (ReadCanisterSnapshotMetadataResult,)> {
427        self.update(MgmtMethod::ReadCanisterSnapshotMetadata.as_ref())
428            .with_arg(metadata_args)
429            .with_effective_canister_id(*canister_id)
430            .build()
431    }
432
433    /// Reads the data of a recorded canister snapshot by canister ID and snapshot ID.
434    pub fn read_canister_snapshot_data(
435        &self,
436        canister_id: &Principal,
437        data_args: &ReadCanisterSnapshotDataArgs,
438    ) -> impl 'agent + AsyncCall<Value = (ReadCanisterSnapshotDataResult,)> {
439        self.update(MgmtMethod::ReadCanisterSnapshotData.as_ref())
440            .with_arg(data_args)
441            .with_effective_canister_id(*canister_id)
442            .build()
443    }
444
445    /// Uploads the metadata of a canister snapshot by canister ID.
446    pub fn upload_canister_snapshot_metadata(
447        &self,
448        canister_id: &Principal,
449        metadata_args: &UploadCanisterSnapshotMetadataArgs,
450    ) -> impl 'agent + AsyncCall<Value = (UploadCanisterSnapshotMetadataResult,)> {
451        self.update(MgmtMethod::UploadCanisterSnapshotMetadata.as_ref())
452            .with_arg(metadata_args)
453            .with_effective_canister_id(*canister_id)
454            .build()
455    }
456
457    /// Uploads the data of a canister snapshot by canister ID and snapshot ID..
458    pub fn upload_canister_snapshot_data(
459        &self,
460        canister_id: &Principal,
461        data_args: &UploadCanisterSnapshotDataArgs,
462    ) -> impl 'agent + AsyncCall<Value = ()> {
463        self.update(MgmtMethod::UploadCanisterSnapshotData.as_ref())
464            .with_arg(data_args)
465            .with_effective_canister_id(*canister_id)
466            .build()
467    }
468}