聊聊 IC 的 Certified Variable
本文聊聊 Certified Variable 是什么、它的使用场景,然后介绍一个例子,最后看看它在 II 中的应用。
Certified Variable 的本质是给 Canister 提供了一种验证数据是否被篡改的方式,在 Canister 中可以使用 CertifiedData API set 一个数据,然后在前端请求的时候可以 get 这个数据的证书,证书中包括 时间、Canister ID 和 数据,通过对比,来判断是否被篡改。
Certified Variable 只适用于 Query Call,不适用 Update Call,因为 Update Call 本身要经过共识,无法篡改,而 Query Call 只会查询一个 Node,Node 有被侵入的可能性。
所以 Certified Variable 是解决 Query Call 场景下数据的安全性问题。
下面是官方提供的一个例子,具体链接见 这里。
也可以看下图。
在服务端,inc() 和 set() 都调用了 CD.set(Motoko 语言) 设置数据,在 get() 中使用 CD.getCertificate 返回证书。
在客户端,可以看到:
- 先使用 cert.verify() 验证了证书的有效性
- 然后对比时间,如果时间差距大于 5s,表示数据有问题
- 最后对比数据,如果返回的数据和证书中的数据不一致,表示数据有问题
有一个问题,cert.verify() 是怎么做验证的呢?
这块逻辑在 Certificate 中:
- key 是子网的 Root Key,也就是子网公钥
- sig 是子网 Root Key 对应的私钥计算出来的签名
- msg 是 Root Hash,根据服务端返回的 HashTree(即默克尔树) 计算出来
通过 BLS 算法,对 Root Key、Root Hash 和 Signature 分别做计算,并做对比,如果一致,表示正常。
另外在 Agent JS 中,可以单独用 pullForResponse 获取请求的状态(readState),并对证书进行验证。
最后,看看 II 中 Certified Variable 的应用。
在 get_signature 中用 data_certificate(Rust 语言)获取证书(包含 Root Hash),返回的 sig 里包含 证书 和 HashTree。
在 update_root_hash 中用 set_certified_data 设置 Root Hash。
另外在 get_signature 中有一些细节:
-
通过用户的 seed 和 Delegation 计算出来了 witness(HashTree),然后对比 witness 的 Hash 和 SignatureMap 的 Hash 是否一致,如果不一致表示用户请求有问题。
不过这里不太明白为什么使用 SignatureMap 的 Hash 做对比,SignatureMap 不是全量用户的数据吗?
- SignatureMap 是保存后端数据,AssetHashes 是保存前端数据
- SignatureMap 和 AssetHashes 都使用红黑树,内部使用了 Hash,返回给客户端的是 HashTree





