ic_cdk/api/call.rs
1//! APIs to make and manage calls in the canister.
2
3#![allow(deprecated)]
4use crate::api::trap;
5use candid::utils::{decode_args_with_config_debug, ArgumentDecoder, ArgumentEncoder};
6use candid::{
7 decode_args, encode_args, write_args, CandidType, DecoderConfig, Deserialize, Principal,
8};
9use serde::ser::Error;
10use std::future::Future;
11use std::marker::PhantomData;
12use std::mem;
13use std::pin::Pin;
14use std::sync::{Arc, RwLock};
15use std::task::{Context, Poll, Waker};
16
17/// Rejection code from calling another canister.
18///
19/// These can be obtained either using `reject_code()` or `reject_result()`.
20#[allow(missing_docs)]
21#[deprecated(
22 since = "0.18.0",
23 note = "Please use `ic_cdk::call::RejectCode` instead."
24)]
25#[repr(usize)]
26#[derive(CandidType, Deserialize, Clone, Copy, Hash, Debug, PartialEq, Eq, PartialOrd, Ord)]
27pub enum RejectionCode {
28 NoError = 0,
29
30 SysFatal = 1,
31 SysTransient = 2,
32 DestinationInvalid = 3,
33 CanisterReject = 4,
34 CanisterError = 5,
35
36 Unknown,
37}
38
39impl From<usize> for RejectionCode {
40 fn from(code: usize) -> Self {
41 match code {
42 0 => RejectionCode::NoError,
43 1 => RejectionCode::SysFatal,
44 2 => RejectionCode::SysTransient,
45 3 => RejectionCode::DestinationInvalid,
46 4 => RejectionCode::CanisterReject,
47 5 => RejectionCode::CanisterError,
48 _ => RejectionCode::Unknown,
49 }
50 }
51}
52
53impl From<u32> for RejectionCode {
54 fn from(code: u32) -> Self {
55 RejectionCode::from(code as usize)
56 }
57}
58
59/// The result of a Call.
60///
61/// Errors on the IC have two components; a Code and a message associated with it.
62#[deprecated(
63 since = "0.18.0",
64 note = "Please use `ic_cdk::call::CallResult` instead."
65)]
66pub type CallResult<R> = Result<R, (RejectionCode, String)>;
67
68/// Internal state for the Future when sending a call.
69#[derive(Debug, Default)]
70enum CallFutureState<T: AsRef<[u8]>> {
71 /// The future has been constructed, and the call has not yet been performed.
72 /// Needed because futures are supposed to do nothing unless polled.
73 /// Polling will attempt to fire off the request. Success returns `Pending` and transitions to `Executing`,
74 /// failure returns `Ready` and transitions to `PostComplete.`
75 Prepared {
76 id: Principal,
77 method: String,
78 arg: T,
79 payment: u128,
80 },
81 /// The call has been performed and the message is in flight. Neither callback has been called. Polling will return `Pending`.
82 /// This state will transition to `Trapped` if the future is canceled because of a trap in another future.
83 Executing { waker: Waker },
84 /// `callback` has been called, so the call has been completed. This completion state has not yet been read by the user.
85 /// Polling will return `Ready` and transition to `PostComplete`.
86 Complete { result: CallResult<Vec<u8>> },
87 /// The completion state of `Complete` has been returned from `poll` as `Poll::Ready`. Polling again will trap.
88 #[default]
89 PostComplete,
90 /// The future (*not* the state) was canceled because of a trap in another future during `Executing`. Polling will trap.
91 Trapped,
92}
93
94struct CallFuture<T: AsRef<[u8]>> {
95 state: Arc<RwLock<CallFutureState<T>>>,
96}
97
98impl<T: AsRef<[u8]>> Future for CallFuture<T> {
99 type Output = CallResult<Vec<u8>>;
100
101 fn poll(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
102 let self_ref = Pin::into_inner(self);
103 let mut state = self_ref.state.write().unwrap();
104 match mem::take(&mut *state) {
105 CallFutureState::Prepared {
106 id,
107 method,
108 arg,
109 payment,
110 } => {
111 let callee = id.as_slice();
112 let args = arg.as_ref();
113 let state_ptr = Arc::into_raw(Arc::clone(&self_ref.state));
114 // SAFETY:
115 // `callee`, being &[u8], is a readable sequence of bytes and therefore can be passed to ic0.call_new.
116 // `method`, being &str, is a readable sequence of bytes and therefore can be passed to ic0.call_new.
117 // `callback` is a function with signature (env : usize) -> () and therefore can be called as both reply and reject fn for ic0.call_new.
118 // `state_ptr` is a pointer created via Arc::into_raw, and can therefore be passed as the userdata for `callback`.
119 // `args`, being a &[u8], is a readable sequence of bytes and therefore can be passed to ic0.call_data_append.
120 // `cleanup` is a function with signature (env : usize) -> () and therefore can be called as a cleanup fn for ic0.call_on_cleanup.
121 // `state_ptr` is a pointer created via Arc::into_raw, and can therefore be passed as the userdata for `cleanup`.
122 // ic0.call_perform is always safe to call.
123 // callback and cleanup are safe to parameterize with T because they will always be called in the
124 // Executing or Trapped states which do not contain a T.
125 let err_code = unsafe {
126 ic0::call_new(
127 callee.as_ptr() as usize,
128 callee.len(),
129 method.as_ptr() as usize,
130 method.len(),
131 callback::<T> as usize as usize,
132 state_ptr as usize,
133 callback::<T> as usize as usize,
134 state_ptr as usize,
135 );
136
137 ic0::call_data_append(args.as_ptr() as usize, args.len());
138 add_payment(payment);
139 ic0::call_on_cleanup(cleanup::<T> as usize as usize, state_ptr as usize);
140 ic0::call_perform()
141 };
142
143 // 0 is a special error code meaning call succeeded.
144 if err_code != 0 {
145 *state = CallFutureState::PostComplete;
146 // SAFETY: We just constructed this from Arc::into_raw.
147 unsafe {
148 Arc::from_raw(state_ptr);
149 }
150 let result = Err((
151 RejectionCode::from(err_code),
152 "Couldn't send message".to_string(),
153 ));
154 return Poll::Ready(result);
155 }
156 *state = CallFutureState::Executing {
157 waker: context.waker().clone(),
158 };
159 Poll::Pending
160 }
161 CallFutureState::Executing { .. } => {
162 *state = CallFutureState::Executing {
163 waker: context.waker().clone(),
164 };
165 Poll::Pending
166 }
167 CallFutureState::Complete { result } => {
168 *state = CallFutureState::PostComplete;
169 Poll::Ready(result)
170 }
171 CallFutureState::Trapped => trap("Call already trapped"),
172 CallFutureState::PostComplete => trap("CallFuture polled after completing"),
173 }
174 }
175}
176
177impl<T: AsRef<[u8]>> Drop for CallFuture<T> {
178 fn drop(&mut self) {
179 // If this future is dropped while is_recovering_from_trap is true,
180 // then it has been canceled due to a trap in another future.
181 if is_recovering_from_trap() {
182 *self.state.write().unwrap() = CallFutureState::Trapped;
183 }
184 }
185}
186
187/// The callback from IC dereferences the future from a raw pointer, assigns the
188/// result and calls the waker. We cannot use a closure here because we pass raw
189/// pointers to the System and back.
190///
191/// # Safety
192///
193/// This function must only be passed to the IC with a pointer from Arc::into_raw as userdata.
194unsafe extern "C" fn callback<T: AsRef<[u8]>>(state_ptr: *const RwLock<CallFutureState<T>>) {
195 crate::futures::in_callback_executor_context(|| {
196 // SAFETY: This function is only ever called by the IC, and we only ever pass an Arc as userdata.
197 let state = unsafe { Arc::from_raw(state_ptr) };
198 let completed_state = CallFutureState::Complete {
199 result: match reject_code() {
200 RejectionCode::NoError => Ok(arg_data_raw()),
201 n => Err((n, reject_message())),
202 },
203 };
204 let waker = match mem::replace(&mut *state.write().unwrap(), completed_state) {
205 CallFutureState::Executing { waker } => waker,
206 // This future has already been cancelled and waking it will do nothing.
207 // All that's left is to explicitly trap in case this is the last call being multiplexed,
208 // to replace an automatic trap from not replying.
209 CallFutureState::Trapped => trap("Call already trapped"),
210 _ => {
211 unreachable!(
212 "CallFutureState for in-flight calls should only be Executing or Trapped"
213 )
214 }
215 };
216 waker.wake();
217 });
218}
219
220/// This function is called when [callback] was just called with the same parameter, and trapped.
221/// We can't guarantee internal consistency at this point, but we can at least e.g. drop mutex guards.
222/// Waker is a very opaque API, so the best we can do is set a global flag and proceed normally.
223///
224/// # Safety
225///
226/// This function must only be passed to the IC with a pointer from Arc::into_raw as userdata.
227unsafe extern "C" fn cleanup<T: AsRef<[u8]>>(state_ptr: *const RwLock<CallFutureState<T>>) {
228 // Flag that we do not want to actually wake the task - we
229 // want to drop it *without* executing it.
230 crate::futures::in_callback_cancellation_context(|| {
231 // SAFETY: This function is only ever called by the IC, and we only ever pass an Arc as userdata.
232 let state = unsafe { Arc::from_raw(state_ptr) };
233 // We set the call result, even though it won't be read on the
234 // default executor, because we can't guarantee it was called on
235 // our executor. However, we are not allowed to inspect
236 // reject_code() inside of a cleanup callback, so always set the
237 // result to a reject.
238 //
239 // Borrowing does not trap - the rollback from the
240 // previous trap ensures that the RwLock can be borrowed again.
241 let err_state = CallFutureState::Complete {
242 result: Err((RejectionCode::NoError, "cleanup".to_string())),
243 };
244 let waker = match mem::replace(&mut *state.write().unwrap(), err_state) {
245 CallFutureState::Executing { waker } => waker,
246 CallFutureState::Trapped => {
247 // The future has already been canceled and dropped. There is nothing
248 // more to clean up except for the CallFutureState.
249 return;
250 }
251 _ => {
252 unreachable!(
253 "CallFutureState for in-flight calls should only be Executing or Trapped"
254 )
255 }
256 };
257 waker.wake();
258 });
259}
260
261fn add_payment(payment: u128) {
262 if payment == 0 {
263 return;
264 }
265 let high = (payment >> 64) as u64;
266 let low = (payment & u64::MAX as u128) as u64;
267 // SAFETY: ic0.call_cycles_add128 is always safe to call.
268 unsafe {
269 ic0::call_cycles_add128(high, low);
270 }
271}
272
273/// Sends a one-way message with `payment` cycles attached to it that invokes `method` with
274/// arguments `args` on the principal identified by `id`, ignoring the reply.
275///
276/// Returns `Ok(())` if the message was successfully enqueued, otherwise returns a reject code.
277///
278/// # Notes
279///
280/// * The caller has no way of checking whether the destination processed the notification.
281/// The system can drop the notification if the destination does not have resources to
282/// process the message (for example, if it's out of cycles or queue slots).
283///
284/// * The callee cannot tell whether the call is one-way or not.
285/// The callee must produce replies for all incoming messages.
286///
287/// * It is safe to upgrade a canister without stopping it first if it sends out *only*
288/// one-way messages.
289///
290/// * If the payment is non-zero and the system fails to deliver the notification, the behaviour
291/// is unspecified: the funds can be either reimbursed or consumed irrevocably by the IC depending
292/// on the underlying implementation of one-way calls.
293#[deprecated(
294 since = "0.18.0",
295 note = "Please use `ic_cdk::call::Call::unbounded_wait()` instead."
296)]
297pub fn notify_with_payment128<T: ArgumentEncoder>(
298 id: Principal,
299 method: &str,
300 args: T,
301 payment: u128,
302) -> Result<(), RejectionCode> {
303 let args_raw = encode_args(args).expect("failed to encode arguments");
304 notify_raw(id, method, &args_raw, payment)
305}
306
307/// Like [notify_with_payment128], but sets the payment to zero.
308#[deprecated(
309 since = "0.18.0",
310 note = "Please use `ic_cdk::call::Call::unbounded_wait()` instead."
311)]
312pub fn notify<T: ArgumentEncoder>(
313 id: Principal,
314 method: &str,
315 args: T,
316) -> Result<(), RejectionCode> {
317 notify_with_payment128(id, method, args, 0)
318}
319
320/// Like [notify], but sends the argument as raw bytes, skipping Candid serialization.
321#[deprecated(
322 since = "0.18.0",
323 note = "Please use `ic_cdk::call::Call::unbounded_wait()` instead."
324)]
325pub fn notify_raw(
326 id: Principal,
327 method: &str,
328 args_raw: &[u8],
329 payment: u128,
330) -> Result<(), RejectionCode> {
331 let callee = id.as_slice();
332 // We set all callbacks to -1, which is guaranteed to be invalid callback index.
333 // The system will still deliver the reply, but it will trap immediately because the callback
334 // is not a valid function. See
335 // https://www.joachim-breitner.de/blog/789-Zero-downtime_upgrades_of_Internet_Computer_canisters#one-way-calls
336 // for more context.
337
338 // SAFETY:
339 // `callee`, being &[u8], is a readable sequence of bytes and therefore can be passed to ic0.call_new.
340 // `method`, being &str, is a readable sequence of bytes and therefore can be passed to ic0.call_new.
341 // -1, i.e. usize::MAX, is a function pointer the wasm module cannot possibly contain, and therefore can be passed as both reply and reject fn for ic0.call_new.
342 // Since the callback function will never be called, any value can be passed as its context parameter, and therefore -1 can be passed for those values.
343 // `args`, being a &[u8], is a readable sequence of bytes and therefore can be passed to ic0.call_data_append.
344 // ic0.call_perform is always safe to call.
345 let err_code = unsafe {
346 ic0::call_new(
347 callee.as_ptr() as usize,
348 callee.len(),
349 method.as_ptr() as usize,
350 method.len(),
351 /* reply_fun = */ usize::MAX,
352 /* reply_env = */ usize::MAX,
353 /* reject_fun = */ usize::MAX,
354 /* reject_env = */ usize::MAX,
355 );
356 add_payment(payment);
357 ic0::call_data_append(args_raw.as_ptr() as usize, args_raw.len());
358 ic0::call_perform()
359 };
360 match err_code {
361 0 => Ok(()),
362 c => Err(RejectionCode::from(c)),
363 }
364}
365
366/// Performs an asynchronous call to another canister and pay cycles at the same time.
367///
368/// Treats arguments and returns as raw bytes. No data serialization and deserialization is performed.
369///
370/// # Example
371///
372/// It can be called:
373///
374/// ```rust
375/// # use ic_cdk::api::call::call_raw;
376/// # fn callee_canister() -> candid::Principal { unimplemented!() }
377/// async fn call_add_user() -> Vec<u8>{
378/// call_raw(callee_canister(), "add_user", b"abcd", 1_000_000u64).await.unwrap()
379/// }
380/// ```
381#[deprecated(
382 since = "0.18.0",
383 note = "Please use `ic_cdk::call::Call::unbounded_wait()` instead."
384)]
385pub fn call_raw<'a, T: AsRef<[u8]> + Send + Sync + 'a>(
386 id: Principal,
387 method: &str,
388 args_raw: T,
389 payment: u64,
390) -> impl Future<Output = CallResult<Vec<u8>>> + Send + Sync + 'a {
391 call_raw_internal(id, method, args_raw, payment.into())
392}
393
394/// Performs an asynchronous call to another canister and pay cycles (in `u128`) at the same time.
395///
396/// Treats arguments and returns as raw bytes. No data serialization and deserialization is performed.
397/// # Example
398///
399/// It can be called:
400///
401/// ```rust
402/// # use ic_cdk::api::call::call_raw128;
403/// # fn callee_canister() -> candid::Principal { unimplemented!() }
404/// async fn call_add_user() -> Vec<u8>{
405/// call_raw128(callee_canister(), "add_user", b"abcd", 1_000_000u128).await.unwrap()
406/// }
407/// ```
408#[deprecated(
409 since = "0.18.0",
410 note = "Please use `ic_cdk::call::Call::unbounded_wait()` instead."
411)]
412pub fn call_raw128<'a, T: AsRef<[u8]> + Send + Sync + 'a>(
413 id: Principal,
414 method: &str,
415 args_raw: T,
416 payment: u128,
417) -> impl Future<Output = CallResult<Vec<u8>>> + Send + Sync + 'a {
418 call_raw_internal(id, method, args_raw, payment)
419}
420
421fn call_raw_internal<'a, T: AsRef<[u8]> + Send + Sync + 'a>(
422 id: Principal,
423 method: &str,
424 args_raw: T,
425 payment: u128,
426) -> impl Future<Output = CallResult<Vec<u8>>> + Send + Sync + 'a {
427 let state = Arc::new(RwLock::new(CallFutureState::Prepared {
428 id,
429 method: method.to_string(),
430 arg: args_raw,
431 payment,
432 }));
433 CallFuture { state }
434}
435
436fn decoder_error_to_reject<T>(err: candid::error::Error) -> (RejectionCode, String) {
437 (
438 RejectionCode::CanisterError,
439 format!(
440 "failed to decode canister response as {}: {}",
441 std::any::type_name::<T>(),
442 err
443 ),
444 )
445}
446
447/// Performs an asynchronous call to another canister.
448///
449/// # Example
450///
451/// Assuming that the callee canister has following interface:
452///
453/// ```text
454/// service : {
455/// add_user: (name: text) -> (nat64);
456/// }
457/// ```
458///
459/// It can be called:
460///
461/// ```rust
462/// # use ic_cdk::api::call::call;
463/// # fn callee_canister() -> candid::Principal { unimplemented!() }
464/// async fn call_add_user() -> u64 {
465/// let (user_id,) = call(callee_canister(), "add_user", ("Alice".to_string(),)).await.unwrap();
466/// user_id
467/// }
468/// ```
469///
470/// # Note
471///
472/// * Both argument and return types are tuples even if it has only one value, e.g `(user_id,)`, `("Alice".to_string(),)`.
473/// * The type annotation on return type is required. Or the return type can be inferred from the context.
474/// * The asynchronous call must be awaited in order for the inter-canister call to be made.
475/// * If the reply payload is not a valid encoding of the expected type `T`, the call results in [RejectionCode::CanisterError] error.
476#[deprecated(
477 since = "0.18.0",
478 note = "Please use `ic_cdk::call::Call::unbounded_wait()` instead."
479)]
480pub fn call<T: ArgumentEncoder, R: for<'a> ArgumentDecoder<'a>>(
481 id: Principal,
482 method: &str,
483 args: T,
484) -> impl Future<Output = CallResult<R>> + Send + Sync {
485 let args_raw = encode_args(args).expect("Failed to encode arguments.");
486 let fut = call_raw(id, method, args_raw, 0);
487 async {
488 let bytes = fut.await?;
489 decode_args(&bytes).map_err(decoder_error_to_reject::<R>)
490 }
491}
492
493/// Performs an asynchronous call to another canister and pay cycles at the same time.
494///
495/// # Example
496///
497/// Assuming that the callee canister has following interface:
498///
499/// ```text
500/// service : {
501/// add_user: (name: text) -> (nat64);
502/// }
503/// ```
504///
505/// It can be called:
506///
507/// ```rust
508/// # use ic_cdk::api::call::call_with_payment;
509/// # fn callee_canister() -> candid::Principal { unimplemented!() }
510/// async fn call_add_user() -> u64 {
511/// let (user_id,) = call_with_payment(callee_canister(), "add_user", ("Alice".to_string(),), 1_000_000u64).await.unwrap();
512/// user_id
513/// }
514/// ```
515///
516/// # Note
517///
518/// * Both argument and return types are tuples even if it has only one value, e.g `(user_id,)`, `("Alice".to_string(),)`.
519/// * The type annotation on return type is required. Or the return type can be inferred from the context.
520/// * The asynchronous call must be awaited in order for the inter-canister call to be made.
521/// * If the reply payload is not a valid encoding of the expected type `T`, the call results in [RejectionCode::CanisterError] error.
522#[deprecated(
523 since = "0.18.0",
524 note = "Please use `ic_cdk::call::Call::unbounded_wait()` instead."
525)]
526pub fn call_with_payment<T: ArgumentEncoder, R: for<'a> ArgumentDecoder<'a>>(
527 id: Principal,
528 method: &str,
529 args: T,
530 cycles: u64,
531) -> impl Future<Output = CallResult<R>> + Send + Sync {
532 let args_raw = encode_args(args).expect("Failed to encode arguments.");
533 let fut = call_raw(id, method, args_raw, cycles);
534 async {
535 let bytes = fut.await?;
536 decode_args(&bytes).map_err(decoder_error_to_reject::<R>)
537 }
538}
539
540/// Performs an asynchronous call to another canister and pay cycles (in `u128`) at the same time.
541///
542/// # Example
543///
544/// Assuming that the callee canister has following interface:
545///
546/// ```text
547/// service : {
548/// add_user: (name: text) -> (nat64);
549/// }
550/// ```
551///
552/// It can be called:
553///
554/// ```rust
555/// # use ic_cdk::api::call::call_with_payment128;
556/// # fn callee_canister() -> candid::Principal { unimplemented!() }
557/// async fn call_add_user() -> u64 {
558/// let (user_id,) = call_with_payment128(callee_canister(), "add_user", ("Alice".to_string(),), 1_000_000u128).await.unwrap();
559/// user_id
560/// }
561/// ```
562///
563/// # Note
564///
565/// * Both argument and return types are tuples even if it has only one value, e.g `(user_id,)`, `("Alice".to_string(),)`.
566/// * The type annotation on return type is required. Or the return type can be inferred from the context.
567/// * The asynchronous call must be awaited in order for the inter-canister call to be made.
568/// * If the reply payload is not a valid encoding of the expected type `T`, the call results in [RejectionCode::CanisterError] error.
569#[deprecated(
570 since = "0.18.0",
571 note = "Please use `ic_cdk::call::Call::unbounded_wait()` instead."
572)]
573pub fn call_with_payment128<T: ArgumentEncoder, R: for<'a> ArgumentDecoder<'a>>(
574 id: Principal,
575 method: &str,
576 args: T,
577 cycles: u128,
578) -> impl Future<Output = CallResult<R>> + Send + Sync {
579 let args_raw = encode_args(args).expect("Failed to encode arguments.");
580 let fut = call_raw128(id, method, args_raw, cycles);
581 async {
582 let bytes = fut.await?;
583 decode_args(&bytes).map_err(decoder_error_to_reject::<R>)
584 }
585}
586
587/// Performs an asynchronous call to another canister and pay cycles (in `u128`).
588/// It also allows setting a quota for decoding the return values.
589/// The decoding quota is strongly recommended when calling third-party or untrusted canisters.
590///
591/// # Example
592///
593/// Assuming that the callee canister has following interface:
594///
595/// ```text
596/// service : {
597/// add_user: (name: text) -> (nat64);
598/// }
599/// ```
600///
601/// It can be called:
602///
603/// ```rust
604/// # use ic_cdk::api::call::{call_with_config, ArgDecoderConfig};
605/// # fn callee_canister() -> candid::Principal { unimplemented!() }
606/// async fn call_add_user() -> u64 {
607/// let config = ArgDecoderConfig {
608/// // The function only returns a nat64, to accomodate future upgrades, we set a larger decoding_quota.
609/// decoding_quota: Some(10_000),
610/// // To accomodate future upgrades, reserve some skipping_quota.
611/// skipping_quota: Some(100),
612/// // Enable debug mode to print decoding instructions and cost to the replica log.
613/// debug: true,
614/// };
615/// let (user_id,) = call_with_config(callee_canister(), "add_user", ("Alice".to_string(),), 1_000_000u128, &config).await.unwrap();
616/// user_id
617/// }
618/// ```
619#[deprecated(
620 since = "0.18.0",
621 note = "Please use `ic_cdk::call::Call::unbounded_wait()` instead."
622)]
623pub fn call_with_config<'b, T: ArgumentEncoder, R: for<'a> ArgumentDecoder<'a>>(
624 id: Principal,
625 method: &'b str,
626 args: T,
627 cycles: u128,
628 arg_config: &'b ArgDecoderConfig,
629) -> impl Future<Output = CallResult<R>> + Send + Sync + 'b {
630 let args_raw = encode_args(args).expect("Failed to encode arguments.");
631 let fut = call_raw128(id, method, args_raw, cycles);
632 async move {
633 let bytes = fut.await?;
634 let config = arg_config.to_candid_config();
635 let pre_cycles = if arg_config.debug {
636 Some(crate::api::performance_counter(0))
637 } else {
638 None
639 };
640 match decode_args_with_config_debug(&bytes, &config) {
641 Err(e) => Err(decoder_error_to_reject::<R>(e)),
642 Ok((r, cost)) => {
643 if arg_config.debug {
644 print_decoding_debug_info(&format!("{method} return"), &cost, pre_cycles);
645 }
646 Ok(r)
647 }
648 }
649 }
650}
651
652fn print_decoding_debug_info(title: &str, cost: &DecoderConfig, pre_cycles: Option<u64>) {
653 use crate::api::{performance_counter, print};
654 let pre_cycles = pre_cycles.unwrap_or(0);
655 let instrs = performance_counter(0) - pre_cycles;
656 print(format!("[Debug] {title} decoding instructions: {instrs}"));
657 if let Some(n) = cost.decoding_quota {
658 print(format!("[Debug] {title} decoding cost: {n}"));
659 }
660 if let Some(n) = cost.skipping_quota {
661 print(format!("[Debug] {title} skipping cost: {n}"));
662 }
663}
664
665/// Returns a result that maps over the call
666///
667/// It will be Ok(T) if the call succeeded (with T being the arg_data),
668/// and [reject_message()] if it failed.
669#[deprecated(
670 since = "0.18.0",
671 note = "Please use `ic_cdk::api::{msg_reject_code, msg_reject_msg}` instead."
672)]
673pub fn result<T: for<'a> ArgumentDecoder<'a>>() -> Result<T, String> {
674 match reject_code() {
675 RejectionCode::NoError => {
676 decode_args(&arg_data_raw()).map_err(|e| format!("Failed to decode arguments: {}", e))
677 }
678 _ => Err(reject_message()),
679 }
680}
681
682/// Returns the rejection code for the call.
683#[deprecated(
684 since = "0.18.0",
685 note = "Please use `ic_cdk::api::msg_reject_code` instead."
686)]
687pub fn reject_code() -> RejectionCode {
688 // SAFETY: ic0.msg_reject_code is always safe to call.
689 let code = unsafe { ic0::msg_reject_code() };
690 RejectionCode::from(code)
691}
692
693/// Returns the rejection message.
694#[deprecated(
695 since = "0.18.0",
696 note = "Please use `ic_cdk::api::msg_reject_msg` instead."
697)]
698pub fn reject_message() -> String {
699 // SAFETY: ic0.msg_reject_msg_size is always safe to call.
700 let len: u32 = unsafe { ic0::msg_reject_msg_size() as u32 };
701 let mut bytes = vec![0u8; len as usize];
702 // SAFETY: `bytes`, being mutable and allocated to `len` bytes, is safe to pass to ic0.msg_reject_msg_copy with no offset
703 unsafe {
704 ic0::msg_reject_msg_copy(bytes.as_mut_ptr() as usize, 0, len as usize);
705 }
706 String::from_utf8_lossy(&bytes).into_owned()
707}
708
709/// Rejects the current call with the message.
710#[deprecated(
711 since = "0.18.0",
712 note = "Please use `ic_cdk::api::msg_reject` instead."
713)]
714pub fn reject(message: &str) {
715 let err_message = message.as_bytes();
716 // SAFETY: `err_message`, being &[u8], is a readable sequence of bytes, and therefore valid to pass to ic0.msg_reject.
717 unsafe {
718 ic0::msg_reject(err_message.as_ptr() as usize, err_message.len());
719 }
720}
721
722/// An io::Write for message replies.
723#[derive(Debug, Copy, Clone)]
724#[deprecated(
725 since = "0.18.0",
726 note = "Please use `ic_cdk::api::msg_reply` instead."
727)]
728pub struct CallReplyWriter;
729
730impl std::io::Write for CallReplyWriter {
731 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
732 // SAFETY: buf, being &[u8], is a readable sequence of bytes, and therefore valid to pass to ic0.msg_reply_data_append.
733 unsafe {
734 ic0::msg_reply_data_append(buf.as_ptr() as usize, buf.len());
735 }
736 Ok(buf.len())
737 }
738
739 fn flush(&mut self) -> std::io::Result<()> {
740 Ok(())
741 }
742}
743
744/// Replies to the current call with a candid argument.
745#[deprecated(
746 since = "0.18.0",
747 note = "Please use `ic_cdk::api::msg_reply` instead."
748)]
749pub fn reply<T: ArgumentEncoder>(reply: T) {
750 write_args(&mut CallReplyWriter, reply).expect("Could not encode reply.");
751 // SAFETY: ic0.msg_reply is always safe to call.
752 unsafe {
753 ic0::msg_reply();
754 }
755}
756
757/// Returns the amount of cycles that were transferred by the caller
758/// of the current call, and is still available in this message.
759#[deprecated(
760 since = "0.18.0",
761 note = "Please use `ic_cdk::api::msg_cycles_available` instead."
762)]
763pub fn msg_cycles_available() -> u64 {
764 msg_cycles_available128() as u64
765}
766
767/// Returns the amount of cycles that were transferred by the caller
768/// of the current call, and is still available in this message.
769#[deprecated(
770 since = "0.18.0",
771 note = "Please use `ic_cdk::api::msg_cycles_available` instead."
772)]
773pub fn msg_cycles_available128() -> u128 {
774 let mut recv = 0u128;
775 // SAFETY: recv is writable and sixteen bytes wide, and therefore is safe to pass to ic0.msg_cycles_available128
776 unsafe {
777 ic0::msg_cycles_available128(&mut recv as *mut u128 as usize);
778 }
779 recv
780}
781
782/// Returns the amount of cycles that came back with the response as a refund.
783///
784/// The refund has already been added to the canister balance automatically.
785#[deprecated(
786 since = "0.18.0",
787 note = "Please use `ic_cdk::api::msg_cycles_refunded` instead."
788)]
789pub fn msg_cycles_refunded() -> u64 {
790 msg_cycles_refunded128() as u64
791}
792
793/// Returns the amount of cycles that came back with the response as a refund.
794///
795/// The refund has already been added to the canister balance automatically.
796#[deprecated(
797 since = "0.18.0",
798 note = "Please use `ic_cdk::api::msg_cycles_refunded` instead."
799)]
800pub fn msg_cycles_refunded128() -> u128 {
801 let mut recv = 0u128;
802 // SAFETY: recv is writable and sixteen bytes wide, and therefore is safe to pass to ic0.msg_cycles_refunded128
803 unsafe {
804 ic0::msg_cycles_refunded128(&mut recv as *mut u128 as usize);
805 }
806 recv
807}
808
809/// Moves cycles from the call to the canister balance.
810///
811/// The actual amount moved will be returned.
812#[deprecated(
813 since = "0.18.0",
814 note = "Please use `ic_cdk::api::msg_cycles_accept` instead."
815)]
816pub fn msg_cycles_accept(max_amount: u64) -> u64 {
817 msg_cycles_accept128(max_amount as u128) as u64
818}
819
820/// Moves cycles from the call to the canister balance.
821///
822/// The actual amount moved will be returned.
823#[deprecated(
824 since = "0.18.0",
825 note = "Please use `ic_cdk::api::msg_cycles_accept` instead."
826)]
827pub fn msg_cycles_accept128(max_amount: u128) -> u128 {
828 let high = (max_amount >> 64) as u64;
829 let low = (max_amount & u64::MAX as u128) as u64;
830 let mut recv = 0u128;
831 // SAFETY: `recv` is writable and sixteen bytes wide, and therefore safe to pass to ic0.msg_cycles_accept128
832 unsafe {
833 ic0::msg_cycles_accept128(high, low, &mut recv as *mut u128 as usize);
834 }
835 recv
836}
837
838/// Returns the argument data as bytes.
839#[deprecated(
840 since = "0.18.0",
841 note = "Please use `ic_cdk::api::msg_arg_data` instead."
842)]
843pub fn arg_data_raw() -> Vec<u8> {
844 // SAFETY: ic0.msg_arg_data_size is always safe to call.
845 let len: usize = unsafe { ic0::msg_arg_data_size() };
846 let mut bytes = Vec::with_capacity(len);
847 // SAFETY:
848 // `bytes`, being mutable and allocated to `len` bytes, is safe to pass to ic0.msg_arg_data_copy with no offset
849 // ic0.msg_arg_data_copy writes to all of `bytes[0..len]`, so `set_len` is safe to call with the new len.
850 unsafe {
851 ic0::msg_arg_data_copy(bytes.as_mut_ptr() as usize, 0, len);
852 bytes.set_len(len);
853 }
854 bytes
855}
856
857/// Gets the len of the raw-argument-data-bytes.
858#[deprecated(
859 since = "0.18.0",
860 note = "Please use `ic_cdk::api::msg_arg_data` instead."
861)]
862pub fn arg_data_raw_size() -> usize {
863 // SAFETY: ic0.msg_arg_data_size is always safe to call.
864 unsafe { ic0::msg_arg_data_size() }
865}
866
867/// Replies with the bytes passed
868#[deprecated(
869 since = "0.18.0",
870 note = "Please use `ic_cdk::api::msg_reply` instead."
871)]
872pub fn reply_raw(buf: &[u8]) {
873 if !buf.is_empty() {
874 // SAFETY: `buf`, being &[u8], is a readable sequence of bytes, and therefore valid to pass to ic0.msg_reject.
875 unsafe { ic0::msg_reply_data_append(buf.as_ptr() as usize, buf.len()) }
876 };
877 // SAFETY: ic0.msg_reply is always safe to call.
878 unsafe { ic0::msg_reply() };
879}
880
881#[deprecated(
882 since = "0.18.0",
883 note = "Please use `candid::de::DecoderConfig` instead."
884)]
885#[derive(Debug)]
886/// Config to control the behavior of decoding canister endpoint arguments.
887pub struct ArgDecoderConfig {
888 /// Limit the total amount of work the deserializer can perform. See [docs on the Candid library](https://docs.rs/candid/latest/candid/de/struct.DecoderConfig.html#method.set_decoding_quota) to understand the cost model.
889 pub decoding_quota: Option<usize>,
890 /// Limit the total amount of work for skipping unneeded data on the wire. See [docs on the Candid library](https://docs.rs/candid/latest/candid/de/struct.DecoderConfig.html#method.set_skipping_quota) to understand the skipping cost.
891 pub skipping_quota: Option<usize>,
892 /// When set to true, print instruction count and the decoding/skipping cost to the replica log.
893 pub debug: bool,
894}
895impl ArgDecoderConfig {
896 fn to_candid_config(&self) -> DecoderConfig {
897 let mut config = DecoderConfig::new();
898 if let Some(n) = self.decoding_quota {
899 config.set_decoding_quota(n);
900 }
901 if let Some(n) = self.skipping_quota {
902 config.set_skipping_quota(n);
903 }
904 if self.debug {
905 config.set_full_error_message(true);
906 }
907 config
908 }
909}
910impl Default for ArgDecoderConfig {
911 fn default() -> Self {
912 Self {
913 decoding_quota: None,
914 skipping_quota: Some(10_000),
915 debug: false,
916 }
917 }
918}
919
920/// Returns the argument data in the current call. Traps if the data cannot be
921/// decoded.
922#[deprecated(
923 since = "0.18.0",
924 note = "Please use `ic_cdk::call::msg_arg_data` instead."
925)]
926pub fn arg_data<R: for<'a> ArgumentDecoder<'a>>(arg_config: ArgDecoderConfig) -> R {
927 let bytes = arg_data_raw();
928
929 let config = arg_config.to_candid_config();
930 let res = decode_args_with_config_debug(&bytes, &config);
931 match res {
932 Err(e) => trap(format!("failed to decode call arguments: {:?}", e)),
933 Ok((r, cost)) => {
934 if arg_config.debug {
935 print_decoding_debug_info("Argument", &cost, None);
936 }
937 r
938 }
939 }
940}
941
942/// Accepts the ingress message.
943#[deprecated(
944 since = "0.18.0",
945 note = "Please use `ic_cdk::api::accept_message` instead."
946)]
947pub fn accept_message() {
948 // SAFETY: ic0.accept_message is always safe to call.
949 unsafe {
950 ic0::accept_message();
951 }
952}
953
954/// Returns the name of current canister method.
955#[deprecated(
956 since = "0.18.0",
957 note = "Please use `ic_cdk::api::msg_method_name` instead."
958)]
959pub fn method_name() -> String {
960 // SAFETY: ic0.msg_method_name_size is always safe to call.
961 let len: u32 = unsafe { ic0::msg_method_name_size() as u32 };
962 let mut bytes = vec![0u8; len as usize];
963 // SAFETY: `bytes` is writable and allocated to `len` bytes, and therefore can be safely passed to ic0.msg_method_name_copy
964 unsafe {
965 ic0::msg_method_name_copy(bytes.as_mut_ptr() as usize, 0, len as usize);
966 }
967 String::from_utf8_lossy(&bytes).into_owned()
968}
969
970/// Gets the value of specified performance counter
971///
972/// See [`crate::api::performance_counter`].
973#[deprecated(
974 since = "0.11.3",
975 note = "This method conceptually doesn't belong to this module. Please use `ic_cdk::api::performance_counter` instead."
976)]
977pub fn performance_counter(counter_type: u32) -> u64 {
978 // SAFETY: ic0.performance_counter is always safe to call.
979 unsafe { ic0::performance_counter(counter_type) }
980}
981
982/// Pretends to have the Candid type `T`, but unconditionally errors
983/// when serialized.
984///
985/// Usable, but not required, as metadata when using `#[query(manual_reply = true)]`,
986/// so an accurate Candid file can still be generated.
987#[deprecated(
988 since = "0.18.0",
989 note = "Please use `std::marker::PhantomData` with manual_reply instead."
990)]
991#[derive(Debug, Copy, Clone, Default)]
992pub struct ManualReply<T: ?Sized>(PhantomData<T>);
993
994impl<T: ?Sized> ManualReply<T> {
995 /// Constructs a new `ManualReply`.
996 #[allow(clippy::self_named_constructors)]
997 pub const fn empty() -> Self {
998 Self(PhantomData)
999 }
1000 /// Replies with the given value and returns a new `ManualReply`,
1001 /// for a useful reply-then-return shortcut.
1002 pub fn all<U>(value: U) -> Self
1003 where
1004 U: ArgumentEncoder,
1005 {
1006 reply(value);
1007 Self::empty()
1008 }
1009 /// Replies with a one-element tuple around the given value and returns
1010 /// a new `ManualReply`, for a useful reply-then-return shortcut.
1011 pub fn one<U>(value: U) -> Self
1012 where
1013 U: CandidType,
1014 {
1015 reply((value,));
1016 Self::empty()
1017 }
1018
1019 /// Rejects the call with the specified message and returns a new
1020 /// `ManualReply`, for a useful reply-then-return shortcut.
1021 pub fn reject(message: impl AsRef<str>) -> Self {
1022 reject(message.as_ref());
1023 Self::empty()
1024 }
1025}
1026
1027impl<T> CandidType for ManualReply<T>
1028where
1029 T: CandidType + ?Sized,
1030{
1031 fn _ty() -> candid::types::Type {
1032 T::_ty()
1033 }
1034 /// Unconditionally errors.
1035 fn idl_serialize<S>(&self, _: S) -> Result<(), S::Error>
1036 where
1037 S: candid::types::Serializer,
1038 {
1039 Err(S::Error::custom("`Empty` cannot be serialized"))
1040 }
1041}
1042
1043/// Tells you whether the current async fn is being canceled due to a trap/panic.
1044///
1045/// If a function traps/panics, then the canister state is rewound to the beginning of the function.
1046/// However, due to the way async works, the beginning of the function as the IC understands it is actually
1047/// the most recent `await` from an inter-canister-call. This means that part of the function will have executed,
1048/// and part of it won't.
1049///
1050/// When this happens the CDK will cancel the task, causing destructors to be run. If you need any functions to be run
1051/// no matter what happens, they should happen in a destructor; the [`scopeguard`](https://docs.rs/scopeguard) crate
1052/// provides a convenient wrapper for this. In a destructor, `is_recovering_from_trap` serves the same purpose as
1053/// [`is_panicking`](std::thread::panicking) - it tells you whether the destructor is executing *because* of a trap,
1054/// as opposed to just because the scope was exited, so you could e.g. implement mutex poisoning.
1055#[deprecated(
1056 since = "0.18.0",
1057 note = "Please use `ic_cdk::futures::is_recovering_from_trap` instead."
1058)]
1059pub fn is_recovering_from_trap() -> bool {
1060 crate::futures::is_recovering_from_trap()
1061}