diff --git a/canisters/management/canister_info.ts b/canisters/management/canister_info.ts new file mode 100644 index 0000000000..ef82839764 --- /dev/null +++ b/canisters/management/canister_info.ts @@ -0,0 +1,137 @@ +// JS docs licensed under: +// +// - https://github.com/dfinity/cdk-rs/blob/main/LICENSE +// +// Some documentation changed from original work. + +import { + Record, + Opt, + Vec, + Principal, + Variant, + nat64, + nat8, + Null +} from '../../src/lib_functional'; +import { managementCanister } from '.'; + +/** Argument type of {@link managementCanister.canister_info}. */ +export const CanisterInfoArgs = Record({ + /** Principle of the canister. */ + canister_id: Principal, + /** + * Number of most recent changes requested to be retrieved from canister + * history. No changes are retrieved if this field is `None`. + */ + num_requested_changes: Opt(nat64) +}); + +/** Details about a canister creation. */ +export const CreationRecord = Record({ + /** Initial set of canister controllers. */ + controllers: Vec(Principal) +}); + +/** The mode with which a canister is installed. */ +export const CanisterInstallMode = Variant({ + /** A fresh install of a new canister. */ + install: Null, + /** Reinstalling a canister that was already installed. */ + reinstall: Null, + /** Upgrade an existing canister. */ + upgrade: Null +}); + +/** Details about a canister code deployment. */ +export const CodeDeploymentRecord = Record({ + /** See {@link CanisterInstallMode}. */ + mode: CanisterInstallMode, + /** A SHA256 hash of the new module installed on the canister. */ + module_hash: Vec(nat8) +}); + +/** Details about updating canister controllers. */ +export const ControllersChangeRecord = Record({ + /** The full new set of canister controllers. */ + controllers: Vec(Principal) +}); + +/** Provides details on the respective canister change. */ +export const CanisterChangeDetails = Variant({ + /** See {@link CreationRecord}. */ + creation: CreationRecord, + /** Uninstalling canister's module */ + code_uninstall: Null, + /** See {@link CodeDeploymentRecord}. */ + code_deployment: CodeDeploymentRecord, + /** See {@link ControllersChangeRecord}. */ + controllers_change: ControllersChangeRecord +}); + +/** Details about a canister change initiated by a user. */ +export const FromUserRecord = Record({ + /** Principle of the user. */ + user_id: Principal +}); + +/** + * Details about a canister change initiated by a canister (called *originator*). + */ +export const FromCanisterRecord = Record({ + /** Principle of the originator. */ + canister_id: Principal, + /** + * Canister version of the originator when the originator initiated the + * change. This is `None` if the original does not include its canister + * version in the field `sender_canister_version` of the management canister + * payload. + */ + canister_version: Opt(nat64) +}); + +/** Provides details on who initiated a canister change. */ +export const CanisterChangeOrigin = Variant({ + /** See {@link FromUserRecord}. */ + from_user: FromUserRecord, + /** See {@link FromCanisterRecord}. */ + from_canister: FromCanisterRecord +}); + +/** Represents a canister change as stored in the canister history. */ +export const CanisterChange = Record({ + /** + * The system timestamp (in nanoseconds since Unix Epoch) at which the + * change was performed + */ + timestamp_nanos: nat64, + /** The canister version after performing the change. */ + canister_version: nat64, + /** The change’s origin (a user or a canister). */ + origin: CanisterChangeOrigin, + /** The change’s details. */ + details: CanisterChangeDetails +}); + +/** Return type of {@link managementCanister.canister_info}. */ +export const CanisterInfoResult = Record({ + /** + * Total number of changes ever recorded in canister history. This might be + * higher than the number of canister changes in recent_changes because the + * IC might drop old canister changes from its history (with 20 most recent + * canister changes to always remain in the list). + */ + total_num_changes: nat64, + /** + * The canister changes stored in the order from the oldest to the most + * recent. + */ + recent_changes: Vec(CanisterChange), + /** + * A SHA256 hash of the module installed on the canister. This is null if + * the canister is empty. + */ + module_hash: Opt(Vec(nat8)), + /** Controllers of the canister. */ + controllers: Vec(Principal) +}); diff --git a/canisters/management/index.ts b/canisters/management/index.ts index 4202551d9c..33a44e0b82 100644 --- a/canisters/management/index.ts +++ b/canisters/management/index.ts @@ -1,3 +1,9 @@ +// Some JS docs licensed under: +// +// - https://github.com/dfinity/cdk-rs/blob/main/LICENSE +// +// Some documentation changed from original work. + import { blob, Canister, @@ -15,6 +21,7 @@ import { Satoshi, SendTransactionArgs } from './bitcoin'; +import { CanisterInfoArgs, CanisterInfoResult } from './canister_info'; import { CanisterStatusArgs, CanisterStatusResult, @@ -40,6 +47,7 @@ import { } from './t_ecdsa'; export * from './bitcoin'; +export * from './canister_info'; export * from './canister_management'; export * from './http_request'; export * from './t_ecdsa'; @@ -60,6 +68,8 @@ export const managementCanister = Canister({ uninstall_code: update([UninstallCodeArgs], Void), start_canister: update([StartCanisterArgs], Void), stop_canister: update([StopCanisterArgs], Void), + /** Get public information about the canister. */ + canister_info: update([CanisterInfoArgs], CanisterInfoResult), canister_status: update([CanisterStatusArgs], CanisterStatusResult), delete_canister: update([DeleteCanisterArgs], Void), deposit_cycles: update([DepositCyclesArgs], Void), diff --git a/examples/management_canister/src/index.did b/examples/management_canister/src/index.did index 1c11a82a54..ba311bf24e 100644 --- a/examples/management_canister/src/index.did +++ b/examples/management_canister/src/index.did @@ -7,6 +7,7 @@ service: () -> { executeStopCanister: (principal) -> (bool); executeUninstallCode: (principal) -> (bool); executeUpdateSettings: (principal) -> (bool); + getCanisterInfo: (record {canister_id:principal; num_requested_changes:opt nat64}) -> (record {controllers:vec principal; module_hash:opt vec nat8; recent_changes:vec record {timestamp_nanos:nat64; canister_version:nat64; origin:variant {from_user:record {user_id:principal}; from_canister:record {canister_version:opt nat64; canister_id:principal}}; details:variant {creation:record {controllers:vec principal}; code_deployment:record {mode:variant {reinstall; upgrade; install}; module_hash:vec nat8}; controllers_change:record {controllers:vec principal}; code_uninstall}}; total_num_changes:nat64}); getCanisterStatus: (record {canister_id:principal}) -> (record {status:variant {stopped; stopping; running}; memory_size:nat; cycles:nat; settings:record {freezing_threshold:nat; controllers:vec principal; memory_allocation:nat; compute_allocation:nat}; module_hash:opt vec nat8}); getCreatedCanisterId: () -> (principal) query; getRawRand: () -> (vec nat8); diff --git a/examples/management_canister/src/index.ts b/examples/management_canister/src/index.ts index 4e7027998c..f4680ad64c 100644 --- a/examples/management_canister/src/index.ts +++ b/examples/management_canister/src/index.ts @@ -13,6 +13,8 @@ import { update } from 'azle'; import { + CanisterInfoArgs, + CanisterInfoResult, CanisterStatusArgs, CanisterStatusResult, CreateCanisterResult, @@ -113,16 +115,22 @@ export default Canister({ return true; }), + getCanisterInfo: update( + [CanisterInfoArgs], + CanisterInfoResult, + async (args) => { + const result = await ic.call(managementCanister.canister_info, { + args: [args] + }); + return result; + } + ), getCanisterStatus: update( [CanisterStatusArgs], CanisterStatusResult, async (args) => { return await ic.call(managementCanister.canister_status, { - args: [ - { - canister_id: args.canister_id - } - ] + args: [args] }); } ), diff --git a/examples/management_canister/test/tests.ts b/examples/management_canister/test/tests.ts index 33a1c3b044..ad06213c6e 100644 --- a/examples/management_canister/test/tests.ts +++ b/examples/management_canister/test/tests.ts @@ -1,5 +1,5 @@ import { ActorSubclass } from '@dfinity/agent'; -import { ok, Test } from 'azle/test'; +import { Test } from 'azle/test'; import { _SERVICE } from './dfx_generated/management_canister/management_canister.did'; import { readFileSync } from 'fs'; @@ -192,6 +192,26 @@ export function getTests(managementCanister: ActorSubclass<_SERVICE>): Test[] { }; } }, + { + name: 'getCanisterInfo', + test: async () => { + const canisterId = + await managementCanister.getCreatedCanisterId(); + + const canisterInfo = await managementCanister.getCanisterInfo({ + canister_id: canisterId, + num_requested_changes: [50n] + }); + + return { + Ok: + canisterInfo.total_num_changes === 3n && + canisterInfo.recent_changes.length === 3 && + canisterInfo.module_hash.length === 0 && + canisterInfo.controllers.length === 1 + }; + } + }, { name: 'executeDeleteCanister', test: async () => { diff --git a/src/lib_new/ic.ts b/src/lib_new/ic.ts index 385d46f185..353bb5fdc6 100644 --- a/src/lib_new/ic.ts +++ b/src/lib_new/ic.ts @@ -929,9 +929,11 @@ export const ic: Ic = globalThis._azleIc replyRaw: () => {}, setCertifiedData: () => {}, stableBytes: () => {}, + stableGrow: () => {}, stableRead: () => {}, stableSize: () => {}, stableWrite: () => {}, + stable64Grow: () => {}, stable64Read: () => {}, stable64Size: () => {}, stable64Write: () => {},