1#[doc(inline)]
4pub use super::attributes::{
5 ComputeAllocation, FreezingThreshold, MemoryAllocation, ReservedCyclesLimit, WasmMemoryLimit,
6};
7use super::{ChunkHash, LogVisibility, ManagementCanister};
8use crate::call::CallFuture;
9use crate::{
10 call::AsyncCall, canister::Argument, interfaces::management_canister::MgmtMethod, Canister,
11};
12use async_trait::async_trait;
13use candid::{utils::ArgumentEncoder, CandidType, Deserialize, Nat};
14use futures_util::{
15 future::ready,
16 stream::{self, FuturesUnordered},
17 FutureExt, Stream, StreamExt, TryStreamExt,
18};
19use ic_agent::{agent::CallResponse, export::Principal, AgentError};
20pub use ic_management_canister_types::{
21 CanisterInstallMode, CanisterSettings, InstallCodeArgs, UpgradeFlags, WasmMemoryPersistence,
22};
23use sha2::{Digest, Sha256};
24use std::{
25 collections::BTreeSet,
26 convert::{From, TryInto},
27 future::IntoFuture,
28 pin::Pin,
29};
30
31#[derive(Debug)]
33pub struct CreateCanisterBuilder<'agent, 'canister: 'agent> {
34 canister: &'canister Canister<'agent>,
35 effective_canister_id: Principal,
36 controllers: Option<Result<Vec<Principal>, AgentError>>,
37 compute_allocation: Option<Result<ComputeAllocation, AgentError>>,
38 memory_allocation: Option<Result<MemoryAllocation, AgentError>>,
39 freezing_threshold: Option<Result<FreezingThreshold, AgentError>>,
40 reserved_cycles_limit: Option<Result<ReservedCyclesLimit, AgentError>>,
41 wasm_memory_limit: Option<Result<WasmMemoryLimit, AgentError>>,
42 wasm_memory_threshold: Option<Result<WasmMemoryLimit, AgentError>>,
43 log_visibility: Option<Result<LogVisibility, AgentError>>,
44 is_provisional_create: bool,
45 amount: Option<u128>,
46 specified_id: Option<Principal>,
47}
48
49impl<'agent, 'canister: 'agent> CreateCanisterBuilder<'agent, 'canister> {
50 pub fn builder(canister: &'canister Canister<'agent>) -> Self {
52 Self {
53 canister,
54 effective_canister_id: Principal::management_canister(),
55 controllers: None,
56 compute_allocation: None,
57 memory_allocation: None,
58 freezing_threshold: None,
59 reserved_cycles_limit: None,
60 wasm_memory_limit: None,
61 wasm_memory_threshold: None,
62 log_visibility: None,
63 is_provisional_create: false,
64 amount: None,
65 specified_id: None,
66 }
67 }
68
69 #[allow(clippy::wrong_self_convention)]
76 pub fn as_provisional_create_with_amount(self, amount: Option<u128>) -> Self {
77 Self {
78 is_provisional_create: true,
79 amount,
80 ..self
81 }
82 }
83
84 pub fn as_provisional_create_with_specified_id(self, specified_id: Principal) -> Self {
89 Self {
90 is_provisional_create: true,
91 specified_id: Some(specified_id),
92 effective_canister_id: specified_id,
93 ..self
94 }
95 }
96
97 pub fn with_effective_canister_id<C, E>(self, effective_canister_id: C) -> Self
99 where
100 E: std::fmt::Display,
101 C: TryInto<Principal, Error = E>,
102 {
103 match effective_canister_id.try_into() {
104 Ok(effective_canister_id) => Self {
105 effective_canister_id,
106 ..self
107 },
108 Err(_) => self,
109 }
110 }
111
112 pub fn with_optional_controller<C, E>(self, controller: Option<C>) -> Self
115 where
116 E: std::fmt::Display,
117 C: TryInto<Principal, Error = E>,
118 {
119 let controller_to_add: Option<Result<Principal, _>> = controller.map(|ca| {
120 ca.try_into()
121 .map_err(|e| AgentError::MessageError(format!("{e}")))
122 });
123 let controllers: Option<Result<Vec<Principal>, _>> =
124 match (controller_to_add, self.controllers) {
125 (_, Some(Err(sticky))) => Some(Err(sticky)),
126 (Some(Err(e)), _) => Some(Err(e)),
127 (None, _) => None,
128 (Some(Ok(controller)), Some(Ok(controllers))) => {
129 let mut controllers = controllers;
130 controllers.push(controller);
131 Some(Ok(controllers))
132 }
133 (Some(Ok(controller)), None) => Some(Ok(vec![controller])),
134 };
135 Self {
136 controllers,
137 ..self
138 }
139 }
140
141 pub fn with_controller<C, E>(self, controller: C) -> Self
143 where
144 E: std::fmt::Display,
145 C: TryInto<Principal, Error = E>,
146 {
147 self.with_optional_controller(Some(controller))
148 }
149
150 pub fn with_optional_compute_allocation<C, E>(self, compute_allocation: Option<C>) -> Self
153 where
154 E: std::fmt::Display,
155 C: TryInto<ComputeAllocation, Error = E>,
156 {
157 Self {
158 compute_allocation: compute_allocation.map(|ca| {
159 ca.try_into()
160 .map_err(|e| AgentError::MessageError(format!("{e}")))
161 }),
162 ..self
163 }
164 }
165
166 pub fn with_compute_allocation<C, E>(self, compute_allocation: C) -> Self
168 where
169 E: std::fmt::Display,
170 C: TryInto<ComputeAllocation, Error = E>,
171 {
172 self.with_optional_compute_allocation(Some(compute_allocation))
173 }
174
175 pub fn with_optional_memory_allocation<E, C>(self, memory_allocation: Option<C>) -> Self
178 where
179 E: std::fmt::Display,
180 C: TryInto<MemoryAllocation, Error = E>,
181 {
182 Self {
183 memory_allocation: memory_allocation.map(|ma| {
184 ma.try_into()
185 .map_err(|e| AgentError::MessageError(format!("{e}")))
186 }),
187 ..self
188 }
189 }
190
191 pub fn with_memory_allocation<C, E>(self, memory_allocation: C) -> Self
193 where
194 E: std::fmt::Display,
195 C: TryInto<MemoryAllocation, Error = E>,
196 {
197 self.with_optional_memory_allocation(Some(memory_allocation))
198 }
199
200 pub fn with_optional_freezing_threshold<E, C>(self, freezing_threshold: Option<C>) -> Self
203 where
204 E: std::fmt::Display,
205 C: TryInto<FreezingThreshold, Error = E>,
206 {
207 Self {
208 freezing_threshold: freezing_threshold.map(|ma| {
209 ma.try_into()
210 .map_err(|e| AgentError::MessageError(format!("{e}")))
211 }),
212 ..self
213 }
214 }
215
216 pub fn with_freezing_threshold<C, E>(self, freezing_threshold: C) -> Self
218 where
219 E: std::fmt::Display,
220 C: TryInto<FreezingThreshold, Error = E>,
221 {
222 self.with_optional_freezing_threshold(Some(freezing_threshold))
223 }
224
225 pub fn with_reserved_cycles_limit<C, E>(self, limit: C) -> Self
227 where
228 E: std::fmt::Display,
229 C: TryInto<ReservedCyclesLimit, Error = E>,
230 {
231 self.with_optional_reserved_cycles_limit(Some(limit))
232 }
233
234 pub fn with_optional_reserved_cycles_limit<E, C>(self, limit: Option<C>) -> Self
237 where
238 E: std::fmt::Display,
239 C: TryInto<ReservedCyclesLimit, Error = E>,
240 {
241 Self {
242 reserved_cycles_limit: limit.map(|limit| {
243 limit
244 .try_into()
245 .map_err(|e| AgentError::MessageError(format!("{e}")))
246 }),
247 ..self
248 }
249 }
250
251 pub fn with_wasm_memory_limit<C, E>(self, wasm_memory_limit: C) -> Self
253 where
254 E: std::fmt::Display,
255 C: TryInto<WasmMemoryLimit, Error = E>,
256 {
257 self.with_optional_wasm_memory_limit(Some(wasm_memory_limit))
258 }
259
260 pub fn with_optional_wasm_memory_limit<E, C>(self, wasm_memory_limit: Option<C>) -> Self
263 where
264 E: std::fmt::Display,
265 C: TryInto<WasmMemoryLimit, Error = E>,
266 {
267 Self {
268 wasm_memory_limit: wasm_memory_limit.map(|limit| {
269 limit
270 .try_into()
271 .map_err(|e| AgentError::MessageError(format!("{e}")))
272 }),
273 ..self
274 }
275 }
276
277 pub fn with_wasm_memory_threshold<C, E>(self, wasm_memory_threshold: C) -> Self
279 where
280 E: std::fmt::Display,
281 C: TryInto<WasmMemoryLimit, Error = E>,
282 {
283 self.with_optional_wasm_memory_threshold(Some(wasm_memory_threshold))
284 }
285
286 pub fn with_optional_wasm_memory_threshold<E, C>(self, wasm_memory_threshold: Option<C>) -> Self
289 where
290 E: std::fmt::Display,
291 C: TryInto<WasmMemoryLimit, Error = E>,
292 {
293 Self {
294 wasm_memory_threshold: wasm_memory_threshold.map(|limit| {
295 limit
296 .try_into()
297 .map_err(|e| AgentError::MessageError(format!("{e}")))
298 }),
299 ..self
300 }
301 }
302
303 pub fn with_log_visibility<C, E>(self, log_visibility: C) -> Self
305 where
306 E: std::fmt::Display,
307 C: TryInto<LogVisibility, Error = E>,
308 {
309 self.with_optional_log_visibility(Some(log_visibility))
310 }
311
312 pub fn with_optional_log_visibility<E, C>(self, log_visibility: Option<C>) -> Self
315 where
316 E: std::fmt::Display,
317 C: TryInto<LogVisibility, Error = E>,
318 {
319 Self {
320 log_visibility: log_visibility.map(|visibility| {
321 visibility
322 .try_into()
323 .map_err(|e| AgentError::MessageError(format!("{e}")))
324 }),
325 ..self
326 }
327 }
328
329 pub fn build(self) -> Result<impl 'agent + AsyncCall<Value = (Principal,)>, AgentError> {
332 let controllers = match self.controllers {
333 Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
334 Some(Ok(x)) => Some(x),
335 None => None,
336 };
337 let compute_allocation = match self.compute_allocation {
338 Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
339 Some(Ok(x)) => Some(Nat::from(u8::from(x))),
340 None => None,
341 };
342 let memory_allocation = match self.memory_allocation {
343 Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
344 Some(Ok(x)) => Some(Nat::from(u64::from(x))),
345 None => None,
346 };
347 let freezing_threshold = match self.freezing_threshold {
348 Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
349 Some(Ok(x)) => Some(Nat::from(u64::from(x))),
350 None => None,
351 };
352 let reserved_cycles_limit = match self.reserved_cycles_limit {
353 Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
354 Some(Ok(x)) => Some(Nat::from(u128::from(x))),
355 None => None,
356 };
357 let wasm_memory_limit = match self.wasm_memory_limit {
358 Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
359 Some(Ok(x)) => Some(Nat::from(u64::from(x))),
360 None => None,
361 };
362 let wasm_memory_threshold = match self.wasm_memory_threshold {
363 Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
364 Some(Ok(x)) => Some(Nat::from(u64::from(x))),
365 None => None,
366 };
367 let log_visibility = match self.log_visibility {
368 Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
369 Some(Ok(x)) => Some(x),
370 None => None,
371 };
372
373 #[derive(Deserialize, CandidType)]
374 struct Out {
375 canister_id: Principal,
376 }
377
378 let async_builder = if self.is_provisional_create {
379 #[derive(CandidType)]
380 struct In {
381 amount: Option<Nat>,
382 settings: CanisterSettings,
383 specified_id: Option<Principal>,
384 }
385 let in_arg = In {
386 amount: self.amount.map(Nat::from),
387 settings: CanisterSettings {
388 controllers,
389 compute_allocation,
390 memory_allocation,
391 freezing_threshold,
392 reserved_cycles_limit,
393 wasm_memory_limit,
394 wasm_memory_threshold,
395 log_visibility,
396 },
397 specified_id: self.specified_id,
398 };
399 self.canister
400 .update(MgmtMethod::ProvisionalCreateCanisterWithCycles.as_ref())
401 .with_arg(in_arg)
402 .with_effective_canister_id(self.effective_canister_id)
403 } else {
404 self.canister
405 .update(MgmtMethod::CreateCanister.as_ref())
406 .with_arg(CanisterSettings {
407 controllers,
408 compute_allocation,
409 memory_allocation,
410 freezing_threshold,
411 reserved_cycles_limit,
412 wasm_memory_limit,
413 wasm_memory_threshold,
414 log_visibility,
415 })
416 .with_effective_canister_id(self.effective_canister_id)
417 };
418
419 Ok(async_builder
420 .build()
421 .map(|result: (Out,)| (result.0.canister_id,)))
422 }
423
424 pub async fn call(self) -> Result<CallResponse<(Principal,)>, AgentError> {
426 self.build()?.call().await
427 }
428
429 pub async fn call_and_wait(self) -> Result<(Principal,), AgentError> {
431 self.build()?.call_and_wait().await
432 }
433}
434
435#[cfg_attr(target_family = "wasm", async_trait(?Send))]
436#[cfg_attr(not(target_family = "wasm"), async_trait)]
437impl<'agent, 'canister: 'agent> AsyncCall for CreateCanisterBuilder<'agent, 'canister> {
438 type Value = (Principal,);
439
440 async fn call(self) -> Result<CallResponse<(Principal,)>, AgentError> {
441 self.build()?.call().await
442 }
443
444 async fn call_and_wait(self) -> Result<(Principal,), AgentError> {
445 self.build()?.call_and_wait().await
446 }
447}
448
449impl<'agent, 'canister: 'agent> IntoFuture for CreateCanisterBuilder<'agent, 'canister> {
450 type IntoFuture = CallFuture<'agent, (Principal,)>;
451 type Output = Result<(Principal,), AgentError>;
452
453 fn into_future(self) -> Self::IntoFuture {
454 AsyncCall::call_and_wait(self)
455 }
456}
457
458#[doc(hidden)]
459#[deprecated(since = "0.42.0", note = "Please use UpgradeFlags instead")]
460pub type CanisterUpgradeOptions = UpgradeFlags;
461
462#[doc(hidden)]
463#[deprecated(since = "0.42.0", note = "Please use CanisterInstallMode instead")]
464pub type InstallMode = CanisterInstallMode;
465
466#[doc(hidden)]
467#[deprecated(since = "0.42.0", note = "Please use InstallCodeArgs instead")]
468pub type CanisterInstall = InstallCodeArgs;
469
470#[derive(Debug)]
472pub struct InstallCodeBuilder<'agent, 'canister: 'agent> {
473 canister: &'canister Canister<'agent>,
474 canister_id: Principal,
475 wasm: &'canister [u8],
476 arg: Argument,
477 mode: Option<CanisterInstallMode>,
478}
479
480impl<'agent, 'canister: 'agent> InstallCodeBuilder<'agent, 'canister> {
481 pub fn builder(
483 canister: &'canister Canister<'agent>,
484 canister_id: &Principal,
485 wasm: &'canister [u8],
486 ) -> Self {
487 Self {
488 canister,
489 canister_id: *canister_id,
490 wasm,
491 arg: Default::default(),
492 mode: None,
493 }
494 }
495
496 pub fn with_arg<Argument: CandidType>(
499 mut self,
500 arg: Argument,
501 ) -> InstallCodeBuilder<'agent, 'canister> {
502 self.arg.set_idl_arg(arg);
503 self
504 }
505 pub fn with_args(mut self, tuple: impl ArgumentEncoder) -> Self {
508 assert!(self.arg.0.is_none(), "argument is being set more than once");
509 self.arg = Argument::from_candid(tuple);
510 self
511 }
512 pub fn with_raw_arg(mut self, arg: Vec<u8>) -> InstallCodeBuilder<'agent, 'canister> {
514 self.arg.set_raw_arg(arg);
515 self
516 }
517
518 pub fn with_mode(self, mode: CanisterInstallMode) -> Self {
520 Self {
521 mode: Some(mode),
522 ..self
523 }
524 }
525
526 pub fn build(self) -> Result<impl 'agent + AsyncCall<Value = ()>, AgentError> {
529 Ok(self
530 .canister
531 .update(MgmtMethod::InstallCode.as_ref())
532 .with_arg(InstallCodeArgs {
533 mode: self.mode.unwrap_or(CanisterInstallMode::Install),
534 canister_id: self.canister_id,
535 wasm_module: self.wasm.to_owned(),
536 arg: self.arg.serialize()?,
537 sender_canister_version: None,
538 })
539 .with_effective_canister_id(self.canister_id)
540 .build())
541 }
542
543 pub async fn call(self) -> Result<CallResponse<()>, AgentError> {
545 self.build()?.call().await
546 }
547
548 pub async fn call_and_wait(self) -> Result<(), AgentError> {
550 self.build()?.call_and_wait().await
551 }
552}
553
554#[cfg_attr(target_family = "wasm", async_trait(?Send))]
555#[cfg_attr(not(target_family = "wasm"), async_trait)]
556impl<'agent, 'canister: 'agent> AsyncCall for InstallCodeBuilder<'agent, 'canister> {
557 type Value = ();
558
559 async fn call(self) -> Result<CallResponse<()>, AgentError> {
560 self.build()?.call().await
561 }
562
563 async fn call_and_wait(self) -> Result<(), AgentError> {
564 self.build()?.call_and_wait().await
565 }
566}
567
568impl<'agent, 'canister: 'agent> IntoFuture for InstallCodeBuilder<'agent, 'canister> {
569 type IntoFuture = CallFuture<'agent, ()>;
570 type Output = Result<(), AgentError>;
571
572 fn into_future(self) -> Self::IntoFuture {
573 AsyncCall::call_and_wait(self)
574 }
575}
576
577#[derive(Debug)]
579pub struct InstallChunkedCodeBuilder<'agent, 'canister> {
580 canister: &'canister Canister<'agent>,
581 target_canister: Principal,
582 store_canister: Option<Principal>,
583 chunk_hashes_list: Vec<ChunkHash>,
584 wasm_module_hash: Vec<u8>,
585 arg: Argument,
586 mode: CanisterInstallMode,
587}
588
589impl<'agent: 'canister, 'canister> InstallChunkedCodeBuilder<'agent, 'canister> {
590 pub fn builder(
592 canister: &'canister Canister<'agent>,
593 target_canister: Principal,
594 wasm_module_hash: &[u8],
595 ) -> Self {
596 Self {
597 canister,
598 target_canister,
599 wasm_module_hash: wasm_module_hash.to_vec(),
600 store_canister: None,
601 chunk_hashes_list: vec![],
602 arg: Argument::new(),
603 mode: CanisterInstallMode::Install,
604 }
605 }
606
607 pub fn with_chunk_hashes(mut self, chunk_hashes: Vec<ChunkHash>) -> Self {
609 self.chunk_hashes_list = chunk_hashes;
610 self
611 }
612
613 pub fn with_store_canister(mut self, store_canister: Principal) -> Self {
615 self.store_canister = Some(store_canister);
616 self
617 }
618
619 pub fn with_arg(mut self, argument: impl CandidType) -> Self {
622 self.arg.set_idl_arg(argument);
623 self
624 }
625
626 pub fn with_args(mut self, argument: impl ArgumentEncoder) -> Self {
629 assert!(self.arg.0.is_none(), "argument is being set more than once");
630 self.arg = Argument::from_candid(argument);
631 self
632 }
633
634 pub fn with_raw_arg(mut self, argument: Vec<u8>) -> Self {
636 self.arg.set_raw_arg(argument);
637 self
638 }
639
640 pub fn with_install_mode(mut self, mode: CanisterInstallMode) -> Self {
642 self.mode = mode;
643 self
644 }
645
646 pub fn build(self) -> Result<impl 'agent + AsyncCall<Value = ()>, AgentError> {
648 #[derive(CandidType)]
649 struct In {
650 mode: CanisterInstallMode,
651 target_canister: Principal,
652 store_canister: Option<Principal>,
653 chunk_hashes_list: Vec<ChunkHash>,
654 wasm_module_hash: Vec<u8>,
655 arg: Vec<u8>,
656 sender_canister_version: Option<u64>,
657 }
658 let Self {
659 mode,
660 target_canister,
661 store_canister,
662 chunk_hashes_list,
663 wasm_module_hash,
664 arg,
665 ..
666 } = self;
667 Ok(self
668 .canister
669 .update(MgmtMethod::InstallChunkedCode.as_ref())
670 .with_arg(In {
671 mode,
672 target_canister,
673 store_canister,
674 chunk_hashes_list,
675 wasm_module_hash,
676 arg: arg.serialize()?,
677 sender_canister_version: None,
678 })
679 .with_effective_canister_id(target_canister)
680 .build())
681 }
682
683 pub async fn call(self) -> Result<CallResponse<()>, AgentError> {
685 self.build()?.call().await
686 }
687
688 pub async fn call_and_wait(self) -> Result<(), AgentError> {
690 self.build()?.call_and_wait().await
691 }
692}
693
694#[cfg_attr(target_family = "wasm", async_trait(?Send))]
695#[cfg_attr(not(target_family = "wasm"), async_trait)]
696impl<'agent, 'canister: 'agent> AsyncCall for InstallChunkedCodeBuilder<'agent, 'canister> {
697 type Value = ();
698
699 async fn call(self) -> Result<CallResponse<()>, AgentError> {
700 self.call().await
701 }
702
703 async fn call_and_wait(self) -> Result<(), AgentError> {
704 self.call_and_wait().await
705 }
706}
707
708impl<'agent, 'canister: 'agent> IntoFuture for InstallChunkedCodeBuilder<'agent, 'canister> {
709 type IntoFuture = CallFuture<'agent, ()>;
710 type Output = Result<(), AgentError>;
711
712 fn into_future(self) -> Self::IntoFuture {
713 AsyncCall::call_and_wait(self)
714 }
715}
716
717#[derive(Debug)]
723pub struct InstallBuilder<'agent, 'canister, 'builder> {
724 canister: &'canister ManagementCanister<'agent>,
725 canister_id: Principal,
726 wasm: &'builder [u8],
729 arg: Argument,
730 mode: CanisterInstallMode,
731}
732
733impl<'agent: 'canister, 'canister: 'builder, 'builder> InstallBuilder<'agent, 'canister, 'builder> {
734 const CHUNK_CUTOFF: usize = (1.85 * 1024. * 1024.) as usize;
737
738 pub fn builder(
740 canister: &'canister ManagementCanister<'agent>,
741 canister_id: &Principal,
742 wasm: &'builder [u8],
743 ) -> Self {
744 Self {
745 canister,
746 canister_id: *canister_id,
747 wasm,
748 arg: Default::default(),
749 mode: CanisterInstallMode::Install,
750 }
751 }
752
753 pub fn with_arg<Argument: CandidType>(mut self, arg: Argument) -> Self {
756 self.arg.set_idl_arg(arg);
757 self
758 }
759 pub fn with_args(mut self, tuple: impl ArgumentEncoder) -> Self {
762 assert!(self.arg.0.is_none(), "argument is being set more than once");
763 self.arg = Argument::from_candid(tuple);
764 self
765 }
766 pub fn with_raw_arg(mut self, arg: Vec<u8>) -> Self {
768 self.arg.set_raw_arg(arg);
769 self
770 }
771
772 pub fn with_mode(self, mode: CanisterInstallMode) -> Self {
774 Self { mode, ..self }
775 }
776
777 pub async fn call_and_wait(self) -> Result<(), AgentError> {
780 self.call_and_wait_with_progress()
781 .await
782 .try_for_each(|_| ready(Ok(())))
783 .await
784 }
785
786 pub async fn call_and_wait_with_progress(
790 self,
791 ) -> impl Stream<Item = Result<(), AgentError>> + 'builder {
792 let stream_res = async move {
793 let arg = self.arg.serialize()?;
794 let stream: BoxStream<'_, _> =
795 if self.wasm.len() + arg.len() < Self::CHUNK_CUTOFF {
796 Box::pin(
797 async move {
798 self.canister
799 .install_code(&self.canister_id, self.wasm)
800 .with_raw_arg(arg)
801 .with_mode(self.mode)
802 .call_and_wait()
803 .await
804 }
805 .into_stream(),
806 )
807 } else {
808 let (existing_chunks,) = self.canister.stored_chunks(&self.canister_id).call_and_wait().await?;
809 let existing_chunks = existing_chunks.into_iter().map(|c| c.hash).collect::<BTreeSet<_>>();
810 let all_chunks = self.wasm.chunks(1024 * 1024).map(|x| (Sha256::digest(x).to_vec(), x)).collect::<Vec<_>>();
811 let mut to_upload_chunks = vec![];
812 for (hash, chunk) in &all_chunks {
813 if !existing_chunks.contains(hash) {
814 to_upload_chunks.push(*chunk);
815 }
816 }
817
818 let upload_chunks_stream = FuturesUnordered::new();
819 for chunk in to_upload_chunks {
820 upload_chunks_stream.push(async move {
821 let (_res,) = self
822 .canister
823 .upload_chunk(&self.canister_id, &ic_management_canister_types::UploadChunkArgs {
824 canister_id: self.canister_id,
825 chunk: chunk.to_vec(),
826 })
827 .call_and_wait()
828 .await?;
829 Ok(())
830 });
831 }
832 let install_chunked_code_stream = async move {
833 let results = all_chunks.iter().map(|(hash,_)| ChunkHash{ hash: hash.clone() }).collect();
834 self.canister
835 .install_chunked_code(
836 &self.canister_id,
837 &Sha256::digest(self.wasm),
838 )
839 .with_chunk_hashes(results)
840 .with_raw_arg(arg)
841 .with_install_mode(self.mode)
842 .call_and_wait()
843 .await
844 }
845 .into_stream();
846 let clear_store_stream = async move {
847 self.canister
848 .clear_chunk_store(&self.canister_id)
849 .call_and_wait()
850 .await
851 }
852 .into_stream();
853
854 Box::pin(
855 upload_chunks_stream
856 .chain(install_chunked_code_stream)
857 .chain(clear_store_stream ),
858 )
859 };
860 Ok(stream)
861 }.await;
862 match stream_res {
863 Ok(stream) => stream,
864 Err(err) => Box::pin(stream::once(async { Err(err) })),
865 }
866 }
867}
868
869impl<'agent: 'canister, 'canister: 'builder, 'builder> IntoFuture
870 for InstallBuilder<'agent, 'canister, 'builder>
871{
872 type IntoFuture = CallFuture<'builder, ()>;
873 type Output = Result<(), AgentError>;
874
875 fn into_future(self) -> Self::IntoFuture {
876 Box::pin(self.call_and_wait())
877 }
878}
879
880#[derive(Debug)]
882pub struct UpdateCanisterBuilder<'agent, 'canister: 'agent> {
883 canister: &'canister Canister<'agent>,
884 canister_id: Principal,
885 controllers: Option<Result<Vec<Principal>, AgentError>>,
886 compute_allocation: Option<Result<ComputeAllocation, AgentError>>,
887 memory_allocation: Option<Result<MemoryAllocation, AgentError>>,
888 freezing_threshold: Option<Result<FreezingThreshold, AgentError>>,
889 reserved_cycles_limit: Option<Result<ReservedCyclesLimit, AgentError>>,
890 wasm_memory_limit: Option<Result<WasmMemoryLimit, AgentError>>,
891 wasm_memory_threshold: Option<Result<WasmMemoryLimit, AgentError>>,
892 log_visibility: Option<Result<LogVisibility, AgentError>>,
893}
894
895impl<'agent, 'canister: 'agent> UpdateCanisterBuilder<'agent, 'canister> {
896 pub fn builder(canister: &'canister Canister<'agent>, canister_id: &Principal) -> Self {
898 Self {
899 canister,
900 canister_id: *canister_id,
901 controllers: None,
902 compute_allocation: None,
903 memory_allocation: None,
904 freezing_threshold: None,
905 reserved_cycles_limit: None,
906 wasm_memory_limit: None,
907 wasm_memory_threshold: None,
908 log_visibility: None,
909 }
910 }
911
912 pub fn with_optional_controller<C, E>(self, controller: Option<C>) -> Self
915 where
916 E: std::fmt::Display,
917 C: TryInto<Principal, Error = E>,
918 {
919 let controller_to_add: Option<Result<Principal, _>> = controller.map(|ca| {
920 ca.try_into()
921 .map_err(|e| AgentError::MessageError(format!("{e}")))
922 });
923 let controllers: Option<Result<Vec<Principal>, _>> =
924 match (controller_to_add, self.controllers) {
925 (_, Some(Err(sticky))) => Some(Err(sticky)),
926 (Some(Err(e)), _) => Some(Err(e)),
927 (None, _) => None,
928 (Some(Ok(controller)), Some(Ok(controllers))) => {
929 let mut controllers = controllers;
930 controllers.push(controller);
931 Some(Ok(controllers))
932 }
933 (Some(Ok(controller)), None) => Some(Ok(vec![controller])),
934 };
935
936 Self {
937 controllers,
938 ..self
939 }
940 }
941
942 pub fn with_controller<C, E>(self, controller: C) -> Self
944 where
945 E: std::fmt::Display,
946 C: TryInto<Principal, Error = E>,
947 {
948 self.with_optional_controller(Some(controller))
949 }
950
951 pub fn with_optional_compute_allocation<C, E>(self, compute_allocation: Option<C>) -> Self
954 where
955 E: std::fmt::Display,
956 C: TryInto<ComputeAllocation, Error = E>,
957 {
958 Self {
959 compute_allocation: compute_allocation.map(|ca| {
960 ca.try_into()
961 .map_err(|e| AgentError::MessageError(format!("{e}")))
962 }),
963 ..self
964 }
965 }
966
967 pub fn with_compute_allocation<C, E>(self, compute_allocation: C) -> Self
969 where
970 E: std::fmt::Display,
971 C: TryInto<ComputeAllocation, Error = E>,
972 {
973 self.with_optional_compute_allocation(Some(compute_allocation))
974 }
975
976 pub fn with_optional_memory_allocation<E, C>(self, memory_allocation: Option<C>) -> Self
979 where
980 E: std::fmt::Display,
981 C: TryInto<MemoryAllocation, Error = E>,
982 {
983 Self {
984 memory_allocation: memory_allocation.map(|ma| {
985 ma.try_into()
986 .map_err(|e| AgentError::MessageError(format!("{e}")))
987 }),
988 ..self
989 }
990 }
991
992 pub fn with_memory_allocation<C, E>(self, memory_allocation: C) -> Self
994 where
995 E: std::fmt::Display,
996 C: TryInto<MemoryAllocation, Error = E>,
997 {
998 self.with_optional_memory_allocation(Some(memory_allocation))
999 }
1000
1001 pub fn with_optional_freezing_threshold<E, C>(self, freezing_threshold: Option<C>) -> Self
1004 where
1005 E: std::fmt::Display,
1006 C: TryInto<FreezingThreshold, Error = E>,
1007 {
1008 Self {
1009 freezing_threshold: freezing_threshold.map(|ma| {
1010 ma.try_into()
1011 .map_err(|e| AgentError::MessageError(format!("{e}")))
1012 }),
1013 ..self
1014 }
1015 }
1016
1017 pub fn with_freezing_threshold<C, E>(self, freezing_threshold: C) -> Self
1019 where
1020 E: std::fmt::Display,
1021 C: TryInto<FreezingThreshold, Error = E>,
1022 {
1023 self.with_optional_freezing_threshold(Some(freezing_threshold))
1024 }
1025
1026 pub fn with_reserved_cycles_limit<C, E>(self, limit: C) -> Self
1028 where
1029 E: std::fmt::Display,
1030 C: TryInto<ReservedCyclesLimit, Error = E>,
1031 {
1032 self.with_optional_reserved_cycles_limit(Some(limit))
1033 }
1034
1035 pub fn with_optional_reserved_cycles_limit<E, C>(self, limit: Option<C>) -> Self
1038 where
1039 E: std::fmt::Display,
1040 C: TryInto<ReservedCyclesLimit, Error = E>,
1041 {
1042 Self {
1043 reserved_cycles_limit: limit.map(|ma| {
1044 ma.try_into()
1045 .map_err(|e| AgentError::MessageError(format!("{e}")))
1046 }),
1047 ..self
1048 }
1049 }
1050
1051 pub fn with_wasm_memory_limit<C, E>(self, wasm_memory_limit: C) -> Self
1053 where
1054 E: std::fmt::Display,
1055 C: TryInto<WasmMemoryLimit, Error = E>,
1056 {
1057 self.with_optional_wasm_memory_limit(Some(wasm_memory_limit))
1058 }
1059
1060 pub fn with_optional_wasm_memory_limit<E, C>(self, wasm_memory_limit: Option<C>) -> Self
1063 where
1064 E: std::fmt::Display,
1065 C: TryInto<WasmMemoryLimit, Error = E>,
1066 {
1067 Self {
1068 wasm_memory_limit: wasm_memory_limit.map(|limit| {
1069 limit
1070 .try_into()
1071 .map_err(|e| AgentError::MessageError(format!("{e}")))
1072 }),
1073 ..self
1074 }
1075 }
1076
1077 pub fn with_wasm_memory_threshold<C, E>(self, wasm_memory_threshold: C) -> Self
1079 where
1080 E: std::fmt::Display,
1081 C: TryInto<WasmMemoryLimit, Error = E>,
1082 {
1083 self.with_optional_wasm_memory_threshold(Some(wasm_memory_threshold))
1084 }
1085
1086 pub fn with_optional_wasm_memory_threshold<E, C>(self, wasm_memory_threshold: Option<C>) -> Self
1089 where
1090 E: std::fmt::Display,
1091 C: TryInto<WasmMemoryLimit, Error = E>,
1092 {
1093 Self {
1094 wasm_memory_threshold: wasm_memory_threshold.map(|limit| {
1095 limit
1096 .try_into()
1097 .map_err(|e| AgentError::MessageError(format!("{e}")))
1098 }),
1099 ..self
1100 }
1101 }
1102
1103 pub fn with_log_visibility<C, E>(self, log_visibility: C) -> Self
1105 where
1106 E: std::fmt::Display,
1107 C: TryInto<LogVisibility, Error = E>,
1108 {
1109 self.with_optional_log_visibility(Some(log_visibility))
1110 }
1111
1112 pub fn with_optional_log_visibility<E, C>(self, log_visibility: Option<C>) -> Self
1115 where
1116 E: std::fmt::Display,
1117 C: TryInto<LogVisibility, Error = E>,
1118 {
1119 Self {
1120 log_visibility: log_visibility.map(|limit| {
1121 limit
1122 .try_into()
1123 .map_err(|e| AgentError::MessageError(format!("{e}")))
1124 }),
1125 ..self
1126 }
1127 }
1128
1129 pub fn build(self) -> Result<impl 'agent + AsyncCall<Value = ()>, AgentError> {
1132 #[derive(CandidType)]
1133 struct In {
1134 canister_id: Principal,
1135 settings: CanisterSettings,
1136 }
1137
1138 let controllers = match self.controllers {
1139 Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
1140 Some(Ok(x)) => Some(x),
1141 None => None,
1142 };
1143 let compute_allocation = match self.compute_allocation {
1144 Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
1145 Some(Ok(x)) => Some(Nat::from(u8::from(x))),
1146 None => None,
1147 };
1148 let memory_allocation = match self.memory_allocation {
1149 Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
1150 Some(Ok(x)) => Some(Nat::from(u64::from(x))),
1151 None => None,
1152 };
1153 let freezing_threshold = match self.freezing_threshold {
1154 Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
1155 Some(Ok(x)) => Some(Nat::from(u64::from(x))),
1156 None => None,
1157 };
1158 let reserved_cycles_limit = match self.reserved_cycles_limit {
1159 Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
1160 Some(Ok(x)) => Some(Nat::from(u128::from(x))),
1161 None => None,
1162 };
1163 let wasm_memory_limit = match self.wasm_memory_limit {
1164 Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
1165 Some(Ok(x)) => Some(Nat::from(u64::from(x))),
1166 None => None,
1167 };
1168 let wasm_memory_threshold = match self.wasm_memory_threshold {
1169 Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
1170 Some(Ok(x)) => Some(Nat::from(u64::from(x))),
1171 None => None,
1172 };
1173 let log_visibility = match self.log_visibility {
1174 Some(Err(x)) => return Err(AgentError::MessageError(format!("{x}"))),
1175 Some(Ok(x)) => Some(x),
1176 None => None,
1177 };
1178
1179 Ok(self
1180 .canister
1181 .update(MgmtMethod::UpdateSettings.as_ref())
1182 .with_arg(In {
1183 canister_id: self.canister_id,
1184 settings: CanisterSettings {
1185 controllers,
1186 compute_allocation,
1187 memory_allocation,
1188 freezing_threshold,
1189 reserved_cycles_limit,
1190 wasm_memory_limit,
1191 wasm_memory_threshold,
1192 log_visibility,
1193 },
1194 })
1195 .with_effective_canister_id(self.canister_id)
1196 .build())
1197 }
1198
1199 pub async fn call(self) -> Result<CallResponse<()>, AgentError> {
1201 self.build()?.call().await
1202 }
1203
1204 pub async fn call_and_wait(self) -> Result<(), AgentError> {
1206 self.build()?.call_and_wait().await
1207 }
1208}
1209
1210#[cfg_attr(target_family = "wasm", async_trait(?Send))]
1211#[cfg_attr(not(target_family = "wasm"), async_trait)]
1212impl<'agent, 'canister: 'agent> AsyncCall for UpdateCanisterBuilder<'agent, 'canister> {
1213 type Value = ();
1214 async fn call(self) -> Result<CallResponse<()>, AgentError> {
1215 self.build()?.call().await
1216 }
1217
1218 async fn call_and_wait(self) -> Result<(), AgentError> {
1219 self.build()?.call_and_wait().await
1220 }
1221}
1222
1223impl<'agent, 'canister: 'agent> IntoFuture for UpdateCanisterBuilder<'agent, 'canister> {
1224 type IntoFuture = CallFuture<'agent, ()>;
1225 type Output = Result<(), AgentError>;
1226 fn into_future(self) -> Self::IntoFuture {
1227 AsyncCall::call_and_wait(self)
1228 }
1229}
1230
1231#[cfg(not(target_family = "wasm"))]
1232type BoxStream<'a, T> = Pin<Box<dyn Stream<Item = T> + Send + 'a>>;
1233#[cfg(target_family = "wasm")]
1234type BoxStream<'a, T> = Pin<Box<dyn Stream<Item = T> + 'a>>;