Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

rfc9162

rfc9162 hashes a tree of 32-byte leaves. Record bytes go through leaf_hash. The root and the proofs are functions of the leaf slice. Storage and the signed-note envelope are the other two crates.

Hash records and check every leaf

The getting started program is the whole task. The root, the proof lengths and Head::material are:

size 3
root 385da30f3917282c8939dff851957e519ab1846b1351a14c0adb3b11632742aa
inclusion 0 alpha steps 2 ok true
inclusion 1 beta steps 2 ok true
inclusion 2 gamma steps 1 ok true
later size 4 root 42fc54eeb6352f90cc81fdd5791292cca3974a168208b395b06a76240b24884d
consistency 3 -> 4 steps 3 ok true
material example.org/log	3	385da30f3917282c8939dff851957e519ab1846b1351a14c0adb3b11632742aa

Head::material("example.org/log") is the domain, the size 3 and the root, separated by tabs, with no trailing newline.

Check a proof against other inputs

verify_inclusion returns true for the leaf at the index the proof was built for. The same path with another leaf, another index, a shorter path or another root returns false. verify_consistency returns true for a prefix and false when the sizes are swapped. from_hex accepts the 64 lowercase digits hex writes. Uppercase digits return None.

//! Checks around one inclusion proof and one consistency proof.

fn main() {
    let leaves: Vec<[u8; 32]> = ["alpha", "beta", "gamma"]
        .iter()
        .map(|record| rfc9162::leaf_hash(record.as_bytes()))
        .collect();
    let head = rfc9162::Head::over(&leaves);
    let path = rfc9162::inclusion_proof(&leaves, 1).expect("index 1");
    println!(
        "right leaf {}",
        rfc9162::verify_inclusion(&leaves[1], 1, head.size, &path, &head.root)
    );
    println!(
        "other leaf {}",
        rfc9162::verify_inclusion(&leaves[0], 1, head.size, &path, &head.root)
    );
    println!(
        "other index {}",
        rfc9162::verify_inclusion(&leaves[1], 0, head.size, &path, &head.root)
    );
    println!(
        "short path {}",
        rfc9162::verify_inclusion(
            &leaves[1],
            1,
            head.size,
            &path[..path.len() - 1],
            &head.root
        )
    );
    let other_root = "00".repeat(32);
    println!(
        "other root {}",
        rfc9162::verify_inclusion(&leaves[1], 1, head.size, &path, &other_root)
    );

    let grown: Vec<[u8; 32]> = ["alpha", "beta", "gamma", "delta"]
        .iter()
        .map(|record| rfc9162::leaf_hash(record.as_bytes()))
        .collect();
    let later = rfc9162::Head::over(&grown);
    let proof = rfc9162::consistency_proof(&grown, head.size).expect("prefix");
    println!(
        "prefix {}",
        rfc9162::verify_consistency(head.size, &head.root, later.size, &later.root, &proof)
    );
    println!(
        "swapped sizes {}",
        rfc9162::verify_consistency(later.size, &later.root, head.size, &head.root, &proof)
    );

    println!("from_hex {}", rfc9162::from_hex(&head.root).is_some());
    println!(
        "from_hex uppercase {}",
        rfc9162::from_hex(&head.root.to_ascii_uppercase()).is_some()
    );
}
//! Checks around one inclusion proof and one consistency proof.

