1use crate::{
6 call::{AsyncCall, SyncCall},
7 Canister,
8};
9use candid::{CandidType, Deserialize, Nat};
10use ic_agent::{export::Principal, Agent};
11use serde::Serialize;
12use std::{convert::AsRef, ops::Deref};
13use strum_macros::{AsRefStr, Display, EnumString};
14
15pub mod attributes;
16pub mod builders;
17
18#[doc(inline)]
19pub use builders::{
20 CreateCanisterBuilder, InstallBuilder, InstallChunkedCodeBuilder, InstallCodeBuilder,
21 UpdateCanisterBuilder,
22};
23
24#[derive(Debug, Clone)]
26pub struct ManagementCanister<'agent>(Canister<'agent>);
27
28impl<'agent> Deref for ManagementCanister<'agent> {
29 type Target = Canister<'agent>;
30 fn deref(&self) -> &Self::Target {
31 &self.0
32 }
33}
34
35#[derive(AsRefStr, Debug, EnumString, Display)]
37#[strum(serialize_all = "snake_case")]
38pub enum MgmtMethod {
39 CreateCanister,
41 InstallCode,
43 StartCanister,
45 StopCanister,
47 CanisterStatus,
49 DeleteCanister,
51 DepositCycles,
53 RawRand,
55 ProvisionalCreateCanisterWithCycles,
57 ProvisionalTopUpCanister,
59 UninstallCode,
61 UpdateSettings,
63 UploadChunk,
65 ClearChunkStore,
67 StoredChunks,
69 InstallChunkedCode,
71 FetchCanisterLogs,
73 TakeCanisterSnapshot,
75 LoadCanisterSnapshot,
77 ListCanisterSnapshots,
79 DeleteCanisterSnapshot,
81 ReadCanisterSnapshotMetadata,
83 ReadCanisterSnapshotData,
85 UploadCanisterSnapshotMetadata,
87 UploadCanisterSnapshotData,
89 EcdsaPublicKey,
91 SignWithEcdsa,
93 BitcoinGetBalance,
95 BitcoinGetUtxos,
97 BitcoinSendTransaction,
99 BitcoinGetCurrentFeePercentiles,
101 BitcoinGetBlockHeaders,
103 NodeMetricsHistory,
105 CanisterInfo,
107}
108
109impl<'agent> ManagementCanister<'agent> {
110 pub fn create(agent: &'agent Agent) -> Self {
112 Self(
113 Canister::builder()
114 .with_agent(agent)
115 .with_canister_id(Principal::management_canister())
116 .build()
117 .unwrap(),
118 )
119 }
120
121 pub fn from_canister(canister: Canister<'agent>) -> Self {
123 Self(canister)
124 }
125}
126
127#[derive(Clone, Debug, Deserialize, CandidType)]
131pub struct StatusCallResult {
132 pub status: CanisterStatus,
134 pub settings: DefiniteCanisterSettings,
136 pub module_hash: Option<Vec<u8>>,
138 pub memory_size: Nat,
140 pub cycles: Nat,
142 pub reserved_cycles: Nat,
144 pub idle_cycles_burned_per_day: Nat,
147 pub query_stats: QueryStats,
149}
150
151#[derive(Clone, Debug, Deserialize, CandidType)]
153pub struct QueryStats {
154 pub num_calls_total: Nat,
156 pub num_instructions_total: Nat,
158 pub request_payload_bytes_total: Nat,
160 pub response_payload_bytes_total: Nat,
162}
163
164#[derive(Default, Clone, CandidType, Deserialize, Debug, PartialEq, Eq)]
166pub enum LogVisibility {
167 #[default]
168 #[serde(rename = "controllers")]
169 Controllers,
171 #[serde(rename = "public")]
172 Public,
174 #[serde(rename = "allowed_viewers")]
175 AllowedViewers(Vec<Principal>),
177}
178
179#[derive(Clone, Debug, Deserialize, CandidType)]
181pub struct DefiniteCanisterSettings {
182 pub controllers: Vec<Principal>,
184 pub compute_allocation: Nat,
186 pub memory_allocation: Nat,
188 pub freezing_threshold: Nat,
191 pub reserved_cycles_limit: Option<Nat>,
193 pub wasm_memory_limit: Option<Nat>,
195 pub wasm_memory_threshold: Option<Nat>,
198 pub log_visibility: LogVisibility,
200}
201
202impl std::fmt::Display for StatusCallResult {
203 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
204 std::fmt::Debug::fmt(self, f)
205 }
206}
207
208#[derive(Clone, Debug, Deserialize, Eq, PartialEq, CandidType)]
211pub enum CanisterStatus {
212 #[serde(rename = "running")]
214 Running,
215 #[serde(rename = "stopping")]
217 Stopping,
218 #[serde(rename = "stopped")]
220 Stopped,
221}
222
223impl std::fmt::Display for CanisterStatus {
224 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
225 std::fmt::Debug::fmt(self, f)
226 }
227}
228
229#[derive(Default, Clone, CandidType, Deserialize, Debug, PartialEq, Eq)]
231pub struct CanisterLogRecord {
232 pub idx: u64,
234 pub timestamp_nanos: u64,
236 #[serde(with = "serde_bytes")]
238 pub content: Vec<u8>,
239}
240
241#[derive(Clone, Debug, Deserialize, Eq, PartialEq, CandidType)]
243pub struct FetchCanisterLogsResponse {
244 pub canister_log_records: Vec<CanisterLogRecord>,
246}
247
248#[derive(
250 CandidType, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Serialize,
251)]
252pub struct ChunkHash {
253 #[serde(with = "serde_bytes")]
255 pub hash: Vec<u8>,
256}
257
258pub type StoreChunksResult = Vec<ChunkHash>;
260
261pub type UploadChunkResult = ChunkHash;
263
264#[derive(Debug, Clone, CandidType, Deserialize)]
266pub struct Snapshot {
267 #[serde(with = "serde_bytes")]
269 pub id: Vec<u8>,
270 pub taken_at_timestamp: u64,
272 pub total_size: u64,
274}
275
276#[derive(Debug, Clone, CandidType, Deserialize, Serialize)]
278pub enum SnapshotSource {
279 #[serde(rename = "taken_from_canister")]
281 TakenFromCanister,
282 #[serde(rename = "metadata_upload")]
284 MetadataUpload,
285}
286
287#[derive(Debug, Clone, CandidType, Deserialize, Serialize)]
289pub enum ExportedGlobal {
290 #[serde(rename = "i32")]
292 I32(i32),
293 #[serde(rename = "i64")]
295 I64(i64),
296 #[serde(rename = "f32")]
298 F32(f32),
299 #[serde(rename = "f64")]
301 F64(f64),
302 #[serde(rename = "v128")]
304 V128(Nat),
305}
306
307#[derive(Debug, Clone, CandidType, Deserialize, Serialize)]
309pub enum CanisterTimer {
310 #[serde(rename = "inactive")]
312 Inactive,
313 #[serde(rename = "active")]
315 Active(u64),
316}
317
318#[derive(Debug, Clone, CandidType, Deserialize, Serialize)]
320pub enum OnLowWasmMemoryHookStatus {
321 #[serde(rename = "condition_not_satisfied")]
323 ConditionNotSatisfied,
324 #[serde(rename = "ready")]
326 Ready,
327 #[serde(rename = "executed")]
329 Executed,
330}
331
332#[derive(Debug, Clone, CandidType, Deserialize, Serialize)]
334pub struct SnapshotMetadata {
335 pub source: SnapshotSource,
337 pub taken_at_timestamp: u64,
339 pub wasm_module_size: u64,
341 pub exported_globals: Vec<ExportedGlobal>,
343 pub wasm_memory_size: u64,
345 pub stable_memory_size: u64,
347 pub wasm_chunk_store: StoreChunksResult,
349 pub canister_version: u64,
351 #[serde(with = "serde_bytes")]
353 pub certified_data: Vec<u8>,
354 pub global_timer: Option<CanisterTimer>,
356 pub on_low_wasm_memory_hook_status: Option<OnLowWasmMemoryHookStatus>,
358}
359
360#[derive(Debug, Clone, CandidType, Deserialize, Serialize)]
362pub enum SnapshotDataKind {
363 #[serde(rename = "wasm_module")]
365 WasmModule {
366 offset: u64,
368 size: u64,
370 },
371 #[serde(rename = "main_memory")]
373 MainMemory {
374 offset: u64,
376 size: u64,
378 },
379 #[serde(rename = "stable_memory")]
381 StableMemory {
382 offset: u64,
384 size: u64,
386 },
387 #[serde(rename = "wasm_chunk")]
389 WasmChunk {
390 #[serde(with = "serde_bytes")]
392 hash: Vec<u8>,
393 },
394}
395
396#[derive(Debug, Clone, CandidType, Deserialize, Serialize)]
398pub struct SnapshotDataResult {
399 #[serde(with = "serde_bytes")]
401 pub chunk: Vec<u8>,
402}
403
404#[derive(Debug, Clone, CandidType, Deserialize, Serialize)]
406pub struct CanisterSnapshotId {
407 #[serde(with = "serde_bytes")]
409 pub snapshot_id: Vec<u8>,
410}
411
412#[derive(Debug, Clone, CandidType, Deserialize, Serialize)]
414pub enum SnapshotDataOffset {
415 #[serde(rename = "wasm_module")]
417 WasmModule {
418 offset: u64,
420 },
421 #[serde(rename = "main_memory")]
423 MainMemory {
424 offset: u64,
426 },
427 #[serde(rename = "stable_memory")]
429 StableMemory {
430 offset: u64,
432 },
433 #[serde(rename = "wasm_chunk")]
435 WasmChunk,
436}
437
438impl<'agent> ManagementCanister<'agent> {
439 pub fn canister_status(
441 &self,
442 canister_id: &Principal,
443 ) -> impl 'agent + AsyncCall<Value = (StatusCallResult,)> {
444 #[derive(CandidType)]
445 struct In {
446 canister_id: Principal,
447 }
448
449 self.update(MgmtMethod::CanisterStatus.as_ref())
450 .with_arg(In {
451 canister_id: *canister_id,
452 })
453 .with_effective_canister_id(canister_id.to_owned())
454 .build()
455 .map(|result: (StatusCallResult,)| (result.0,))
456 }
457
458 pub fn create_canister<'canister>(&'canister self) -> CreateCanisterBuilder<'agent, 'canister> {
460 CreateCanisterBuilder::builder(self)
461 }
462
463 pub fn deposit_cycles(&self, canister_id: &Principal) -> impl 'agent + AsyncCall<Value = ()> {
466 #[derive(CandidType)]
467 struct Argument {
468 canister_id: Principal,
469 }
470
471 self.update(MgmtMethod::DepositCycles.as_ref())
472 .with_arg(Argument {
473 canister_id: *canister_id,
474 })
475 .with_effective_canister_id(canister_id.to_owned())
476 .build()
477 }
478
479 pub fn delete_canister(&self, canister_id: &Principal) -> impl 'agent + AsyncCall<Value = ()> {
481 #[derive(CandidType)]
482 struct Argument {
483 canister_id: Principal,
484 }
485
486 self.update(MgmtMethod::DeleteCanister.as_ref())
487 .with_arg(Argument {
488 canister_id: *canister_id,
489 })
490 .with_effective_canister_id(canister_id.to_owned())
491 .build()
492 }
493
494 pub fn provisional_top_up_canister(
499 &self,
500 canister_id: &Principal,
501 amount: u64,
502 ) -> impl 'agent + AsyncCall<Value = ()> {
503 #[derive(CandidType)]
504 struct Argument {
505 canister_id: Principal,
506 amount: u64,
507 }
508
509 self.update(MgmtMethod::ProvisionalTopUpCanister.as_ref())
510 .with_arg(Argument {
511 canister_id: *canister_id,
512 amount,
513 })
514 .with_effective_canister_id(canister_id.to_owned())
515 .build()
516 }
517
518 pub fn raw_rand(&self) -> impl 'agent + AsyncCall<Value = (Vec<u8>,)> {
522 self.update(MgmtMethod::RawRand.as_ref())
523 .build()
524 .map(|result: (Vec<u8>,)| (result.0,))
525 }
526
527 pub fn start_canister(&self, canister_id: &Principal) -> impl 'agent + AsyncCall<Value = ()> {
529 #[derive(CandidType)]
530 struct Argument {
531 canister_id: Principal,
532 }
533
534 self.update(MgmtMethod::StartCanister.as_ref())
535 .with_arg(Argument {
536 canister_id: *canister_id,
537 })
538 .with_effective_canister_id(canister_id.to_owned())
539 .build()
540 }
541
542 pub fn stop_canister(&self, canister_id: &Principal) -> impl 'agent + AsyncCall<Value = ()> {
544 #[derive(CandidType)]
545 struct Argument {
546 canister_id: Principal,
547 }
548
549 self.update(MgmtMethod::StopCanister.as_ref())
550 .with_arg(Argument {
551 canister_id: *canister_id,
552 })
553 .with_effective_canister_id(canister_id.to_owned())
554 .build()
555 }
556
557 pub fn uninstall_code(&self, canister_id: &Principal) -> impl 'agent + AsyncCall<Value = ()> {
565 #[derive(CandidType)]
566 struct Argument {
567 canister_id: Principal,
568 }
569
570 self.update(MgmtMethod::UninstallCode.as_ref())
571 .with_arg(Argument {
572 canister_id: *canister_id,
573 })
574 .with_effective_canister_id(canister_id.to_owned())
575 .build()
576 }
577
578 pub fn install_code<'canister>(
580 &'canister self,
581 canister_id: &Principal,
582 wasm: &'canister [u8],
583 ) -> InstallCodeBuilder<'agent, 'canister> {
584 InstallCodeBuilder::builder(self, canister_id, wasm)
585 }
586
587 pub fn update_settings<'canister>(
589 &'canister self,
590 canister_id: &Principal,
591 ) -> UpdateCanisterBuilder<'agent, 'canister> {
592 UpdateCanisterBuilder::builder(self, canister_id)
593 }
594
595 pub fn upload_chunk(
597 &self,
598 canister_id: &Principal,
599 chunk: &[u8],
600 ) -> impl 'agent + AsyncCall<Value = (UploadChunkResult,)> {
601 #[derive(CandidType, Deserialize)]
602 struct Argument<'a> {
603 canister_id: Principal,
604 #[serde(with = "serde_bytes")]
605 chunk: &'a [u8],
606 }
607
608 self.update(MgmtMethod::UploadChunk.as_ref())
609 .with_arg(Argument {
610 canister_id: *canister_id,
611 chunk,
612 })
613 .with_effective_canister_id(*canister_id)
614 .build()
615 }
616
617 pub fn clear_chunk_store(
619 &self,
620 canister_id: &Principal,
621 ) -> impl 'agent + AsyncCall<Value = ()> {
622 #[derive(CandidType)]
623 struct Argument<'a> {
624 canister_id: &'a Principal,
625 }
626 self.update(MgmtMethod::ClearChunkStore.as_ref())
627 .with_arg(Argument { canister_id })
628 .with_effective_canister_id(*canister_id)
629 .build()
630 }
631
632 pub fn stored_chunks(
634 &self,
635 canister_id: &Principal,
636 ) -> impl 'agent + AsyncCall<Value = (StoreChunksResult,)> {
637 #[derive(CandidType)]
638 struct Argument<'a> {
639 canister_id: &'a Principal,
640 }
641 self.update(MgmtMethod::StoredChunks.as_ref())
642 .with_arg(Argument { canister_id })
643 .with_effective_canister_id(*canister_id)
644 .build()
645 }
646
647 pub fn install_chunked_code<'canister>(
649 &'canister self,
650 canister_id: &Principal,
651 wasm_module_hash: &[u8],
652 ) -> InstallChunkedCodeBuilder<'agent, 'canister> {
653 InstallChunkedCodeBuilder::builder(self, *canister_id, wasm_module_hash)
654 }
655
656 pub fn install<'canister: 'builder, 'builder>(
662 &'canister self,
663 canister_id: &Principal,
664 wasm: &'builder [u8],
665 ) -> InstallBuilder<'agent, 'canister, 'builder> {
666 InstallBuilder::builder(self, canister_id, wasm)
667 }
668
669 pub fn fetch_canister_logs(
671 &self,
672 canister_id: &Principal,
673 ) -> impl 'agent + SyncCall<Value = (FetchCanisterLogsResponse,)> {
674 #[derive(CandidType)]
675 struct In {
676 canister_id: Principal,
677 }
678
679 self.query(MgmtMethod::FetchCanisterLogs.as_ref())
681 .with_arg(In {
682 canister_id: *canister_id,
683 })
684 .with_effective_canister_id(*canister_id)
685 .build()
686 }
687
688 pub fn take_canister_snapshot(
692 &self,
693 canister_id: &Principal,
694 replace_snapshot: Option<&[u8]>,
695 ) -> impl 'agent + AsyncCall<Value = (Snapshot,)> {
696 #[derive(CandidType)]
697 struct In<'a> {
698 canister_id: Principal,
699 replace_snapshot: Option<&'a [u8]>,
700 }
701 self.update(MgmtMethod::TakeCanisterSnapshot.as_ref())
702 .with_arg(In {
703 canister_id: *canister_id,
704 replace_snapshot,
705 })
706 .with_effective_canister_id(*canister_id)
707 .build()
708 }
709
710 pub fn load_canister_snapshot(
714 &self,
715 canister_id: &Principal,
716 snapshot_id: &[u8],
717 ) -> impl 'agent + AsyncCall<Value = ()> {
718 #[derive(CandidType)]
719 struct In<'a> {
720 canister_id: Principal,
721 snapshot_id: &'a [u8],
722 sender_canister_version: Option<u64>,
723 }
724 self.update(MgmtMethod::LoadCanisterSnapshot.as_ref())
725 .with_arg(In {
726 canister_id: *canister_id,
727 snapshot_id,
728 sender_canister_version: None,
729 })
730 .with_effective_canister_id(*canister_id)
731 .build()
732 }
733
734 pub fn list_canister_snapshots(
736 &self,
737 canister_id: &Principal,
738 ) -> impl 'agent + AsyncCall<Value = (Vec<Snapshot>,)> {
739 #[derive(CandidType)]
740 struct In {
741 canister_id: Principal,
742 }
743 self.update(MgmtMethod::ListCanisterSnapshots.as_ref())
744 .with_arg(In {
745 canister_id: *canister_id,
746 })
747 .with_effective_canister_id(*canister_id)
748 .build()
749 }
750
751 pub fn delete_canister_snapshot(
753 &self,
754 canister_id: &Principal,
755 snapshot_id: &[u8],
756 ) -> impl 'agent + AsyncCall<Value = ()> {
757 #[derive(CandidType)]
758 struct In<'a> {
759 canister_id: Principal,
760 snapshot_id: &'a [u8],
761 }
762 self.update(MgmtMethod::DeleteCanisterSnapshot.as_ref())
763 .with_arg(In {
764 canister_id: *canister_id,
765 snapshot_id,
766 })
767 .build()
768 }
769
770 pub fn read_canister_snapshot_metadata(
772 &self,
773 canister_id: &Principal,
774 snapshot_id: &[u8],
775 ) -> impl 'agent + AsyncCall<Value = (SnapshotMetadata,)> {
776 #[derive(CandidType)]
777 struct In<'a> {
778 canister_id: Principal,
779 snapshot_id: &'a [u8],
780 }
781 self.update(MgmtMethod::ReadCanisterSnapshotMetadata.as_ref())
782 .with_arg(In {
783 canister_id: *canister_id,
784 snapshot_id,
785 })
786 .with_effective_canister_id(*canister_id)
787 .build()
788 }
789
790 pub fn read_canister_snapshot_data(
792 &self,
793 canister_id: &Principal,
794 snapshot_id: &[u8],
795 kind: &SnapshotDataKind,
796 ) -> impl 'agent + AsyncCall<Value = (SnapshotDataResult,)> {
797 #[derive(CandidType)]
798 struct In<'a> {
799 canister_id: Principal,
800 snapshot_id: &'a [u8],
801 kind: &'a SnapshotDataKind,
802 }
803 self.update(MgmtMethod::ReadCanisterSnapshotData.as_ref())
804 .with_arg(In {
805 canister_id: *canister_id,
806 snapshot_id,
807 kind,
808 })
809 .with_effective_canister_id(*canister_id)
810 .build()
811 }
812
813 pub fn upload_canister_snapshot_metadata(
815 &self,
816 canister_id: &Principal,
817 replace_snapshot: Option<&[u8]>,
818 metadata: &SnapshotMetadata,
819 ) -> impl 'agent + AsyncCall<Value = (CanisterSnapshotId,)> {
820 #[derive(CandidType)]
821 struct In<'a> {
822 canister_id: Principal,
823 replace_snapshot: Option<&'a [u8]>,
824 wasm_module_size: u64,
825 exported_globals: &'a Vec<ExportedGlobal>,
826 wasm_memory_size: u64,
827 stable_memory_size: u64,
828 certified_data: &'a Vec<u8>,
829 global_timer: Option<&'a CanisterTimer>,
830 on_low_wasm_memory_hook_status: Option<&'a OnLowWasmMemoryHookStatus>,
831 }
832 self.update(MgmtMethod::UploadCanisterSnapshotMetadata.as_ref())
833 .with_arg(In {
834 canister_id: *canister_id,
835 replace_snapshot,
836 wasm_module_size: metadata.wasm_module_size,
837 exported_globals: &metadata.exported_globals,
838 wasm_memory_size: metadata.wasm_memory_size,
839 stable_memory_size: metadata.stable_memory_size,
840 certified_data: &metadata.certified_data,
841 global_timer: metadata.global_timer.as_ref(),
842 on_low_wasm_memory_hook_status: metadata.on_low_wasm_memory_hook_status.as_ref(),
843 })
844 .with_effective_canister_id(*canister_id)
845 .build()
846 }
847
848 pub fn upload_canister_snapshot_data(
850 &self,
851 canister_id: &Principal,
852 snapshot_id: &[u8],
853 kind: &SnapshotDataOffset,
854 chunk: &[u8],
855 ) -> impl 'agent + AsyncCall<Value = ()> {
856 #[derive(CandidType)]
857 struct In<'a> {
858 canister_id: Principal,
859 snapshot_id: &'a [u8],
860 kind: &'a SnapshotDataOffset,
861 chunk: &'a [u8],
862 }
863 self.update(MgmtMethod::UploadCanisterSnapshotData.as_ref())
864 .with_arg(In {
865 canister_id: *canister_id,
866 snapshot_id,
867 kind,
868 chunk,
869 })
870 .with_effective_canister_id(*canister_id)
871 .build()
872 }
873}