8 releases

0.4.0 Jul 13, 2023
0.3.4 Mar 1, 2023
0.3.3 Feb 22, 2023
0.3.2 Nov 10, 2022
0.1.0 Jun 4, 2021

#28 in #dfinity

Download history 4076/week @ 2024-07-22 3813/week @ 2024-07-29 4773/week @ 2024-08-05 4058/week @ 2024-08-12 4852/week @ 2024-08-19 4313/week @ 2024-08-26 4434/week @ 2024-09-02 4260/week @ 2024-09-09 3924/week @ 2024-09-16 4193/week @ 2024-09-23 4540/week @ 2024-09-30 4284/week @ 2024-10-07 4613/week @ 2024-10-14 4455/week @ 2024-10-21 5190/week @ 2024-10-28 4621/week @ 2024-11-04

18,993 downloads per month
Used in 8 crates (5 directly)

Apache-2.0

55KB
1.5K SLoC

Certified Map

This package provides a map that can be used by Internet Computer canisters to implement certified queries.

Features

  • Incremental certification. The canister can store thousands of entries while keeping the cost of certification relatively low.

  • Proofs of absence. If the requested key is not present in the map, the returned tree structure allows the caller to verify that fact.

  • Relatively small merkle proofs. The size overhead of the certificate is O(log N), where N is the number of entries in the map.

Implementation Details

The canister uses an augmented Red-Black binary search tree to store the entries. Each node of the search tree is annotated with the root hash of the hash tree built from the subtree rooted at this node. Each time the tree is rotated or modified, the corresponding hashes are recomputed in O(1) time.


lib.rs:

This package provides a map backed by a Merkle tree that can be used by Internet Computer canisters to implement certified queries.

You can certify your data by using the RbTree type as a map of known names to the values you want certified. After you record its root_hash into your canister's certified data, query calls can access a data certificate proving that the IC certified the hash, under the path /canister/<canister id>/certified_data. By providing this certificate, as well as a witness that the value exists in the hash, you can then prove to the caller that the IC certified the data.

Example


thread_local! {
    static COUNTER: Cell<i32> = Cell::new(0);
    static TREE: RefCell<RbTree<&'static str, Hash>> = RefCell::new(RbTree::new());
}

#[update]
fn inc() {
    let count = COUNTER.with(|counter| {
        let count = counter.get() + 1;
        counter.set(count);
        count
    });
    TREE.with(|tree| {
        let mut tree = tree.borrow_mut();
        tree.insert("counter", leaf_hash(&count.to_be_bytes()));
        ic_cdk::api::set_certified_data(&tree.root_hash());
    })
}

#[derive(CandidType)]
struct CertifiedCounter {
    count: i32,
    certificate: Vec<u8>,
    witness: Vec<u8>,
}

#[query]
fn get() -> CertifiedCounter {
    let certificate = ic_cdk::api::data_certificate().expect("No data certificate available");
    let witness = TREE.with(|tree| {
        let tree = tree.borrow();
        let mut witness = vec![];
        let mut witness_serializer = serde_cbor::Serializer::new(&mut witness);
        witness_serializer.self_describe();
        tree.witness(b"counter").serialize(&mut witness_serializer).unwrap();
        witness
    });
    let count = COUNTER.with(|counter| counter.get());
    CertifiedCounter {
        count,
        certificate,
        witness,
    }
}

Dependencies

~1MB
~24K SLoC