Skip to main content

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