Every now and then someone asks whether Motoko canisters can use certified variables, or whether they can serve HTTP requests with certification, and I always responded that yes, Motoko can do that, all that’s missing are a few libraries. But I figured I should put my hand where my mouth is, and actually demonstrate that it’s possible.
Actually, the code is not here, because of this annoying 403 bug of the forum where after carefully editing and formatting the post, it says I can’t post it. Too many URLs maybe? So unfortunately, you’ll have to go to https://gist.github.com/nomeata/f325fcd2a6692df06e38adedf9ca1877 to read it.
Oh, even nicer: This file can be loaded into Motoko Playground! So if you go to Motoko Playground - DFINITY you can click Deploy, and see it in action! You’ll have to take the canister it reported by Motoko Playground and construct the .ic0.app URL from it. And certification will fail until you send a message (most easily done directly from Motoko Playground) or upgrade the canister.
This demo is very helpful to understand certificate.
I have some questions:
If leave_message is not called, the signature of the certificate should be the same, but each time the certificate obtained has a small part of the string different, and the guess /time field is different, is it right?
The /time field is not included in the signed message, can the node theoretically modify /time field?
Similar to “http_assets” in asset_tree, is there any other? What are the other certificate application scenarios, and how do they need to construct this asset_tree.
No: while it is possible to omit data from a Merkle tree without breaking the signature, you can’t add or modify fields.
But since the spec says that the time field ought to be there, a validating client should reject a certificate that doesn’t include it.
The canister signature schemes used by the Internet Identity is another protocol based on certified variables. I don’t know of any other common protocols yet, but assume that there could be application-specific ones.
I’m trying to wrap my head around certified variables, and it seems like there are two Merkle trees (and their respective root hashes) at play here.
The system-level Merkle tree representing the IC state tree. This is returned by CertifiedData.getCertificate in Motoko, which can be passed to a client in the response of a query call for verification. It is always passed to a client who polls for the response of an update call. This Merkle tree includes the 32-byte blob that represents the certified variable at /canisters/<canister_id>/certified_data. This Merkle tree is updated and appropriately pruned by the replica.
The application-level Merkle tree representing the application-specific data that needs to be certified and thus made secure. This is constructed and maintained in memory inside the canister, and must be updated and pruned by the canister logic. The root hash of this Merkle tree is stored as a certified variable inside /canisters/<canister_id>/certified_data using CertifiedData.set in Motoko, and is signed by the subnet by virtue of being part of the IC state tree, whose root hash is always signed by the subnet.
It seems to me that both Merkle trees are necessary to implement certified variables—having one is not enough. The second Merkle tree is directly related to the first Merkle tree, because the root hash of the second tree is stored inside the first tree (whose root hash is returned to the user as part of a Certificate).
The client that queries for a certified asset (or other certified data) must validate both Merkle trees by recomputing the root hash and comparing it with the provided root hash, for each tree. The client only needs to validate one signature, however, and that’s the signature of the root hash of the first Merkle tree for the IC state tree.
Does this sound right to you @nomeata? This was not trivial to figure out.
Note that always pruned merkle trees are passed around, revealing different information. Certified.getCertificate reveals the certified_data of the canister, while the partial merkle tree polled in an update call reveals the request status. But they are both part of the same per-subnet state tree.
Is there a memory limit/cycles limit to this library or can I just do a debug_show and aggregate every single data structure into a single call to this function from the example code (specific part shown below) as a really lazy way of doing things. Basically making storage O(2n)
Also I am curious about cost to store, and read and write costs to it as well in terms of cycles if someone knows.
Everything on the IC has memory and cycles limits. I would assume that this library does not use stable memory, therefore you’d have the standard 4GB WASM heap size limit, and you always will have the default cycles limit
Hey, can you lend me some good example of how you created merkle tree in :
func asset_tree() : HashTree {}, like for canister holding different assets.
Just curious, if we aren’t using this for web but just verifying a variable was certified how do we use this?
Like I get to the point I get a certificate like below. Just don’t know what to do next