Skip to main content

History

Beginner
Tutorial

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.

Canister history is only available for changes that have happened since this feature was rolled out on 23-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:

import Principal "mo:base/Principal";

actor Info {
type canister_info_args = {
canister_id : Principal;
num_requested_changes : ?Nat64
};

type canister_info_result = {
total_num_changes : Nat64;
module_hash : ?Blob;
controllers : [Principal]
};

let IC = actor "aaaaa-aa" : actor {
canister_info : {canister_id : Principal} -> async canister_info_result
};

public func getInfo() : async canister_info_result {
await IC.canister_info {canister_id = Principal.fromActor(Info)}
}

}