diff --git a/.github/workflows/release-candid-extractor.yml b/.github/workflows/release-candid-extractor.yml index e5ae44bfb..fdf73b069 100644 --- a/.github/workflows/release-candid-extractor.yml +++ b/.github/workflows/release-candid-extractor.yml @@ -20,10 +20,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ~/.cargo/bin/ diff --git a/Cargo.lock b/Cargo.lock index 93eb9d023..735cf6073 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1135,11 +1135,11 @@ dependencies = [ [[package]] name = "ic-cdk" -version = "0.15.0" +version = "0.16.0" dependencies = [ "anyhow", "candid", - "ic-cdk-macros 0.15.0", + "ic-cdk-macros 0.16.0", "ic0 0.23.0", "rstest", "serde", @@ -1166,7 +1166,7 @@ dependencies = [ "escargot", "futures", "hex", - "ic-cdk 0.15.0", + "ic-cdk 0.16.0", "ic-cdk-timers", "lazy_static", "pocket-ic", @@ -1190,7 +1190,7 @@ dependencies = [ [[package]] name = "ic-cdk-macros" -version = "0.15.0" +version = "0.16.0" dependencies = [ "candid", "proc-macro2", @@ -1202,10 +1202,10 @@ dependencies = [ [[package]] name = "ic-cdk-timers" -version = "0.9.0" +version = "0.10.0" dependencies = [ "futures", - "ic-cdk 0.15.0", + "ic-cdk 0.16.0", "ic0 0.23.0", "serde", "serde_bytes", @@ -1219,7 +1219,7 @@ dependencies = [ "bincode", "candid", "hex", - "ic-cdk 0.15.0", + "ic-cdk 0.16.0", "serde", "serde_bytes", "serde_cbor", @@ -1228,12 +1228,12 @@ dependencies = [ [[package]] name = "ic-ledger-types" -version = "0.12.0" +version = "0.13.0" dependencies = [ "candid", "crc32fast", "hex", - "ic-cdk 0.15.0", + "ic-cdk 0.16.0", "serde", "serde_bytes", "sha2", @@ -1530,6 +1530,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "miniz_oxide" version = "0.7.3" @@ -1541,13 +1551,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.11" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" dependencies = [ + "hermit-abi", "libc", "wasi", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1601,16 +1612,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "object" version = "0.32.2" @@ -1786,11 +1787,10 @@ checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "pocket-ic" -version = "3.1.0" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9765eeff77b8750cf6258eaeea237b96607cd770aa3d4003f021924192b7e4e" +checksum = "629f46b7ab8a8d2fee02220ef8e99ae552c7e220117efa1ce0882ff09c8fb038" dependencies = [ - "async-trait", "base64 0.13.1", "candid", "hex", @@ -1800,6 +1800,8 @@ dependencies = [ "serde", "serde_bytes", "serde_json", + "sha2", + "tokio", "tracing", "tracing-appender", "tracing-subscriber", @@ -1974,6 +1976,7 @@ dependencies = [ "js-sys", "log", "mime", + "mime_guess", "once_cell", "percent-encoding", "pin-project-lite", @@ -2209,9 +2212,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.201" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] @@ -2237,9 +2240,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.201" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", @@ -2332,6 +2335,15 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + [[package]] name = "siphasher" version = "0.3.11" @@ -2575,18 +2587,31 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.37.0" +version = "1.39.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", + "parking_lot", "pin-project-lite", + "signal-hook-registry", "socket2", - "windows-sys 0.48.0", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.61", ] [[package]] @@ -2810,6 +2835,15 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.15" diff --git a/Cargo.toml b/Cargo.toml index 1e745cc3f..aef827dde 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,8 +21,8 @@ opt-level = 'z' [workspace.dependencies] ic0 = { path = "src/ic0", version = "0.23.0" } -ic-cdk = { path = "src/ic-cdk", version = "0.15.0" } -ic-cdk-timers = { path = "src/ic-cdk-timers", version = "0.9.0" } +ic-cdk = { path = "src/ic-cdk", version = "0.16.0" } +ic-cdk-timers = { path = "src/ic-cdk-timers", version = "0.10.0" } candid = "0.10.9" candid_parser = "0.2.0-beta.3" diff --git a/README.md b/README.md index 6aff8e388..2e0ecadc7 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ This repo provides libraries and tools to facilitate developing canisters in Rus - [`ic0`](src/ic0): Internet Computer System API binding. - [`ic-cdk`](src/ic-cdk): -Internet Computer Canister Development Kit +Internet Computer Canister Development Kit. - [`ic-cdk-bindgen`](src/ic-cdk-bindgen): Generate Rust bindings from Candid to make inter-canister calls. - [`ic-cdk-macros`](src/ic-cdk-macros): @@ -53,8 +53,7 @@ crate-type = ["cdylib"] [dependencies] ic-cdk = "0.15" -# Only necessary if you want to define Candid data types -candid = "0.10" +candid = "0.10" # required if you want to define Candid data types ``` Then in Rust source code: diff --git a/e2e-tests/Cargo.toml b/e2e-tests/Cargo.toml index 2466e94a2..ac646bb3b 100644 --- a/e2e-tests/Cargo.toml +++ b/e2e-tests/Cargo.toml @@ -53,4 +53,4 @@ path = "canisters/chunk.rs" [dev-dependencies] hex.workspace = true -pocket-ic = "3" +pocket-ic = "4" diff --git a/e2e-tests/canisters/management_caller.rs b/e2e-tests/canisters/management_caller.rs index db662d6b4..6c9ee51ba 100644 --- a/e2e-tests/canisters/management_caller.rs +++ b/e2e-tests/canisters/management_caller.rs @@ -109,4 +109,72 @@ mod provisional { } } +mod snapshot { + use super::*; + use ic_cdk::api::management_canister::main::*; + + #[update] + async fn execute_snapshot_methods() { + let arg = CreateCanisterArgument::default(); + let canister_id = create_canister(arg, 2_000_000_000_000u128) + .await + .unwrap() + .0 + .canister_id; + + // Cannot take a snapshot of a canister that is empty. + // So we install a minimal wasm module. + let arg = InstallCodeArgument { + mode: CanisterInstallMode::Install, + canister_id, + // A minimal valid wasm module + // wat2wasm "(module)" + wasm_module: b"\x00asm\x01\x00\x00\x00".to_vec(), + arg: vec![], + }; + install_code(arg).await.unwrap(); + + let arg = TakeCanisterSnapshotArgs { + canister_id, + replace_snapshot: None, + }; + let snapshot = take_canister_snapshot(arg).await.unwrap().0; + + let arg = LoadCanisterSnapshotArgs { + canister_id, + snapshot_id: snapshot.id.clone(), + sender_canister_version: None, + }; + assert!(load_canister_snapshot(arg).await.is_ok()); + + let canister_id_record = CanisterIdRecord { canister_id }; + let snapshots = list_canister_snapshots(canister_id_record).await.unwrap().0; + assert_eq!(snapshots.len(), 1); + assert_eq!(snapshots[0].id, snapshot.id); + + let arg = DeleteCanisterSnapshotArgs { + canister_id, + snapshot_id: snapshot.id.clone(), + }; + assert!(delete_canister_snapshot(arg).await.is_ok()); + + let arg = CanisterInfoRequest { + canister_id, + num_requested_changes: Some(1), + }; + let canister_info_response = canister_info(arg).await.unwrap().0; + assert_eq!(canister_info_response.total_num_changes, 3); + assert_eq!(canister_info_response.recent_changes.len(), 1); + if let CanisterChange { + details: CanisterChangeDetails::LoadSnapshot(load_snapshot_record), + .. + } = &canister_info_response.recent_changes[0] + { + assert_eq!(load_snapshot_record.snapshot_id, snapshot.id); + } else { + panic!("Expected the most recent change to be LoadSnapshot"); + } + } +} + fn main() {} diff --git a/e2e-tests/tests/e2e.rs b/e2e-tests/tests/e2e.rs index 54a4f652c..57268ae5f 100644 --- a/e2e-tests/tests/e2e.rs +++ b/e2e-tests/tests/e2e.rs @@ -1,5 +1,6 @@ use std::time::Duration; use std::time::SystemTime; +use std::time::UNIX_EPOCH; use candid::utils::ArgumentDecoder; use candid::utils::ArgumentEncoder; @@ -13,6 +14,7 @@ use ic_cdk::api::management_canister::main::{ }; use ic_cdk_e2e_tests::cargo_build_canister; use pocket_ic::common::rest::RawEffectivePrincipal; +use pocket_ic::PocketIcBuilder; use pocket_ic::{call_candid_as, query_candid, CallError, ErrorCode, PocketIc, WasmResult}; use serde_bytes::ByteBuf; @@ -334,7 +336,15 @@ fn test_set_global_timers() { fn test_canister_info() { let pic = PocketIc::new(); let wasm = cargo_build_canister("canister_info"); - pic.set_time(SystemTime::UNIX_EPOCH); + // As of PocketIC server v5.0.0 and client v4.0.0, the first canister creation happens at (time0+4). + // Each operation advances the Pic by 2 nanos, except for the last operation which advances only by 1 nano. + let time0: u64 = pic + .get_time() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos() + .try_into() + .unwrap(); let canister_id = pic.create_canister(); pic.add_cycles(canister_id, INIT_CYCLES); pic.install_canister(canister_id, wasm, vec![], None); @@ -377,7 +387,7 @@ fn test_canister_info() { total_num_changes: 9, recent_changes: vec![ CanisterChange { - timestamp_nanos: 4, + timestamp_nanos: time0 + 4, canister_version: 0, origin: CanisterChangeOrigin::FromCanister(FromCanisterRecord { canister_id, @@ -388,7 +398,7 @@ fn test_canister_info() { }), }, CanisterChange { - timestamp_nanos: 6, + timestamp_nanos: time0 + 6, canister_version: 1, origin: CanisterChangeOrigin::FromCanister(FromCanisterRecord { canister_id, @@ -403,7 +413,7 @@ fn test_canister_info() { }), }, CanisterChange { - timestamp_nanos: 8, + timestamp_nanos: time0 + 8, canister_version: 2, origin: CanisterChangeOrigin::FromCanister(FromCanisterRecord { canister_id, @@ -412,7 +422,7 @@ fn test_canister_info() { details: CanisterChangeDetails::CodeUninstall, }, CanisterChange { - timestamp_nanos: 10, + timestamp_nanos: time0 + 10, canister_version: 3, origin: CanisterChangeOrigin::FromCanister(FromCanisterRecord { canister_id, @@ -427,7 +437,7 @@ fn test_canister_info() { }), }, CanisterChange { - timestamp_nanos: 12, + timestamp_nanos: time0 + 12, canister_version: 4, origin: CanisterChangeOrigin::FromCanister(FromCanisterRecord { canister_id, @@ -442,7 +452,7 @@ fn test_canister_info() { }), }, CanisterChange { - timestamp_nanos: 14, + timestamp_nanos: time0 + 14, canister_version: 5, origin: CanisterChangeOrigin::FromCanister(FromCanisterRecord { canister_id, @@ -457,7 +467,7 @@ fn test_canister_info() { }), }, CanisterChange { - timestamp_nanos: 16, + timestamp_nanos: time0 + 16, canister_version: 6, origin: CanisterChangeOrigin::FromCanister(FromCanisterRecord { canister_id, @@ -468,7 +478,7 @@ fn test_canister_info() { }), }, CanisterChange { - timestamp_nanos: 18, + timestamp_nanos: time0 + 18, canister_version: 7, origin: CanisterChangeOrigin::FromUser(FromUserRecord { user_id: Principal::anonymous(), @@ -476,7 +486,7 @@ fn test_canister_info() { details: CanisterChangeDetails::CodeUninstall, }, CanisterChange { - timestamp_nanos: 19, + timestamp_nanos: time0 + 19, canister_version: 8, origin: CanisterChangeOrigin::FromUser(FromUserRecord { user_id: Principal::anonymous(), @@ -543,6 +553,20 @@ fn test_call_management() { .expect("Error calling execute_provisional_methods"); } +#[test] +fn test_snapshot() { + let pic = PocketIcBuilder::new() + .with_application_subnet() + .with_nonmainnet_features(true) + .build(); + let wasm = cargo_build_canister("management_caller"); + let canister_id = pic.create_canister(); + pic.add_cycles(canister_id, 300_000_000_000_000_000_000_000_000u128); + pic.install_canister(canister_id, wasm, vec![], None); + let () = call_candid(&pic, canister_id, "execute_snapshot_methods", ()) + .expect("Error calling execute_snapshot_methods"); +} + #[test] fn test_chunk() { let pic = PocketIc::new(); diff --git a/examples/management_canister/src/caller/lib.rs b/examples/management_canister/src/caller/lib.rs index 901b0362e..2ac5d9b94 100644 --- a/examples/management_canister/src/caller/lib.rs +++ b/examples/management_canister/src/caller/lib.rs @@ -85,6 +85,40 @@ mod ecdsa { } } +mod schnorr { + use super::*; + use ic_cdk::api::management_canister::schnorr::*; + + #[update] + async fn execute_schnorr_methods() { + let key_id = SchnorrKeyId { + algorithm: SchnorrAlgorithm::Bip340secp256k1, + name: "dfx_test_key".to_string(), + }; + let derivation_path = vec![]; + let arg = SchnorrPublicKeyArgument { + canister_id: None, + derivation_path: derivation_path.clone(), + key_id: key_id.clone(), + }; + let SchnorrPublicKeyResponse { + public_key, + chain_code, + } = schnorr_public_key(arg).await.unwrap().0; + assert_eq!(public_key.len(), 33); + assert_eq!(chain_code.len(), 32); + + let message = "hello world".into(); + let arg = SignWithSchnorrArgument { + message, + derivation_path, + key_id, + }; + let SignWithSchnorrResponse { signature } = sign_with_schnorr(arg).await.unwrap().0; + assert_eq!(signature.len(), 64); + } +} + mod bitcoin { use super::*; use ic_cdk::api::{call::RejectionCode, management_canister::bitcoin::*}; diff --git a/examples/management_canister/tests/basic.bats b/examples/management_canister/tests/basic.bats index 8c51a7fa8..6abf4d195 100644 --- a/examples/management_canister/tests/basic.bats +++ b/examples/management_canister/tests/basic.bats @@ -24,6 +24,13 @@ teardown() { assert_success } +@test "schnorr methods succeed" { + dfx start --clean --background + dfx deploy + run dfx canister call caller execute_schnorr_methods + assert_success +} + @test "bitcoin methods succeed" { bitcoind -regtest -daemonwait diff --git a/library/ic-ledger-types/CHANGELOG.md b/library/ic-ledger-types/CHANGELOG.md index 1a0d69ad9..b5d8be669 100644 --- a/library/ic-ledger-types/CHANGELOG.md +++ b/library/ic-ledger-types/CHANGELOG.md @@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [unreleased] +## [0.13.0] - 2024-08-27 + +### Changed + +- Upgrade `ic-cdk` to v0.16. + ## [0.12.0] - 2024-07-01 ### Changed diff --git a/library/ic-ledger-types/Cargo.toml b/library/ic-ledger-types/Cargo.toml index 2e051ff20..2f4d483a0 100644 --- a/library/ic-ledger-types/Cargo.toml +++ b/library/ic-ledger-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ic-ledger-types" -version = "0.12.0" +version = "0.13.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/scripts/download_pocket_ic.sh b/scripts/download_pocket_ic.sh index 545dd8306..b0191645c 100755 --- a/scripts/download_pocket_ic.sh +++ b/scripts/download_pocket_ic.sh @@ -9,7 +9,7 @@ cd "$SCRIPTS_DIR/../e2e-tests" uname_sys=$(uname -s | tr '[:upper:]' '[:lower:]') echo "uname_sys: $uname_sys" -tag="release-2024-05-22_23-01-base" +tag="release-2024-08-02_01-30-base" curl -sL "https://github.com/dfinity/ic/releases/download/$tag/pocket-ic-x86_64-$uname_sys.gz" --output pocket-ic.gz gzip -df pocket-ic.gz diff --git a/src/ic-cdk-macros/CHANGELOG.md b/src/ic-cdk-macros/CHANGELOG.md index 86df405c8..8609dd37e 100644 --- a/src/ic-cdk-macros/CHANGELOG.md +++ b/src/ic-cdk-macros/CHANGELOG.md @@ -1,11 +1,16 @@ # Changelog + +This file will no longer be updated. + +Please check [`ic-cdk` changelog](../ic-cdk/CHANGELOG.md). + +--- + All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [unreleased] - ### Fixed - `cargo build` should no longer give a confusing linkage error on Linux. diff --git a/src/ic-cdk-macros/Cargo.toml b/src/ic-cdk-macros/Cargo.toml index 7be3dc13e..3cc05e464 100644 --- a/src/ic-cdk-macros/Cargo.toml +++ b/src/ic-cdk-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ic-cdk-macros" -version = "0.15.0" # sync with ic-cdk +version = "0.16.0" # sync with ic-cdk authors.workspace = true edition.workspace = true license.workspace = true diff --git a/src/ic-cdk-timers/CHANGELOG.md b/src/ic-cdk-timers/CHANGELOG.md index 743d4fd47..e1bb9e7b8 100644 --- a/src/ic-cdk-timers/CHANGELOG.md +++ b/src/ic-cdk-timers/CHANGELOG.md @@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [unreleased] +## [0.10.0] - 2024-08-27 + +### Changed + +- Upgrade `ic-cdk` to v0.16. + ## [0.9.0] - 2024-07-01 ### Changed diff --git a/src/ic-cdk-timers/Cargo.toml b/src/ic-cdk-timers/Cargo.toml index 1e0b95336..58da23362 100644 --- a/src/ic-cdk-timers/Cargo.toml +++ b/src/ic-cdk-timers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ic-cdk-timers" -version = "0.9.0" +version = "0.10.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/src/ic-cdk/CHANGELOG.md b/src/ic-cdk/CHANGELOG.md index 21850b17d..2c58efa9d 100644 --- a/src/ic-cdk/CHANGELOG.md +++ b/src/ic-cdk/CHANGELOG.md @@ -6,6 +6,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [unreleased] +### Changed + +- Add `AllowedViewers` variant to `LogVisibility` enum. (#512) + +### Added + +- Support Threshold Schnorr signing management canister API. (#518) + +## [0.16.0] - 2024-08-27 + +### Changed + +- BREAKING: Add the `LoadSnapshot` variant to `CanisterChangeDetails`. (#504) + +### Added + +- Support Canister State Snapshots. (#504) + - Add methods: `take_canister_snapshot`, `load_canister_snapshot`, `list_canister_snapshots`, `delete_canister_snapshot` + - Add types: `LoadSnapshotRecord`, `SnapshotId`, `Snapshot`, `TakeCanisterSnapshotArgs`, `LoadCanisterSnapshotArgs`, `DeleteCanisterSnapshotArgs` + ## [0.15.0] - 2024-07-01 ### Changed diff --git a/src/ic-cdk/Cargo.toml b/src/ic-cdk/Cargo.toml index bbda394fd..a4064e045 100644 --- a/src/ic-cdk/Cargo.toml +++ b/src/ic-cdk/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ic-cdk" -version = "0.15.0" # sync with ic-cdk-macros +version = "0.16.0" # sync with ic-cdk-macros authors.workspace = true edition.workspace = true license.workspace = true @@ -27,7 +27,7 @@ ic0.workspace = true # Dependents won't accidentaly upgrading ic-cdk-macros only but not ic-cdk. # ic-cdk-macros is a hidden dependency, re-exported by ic-cdk. # It should not be included by users direcly. -ic-cdk-macros = { path = "../ic-cdk-macros", version = "=0.15.0" } +ic-cdk-macros = { path = "../ic-cdk-macros", version = "=0.16.0" } serde.workspace = true serde_bytes.workspace = true slotmap = { workspace = true, optional = true } diff --git a/src/ic-cdk/README.md b/src/ic-cdk/README.md index 5fa3244e1..aaa8b802d 100644 --- a/src/ic-cdk/README.md +++ b/src/ic-cdk/README.md @@ -26,8 +26,7 @@ crate-type = ["cdylib"] [dependencies] ic-cdk = "0.15" -# Only necessary if you want to define Candid data types -candid = "0.10" +candid = "0.10" # required if you want to define Candid data types ``` Then in Rust source code: diff --git a/src/ic-cdk/src/api/call.rs b/src/ic-cdk/src/api/call.rs index ca6fbac36..2fd130210 100644 --- a/src/ic-cdk/src/api/call.rs +++ b/src/ic-cdk/src/api/call.rs @@ -74,7 +74,6 @@ impl> Future for CallFuture { fn poll(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll { let self_ref = Pin::into_inner(self); - let state_ptr = Weak::into_raw(Arc::downgrade(&self_ref.state)); let mut state = self_ref.state.write().unwrap(); if let Some(result) = state.result.take() { @@ -85,6 +84,7 @@ impl> Future for CallFuture { let method = &state.method; let args = state.arg.as_ref(); let payment = state.payment; + let state_ptr = Weak::into_raw(Arc::downgrade(&self_ref.state)); // SAFETY: // `callee`, being &[u8], is a readable sequence of bytes and therefore can be passed to ic0.call_new. // `method`, being &str, is a readable sequence of bytes and therefore can be passed to ic0.call_new. diff --git a/src/ic-cdk/src/api/management_canister/ecdsa/mod.rs b/src/ic-cdk/src/api/management_canister/ecdsa/mod.rs index edd19ae94..4be77769c 100644 --- a/src/ic-cdk/src/api/management_canister/ecdsa/mod.rs +++ b/src/ic-cdk/src/api/management_canister/ecdsa/mod.rs @@ -1,4 +1,4 @@ -//! The ECDSA API. +//! Threshold ECDSA signing API. use crate::api::call::{call, call_with_payment128, CallResult}; use candid::Principal; @@ -23,7 +23,7 @@ pub async fn ecdsa_public_key( /// /// This call requires cycles payment. /// This method handles the cycles cost under the hood. -/// Check [Gas and cycles cost](https://internetcomputer.org/docs/current/developer-docs/gas-cost) for more details. +/// Check [Threshold signatures](https://internetcomputer.org/docs/current/references/t-sigs-how-it-works) for more details. pub async fn sign_with_ecdsa(arg: SignWithEcdsaArgument) -> CallResult<(SignWithEcdsaResponse,)> { call_with_payment128( Principal::management_canister(), diff --git a/src/ic-cdk/src/api/management_canister/main/mod.rs b/src/ic-cdk/src/api/management_canister/main/mod.rs index d1cd9de3e..9f686c4f8 100644 --- a/src/ic-cdk/src/api/management_canister/main/mod.rs +++ b/src/ic-cdk/src/api/management_canister/main/mod.rs @@ -185,3 +185,57 @@ pub async fn raw_rand() -> CallResult<(Vec,)> { pub async fn canister_info(arg: CanisterInfoRequest) -> CallResult<(CanisterInfoResponse,)> { call(Principal::management_canister(), "canister_info", (arg,)).await } + +/// Take a snapshot of the specified canister. +/// +/// A snapshot consists of the wasm memory, stable memory, certified variables, wasm chunk store and wasm binary. +/// +/// See [IC method `take_canister_snapshot`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-take_canister_snapshot). +pub async fn take_canister_snapshot(arg: TakeCanisterSnapshotArgs) -> CallResult<(Snapshot,)> { + call( + Principal::management_canister(), + "take_canister_snapshot", + (arg,), + ) + .await +} + +/// Load a snapshot onto the canister. +/// +/// It fails if no snapshot with the specified `snapshot_id` can be found. +/// +/// See [IC method `load_canister_snapshot`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-load_canister_snapshot). +pub async fn load_canister_snapshot(arg: LoadCanisterSnapshotArgs) -> CallResult<()> { + call( + Principal::management_canister(), + "load_canister_snapshot", + (arg,), + ) + .await +} + +/// List the snapshots of the canister. +/// +/// See [IC method `list_canister_snapshots`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-list_canister_snapshots). +pub async fn list_canister_snapshots(arg: CanisterIdRecord) -> CallResult<(Vec,)> { + call( + Principal::management_canister(), + "list_canister_snapshots", + (arg,), + ) + .await +} + +/// Delete a specified snapshot that belongs to an existing canister. +/// +/// An error will be returned if the snapshot is not found. +/// +/// See [IC method `delete_canister_snapshot`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-delete_canister_snapshot). +pub async fn delete_canister_snapshot(arg: DeleteCanisterSnapshotArgs) -> CallResult<()> { + call( + Principal::management_canister(), + "delete_canister_snapshot", + (arg,), + ) + .await +} diff --git a/src/ic-cdk/src/api/management_canister/main/types.rs b/src/ic-cdk/src/api/management_canister/main/types.rs index 97c284fd3..ec68304d5 100644 --- a/src/ic-cdk/src/api/management_canister/main/types.rs +++ b/src/ic-cdk/src/api/management_canister/main/types.rs @@ -16,6 +16,9 @@ pub enum LogVisibility { #[serde(rename = "public")] /// Everyone is allowed to access the canister's logs. Public, + #[serde(rename = "allowed_viewers")] + /// Canister logs are visible to a set of principals. + AllowedViewers(Vec), } /// Canister settings. @@ -454,6 +457,19 @@ pub struct CodeDeploymentRecord { pub module_hash: Vec, } +/// Details about loading canister snapshot. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct LoadSnapshotRecord { + /// The version of the canister at the time that the snapshot was taken + pub canister_version: u64, + /// The ID of the snapshot that was loaded. + pub snapshot_id: SnapshotId, + /// The timestamp at which the snapshot was taken. + pub taken_at_timestamp: u64, +} + /// Details about updating canister controllers. #[derive( CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, @@ -477,6 +493,9 @@ pub enum CanisterChangeDetails { /// See [CodeDeploymentRecord]. #[serde(rename = "code_deployment")] CodeDeployment(CodeDeploymentRecord), + /// See [LoadSnapshotRecord]. + #[serde(rename = "load_snapshot")] + LoadSnapshot(LoadSnapshotRecord), /// See [ControllersChangeRecord]. #[serde(rename = "controllers_change")] ControllersChange(ControllersChangeRecord), @@ -526,3 +545,56 @@ pub struct CanisterInfoResponse { /// Controllers of the canister. pub controllers: Vec, } + +/// ID of a canister snapshot. +pub type SnapshotId = Vec; + +/// A snapshot of the state of the canister at a given point in time. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, +)] +pub struct Snapshot { + /// ID of the snapshot. + pub id: SnapshotId, + /// The timestamp at which the snapshot was taken. + pub taken_at_timestamp: u64, + /// The size of the snapshot in bytes. + pub total_size: u64, +} + +/// Argument type of [take_canister_snapshot](super::take_canister_snapshot). +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct TakeCanisterSnapshotArgs { + /// Principal of the canister. + pub canister_id: CanisterId, + /// An optional snapshot ID to be replaced by the new snapshot. + /// + /// The snapshot identified by the specified ID will be deleted once a new snapshot has been successfully created. + pub replace_snapshot: Option, +} + +/// Argument type of [load_canister_snapshot](super::load_canister_snapshot). +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct LoadCanisterSnapshotArgs { + /// Principal of the canister. + pub canister_id: CanisterId, + /// ID of the snapshot to be loaded. + pub snapshot_id: SnapshotId, + /// sender_canister_version must be set to ic_cdk::api::canister_version(). + pub sender_canister_version: Option, +} + +/// Argument type of [delete_canister_snapshot](super::delete_canister_snapshot). +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct DeleteCanisterSnapshotArgs { + /// Principal of the canister. + pub canister_id: CanisterId, + /// ID of the snapshot to be deleted. + pub snapshot_id: SnapshotId, +} diff --git a/src/ic-cdk/src/api/management_canister/mod.rs b/src/ic-cdk/src/api/management_canister/mod.rs index 85481ce5b..dd9c83a77 100644 --- a/src/ic-cdk/src/api/management_canister/mod.rs +++ b/src/ic-cdk/src/api/management_canister/mod.rs @@ -15,3 +15,4 @@ pub mod ecdsa; pub mod http_request; pub mod main; pub mod provisional; +pub mod schnorr; diff --git a/src/ic-cdk/src/api/management_canister/schnorr/mod.rs b/src/ic-cdk/src/api/management_canister/schnorr/mod.rs new file mode 100644 index 000000000..36e6fe15c --- /dev/null +++ b/src/ic-cdk/src/api/management_canister/schnorr/mod.rs @@ -0,0 +1,43 @@ +//! Threshold Schnorr signing API. + +use crate::api::call::{call, call_with_payment128, CallResult}; +use candid::Principal; + +mod types; +pub use types::*; + +// Source: https://internetcomputer.org/docs/current/references/t-sigs-how-it-works/#fees-for-the-t-schnorr-production-key +const SIGN_WITH_SCHNORR_FEE: u128 = 26_153_846_153; + +/// Return a SEC1 encoded Schnorr public key for the given canister using the given derivation path. +/// +/// See [IC method `schnorr_public_key`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-schnorr_public_key). +pub async fn schnorr_public_key( + arg: SchnorrPublicKeyArgument, +) -> CallResult<(SchnorrPublicKeyResponse,)> { + call( + Principal::management_canister(), + "schnorr_public_key", + (arg,), + ) + .await +} + +/// Return a new Schnorr signature of the given message that can be separately verified against a derived Schnorr public key. +/// +/// See [IC method `sign_with_schnorr`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-sign_with_schnorr). +/// +/// This call requires cycles payment. +/// This method handles the cycles cost under the hood. +/// Check [Threshold signatures](https://internetcomputer.org/docs/current/references/t-sigs-how-it-works) for more details. +pub async fn sign_with_schnorr( + arg: SignWithSchnorrArgument, +) -> CallResult<(SignWithSchnorrResponse,)> { + call_with_payment128( + Principal::management_canister(), + "sign_with_schnorr", + (arg,), + SIGN_WITH_SCHNORR_FEE, + ) + .await +} diff --git a/src/ic-cdk/src/api/management_canister/schnorr/types.rs b/src/ic-cdk/src/api/management_canister/schnorr/types.rs new file mode 100644 index 000000000..6760b9097 --- /dev/null +++ b/src/ic-cdk/src/api/management_canister/schnorr/types.rs @@ -0,0 +1,86 @@ +use candid::CandidType; +use serde::{Deserialize, Serialize}; + +use super::super::main::CanisterId; + +/// Argument Type of [schnorr_public_key](super::schnorr_public_key). +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, +)] +pub struct SchnorrPublicKeyArgument { + /// Canister id, default to the canister id of the caller if None. + pub canister_id: Option, + /// A vector of variable length byte strings. + pub derivation_path: Vec>, + /// See [SchnorrKeyId]. + pub key_id: SchnorrKeyId, +} + +/// Response Type of [schnorr_public_key](super::schnorr_public_key). +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, +)] +pub struct SchnorrPublicKeyResponse { + /// An Schnorr public key encoded in SEC1 compressed form. + pub public_key: Vec, + /// Can be used to deterministically derive child keys of the public_key. + pub chain_code: Vec, +} + +/// Argument Type of [sign_with_schnorr](super::sign_with_schnorr). +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, +)] +pub struct SignWithSchnorrArgument { + /// Message to be signed. + pub message: Vec, + /// A vector of variable length byte strings. + pub derivation_path: Vec>, + /// See [SchnorrKeyId]. + pub key_id: SchnorrKeyId, +} + +/// Response Type of [sign_with_schnorr](super::sign_with_schnorr). +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, +)] +pub struct SignWithSchnorrResponse { + /// The encoding of the signature depends on the key ID's algorithm. + pub signature: Vec, +} + +/// Schnorr KeyId. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, +)] +pub struct SchnorrKeyId { + /// See [SchnorrAlgorithm]. + pub algorithm: SchnorrAlgorithm, + /// Name. + pub name: String, +} + +/// Schnorr Algorithm. +#[derive( + CandidType, + Serialize, + Deserialize, + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Clone, + Copy, + Default, +)] +pub enum SchnorrAlgorithm { + /// BIP-340 secp256k1. + #[serde(rename = "bip340secp256k1")] + #[default] + Bip340secp256k1, + /// ed25519. + #[serde(rename = "ed25519")] + Ed25519, +} diff --git a/src/ic-cdk/src/macros.rs b/src/ic-cdk/src/macros.rs index 733be8117..8c3162875 100644 --- a/src/ic-cdk/src/macros.rs +++ b/src/ic-cdk/src/macros.rs @@ -177,12 +177,15 @@ pub use ic_cdk_macros::update; /// } /// ``` /// -/// The init function may accept an argument, if that argument is a `CandidType`: +/// The init function may accept an argument. +/// +/// The argument must implement the `CandidType` trait. +/// +/// And it should match the initialization parameters of the service constructor in the Candid interface. /// /// ```rust /// # use ic_cdk::init; /// # use candid::*; -/// /// #[derive(Clone, Debug, CandidType, Deserialize)] /// struct InitArg { /// foo: u8, @@ -197,6 +200,8 @@ pub use ic_cdk_macros::update; /// /// In this case, the argument will be read from `ic0.msg_arg_data_size/copy` and passed to the /// init function upon successful deserialization. +/// +/// /// Refer to the [`canister_init` Specification](https://internetcomputer.org/docs/current/references/ic-interface-spec/#system-api-init) for more information. pub use ic_cdk_macros::init; @@ -240,6 +245,31 @@ pub use ic_cdk_macros::pre_upgrade; /// # unimplemented!() /// } /// ``` +/// +/// The post_upgrade function may accept an argument. +/// +/// The argument must implement the `CandidType` trait. +/// +/// And it should match the initialization parameters of the service constructor in the Candid interface. +/// Therefore, the init function and the post_upgrade function should take the same argument type. +/// +/// ```rust +/// # use ic_cdk::post_upgrade; +/// # use candid::*; +/// #[derive(Clone, Debug, CandidType, Deserialize)] +/// struct InitArg { +/// foo: u8, +/// } +/// +/// #[post_upgrade] +/// fn post_upgrade_function(arg: InitArg) { +/// // ... +/// # unimplemented!() +/// } +/// ``` +/// +/// In this case, the argument will be read from `ic0.msg_arg_data_size/copy` and passed to the +/// post_upgrade function upon successful deserialization. pub use ic_cdk_macros::post_upgrade; /// Register the `canister_heartbeat` entry point of a canister.