History
Overview
The Internet Computer tracks the history of a deployed canister through the canister's Wasm module hashes and controller changes. This tracking provides insight into the canister's history, such as what code has been used to run the canister, and which controller deployed that code.
Viewing a canister's history can be important in use cases such as:
Verifying that a canister's controller has not maliciously modified the code.
Verifying that a canister's controller does not maliciously change the list of controllers for the canister. For example, if a canister's controller list only displays the governance canister as the current controller, users can trust that the canister's code has not been maliciously altered.
How canister history works
Canister history was introduced and released by the NNS DAO proposal 122617.
Canister history information is made available via inter-canister calls made to the canister_info
method of the management canister, using a canister's ID as an argument for the call. A canister's history stores the 20 most recent changes to the canister, including the canister's creation, Wasm module installations, reinstallations, upgrades, and changes to the list of canister controllers. It is important to note that since only the 20 most recent changes are stored, this feature cannot be used for auditing canisters that are frequently changed within a short period of time.
Note that canister history is only available for changes that have happened since this feature was rolled out on 023-06-05, 9:47:46 AM UTC, across all subnets.
You can read more about this feature on the developer forum.
Getting a canister's history
To get a canister's history, make a call to the IC management canister's canister_info
method:
- Motoko
- Rust
- TypeScript
actor {
let IC =
actor "aaaaa-aa" : actor {
canister_info : { canister_id : Principal } ->
async ();
};
};
use candid::{CandidType, Principal};
use ic_cdk::api::management_canister::main::{
canister_info, CanisterChange,
CanisterChangeDetails::{CodeDeployment, CodeUninstall, ControllersChange, Creation},
CanisterChangeOrigin::{FromCanister, FromUser},
CanisterInfoRequest, CanisterInfoResponse,
};
use serde::Deserialize;
/// Returns canister info with all available canister changes for a canister characterized by a given principal.
/// Traps if the canister_info management call is rejected (in particular, if the principal does not characterize a canister).
#[ic_cdk::update]
async fn info(canister_id: Principal) -> CanisterInfoResponse {
let request = CanisterInfoRequest {
canister_id,
num_requested_changes: Some(20),
};
canister_info(request).await.unwrap().0
}
import { call, IDL, Principal, query, update } from 'azle';
import {
CanisterInfoArgs,
CanisterInfoResult,
} from 'azle/canisters/management';
type State = {
createdCanisterId: Principal;
};
let state: State = {
createdCanisterId: Principal.fromText('aaaaa-aa')
};
export default class {
@update([CanisterInfoArgs], CanisterInfoResult)
async getCanisterInfo(args: CanisterInfoArgs): Promise<CanisterInfoResult> {
return await call('aaaaa-aa', 'canister_info', {
paramIdlTypes: [CanisterInfoArgs],
returnIdlType: CanisterInfoResult,
args: [args]
});
}
}