Rust Canister Development Kit (CDK)

repository·main·Indexed 42 days ago

https://github.com/dfinity/cdk-rs

A collection of libraries and tools for developing WebAssembly-based canisters on the Internet Computer (IC) using Rust. Includes ic-cdk-bindgen for generating Rust bindings from Candid files, ic-cdk-bitcoin-canister for Bitcoin network interactions, ic-cdk-management-canister for IC management canister calls, and ic-cdk-executor for async execution contexts.

Tokens
21.8K
Snippets
67
Records
105
Agent score
53%

What's inside cdk-rs

  1. Overview of Rust CDK components

    main

    The repository contains several specialized libraries for Internet Computer development:

    • ic0: Bindings for the Internet Computer System API.
    • ic-cdk: The core Canister Development Kit.
    • ic-cdk-bindgen: Generates Rust bindings from Candid to enable inter-canister calls.
    • ic-cdk-macros: Provides attribute macros to expose functions as public interfaces.
    • ic-cdk-timers: Implements multiple and periodic timers.
    • ic-certified-map: A map implementation supporting certified queries.
    • ic-ledger-types: Type definitions for communicating with the ICP ledger canister.
  2. Use ic-cdk-management-canister to interact with the IC management canister

    main

    The ic-cdk-management-canister crate provides functions and type definitions for making calls to the Internet Computer (IC) management canister. It simplifies interactions by handling argument types and automatically calculating required cycle costs for many entry points.

    Key features include:

    • Type Definitions: Provides argument and result types for management canister entry points. For types that include a sender_canister_version field, this crate provides reduced versions where that field is omitted because it is set automatically by the canister.
    • Automated Cycle Calculation: Many functions automatically calculate and attach the necessary cycles required for the call. For HTTPS outcalls, use the HttpRequest or FlexibleHttpRequest builders.
    • Wait Strategies: The crate uses different inter-canister call strategies based on the method being called:
      • Unbounded-wait: Used by default for most functions because the management canister is universally trusted.
      • Bounded-wait: Used for methods that only read state (typically with a default 300s timeout).
  3. Key features of Certified Map

    main

    The ic-certified-map provides several optimizations for verifiable data structures on the Internet Computer:

    • Incremental certification: Allows storing thousands of entries while maintaining low certification costs.
    • Proofs of absence: The returned tree structure enables callers to verify that a requested key is indeed not present in the map.
    • Efficient Merkle proofs: The size overhead of the certificate is $O(\log N)$, where $N$ is the number of entries in the map.
  4. Use the ic0 System API bindings

    main

    ic0 provides safe Rust bindings for the Internet Computer (IC) System API, as defined in the IC interface specification. It serves as a safe wrapper around the underlying unsafe direct imports located in the ic0::sys module.

    Use ic0 to interact with core system functions provided by the Internet Computer environment.

  5. Interacting with Bitcoin canisters using ic-cdk-bitcoin-canister

    main

    The ic-cdk-bitcoin-canister crate provides high-level functionality for making inter-canister calls to Bitcoin canisters on the Internet Computer. It facilitates interactions with the Bitcoin network by providing functions and types that adhere to the Bitcoin Canisters Interface Specification.

    Key features include:

    • Automatic calculation of required cycle costs for calls.
    • Automatic determination of the correct Bitcoin canister ID based on the target network (e.g., Mainnet).
    • Re-exports types from the ic_btc_interface crate for ease of use.
  6. Use ic-ledger-types for ICP ledger canister communication

    main
    The ic-ledger-types library provides a collection of shared types specifically designed for communicating with the Internet Computer (ICP) ledger canister. Use this library to ensure your canister's data structures for ledger interactions (such as transfers, balances, and account identifiers) are compatible with the official ledger canister's interface.
  7. What is Certified Map and when to use it

    main

    The ic-certified-map package provides a specialized map implementation designed for Internet Computer canisters to implement certified queries.

    Use this when you need to return data from a canister query that a client can cryptographically verify as being part of the canister's state. It is particularly useful for providing verifiable proofs of both existence (for a given key) and absence (proving a key does not exist).

  8. Choose between Bounded-wait and Unbounded-wait for management canister calls

    main

    When calling the management canister, you can choose between two wait strategies. The crate selects a strategy by default, but you can override it using the ic_cdk::call::Call struct.

    • Unbounded-wait: The default for most functions. Use this when the management canister is trusted and the operation is expected to complete.
    • Bounded-wait: Used for state-reading methods. It includes a default timeout of 300s.

    If the default behavior of a crate function (like sign_with_ecdsa) does not suit your needs, you can manually construct a Call object to specify the wait type.

    use ic_cdk_management_canister::{cost_sign_with_ecdsa, SignCallError, SignWithEcdsaArgs, SignWithEcdsaResult};
    use ic_cdk::call::Call;
    use candid::Principal;
    
    async fn example() -> Result<SignWithEcdsaResult, SignCallError> {
        let callee = Principal::management_canister();
        let arg = SignWithEcdsaArgs::default();
        let cycles = cost_sign_with_ecdsa(&arg)?;
        // Manually using bounded_wait instead of the default unbounded-wait
        let res: SignWithEcdsaResult = Call::bounded_wait(callee, "sign_with_ecdsa")
            .with_arg(&arg)
            .with_cycles(cycles)
            .await?
            .candid()?;
        Ok(res)
    }
  9. Understanding Bounded-wait vs. Unbounded-wait for Bitcoin calls

    main

    When interacting with Bitcoin canisters, you can choose between two types of inter-canister calls:

    1. Bounded-wait: Most functions in this crate use bounded-wait calls because they are primarily used for reading state. This is the default behavior for most helper functions.
    2. Unbounded-wait: Required for functions that involve state changes or longer processing times. The bitcoin_send_transaction function is the only one in this crate that uses unbounded-wait by default.

    If you need to override the default behavior (e.g., performing an unbounded-wait call for a read operation like bitcoin_get_utxos), you can use the ic_cdk::call::Call struct directly.

    use ic_cdk_bitcoin_canister::{cost_get_utxos, get_bitcoin_canister_id, GetUtxosRequest, GetUtxosResponse, Network};
    use ic_cdk::call::Call;
    
    async fn example() -> ic_cdk::call::CallResult<GetUtxosResponse> {
        let arg = GetUtxosRequest {
            address: "bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq".to_string(),
            network: Network::Mainnet.into(),
            filter: None,
        };
        let canister_id = get_bitcoin_canister_id(Network::from(arg.network));
        let cycles = cost_get_utxos(&arg);
        let res: GetUtxosResponse = Call::unbounded_wait(canister_id, "bitcoin_get_utxos")
            .with_arg(&arg)
            .with_cycles(cycles)
            .await?
            .candid()?;
        Ok(res)
    }
  10. How Certified Map is implemented

    main

    The map is implemented using an augmented Red-Black binary search tree.

    To support certification, each node in the search tree is annotated with the root hash of a hash tree built from the subtree rooted at that node. When the tree undergoes rotations or modifications, these hashes are recomputed in $O(1)$ time, ensuring efficient updates while maintaining the integrity of the Merkle proofs.

  11. Manage data in stable memory

    main

    When a canister upgrades, data in standard heap memory is lost unless it is moved to stable memory.

    • For simple use cases, use the ic_cdk::storage::{stable_save, stable_restore} API.
    • For managing large datasets or multiple complex data structures efficiently, it is recommended to use the ic-stable-structures crate instead of the built-in storage API, as the latter may not be performant for large-scale data.
  12. Understand changes to spawn execution order

    main

    In ic-cdk 0.18, the execution order of spawn has changed. The code following a spawn call now executes before the spawned future's first await point.

    0.18 Execution Order:

    1. runs_first();
    2. spawn(async { runs_third().await; runs_fourth(); });
    3. runs_second();

    Warning: Ensure your code does not depend on the spawned future running before the code immediately following the spawn call. This primarily affects timers where code exists after the spawn call.