ic_cdk/api.rs
1//! System API bindings.
2//!
3//! This module provides Rust ergonomic bindings to the system APIs.
4//!
5//! Some APIs require more advanced handling and are organized into separate modules:
6//! * For the inter-canister calls API, see the [`call`](mod@crate::call) module.
7//! * For the stable memory management API, see the .
8//! * The basic bindings are provided in this module including [`stable_size`], [`stable_grow`], [`stable_read`] and [`stable_write`].
9//! * The [`stable`](crate::stable) module provides more advanced functionalities, e.g. support for `std::io` traits.
10//!
11//! APIs that are only available for `wasm32` are not included.
12//! As a result, system APIs with a numeric postfix (indicating the data bit width) are bound to names without the postfix.
13//! For example, `ic0::msg_cycles_available128` is bound to [`msg_cycles_available`], while `ic0::msg_cycles_available` has no binding.
14//!
15//! Functions that provide bindings for a single system API method share the same name as the system API.
16//! For example, `ic0::msg_reject_code` is bound to [`msg_reject_code`].
17//!
18//! Functions that wrap multiple system API methods are named using the common prefix of the wrapped methods.
19//! For example, [`msg_arg_data`] wraps both `ic0::msg_arg_data_size` and `ic0::msg_arg_data_copy`.
20
21use candid::Principal;
22use std::{convert::TryFrom, num::NonZeroU64};
23
24/// Gets the message argument data.
25pub fn msg_arg_data() -> Vec<u8> {
26 let len = ic0::msg_arg_data_size();
27 let mut buf = vec![0u8; len];
28 ic0::msg_arg_data_copy(&mut buf, 0);
29 buf
30}
31
32/// Gets the identity of the caller, which may be a canister id or a user id.
33///
34/// During canister installation or upgrade, this is the id of the user or canister requesting the installation or upgrade.
35/// During a system task (heartbeat or global timer), this is the id of the management canister.
36pub fn msg_caller() -> Principal {
37 let len = ic0::msg_caller_size();
38 let mut buf = vec![0u8; len];
39 ic0::msg_caller_copy(&mut buf, 0);
40 // Trust that the system always returns a valid principal.
41 Principal::try_from(&buf).unwrap()
42}
43
44/// Returns the reject code, if the current function is invoked as a reject callback.
45pub fn msg_reject_code() -> u32 {
46 ic0::msg_reject_code()
47}
48
49/// Gets the reject message.
50///
51/// This function can only be called in the reject callback.
52///
53/// Traps if:
54/// - There is no reject message (i.e. if `reject_code` is 0).
55/// - The message is not valid UTF-8.
56pub fn msg_reject_msg() -> String {
57 let len = ic0::msg_reject_msg_size();
58 let mut buf = vec![0u8; len];
59 ic0::msg_reject_msg_copy(&mut buf, 0);
60 String::from_utf8(buf).expect("reject message is not valid UTF-8")
61}
62
63/// Gets the deadline, in nanoseconds since 1970-01-01, after which the caller might stop waiting for a response.
64///
65/// For calls to update methods with best-effort responses and their callbacks,
66/// the deadline is computed based on the time the call was made,
67/// and the `timeout_seconds` parameter provided by the caller.
68/// In such cases, the deadline value will be converted to `NonZeroU64` and wrapped in `Some`.
69/// To get the deadline value as a `u64`, call `get()` on the `NonZeroU64` value.
70///
71/// ```rust,no_run
72/// use ic_cdk::api::msg_deadline;
73/// if let Some(deadline) = msg_deadline() {
74/// let deadline_value : u64 = deadline.get();
75/// }
76/// ```
77///
78/// For other calls (ingress messages and all calls to query and composite query methods,
79/// including calls in replicated mode), a `None` is returned.
80/// Please note that the raw `msg_deadline` system API returns 0 in such cases.
81/// This function is a wrapper around the raw system API that provides more semantic information through the return type.
82pub fn msg_deadline() -> Option<NonZeroU64> {
83 let nano_seconds = ic0::msg_deadline();
84 match nano_seconds {
85 0 => None,
86 _ => Some(NonZeroU64::new(nano_seconds).unwrap()),
87 }
88}
89
90/// Replies to the sender with the data.
91pub fn msg_reply<T: AsRef<[u8]>>(data: T) {
92 let buf = data.as_ref();
93 if !buf.is_empty() {
94 ic0::msg_reply_data_append(buf);
95 }
96 ic0::msg_reply();
97}
98
99/// Rejects the call with a diagnostic message.
100pub fn msg_reject<T: AsRef<str>>(message: T) {
101 let message = message.as_ref();
102 ic0::msg_reject(message.as_bytes());
103}
104
105/// Gets the number of cycles transferred by the caller of the current call, still available in this message.
106pub fn msg_cycles_available() -> u128 {
107 ic0::msg_cycles_available128()
108}
109
110/// Gets the amount of cycles that came back with the response as a refund
111///
112/// This function can only be used in a callback handler (reply or reject).
113/// The refund has already been added to the canister balance automatically.
114pub fn msg_cycles_refunded() -> u128 {
115 ic0::msg_cycles_refunded128()
116}
117
118/// Moves cycles from the call to the canister balance.
119///
120/// The actual amount moved will be returned.
121pub fn msg_cycles_accept(max_amount: u128) -> u128 {
122 ic0::msg_cycles_accept128(max_amount)
123}
124
125/// Burns cycles from the canister.
126///
127/// Returns the amount of cycles that were actually burned.
128pub fn cycles_burn(amount: u128) -> u128 {
129 ic0::cycles_burn128(amount)
130}
131
132/// Gets canister's own identity.
133pub fn canister_self() -> Principal {
134 let len = ic0::canister_self_size();
135 let mut buf = vec![0u8; len];
136 ic0::canister_self_copy(&mut buf, 0);
137 // Trust that the system always returns a valid principal.
138 Principal::try_from(&buf).unwrap()
139}
140
141/// Gets the current cycle balance of the canister.
142pub fn canister_cycle_balance() -> u128 {
143 ic0::canister_cycle_balance128()
144}
145
146/// Gets the current amount of cycles that is available for spending in calls and execution.
147pub fn canister_liquid_cycle_balance() -> u128 {
148 ic0::canister_liquid_cycle_balance128()
149}
150
151/// Gets the status of the canister.
152///
153/// The status is one of the following:
154/// - 1: Running
155/// - 2: Stopping
156/// - 3: Stopped
157pub fn canister_status() -> CanisterStatusCode {
158 ic0::canister_status().into()
159}
160
161/// The status of a canister.
162///
163/// See [Canister status](https://internetcomputer.org/docs/current/references/ic-interface-spec/#system-api-canister-status).
164#[derive(Debug, PartialEq, Eq, Clone, Copy)]
165#[repr(u32)]
166pub enum CanisterStatusCode {
167 /// Running.
168 Running = 1,
169 /// Stopping.
170 Stopping = 2,
171 /// Stopped.
172 Stopped = 3,
173 /// A status code that is not recognized by this library.
174 Unrecognized(u32),
175}
176
177impl From<u32> for CanisterStatusCode {
178 fn from(value: u32) -> Self {
179 match value {
180 1 => Self::Running,
181 2 => Self::Stopping,
182 3 => Self::Stopped,
183 _ => Self::Unrecognized(value),
184 }
185 }
186}
187
188impl From<CanisterStatusCode> for u32 {
189 fn from(value: CanisterStatusCode) -> Self {
190 match value {
191 CanisterStatusCode::Running => 1,
192 CanisterStatusCode::Stopping => 2,
193 CanisterStatusCode::Stopped => 3,
194 CanisterStatusCode::Unrecognized(value) => value,
195 }
196 }
197}
198
199impl PartialEq<u32> for CanisterStatusCode {
200 fn eq(&self, other: &u32) -> bool {
201 let self_as_u32: u32 = (*self).into();
202 self_as_u32 == *other
203 }
204}
205
206/// Gets the canister version.
207///
208/// See [Canister version](https://internetcomputer.org/docs/current/references/ic-interface-spec/#system-api-canister-version).
209pub fn canister_version() -> u64 {
210 ic0::canister_version()
211}
212
213/// Gets the ID of the subnet on which the canister is running.
214pub fn subnet_self() -> Principal {
215 let len = ic0::subnet_self_size();
216 let mut buf = vec![0u8; len];
217 ic0::subnet_self_copy(&mut buf, 0);
218 // Trust that the system always returns a valid principal.
219 Principal::try_from(&buf).unwrap()
220}
221
222/// Gets the name of the method to be inspected.
223///
224/// This function is only available in the `canister_inspect_message` context.
225///
226/// Traps if the method name is not valid UTF-8.
227pub fn msg_method_name() -> String {
228 let len = ic0::msg_method_name_size();
229 let mut buf = vec![0u8; len];
230 ic0::msg_method_name_copy(&mut buf, 0);
231 String::from_utf8(buf).expect("msg_method_name is not valid UTF-8")
232}
233
234/// Accepts the message in `canister_inspect_message`.
235///
236/// This function is only available in the `canister_inspect_message` context.
237/// This function traps if invoked twice.
238pub fn accept_message() {
239 ic0::accept_message();
240}
241
242/// Gets the current size of the stable memory (in WebAssembly pages).
243///
244/// One WebAssembly page is 64KiB.
245pub fn stable_size() -> u64 {
246 ic0::stable64_size()
247}
248
249/// Attempts to grow the stable memory by `new_pages` many pages containing zeroes.
250///
251/// One WebAssembly page is 64KiB.
252///
253/// If successful, returns the previous size of the memory (in pages).
254/// Otherwise, returns `u64::MAX`.
255pub fn stable_grow(new_pages: u64) -> u64 {
256 ic0::stable64_grow(new_pages)
257}
258
259/// Writes data to the stable memory location specified by an offset.
260///
261/// # Warning
262/// This will panic if `offset + buf.len()` exceeds the current size of stable memory.
263/// Call [`stable_grow`] to request more stable memory if needed.
264pub fn stable_write(offset: u64, buf: &[u8]) {
265 ic0::stable64_write(buf, offset);
266}
267
268/// Reads data from the stable memory location specified by an offset.
269///
270/// # Warning
271/// This will panic if `offset + buf.len()` exceeds the current size of stable memory.
272pub fn stable_read(offset: u64, buf: &mut [u8]) {
273 ic0::stable64_read(buf, offset);
274}
275
276/// Gets the public key (a DER-encoded BLS key) of the root key of this instance of the Internet Computer Protocol.
277///
278/// # Note
279///
280/// This traps in non-replicated mode.
281pub fn root_key() -> Vec<u8> {
282 let len = ic0::root_key_size();
283 let mut buf = vec![0u8; len];
284 ic0::root_key_copy(&mut buf, 0);
285 buf
286}
287
288/// Sets the certified data of this canister.
289///
290/// Canisters can store up to 32 bytes of data that is certified by
291/// the system on a regular basis. One can call [`data_certificate`]
292/// function from a query call to get a certificate authenticating the
293/// value set by calling this function.
294///
295/// This function can only be called from the following contexts:
296/// - `canister_init`, `canister_pre_upgrade` and `canister_post_upgrade`
297/// hooks.
298/// - `canister_update` calls.
299/// - reply or reject callbacks.
300///
301/// # Panics
302///
303/// - This function traps if `data.len() > 32`.
304/// - This function traps if it's called from an illegal context
305/// (e.g., from a query call).
306pub fn certified_data_set<T: AsRef<[u8]>>(data: T) {
307 let buf = data.as_ref();
308 ic0::certified_data_set(buf);
309}
310
311/// When called from a query call, returns the data certificate authenticating
312/// certified data set by this canister.
313///
314/// Returns `None` if called not from a query call.
315pub fn data_certificate() -> Option<Vec<u8>> {
316 if ic0::data_certificate_present() == 0 {
317 return None;
318 }
319 let n = ic0::data_certificate_size();
320 let mut buf = vec![0u8; n];
321 ic0::data_certificate_copy(&mut buf, 0);
322 Some(buf)
323}
324
325/// Gets current timestamp, in nanoseconds since the epoch (1970-01-01)
326pub fn time() -> u64 {
327 ic0::time()
328}
329
330/// Sets global timer.
331///
332/// The canister can set a global timer to make the system
333/// schedule a call to the exported `canister_global_timer`
334/// Wasm method after the specified time.
335/// The time must be provided as nanoseconds since 1970-01-01.
336///
337/// The function returns the previous value of the timer.
338/// If no timer is set before invoking the function, then the function returns zero.
339///
340/// Passing zero as an argument to the function deactivates the timer and thus
341/// prevents the system from scheduling calls to the canister's `canister_global_timer` Wasm method.
342pub fn global_timer_set(timestamp: u64) -> u64 {
343 ic0::global_timer_set(timestamp)
344}
345
346/// Gets the value of specified performance counter.
347///
348/// See [`PerformanceCounterType`] for available counter types.
349#[inline]
350pub fn performance_counter(counter_type: impl Into<PerformanceCounterType>) -> u64 {
351 let counter_type: u32 = counter_type.into().into();
352 ic0::performance_counter(counter_type)
353}
354
355/// The type of performance counter.
356#[derive(Debug, PartialEq, Eq, Clone, Copy)]
357#[repr(u32)]
358pub enum PerformanceCounterType {
359 /// Current execution instruction counter.
360 ///
361 /// The number of WebAssembly instructions the canister has executed
362 /// since the beginning of the current Message execution.
363 InstructionCounter,
364 /// Call context instruction counter
365 ///
366 /// The number of WebAssembly instructions the canister has executed
367 /// within the call context of the current Message execution
368 /// since Call context creation.
369 /// The counter monotonically increases across all message executions
370 /// in the call context until the corresponding call context is removed.
371 CallContextInstructionCounter,
372 /// A performance counter type that is not recognized by this library.
373 Unrecognized(u32),
374}
375
376impl From<u32> for PerformanceCounterType {
377 fn from(value: u32) -> Self {
378 match value {
379 0 => Self::InstructionCounter,
380 1 => Self::CallContextInstructionCounter,
381 _ => Self::Unrecognized(value),
382 }
383 }
384}
385
386impl From<PerformanceCounterType> for u32 {
387 fn from(value: PerformanceCounterType) -> Self {
388 match value {
389 PerformanceCounterType::InstructionCounter => 0,
390 PerformanceCounterType::CallContextInstructionCounter => 1,
391 PerformanceCounterType::Unrecognized(value) => value,
392 }
393 }
394}
395
396impl PartialEq<u32> for PerformanceCounterType {
397 fn eq(&self, other: &u32) -> bool {
398 let self_as_u32: u32 = (*self).into();
399 self_as_u32 == *other
400 }
401}
402
403/// Returns the number of instructions that the canister executed since the last [entry
404/// point](https://internetcomputer.org/docs/current/references/ic-interface-spec/#entry-points).
405#[inline]
406pub fn instruction_counter() -> u64 {
407 performance_counter(0)
408}
409
410/// Returns the number of WebAssembly instructions the canister has executed
411/// within the call context of the current Message execution since
412/// Call context creation.
413///
414/// The counter monotonically increases across all message executions
415/// in the call context until the corresponding call context is removed.
416#[inline]
417pub fn call_context_instruction_counter() -> u64 {
418 performance_counter(1)
419}
420
421/// Determines if a Principal is a controller of the canister.
422pub fn is_controller(principal: &Principal) -> bool {
423 let slice = principal.as_slice();
424 match ic0::is_controller(slice) {
425 0 => false,
426 1 => true,
427 n => panic!("unexpected return value from is_controller: {n}"),
428 }
429}
430
431/// Checks if in replicated execution.
432///
433/// The canister can check whether it is currently running in replicated or non replicated execution.
434pub fn in_replicated_execution() -> bool {
435 match ic0::in_replicated_execution() {
436 0 => false,
437 1 => true,
438 n => panic!("unexpected return value from in_replicated_execution: {n}"),
439 }
440}
441
442/// Gets the amount of cycles that a canister needs to be above the freezing threshold in order to successfully make an inter-canister call.
443pub fn cost_call(method_name_size: u64, payload_size: u64) -> u128 {
444 ic0::cost_call(method_name_size, payload_size)
445}
446
447/// Gets the cycle cost of the Management canister method [`create_canister`](https://internetcomputer.org/docs/references/ic-interface-spec#ic-create_canister).
448pub fn cost_create_canister() -> u128 {
449 ic0::cost_create_canister()
450}
451
452/// Gets the cycle cost of the Management canister method [`http_request`](https://internetcomputer.org/docs/references/ic-interface-spec#ic-http_request).
453pub fn cost_http_request(request_size: u64, max_res_bytes: u64) -> u128 {
454 ic0::cost_http_request(request_size, max_res_bytes)
455}
456
457/// The error type for [`cost_sign_with_ecdsa`] and [`cost_sign_with_schnorr`].
458#[derive(thiserror::Error, Debug, Clone)]
459pub enum SignCostError {
460 /// The ECDSA/vetKD curve or Schnorr algorithm is invalid.
461 #[error("invalid curve or algorithm")]
462 InvalidCurveOrAlgorithm,
463
464 /// The key name is invalid for the provided curve or algorithm.
465 #[error("invalid key name")]
466 InvalidKeyName,
467 /// Unrecognized error.
468 ///
469 /// This error is returned when the System API returns an unrecognized error code.
470 /// Please report to ic-cdk maintainers.
471 #[error("unrecognized error: {0}")]
472 UnrecognizedError(u32),
473}
474
475/// Helper function to handle the result of a signature cost function.
476fn sign_cost_result(dst: u128, code: u32) -> Result<u128, SignCostError> {
477 match code {
478 0 => Ok(dst),
479 1 => Err(SignCostError::InvalidCurveOrAlgorithm),
480 2 => Err(SignCostError::InvalidKeyName),
481 _ => Err(SignCostError::UnrecognizedError(code)),
482 }
483}
484
485/// Gets the cycle cost of the Management canister method [`sign_with_ecdsa`](https://internetcomputer.org/docs/references/ic-interface-spec#ic-sign_with_ecdsa).
486///
487/// # Errors
488///
489/// This function will return an error if the `key_name` or the `ecdsa_curve` is invalid.
490/// The error type [`SignCostError`] provides more information about the reason of the error.
491pub fn cost_sign_with_ecdsa<T: AsRef<str>>(
492 key_name: T,
493 ecdsa_curve: u32,
494) -> Result<u128, SignCostError> {
495 let key_name = key_name.as_ref();
496 let (cost, code) = ic0::cost_sign_with_ecdsa(key_name, ecdsa_curve);
497 sign_cost_result(cost, code)
498}
499
500/// Gets the cycle cost of the Management canister method [`sign_with_schnorr`](https://internetcomputer.org/docs/references/ic-interface-spec#ic-sign_with_schnorr).
501///
502/// # Errors
503///
504/// This function will return an error if the `key_name` or the `algorithm` is invalid.
505/// The error type [`SignCostError`] provides more information about the reason of the error.
506pub fn cost_sign_with_schnorr<T: AsRef<str>>(
507 key_name: T,
508 algorithm: u32,
509) -> Result<u128, SignCostError> {
510 let key_name = key_name.as_ref();
511 let (dst, code) = ic0::cost_sign_with_schnorr(key_name, algorithm);
512 sign_cost_result(dst, code)
513}
514
515/// Gets the cycle cost of the Management canister method [`vetkd_derive_key`](https://github.com/dfinity/portal/pull/3763).
516///
517/// Later, the description will be available in [the interface spec](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-vetkd_derive_key).
518///
519/// # Errors
520///
521/// This function will return an error if the `key_name` or the `vetkd_curve` is invalid.
522/// The error type [`SignCostError`] provides more information about the reason of the error.
523pub fn cost_vetkd_derive_key<T: AsRef<str>>(
524 key_name: T,
525 vetkd_curve: u32,
526) -> Result<u128, SignCostError> {
527 let key_name = key_name.as_ref();
528 let (cost, code) = ic0::cost_vetkd_derive_key(key_name, vetkd_curve);
529 sign_cost_result(cost, code)
530}
531
532/// Gets the number of environment variables available in the canister.
533pub fn env_var_count() -> usize {
534 ic0::env_var_count()
535}
536
537/// Gets the size of the name of the environment variable at the given index.
538///
539/// # Panics
540///
541/// This function traps if:
542/// - The index is out of bounds (>= than value provided by [`env_var_count`])
543/// - The name is not valid UTF-8.
544pub fn env_var_name(index: usize) -> String {
545 let len = ic0::env_var_name_size(index);
546 let mut buf = vec![0u8; len];
547 ic0::env_var_name_copy(index, &mut buf, 0);
548 String::from_utf8(buf).expect("env_var_name is not valid UTF-8")
549}
550
551/// Checks if the environment variable with the given name exists.
552///
553/// # Panics
554///
555/// This function traps if the length of `name` exceeds `MAX_ENV_VAR_NAME_LENGTH`.
556pub fn env_var_name_exists<T: AsRef<str>>(name: T) -> bool {
557 match ic0::env_var_name_exists(name.as_ref()) {
558 0 => false,
559 1 => true,
560 n => panic!("unexpected return value from env_var_name_exists: {n}"),
561 }
562}
563
564/// Gets the value of the environment variable with the given name.
565///
566/// It's recommended to use [`env_var_name_exists`] to check if the variable exists before calling this function.
567///
568/// # Panics
569///
570/// This function traps if:
571/// - The length of `name` exceeds `MAX_ENV_VAR_NAME_LENGTH`.
572/// - The name does not match any existing environment variable.
573/// - The value is not valid UTF-8.
574pub fn env_var_value<T: AsRef<str>>(name: T) -> String {
575 let name = name.as_ref();
576 let len = ic0::env_var_value_size(name);
577 let mut buf = vec![0u8; len];
578 ic0::env_var_value_copy(name, &mut buf, 0);
579 String::from_utf8(buf).expect("env_var_value is not valid UTF-8")
580}
581
582/// Emits textual trace messages.
583///
584/// On the "real" network, these do not do anything.
585///
586/// When executing in an environment that supports debugging, this copies out the data
587/// and logs, prints or stores it in an environment-appropriate way.
588pub fn debug_print<T: AsRef<str>>(data: T) {
589 let buf = data.as_ref();
590 ic0::debug_print(buf.as_bytes());
591}
592
593/// Traps with the given message.
594///
595/// The environment may copy out the data and log, print or store it in an environment-appropriate way,
596/// or include it in system-generated reject messages where appropriate.
597pub fn trap<T: AsRef<str>>(data: T) -> ! {
598 let buf = data.as_ref();
599 ic0::trap(buf.as_bytes());
600}