fn main() {
    let leaves: Vec<[u8; 32]> = ["alpha", "beta", "gamma"]
        .iter()
        .map(|record| rfc9162::leaf_hash(record.as_bytes()))
        .collect();
    let head = rfc9162::Head::over(&leaves);
    let path = rfc9162::inclusion_proof(&leaves, 1).expect("index 1");
    println!(
        "right leaf {}",
        rfc9162::verify_inclusion(&leaves[1], 1, head.size, &path, &head.root)
    );
    println!(
        "other leaf {}",
        rfc9162::verify_inclusion(&leaves[0], 1, head.size, &path, &head.root)
    );
    println!(
        "other index {}",
        rfc9162::verify_inclusion(&leaves[1], 0, head.size, &path, &head.root)
    );
    println!(
        "short path {}",
        rfc9162::verify_inclusion(
            &leaves[1],
            1,
            head.size,
            &path[..path.len() - 1],
            &head.root
        )
    );
    let other_root = "00".repeat(32);
    println!(
        "other root {}",
        rfc9162::verify_inclusion(&leaves[1], 1, head.size, &path, &other_root)
    );

    let grown: Vec<[u8; 32]> = ["alpha", "beta", "gamma", "delta"]
        .iter()
        .map(|record| rfc9162::leaf_hash(record.as_bytes()))
        .collect();
    let later = rfc9162::Head::over(&grown);
    let proof = rfc9162::consistency_proof(&grown, head.size).expect("prefix");
    println!(
        "prefix {}",
        rfc9162::verify_consistency(head.size, &head.root, later.size, &later.root, &proof)
    );
    println!(
        "swapped sizes {}",
        rfc9162::verify_consistency(later.size, &later.root, head.size, &head.root, &proof)
    );

    println!("from_hex {}", rfc9162::from_hex(&head.root).is_some());
    println!(
        "from_hex uppercase {}",
        rfc9162::from_hex(&head.root.to_ascii_uppercase()).is_some()
    );
}
//! Checks around one inclusion proof and one consistency proof.

fn main() {
    let leaves: Vec<[u8; 32]> = ["alpha", "beta", "gamma"]
        .iter()
        .map(|record| rfc9162::leaf_hash(record.as_bytes()))
        .collect();
    let head = rfc9162::Head::over(&leaves);
    let path = rfc9162::inclusion_proof(&leaves, 1).expect("index 1");
    println!(
        "right leaf {}",
        rfc9162::verify_inclusion(&leaves[1], 1, head.size, &path, &head.root)
    );
    println!(
        "other leaf {}",
        rfc9162::verify_inclusion(&leaves[0], 1, head.size, &path, &head.root)
    );
    println!(
        "other index {}",
        rfc9162::verify_inclusion(&leaves[1], 0, head.size, &path, &head.root)
    );
    println!(
        "short path {}",
        rfc9162::verify_inclusion(
            &leaves[1],
            1,
            head.size,
            &path[..path.len() - 1],
            &head.root
        )
    );
    let other_root = "00".repeat(32);
    println!(
        "other root {}",
        rfc9162::verify_inclusion(&leaves[1], 1, head.size, &path, &other_root)
    );

    let grown: Vec<[u8; 32]> = ["alpha", "beta", "gamma", "delta"]
        .iter()
        .map(|record| rfc9162::leaf_hash(record.as_bytes()))
        .collect();
    let later = rfc9162::Head::over(&grown);
    let proof = rfc9162::consistency_proof(&grown, head.size).expect("prefix");
    println!(
        "prefix {}",
        rfc9162::verify_consistency(head.size, &head.root, later.size, &later.root, &proof)
    );
    println!(
        "swapped sizes {}",
        rfc9162::verify_consistency(later.size, &later.root, head.size, &head.root, &proof)
    );

    println!("from_hex {}", rfc9162::from_hex(&head.root).is_some());
    println!(
        "from_hex uppercase {}",
        rfc9162::from_hex(&head.root.to_ascii_uppercase()).is_some()
    );
}
right leaf true
other leaf false
other index false
short path false
other root false
prefix true
swapped sizes false
from_hex true
from_hex uppercase false

Empty log

The root of an empty leaf slice is SHA-256 of the empty input, e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855. inclusion_proof on an empty slice returns None. A one-leaf tree’s root is that leaf. The leaf hash of an empty entry, which is the RFC 6962 one-leaf root for the empty input, is 6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d.

The eight published roots from the RFC 6962 vector set are checked in crates/rfc9162/tests/rfc6962_vectors.rs.

The Merkle tree page is the shape behind these hashes. To store the entries, use rfc9162-tiles.