From c385b688e269366e3fded59eceb2b3044a4a814d Mon Sep 17 00:00:00 2001 From: Kai Peacock Date: Thu, 8 Aug 2024 14:49:08 -0700 Subject: [PATCH] feat: adds management canister support for canister snapshots --- bin/update-management-idl.ts | 2 +- docs/CHANGELOG.md | 24 ++++--- packages/agent/src/canisters/management.did | 72 +++++++++++-------- .../agent/src/canisters/management_idl.ts | 69 ++++++++++-------- .../agent/src/canisters/management_service.ts | 55 +++++++++----- 5 files changed, 132 insertions(+), 90 deletions(-) diff --git a/bin/update-management-idl.ts b/bin/update-management-idl.ts index 9756a8f3..3435c6ae 100644 --- a/bin/update-management-idl.ts +++ b/bin/update-management-idl.ts @@ -3,7 +3,7 @@ import path from 'path'; const main = async () => { const res = await fetch( - 'https://raw.githubusercontent.com/dfinity/interface-spec/master/spec/_attachments/ic.did', + 'https://raw.githubusercontent.com/dfinity/interface-spec/a95de7ba72d180f878875ddcc3af88e23d01dd62/spec/_attachments/ic.did', ); res.text().then(async text => { const root = path.resolve(__dirname, '..'); diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index d4bb990e..a2a9b5d4 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -9,6 +9,8 @@ - docs: documentation and metadata for use-auth-client ### Changed + +- feat: adds management canister support for canister snapshots - feat: replaces hdkey and bip32 implementations with `@scure/bip39` and `@scure/bip32` due to vulnerability and lack of maintenance for `elliptic` - chore: bumps dev dependency versions to remove warnings - chore: addresses eslint errors uncovered by bumping eslint version @@ -189,8 +191,8 @@ const result = await management.bitcoin_get_balance_query({ ### Added * feat: uses expirable map for subnet keys in `agent-js`, with a timeout of 1 hour -* **feat!: node signature verification** -This feature includes additional changes in support of testing and releasing the feature: +* **feat!: node signature verification** +This feature includes additional changes in support of testing and releasing the feature: * Mainnet e2e tests for queries and calls * published counter canister * New `HttpAgent` option - `verifyQuerySignatures`. Defaults to true, but allows you to opt out of verification. Useful for testing against older replica versions @@ -322,7 +324,7 @@ Changes default stored key for `auth-client` to use ECDSAKey* Also updates the * _Breaking change:_ Moves `Secp256k1KeyIdentity` to its own package. `@dfinity/identity-secp256k1` * _Breaking change:_ Deprecates `@dfinity/authentication`. If you relied on the `isDelegationValid` check, it has been moved to `@dfinity/identity` - + * Deprecates `@dfinity/identity-ledgerhq`. Use `@zondax/ledger-icp` instead. * chore: links assets docs in index * chore: sets up new size-limit job for packages, in preparation for CI @@ -415,9 +417,9 @@ Changes default stored key for `auth-client` to use ECDSAKey* Also updates the ### Changed * Adds a default callback to the `IdleManager` that will refresh the page after clearing the storage -* Adds a new utility method, `canisterStatus`, to `agent-js`. Canister status now allows you to query paths from the canister certificate with a simple interface, using the API from the[interface specification](https://internetcomputer.org/docs/current/references/ic-interface-spec#state-tree-canister-information) +* Adds a new utility method, `canisterStatus`, to `agent-js`. Canister status now allows you to query paths from the canister certificate with a simple interface, using the API from the[interface specification](https://internetcomputer.org/docs/current/references/ic-interface-spec#state-tree-canister-information) Comes with nicely configured options for - + `time`, `controllers`, `subnet`, `module_hash`, `candid`. Additionally, has a utility for reading custom MetaData set using [ic-wasm](https://github.com/dfinity/ic-wasm), as well as generic custom paths in the format of ArrayBuffers. * updates to package.json files for metadata in npm @@ -437,9 +439,9 @@ Changes default stored key for `auth-client` to use ECDSAKey* Also updates the * Versioning tool now sets patch version to 0 for minor version updates, or patch and minor versions to 0 for major version updates * Removes jest-expect-message, which was making test error messages less useful * `HttpAgent` now generates a nonce to ensure that calls are unique by default. If you want to opt out or provide your own nonce logic, you can now pass an option of `disableNonce: true`during the agent initialization. - + If you are currently using `agent.addTransform(makeNonceTransform())` , please note that you should remove that logic, or add the `disableNonce` option to your agent when upgrading. - + ## [0.10.3] @@ -454,13 +456,13 @@ Changes default stored key for `auth-client` to use ECDSAKey* Also updates the ### Changed * Security enhancement - adds a rewrite for urls to subdomains of - + `\*.ic0.app/api` - + to - + `ic0.app/api` - + * Improves error messages for when `HttpAgent` cannot infer `fetch` implementation ## [0.10.1] diff --git a/packages/agent/src/canisters/management.did b/packages/agent/src/canisters/management.did index d3acb2af..c3d023fc 100644 --- a/packages/agent/src/canisters/management.did +++ b/packages/agent/src/canisters/management.did @@ -45,6 +45,10 @@ type change_details = variant { mode : variant { install; reinstall; upgrade }; module_hash : blob; }; + load_snapshot : record { + canister_version : nat64; + taken_at_timestamp : nat64; + }; controllers_change : record { controllers : vec principal; }; @@ -76,11 +80,6 @@ type ecdsa_curve = variant { secp256k1; }; -type schnorr_algorithm = variant { - bip340secp256k1; - ed25519; -}; - type satoshi = nat64; type bitcoin_network = variant { @@ -286,27 +285,6 @@ type sign_with_ecdsa_result = record { signature : blob; }; -type schnorr_public_key_args = record { - canister_id : opt canister_id; - derivation_path : vec blob; - key_id : record { algorithm : schnorr_algorithm; name : text }; -}; - -type schnorr_public_key_result = record { - public_key : blob; - chain_code : blob; -}; - -type sign_with_schnorr_args = record { - message : blob; - derivation_path : vec blob; - key_id : record { algorithm : schnorr_algorithm; name : text }; -}; - -type sign_with_schnorr_result = record { - signature : blob; -}; - type node_metrics_history_args = record { subnet_id : principal; start_at_timestamp_nanos : nat64; @@ -343,6 +321,38 @@ type bitcoin_get_balance_result = satoshi; type bitcoin_get_current_fee_percentiles_result = vec millisatoshi_per_byte; +type snapshot_id = blob; + +type snapshot = record { + id : snapshot_id; + taken_at_timestamp : nat64; + total_size : nat64; +}; + +type take_canister_snapshot_args = record { + canister_id : canister_id; + replace_snapshot : opt snapshot_id; +}; + +type take_canister_snapshot_result = snapshot; + +type load_canister_snapshot_args = record { + canister_id : canister_id; + snapshot_id : snapshot_id; + sender_canister_version : opt nat64; +}; + +type list_canister_snapshots_args = record { + canister_id : canister_id; +}; + +type list_canister_snapshots_result = vec snapshot; + +type delete_canister_snapshot_args = record { + canister_id : canister_id; + snapshot_id : snapshot_id; +}; + type fetch_canister_logs_args = record { canister_id : canister_id; }; @@ -379,10 +389,6 @@ service ic : { ecdsa_public_key : (ecdsa_public_key_args) -> (ecdsa_public_key_result); sign_with_ecdsa : (sign_with_ecdsa_args) -> (sign_with_ecdsa_result); - // Threshold Schnorr signature - schnorr_public_key : (schnorr_public_key_args) -> (schnorr_public_key_result); - sign_with_schnorr : (sign_with_schnorr_args) -> (sign_with_schnorr_result); - // bitcoin interface bitcoin_get_balance : (bitcoin_get_balance_args) -> (bitcoin_get_balance_result); @@ -398,6 +404,12 @@ service ic : { provisional_create_canister_with_cycles : (provisional_create_canister_with_cycles_args) -> (provisional_create_canister_with_cycles_result); provisional_top_up_canister : (provisional_top_up_canister_args) -> (); + // Canister snapshots + take_canister_snapshot : (take_canister_snapshot_args) -> (take_canister_snapshot_result); + load_canister_snapshot : (load_canister_snapshot_args) -> (); + list_canister_snapshots : (list_canister_snapshots_args) -> (list_canister_snapshots_result); + delete_canister_snapshot : (delete_canister_snapshot_args) -> (); + // canister logging fetch_canister_logs : (fetch_canister_logs_args) -> (fetch_canister_logs_result) query; }; diff --git a/packages/agent/src/canisters/management_idl.ts b/packages/agent/src/canisters/management_idl.ts index 07f15d1d..bbc158f6 100644 --- a/packages/agent/src/canisters/management_idl.ts +++ b/packages/agent/src/canisters/management_idl.ts @@ -75,6 +75,10 @@ export default ({ IDL }) => { }), module_hash: IDL.Vec(IDL.Nat8), }), + load_snapshot: IDL.Record({ + canister_version: IDL.Nat64, + taken_at_timestamp: IDL.Nat64, + }), controllers_change: IDL.Record({ controllers: IDL.Vec(IDL.Principal), }), @@ -141,6 +145,11 @@ export default ({ IDL }) => { }); const create_canister_result = IDL.Record({ canister_id: canister_id }); const delete_canister_args = IDL.Record({ canister_id: canister_id }); + const snapshot_id = IDL.Vec(IDL.Nat8); + const delete_canister_snapshot_args = IDL.Record({ + canister_id: canister_id, + snapshot_id: snapshot_id, + }); const deposit_cycles_args = IDL.Record({ canister_id: canister_id }); const ecdsa_curve = IDL.Variant({ secp256k1: IDL.Null }); const ecdsa_public_key_args = IDL.Record({ @@ -221,6 +230,20 @@ export default ({ IDL }) => { canister_id: canister_id, sender_canister_version: IDL.Opt(IDL.Nat64), }); + const list_canister_snapshots_args = IDL.Record({ + canister_id: canister_id, + }); + const snapshot = IDL.Record({ + id: snapshot_id, + total_size: IDL.Nat64, + taken_at_timestamp: IDL.Nat64, + }); + const list_canister_snapshots_result = IDL.Vec(snapshot); + const load_canister_snapshot_args = IDL.Record({ + canister_id: canister_id, + sender_canister_version: IDL.Opt(IDL.Nat64), + snapshot_id: snapshot_id, + }); const node_metrics_history_args = IDL.Record({ start_at_timestamp_nanos: IDL.Nat64, subnet_id: IDL.Principal, @@ -250,22 +273,6 @@ export default ({ IDL }) => { amount: IDL.Nat, }); const raw_rand_result = IDL.Vec(IDL.Nat8); - const schnorr_algorithm = IDL.Variant({ - ed25519: IDL.Null, - bip340secp256k1: IDL.Null, - }); - const schnorr_public_key_args = IDL.Record({ - key_id: IDL.Record({ - algorithm: schnorr_algorithm, - name: IDL.Text, - }), - canister_id: IDL.Opt(canister_id), - derivation_path: IDL.Vec(IDL.Vec(IDL.Nat8)), - }); - const schnorr_public_key_result = IDL.Record({ - public_key: IDL.Vec(IDL.Nat8), - chain_code: IDL.Vec(IDL.Nat8), - }); const sign_with_ecdsa_args = IDL.Record({ key_id: IDL.Record({ name: IDL.Text, curve: ecdsa_curve }), derivation_path: IDL.Vec(IDL.Vec(IDL.Nat8)), @@ -274,21 +281,15 @@ export default ({ IDL }) => { const sign_with_ecdsa_result = IDL.Record({ signature: IDL.Vec(IDL.Nat8), }); - const sign_with_schnorr_args = IDL.Record({ - key_id: IDL.Record({ - algorithm: schnorr_algorithm, - name: IDL.Text, - }), - derivation_path: IDL.Vec(IDL.Vec(IDL.Nat8)), - message: IDL.Vec(IDL.Nat8), - }); - const sign_with_schnorr_result = IDL.Record({ - signature: IDL.Vec(IDL.Nat8), - }); const start_canister_args = IDL.Record({ canister_id: canister_id }); const stop_canister_args = IDL.Record({ canister_id: canister_id }); const stored_chunks_args = IDL.Record({ canister_id: canister_id }); const stored_chunks_result = IDL.Vec(chunk_hash); + const take_canister_snapshot_args = IDL.Record({ + replace_snapshot: IDL.Opt(snapshot_id), + canister_id: canister_id, + }); + const take_canister_snapshot_result = snapshot; const uninstall_code_args = IDL.Record({ canister_id: canister_id, sender_canister_version: IDL.Opt(IDL.Nat64), @@ -317,6 +318,7 @@ export default ({ IDL }) => { clear_chunk_store: IDL.Func([clear_chunk_store_args], [], []), create_canister: IDL.Func([create_canister_args], [create_canister_result], []), delete_canister: IDL.Func([delete_canister_args], [], []), + delete_canister_snapshot: IDL.Func([delete_canister_snapshot_args], [], []), deposit_cycles: IDL.Func([deposit_cycles_args], [], []), ecdsa_public_key: IDL.Func([ecdsa_public_key_args], [ecdsa_public_key_result], []), fetch_canister_logs: IDL.Func( @@ -327,6 +329,12 @@ export default ({ IDL }) => { http_request: IDL.Func([http_request_args], [http_request_result], []), install_chunked_code: IDL.Func([install_chunked_code_args], [], []), install_code: IDL.Func([install_code_args], [], []), + list_canister_snapshots: IDL.Func( + [list_canister_snapshots_args], + [list_canister_snapshots_result], + [], + ), + load_canister_snapshot: IDL.Func([load_canister_snapshot_args], [], []), node_metrics_history: IDL.Func([node_metrics_history_args], [node_metrics_history_result], []), provisional_create_canister_with_cycles: IDL.Func( [provisional_create_canister_with_cycles_args], @@ -335,12 +343,15 @@ export default ({ IDL }) => { ), provisional_top_up_canister: IDL.Func([provisional_top_up_canister_args], [], []), raw_rand: IDL.Func([], [raw_rand_result], []), - schnorr_public_key: IDL.Func([schnorr_public_key_args], [schnorr_public_key_result], []), sign_with_ecdsa: IDL.Func([sign_with_ecdsa_args], [sign_with_ecdsa_result], []), - sign_with_schnorr: IDL.Func([sign_with_schnorr_args], [sign_with_schnorr_result], []), start_canister: IDL.Func([start_canister_args], [], []), stop_canister: IDL.Func([stop_canister_args], [], []), stored_chunks: IDL.Func([stored_chunks_args], [stored_chunks_result], []), + take_canister_snapshot: IDL.Func( + [take_canister_snapshot_args], + [take_canister_snapshot_result], + [], + ), uninstall_code: IDL.Func([uninstall_code_args], [], []), update_settings: IDL.Func([update_settings_args], [], []), upload_chunk: IDL.Func([upload_chunk_args], [upload_chunk_result], []), diff --git a/packages/agent/src/canisters/management_service.ts b/packages/agent/src/canisters/management_service.ts index fa4ed243..7f99117d 100644 --- a/packages/agent/src/canisters/management_service.ts +++ b/packages/agent/src/canisters/management_service.ts @@ -109,6 +109,12 @@ export type change_details = module_hash: Uint8Array | number[]; }; } + | { + load_snapshot: { + canister_version: bigint; + taken_at_timestamp: bigint; + }; + } | { controllers_change: { controllers: Array } } | { code_uninstall: null }; export type change_origin = @@ -144,6 +150,10 @@ export interface definite_canister_settings { export interface delete_canister_args { canister_id: canister_id; } +export interface delete_canister_snapshot_args { + canister_id: canister_id; + snapshot_id: snapshot_id; +} export interface deposit_cycles_args { canister_id: canister_id; } @@ -196,6 +206,15 @@ export interface install_code_args { canister_id: canister_id; sender_canister_version: [] | [bigint]; } +export interface list_canister_snapshots_args { + canister_id: canister_id; +} +export type list_canister_snapshots_result = Array; +export interface load_canister_snapshot_args { + canister_id: canister_id; + sender_canister_version: [] | [bigint]; + snapshot_id: snapshot_id; +} export type log_visibility = { controllers: null } | { public: null }; export type millisatoshi_per_byte = bigint; export interface node_metrics { @@ -230,16 +249,6 @@ export interface provisional_top_up_canister_args { } export type raw_rand_result = Uint8Array | number[]; export type satoshi = bigint; -export type schnorr_algorithm = { ed25519: null } | { bip340secp256k1: null }; -export interface schnorr_public_key_args { - key_id: { algorithm: schnorr_algorithm; name: string }; - canister_id: [] | [canister_id]; - derivation_path: Array; -} -export interface schnorr_public_key_result { - public_key: Uint8Array | number[]; - chain_code: Uint8Array | number[]; -} export interface sign_with_ecdsa_args { key_id: { name: string; curve: ecdsa_curve }; derivation_path: Array; @@ -248,14 +257,12 @@ export interface sign_with_ecdsa_args { export interface sign_with_ecdsa_result { signature: Uint8Array | number[]; } -export interface sign_with_schnorr_args { - key_id: { algorithm: schnorr_algorithm; name: string }; - derivation_path: Array; - message: Uint8Array | number[]; -} -export interface sign_with_schnorr_result { - signature: Uint8Array | number[]; +export interface snapshot { + id: snapshot_id; + total_size: bigint; + taken_at_timestamp: bigint; } +export type snapshot_id = Uint8Array | number[]; export interface start_canister_args { canister_id: canister_id; } @@ -266,6 +273,11 @@ export interface stored_chunks_args { canister_id: canister_id; } export type stored_chunks_result = Array; +export interface take_canister_snapshot_args { + replace_snapshot: [] | [snapshot_id]; + canister_id: canister_id; +} +export type take_canister_snapshot_result = snapshot; export interface uninstall_code_args { canister_id: canister_id; sender_canister_version: [] | [bigint]; @@ -299,12 +311,18 @@ export default interface _SERVICE { clear_chunk_store: ActorMethod<[clear_chunk_store_args], undefined>; create_canister: ActorMethod<[create_canister_args], create_canister_result>; delete_canister: ActorMethod<[delete_canister_args], undefined>; + delete_canister_snapshot: ActorMethod<[delete_canister_snapshot_args], undefined>; deposit_cycles: ActorMethod<[deposit_cycles_args], undefined>; ecdsa_public_key: ActorMethod<[ecdsa_public_key_args], ecdsa_public_key_result>; fetch_canister_logs: ActorMethod<[fetch_canister_logs_args], fetch_canister_logs_result>; http_request: ActorMethod<[http_request_args], http_request_result>; install_chunked_code: ActorMethod<[install_chunked_code_args], undefined>; install_code: ActorMethod<[install_code_args], undefined>; + list_canister_snapshots: ActorMethod< + [list_canister_snapshots_args], + list_canister_snapshots_result + >; + load_canister_snapshot: ActorMethod<[load_canister_snapshot_args], undefined>; node_metrics_history: ActorMethod<[node_metrics_history_args], node_metrics_history_result>; provisional_create_canister_with_cycles: ActorMethod< [provisional_create_canister_with_cycles_args], @@ -312,12 +330,11 @@ export default interface _SERVICE { >; provisional_top_up_canister: ActorMethod<[provisional_top_up_canister_args], undefined>; raw_rand: ActorMethod<[], raw_rand_result>; - schnorr_public_key: ActorMethod<[schnorr_public_key_args], schnorr_public_key_result>; sign_with_ecdsa: ActorMethod<[sign_with_ecdsa_args], sign_with_ecdsa_result>; - sign_with_schnorr: ActorMethod<[sign_with_schnorr_args], sign_with_schnorr_result>; start_canister: ActorMethod<[start_canister_args], undefined>; stop_canister: ActorMethod<[stop_canister_args], undefined>; stored_chunks: ActorMethod<[stored_chunks_args], stored_chunks_result>; + take_canister_snapshot: ActorMethod<[take_canister_snapshot_args], take_canister_snapshot_result>; uninstall_code: ActorMethod<[uninstall_code_args], undefined>; update_settings: ActorMethod<[update_settings_args], undefined>; upload_chunk: ActorMethod<[upload_chunk_args], upload_chunk_result>;