diff --git a/packages/ic-management/candid/ic-management.certified.idl.js b/packages/ic-management/candid/ic-management.certified.idl.js index 9677527e..2b270bf6 100644 --- a/packages/ic-management/candid/ic-management.certified.idl.js +++ b/packages/ic-management/candid/ic-management.certified.idl.js @@ -111,10 +111,15 @@ export const idlFactory = ({ IDL }) => { 'total_num_changes' : IDL.Nat64, }); const canister_status_args = IDL.Record({ 'canister_id' : canister_id }); + const log_visibility = IDL.Variant({ + 'controllers' : IDL.Null, + 'public' : IDL.Null, + }); const definite_canister_settings = IDL.Record({ 'freezing_threshold' : IDL.Nat, 'controllers' : IDL.Vec(IDL.Principal), 'reserved_cycles_limit' : IDL.Nat, + 'log_visibility' : log_visibility, 'memory_allocation' : IDL.Nat, 'compute_allocation' : IDL.Nat, }); @@ -142,6 +147,7 @@ export const idlFactory = ({ IDL }) => { 'freezing_threshold' : IDL.Opt(IDL.Nat), 'controllers' : IDL.Opt(IDL.Vec(IDL.Principal)), 'reserved_cycles_limit' : IDL.Opt(IDL.Nat), + 'log_visibility' : IDL.Opt(log_visibility), 'memory_allocation' : IDL.Opt(IDL.Nat), 'compute_allocation' : IDL.Opt(IDL.Nat), }); @@ -162,6 +168,15 @@ export const idlFactory = ({ IDL }) => { 'public_key' : IDL.Vec(IDL.Nat8), 'chain_code' : IDL.Vec(IDL.Nat8), }); + const fetch_canister_logs_args = IDL.Record({ 'canister_id' : canister_id }); + const canister_log_record = IDL.Record({ + 'idx' : IDL.Nat64, + 'timestamp_nanos' : IDL.Nat64, + 'content' : IDL.Vec(IDL.Nat8), + }); + const fetch_canister_logs_result = IDL.Record({ + 'canister_log_records' : IDL.Vec(canister_log_record), + }); const http_header = IDL.Record({ 'value' : IDL.Text, 'name' : IDL.Text }); const http_request_result = IDL.Record({ 'status' : IDL.Nat, @@ -333,6 +348,11 @@ export const idlFactory = ({ IDL }) => { [ecdsa_public_key_result], [], ), + 'fetch_canister_logs' : IDL.Func( + [fetch_canister_logs_args], + [fetch_canister_logs_result], + [], + ), '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], [], []), diff --git a/packages/ic-management/candid/ic-management.d.ts b/packages/ic-management/candid/ic-management.d.ts index 87c2cd5f..61cdf66c 100644 --- a/packages/ic-management/candid/ic-management.d.ts +++ b/packages/ic-management/candid/ic-management.d.ts @@ -79,10 +79,16 @@ export type canister_install_mode = ]; } | { install: null }; +export interface canister_log_record { + idx: bigint; + timestamp_nanos: bigint; + content: Uint8Array | number[]; +} export interface canister_settings { freezing_threshold: [] | [bigint]; controllers: [] | [Array]; reserved_cycles_limit: [] | [bigint]; + log_visibility: [] | [log_visibility]; memory_allocation: [] | [bigint]; compute_allocation: [] | [bigint]; } @@ -147,6 +153,7 @@ export interface definite_canister_settings { freezing_threshold: bigint; controllers: Array; reserved_cycles_limit: bigint; + log_visibility: log_visibility; memory_allocation: bigint; compute_allocation: bigint; } @@ -166,6 +173,12 @@ export interface ecdsa_public_key_result { public_key: Uint8Array | number[]; chain_code: Uint8Array | number[]; } +export interface fetch_canister_logs_args { + canister_id: canister_id; +} +export interface fetch_canister_logs_result { + canister_log_records: Array; +} export interface http_header { value: string; name: string; @@ -201,6 +214,7 @@ export interface install_code_args { canister_id: canister_id; sender_canister_version: [] | [bigint]; } +export type log_visibility = { controllers: null } | { public: null }; export type millisatoshi_per_byte = bigint; export interface node_metrics { num_block_failures_total: bigint; @@ -307,6 +321,10 @@ export interface _SERVICE { [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>; diff --git a/packages/ic-management/candid/ic-management.did b/packages/ic-management/candid/ic-management.did index 79305759..3b634a68 100644 --- a/packages/ic-management/candid/ic-management.did +++ b/packages/ic-management/candid/ic-management.did @@ -1,12 +1,18 @@ type canister_id = principal; type wasm_module = blob; +type log_visibility = variant { + controllers; + public; +}; + type canister_settings = record { controllers : opt vec principal; compute_allocation : opt nat; memory_allocation : opt nat; freezing_threshold : opt nat; reserved_cycles_limit : opt nat; + log_visibility : opt log_visibility; }; type definite_canister_settings = record { @@ -15,6 +21,7 @@ type definite_canister_settings = record { memory_allocation : nat; freezing_threshold : nat; reserved_cycles_limit : nat; + log_visibility : log_visibility; }; type change_origin = variant { @@ -332,6 +339,20 @@ type bitcoin_get_balance_query_result = satoshi; type bitcoin_get_current_fee_percentiles_result = vec millisatoshi_per_byte; +type fetch_canister_logs_args = record { + canister_id : canister_id; +}; + +type canister_log_record = record { + idx: nat64; + timestamp_nanos: nat64; + content: blob; +}; + +type fetch_canister_logs_result = record { + canister_log_records: vec canister_log_record; +}; + service ic : { create_canister : (create_canister_args) -> (create_canister_result); update_settings : (update_settings_args) -> (); @@ -368,4 +389,7 @@ service ic : { // provisional interfaces for the pre-ledger world 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 logging + fetch_canister_logs : (fetch_canister_logs_args) -> (fetch_canister_logs_result) query; }; diff --git a/packages/ic-management/candid/ic-management.idl.js b/packages/ic-management/candid/ic-management.idl.js index 7ecba891..58f3ac59 100644 --- a/packages/ic-management/candid/ic-management.idl.js +++ b/packages/ic-management/candid/ic-management.idl.js @@ -111,10 +111,15 @@ export const idlFactory = ({ IDL }) => { 'total_num_changes' : IDL.Nat64, }); const canister_status_args = IDL.Record({ 'canister_id' : canister_id }); + const log_visibility = IDL.Variant({ + 'controllers' : IDL.Null, + 'public' : IDL.Null, + }); const definite_canister_settings = IDL.Record({ 'freezing_threshold' : IDL.Nat, 'controllers' : IDL.Vec(IDL.Principal), 'reserved_cycles_limit' : IDL.Nat, + 'log_visibility' : log_visibility, 'memory_allocation' : IDL.Nat, 'compute_allocation' : IDL.Nat, }); @@ -142,6 +147,7 @@ export const idlFactory = ({ IDL }) => { 'freezing_threshold' : IDL.Opt(IDL.Nat), 'controllers' : IDL.Opt(IDL.Vec(IDL.Principal)), 'reserved_cycles_limit' : IDL.Opt(IDL.Nat), + 'log_visibility' : IDL.Opt(log_visibility), 'memory_allocation' : IDL.Opt(IDL.Nat), 'compute_allocation' : IDL.Opt(IDL.Nat), }); @@ -162,6 +168,15 @@ export const idlFactory = ({ IDL }) => { 'public_key' : IDL.Vec(IDL.Nat8), 'chain_code' : IDL.Vec(IDL.Nat8), }); + const fetch_canister_logs_args = IDL.Record({ 'canister_id' : canister_id }); + const canister_log_record = IDL.Record({ + 'idx' : IDL.Nat64, + 'timestamp_nanos' : IDL.Nat64, + 'content' : IDL.Vec(IDL.Nat8), + }); + const fetch_canister_logs_result = IDL.Record({ + 'canister_log_records' : IDL.Vec(canister_log_record), + }); const http_header = IDL.Record({ 'value' : IDL.Text, 'name' : IDL.Text }); const http_request_result = IDL.Record({ 'status' : IDL.Nat, @@ -333,6 +348,11 @@ export const idlFactory = ({ IDL }) => { [ecdsa_public_key_result], [], ), + 'fetch_canister_logs' : IDL.Func( + [fetch_canister_logs_args], + [fetch_canister_logs_result], + ['query'], + ), '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], [], []), diff --git a/packages/ic-management/src/ic-management.canister.spec.ts b/packages/ic-management/src/ic-management.canister.spec.ts index aea595b9..b8709abe 100644 --- a/packages/ic-management/src/ic-management.canister.spec.ts +++ b/packages/ic-management/src/ic-management.canister.spec.ts @@ -21,6 +21,8 @@ import { CanisterSettings, InstallCodeParams, InstallMode, + LogVisibility, + UnsupportedLogVisibility, toInstallMode, type BitcoinGetUtxosParams, type ClearChunkStoreParams, @@ -116,10 +118,77 @@ describe("ICManagementCanister", () => { freezing_threshold: [], memory_allocation: [], reserved_cycles_limit: [], + log_visibility: [], }, }); }); + it("calls update_settings with mapped log_visibility controllers", async () => { + const service = mock(); + service.update_settings.mockResolvedValue(undefined); + + const icManagement = await createICManagement(service); + + await icManagement.updateSettings({ + canisterId: mockCanisterId, + settings: { + ...mockCanisterSettings, + logVisibility: LogVisibility.Controllers, + }, + }); + + expect(service.update_settings).toHaveBeenCalledWith({ + canister_id: mockCanisterId, + settings: { + ...mappedMockCanisterSettings, + log_visibility: [{ controllers: null }], + }, + sender_canister_version: [], + }); + }); + + it("calls update_settings with mapped log_visibility public", async () => { + const service = mock(); + service.update_settings.mockResolvedValue(undefined); + + const icManagement = await createICManagement(service); + + await icManagement.updateSettings({ + canisterId: mockCanisterId, + settings: { + ...mockCanisterSettings, + logVisibility: LogVisibility.Public, + }, + }); + + expect(service.update_settings).toHaveBeenCalledWith({ + canister_id: mockCanisterId, + settings: { + ...mappedMockCanisterSettings, + log_visibility: [{ public: null }], + }, + sender_canister_version: [], + }); + }); + + it("throws Error for unsupported log visibility", async () => { + const service = mock(); + service.update_settings.mockResolvedValue(undefined); + + const icManagement = await createICManagement(service); + + const call = () => + icManagement.updateSettings({ + canisterId: mockCanisterId, + settings: { + ...mockCanisterSettings, + logVisibility: 2 as unknown as LogVisibility, + }, + }); + + expect(call).toThrow(UnsupportedLogVisibility); + }); + it("throws Error", async () => { const error = new Error("Test"); const service = mock(); @@ -274,6 +343,7 @@ describe("ICManagementCanister", () => { memory_allocation: BigInt(4), compute_allocation: BigInt(10), reserved_cycles_limit: BigInt(11), + log_visibility: { controllers: null }, }; const response: CanisterStatusResponse = { status: { running: null }, diff --git a/packages/ic-management/src/types/ic-management.params.ts b/packages/ic-management/src/types/ic-management.params.ts index 7827fa78..5d30bfbd 100644 --- a/packages/ic-management/src/types/ic-management.params.ts +++ b/packages/ic-management/src/types/ic-management.params.ts @@ -1,35 +1,57 @@ import { Principal } from "@dfinity/principal"; -import { toNullable } from "@dfinity/utils"; +import { isNullish, toNullable } from "@dfinity/utils"; import type { bitcoin_get_utxos_args, bitcoin_get_utxos_query_args, canister_install_mode, canister_settings, chunk_hash, + log_visibility, upload_chunk_args, } from "../../candid/ic-management"; +export enum LogVisibility { + Controllers, + Public, +} + export interface CanisterSettings { controllers?: string[]; freezingThreshold?: bigint; memoryAllocation?: bigint; computeAllocation?: bigint; reservedCyclesLimit?: bigint; + logVisibility?: LogVisibility; } +export class UnsupportedLogVisibility extends Error {} + export const toCanisterSettings = ({ controllers, freezingThreshold, memoryAllocation, computeAllocation, reservedCyclesLimit, + logVisibility, }: CanisterSettings = {}): canister_settings => { + const toLogVisibility = (): log_visibility => { + switch (logVisibility) { + case LogVisibility.Controllers: + return { controllers: null }; + case LogVisibility.Public: + return { public: null }; + default: + throw new UnsupportedLogVisibility(); + } + }; + return { controllers: toNullable(controllers?.map((c) => Principal.fromText(c))), freezing_threshold: toNullable(freezingThreshold), memory_allocation: toNullable(memoryAllocation), compute_allocation: toNullable(computeAllocation), reserved_cycles_limit: toNullable(reservedCyclesLimit), + log_visibility: isNullish(logVisibility) ? [] : [toLogVisibility()], }; };