From e662b54fcdc7a0aa57f73238f8091c8b95dc9182 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 21 Nov 2024 13:57:21 +0100 Subject: [PATCH] feat: load canister snaptots (#763) # Motivation We continue adding support for snapshots with [load_canister_snapshot](https://internetcomputer.org/docs/current/references/ic-interface-spec#ic-load_canister_snapshot) # Changes - Implement `loadCanisterSnapshot` --------- Signed-off-by: David Dal Busco Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- packages/ic-management/README.md | 18 +++++ .../src/ic-management.canister.spec.ts | 79 +++++++++++++++++++ .../src/ic-management.canister.ts | 32 ++++++++ 3 files changed, 129 insertions(+) diff --git a/packages/ic-management/README.md b/packages/ic-management/README.md index e933eb0a..3184e1e1 100644 --- a/packages/ic-management/README.md +++ b/packages/ic-management/README.md @@ -75,6 +75,7 @@ const { status, memory_size, ...rest } = await canisterStatus(YOUR_CANISTER_ID); - [fetchCanisterLogs](#gear-fetchcanisterlogs) - [takeCanisterSnapshot](#gear-takecanistersnapshot) - [listCanisterSnapshots](#gear-listcanistersnapshots) +- [loadCanisterSnapshot](#gear-loadcanistersnapshot) ##### :gear: create @@ -280,6 +281,23 @@ Parameters: [:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L405) +##### :gear: loadCanisterSnapshot + +Loads a snapshot of a canister's state. + +| Method | Type | +| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `loadCanisterSnapshot` | `({ canisterId, snapshotId, senderCanisterVersion, }: { canisterId: Principal; snapshotId: string or snapshot_id; senderCanisterVersion?: bigint or undefined; }) => Promise` | + +Parameters: + +- `params`: - Parameters for the snapshot loading operation. +- `params.canisterId`: - The ID of the canister for which the snapshot will be loaded. +- `params.snapshotId`: - The ID of the snapshot to load. +- `params.senderCanisterVersion`: - The optional sender canister version. If provided, its value must be equal to ic0.canister_version. + +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L431) + ## Resources diff --git a/packages/ic-management/src/ic-management.canister.spec.ts b/packages/ic-management/src/ic-management.canister.spec.ts index 469cf586..0b9f6b44 100644 --- a/packages/ic-management/src/ic-management.canister.spec.ts +++ b/packages/ic-management/src/ic-management.canister.spec.ts @@ -872,4 +872,83 @@ describe("ICManagementCanister", () => { await expect(call).rejects.toThrow(error); }); }); + + describe("loadCanisterSnapshot", () => { + it("should call load_canister_snapshot with Uint8Array snapshotId", async () => { + const service = mock(); + service.load_canister_snapshot.mockResolvedValue(undefined); + + const icManagement = await createICManagement(service); + + const params = { + canisterId: mockCanisterId, + snapshotId: Uint8Array.from([1, 2, 3, 4]), + }; + + await icManagement.loadCanisterSnapshot(params); + + expect(service.load_canister_snapshot).toHaveBeenCalledWith({ + canister_id: params.canisterId, + snapshot_id: params.snapshotId, + sender_canister_version: [], + }); + }); + + it("should call load_canister_snapshot with string snapshotId", async () => { + const service = mock(); + service.load_canister_snapshot.mockResolvedValue(undefined); + + const icManagement = await createICManagement(service); + + const params = { + canisterId: mockCanisterId, + snapshotId: "000000000000000201010000000000000001", + }; + + await icManagement.loadCanisterSnapshot(params); + + expect(service.load_canister_snapshot).toHaveBeenCalledWith({ + canister_id: params.canisterId, + snapshot_id: decodeSnapshotId(params.snapshotId), + sender_canister_version: [], + }); + }); + + it("should call load_canister_snapshot with senderCanisterVersion", async () => { + const service = mock(); + service.load_canister_snapshot.mockResolvedValue(undefined); + + const icManagement = await createICManagement(service); + + const params = { + canisterId: mockCanisterId, + snapshotId: Uint8Array.from([1, 2, 3, 4]), + senderCanisterVersion: 5n, + }; + + await icManagement.loadCanisterSnapshot(params); + + expect(service.load_canister_snapshot).toHaveBeenCalledWith({ + canister_id: params.canisterId, + snapshot_id: params.snapshotId, + sender_canister_version: [params.senderCanisterVersion], + }); + }); + + it("should throw an Error if load_canister_snapshot fails", async () => { + const error = new Error("Test"); + const service = mock(); + service.load_canister_snapshot.mockRejectedValue(error); + + const icManagement = await createICManagement(service); + + const call = () => + icManagement.loadCanisterSnapshot({ + canisterId: mockCanisterId, + snapshotId: Uint8Array.from([1, 2, 3, 4]), + }); + + await expect(call).rejects.toThrow(error); + }); + }); }); diff --git a/packages/ic-management/src/ic-management.canister.ts b/packages/ic-management/src/ic-management.canister.ts index 583a7899..78aeb4bf 100644 --- a/packages/ic-management/src/ic-management.canister.ts +++ b/packages/ic-management/src/ic-management.canister.ts @@ -413,4 +413,36 @@ export class ICManagementCanister { canister_id: canisterId, }); }; + + /** + * Loads a snapshot of a canister's state. + * + * @link https://internetcomputer.org/docs/current/references/ic-interface-spec#ic-load_canister_snapshot + * + * @param {Object} params - Parameters for the snapshot loading operation. + * @param {Principal} params.canisterId - The ID of the canister for which the snapshot will be loaded. + * @param {snapshot_id} params.snapshotId - The ID of the snapshot to load. + * @param {BigInt} [params.senderCanisterVersion] - The optional sender canister version. If provided, its value must be equal to ic0.canister_version. + * + * @returns {Promise} A promise that resolves when the snapshot is successfully loaded. + * + * @throws {Error} If the snapshot loading operation fails. + */ + loadCanisterSnapshot = async ({ + canisterId, + snapshotId, + senderCanisterVersion, + }: { + canisterId: Principal; + snapshotId: SnapshotIdText | snapshot_id; + senderCanisterVersion?: bigint; + }): Promise => { + const { load_canister_snapshot } = this.service; + + await load_canister_snapshot({ + canister_id: canisterId, + snapshot_id: mapSnapshotId(snapshotId), + sender_canister_version: toNullable(senderCanisterVersion), + }); + }; }