Canister history on the IC

What?

We’re excited to announce that the Internet Computer (IC) now allows users to have more insight into a canister’s history, both in terms of which code has been running and who deployed this code. To achieve this, the IC tracks a canister’s history of deployed canister Wasm module hashes and controller changes.

Why?

This feature provides a new audit trail that can, for example, be helpful for users in the following use cases.

  • A user of a canister can verify that a canister’s controller does not maliciously change the canister’s code. Let’s take as an example a malicious canister controller that tampers with the code by deploying a malicious Wasm module (e.g., with a backdoor) and shortly after redeploys the original Wasm module. If a user checked the Wasm module hash before and after this “attack”, the user would not see anything suspicious. With the newly available canister history, the user can detect such behavior as the user would see in the canister history that a different Wasm module was installed for a while.
  • A user of a canister can verify that a canister’s controller does not maliciously change the canister’s controllers. Let’s take as an example a malicious canister controller that deploys a malicious Wasm module (e.g., with a backdoor) and then sets the NNS governance canister as the new controller of the canister. If a user checked the current set of controllers, the user would only see the NNS governance canister as the single controller and could thus believe that the canister’s code is trustworthy. With the newly available canister history, the user can detect such behavior as the user would see in the canister history who deployed the current canister’s code and that the controllers were changed afterwards.

A concrete example of how to get and interpret the canister history is available in the “canister-info” sample dapp.

How?

The relevant replica code change to enable this feature was approved and released by the NNS DAO in proposal 122617.

The canister history information is available via inter-canister calls to the canister_info method of the management canister taking a canister id as argument. Please refer to the Interface specification for more details including the precise types. The above mentioned example might also be helpful to understand the details.

The canister history stores the 20 latest relevant changes, which include canister creation, Wasm module installations, reinstallations, upgrades, and uninstallations, as well as changes to the canister’s controllers. Note that the canister history only stores the changes that happened after the feature has been rolled out (since 2023-06-05, 9:47:46 AM UTC, on all subnets).

The fact that only the 20 latest relevant changes are stored means that the canister history cannot be used for auditing canisters that are subject to many changes within a short period of time, as changes beyond the limit will be dropped from the history, and thus no longer traceable. However, this is not expected to be a practical issue because the changes can be periodically fetched and stored in a dedicated canister.

We hope that you find this new feature useful.

If you have any questions or feedback, please drop a line in this thread!

19 Likes

This is something that’s concerned me for a while so I’m really glad to see this addressed at the protocol level even if it’s just for recent changes.

2 Likes

Is there a deployed version of the canister info example dapp?

2 Likes

I’ve just deployed it under this canister id: cfxxx-zyaaa-aaaaj-aa32q-cai

3 Likes

Very cool
A concern I had when first discovering the internet computer is the disadvantage of not knowing this information when making a call. In immutable smart contract blockchains, you get that guarantee that it hasn’t changed, so there was a trade off
In my mind there still is a case where you want to make a call and have it rejected if the hash doesn’t match, so no extra call is needed and there is no gap between the hash query and the actual call. Is that a possible future feature or are there complications I’m not seeing

1 Like

This is the first time someone has asked for this feature. I’m not entirely sure it makes sense to add this on the IC. Let me try to explain why I think that.

If you want to have an immutable canister on the Internet Computer this is actually possible: you can remove all controllers from the canister (then no one can update it anymore) or blackhole the canister which is essentially equivalent (the blackhole canister would be a controller of your canister but the blackhole does not allow to upgrade the canister). This gives you the guarantees you seem to be looking for when comparing against immutable smart contract blockchains that you mentioned.

For canisters that are not immutable, adding this would potentially affect UX as every time the canister upgrades, consumers would need to also update the hash they are using if they want their requests to work. It seems that interoperability of canisters would take a hit as they’d need to follow closely on updates of each other. Of course, you can argue that passing this hash would be an option and nothing changes if you do not provide it/use it. It’s still some effort to support this and given that I’m not entirely convinced about the use case (especially given that you can achieve having immutable canisters which would give you the guarantee you’re looking for if it’s important for your use case), I’m not sure we’d plan to add it any time soon.

2 Likes

I remembered that this has come up before, but it was @Gekctek that raised it

3 Likes

Thanks for the context @domwoe. I was clearly unaware of that.

It still sounds like the use case is about immutable smart contracts that you get in other chains and there’s a way to get this also on the IC. So, if that satisfies @Gekctek’s requirement, I would still keep things simple and not add extra features that solve the same use case. Unless of course there is more to it than what I understood.

I don’t have a strong use case, just was playing with ideas. It’s less of being immutable, just locking in a known and validated 3rd party contract/canister. It’s a strength that other blockchains have and might be more important in the future when canisters are interdependent and need to trust that a decentralized service doesn’t silently change.
That being said, it doesn’t solve the problem because everything will just shutdown if it rejects the calls if the canister changes. So probably the better use case would be to have an immutable contract if it’s that important for it but to change
Thanks for the reply

1 Like

Thank you for sharing this exciting update! The new feature that allows users to access a canister’s history is a game-changer for transparency and security.

As a developer working on a decentralized multi-chain wallet project called B3Wallet on the IC, this feature will undoubtedly enhance our users’ experience. It provides an essential audit trail that helps us ensure the integrity of our canisters and detect any malicious changes.

I’m thrilled to see the IC evolving with such powerful capabilities, making it even more attractive for developers like me to build innovative projects. The ability to verify Wasm module installations, upgrades, and canister controllers will significantly contribute to the security and trustworthiness of our applications.

Additionally, I noticed the self-upgrading feature mentioned in the IC roadmap (December 2022 summary). I’m curious about the implementation timeline for this feature. This could open up exciting possibilities for seamless upgrades and continuous improvements without interrupting our users’ experience.

I’m excited to leverage this feature in B3Wallet, empowering our users to have full confidence in their digital assets’ safety.

Best regards,
Behrad

4 Likes

Hi,
is there also a motoko or azle example available? I found one for rust, but not for motoko or azle.

Thanks.

1 Like

I think you just called canister_info on the management canister, here’s the code in Azle: https://github.com/demergent-labs/azle/blob/main/canisters/management/index.ts#L65

It should be no different than any other management canister call

3 Likes

How does this work with the fact that only the last 20 changes are stored? Say wasm module hash X was installed and is legit. Then the attacker installs Y, exploits it, and installs X again 20 times. Then Y would be wiped from the history and the user would only see 20 installations of X.

Or is that not considered a “change”? If I install X twice in a row does that not show up as two history entries?

Anyway, the attacker could still install X1, …, X20 with marginal legit looking modifications.

1 Like

What you pointed out is indeed a possible attack that could be detected by noticing that the canister experienced frequent changes to its installed WASM module.

Yes, but over time the attacker can wipe those traces as well by continuing to do upgrades with larger and larger amounts of time between them. So if the user looks a week later it already appears more benign.

Is there an absolute counter (= total number of upgrades that ever happened) as well?

1 Like

Yes, but over time the attacker can wipe those traces as well by continuing to do upgrades with larger and larger amounts of time between them. So if the user looks a week later it already appears more benign.

Indeed, over time public (trusted) dashboards should have pulled canister history and recorded all the changes if they do not have too frequently.

Is there an absolute counter (= total number of upgrades that ever happened) as well?

Yes, the canister_info endpoint provides such a total count of all changes ever recorded in the canister history. This way, a public dashboard (or anyone else) can soundly detect if it pulled all the changes or actually missed some.

1 Like