Skip to main content

chio_kernel/checkpoint/
consistency.rs

1//! Anchoring rules for checkpoint-chain consistency proofs.
2//!
3//! Split from `checkpoint.rs` to keep that module inside the production file
4//! size limit enforced by `scripts/check-rust-file-hygiene.py`.
5
6use chio_core::hashing::Hash;
7use chio_core::merkle::MerkleProof;
8
9use super::{checkpoint_chain_root, CheckpointError, KernelCheckpoint};
10
11/// What ties the prefix below a consistency proof's earlier endpoint to a
12/// history the verifier accepts.
13///
14/// A pair-level proof constrains nothing before its earlier endpoint, so for a
15/// pair starting after checkpoint 1 a signing-key holder can commit an earlier
16/// tree whose leading leaves are fabricated, append the later real leaf,
17/// re-sign both bodies, and produce paths that verify.
18#[derive(Debug, Clone, Copy)]
19pub enum CheckpointConsistencyAnchor<'a> {
20    /// The earlier endpoint is checkpoint 1, so its chain tree is exactly its
21    /// own leaf and no prefix exists to fabricate.
22    Genesis,
23    /// A chain root the verifier already accepted for the earlier endpoint.
24    VerifiedChainRoot(Hash),
25    /// Every chain leaf from checkpoint 1 through the earlier endpoint.
26    ChainPrefix(&'a [Hash]),
27}
28
29/// Whether `from_chain_root` is anchored under `anchor`. Genesis anchoring
30/// constrains the verifier's inputs rather than the proof, so a mid-chain pair
31/// is an error there instead of a verification failure.
32pub(super) fn consistency_prefix_is_anchored(
33    previous: &KernelCheckpoint,
34    from_size: usize,
35    from_chain_root: &Hash,
36    anchor: CheckpointConsistencyAnchor<'_>,
37) -> Result<bool, CheckpointError> {
38    match anchor {
39        CheckpointConsistencyAnchor::Genesis => {
40            if from_size != 1 {
41                return Err(CheckpointError::Continuity(format!(
42                    "consistency proof from checkpoint {} is unanchored; only a pair starting at checkpoint 1 anchors itself",
43                    previous.body.checkpoint_seq
44                )));
45            }
46            Ok(true)
47        }
48        CheckpointConsistencyAnchor::VerifiedChainRoot(pinned) => Ok(pinned == *from_chain_root),
49        CheckpointConsistencyAnchor::ChainPrefix(chain_leaf_hashes) => Ok(chain_leaf_hashes.len()
50            == from_size
51            && checkpoint_chain_root(chain_leaf_hashes)? == *from_chain_root),
52    }
53}
54
55/// Whether `leaf` is committed by `root` as the final leaf of a `size`-leaf
56/// chain tree, per the supplied inclusion proof.
57pub(super) fn chain_leaf_is_committed(
58    inclusion: &MerkleProof,
59    size: usize,
60    leaf: Hash,
61    root: &Hash,
62) -> bool {
63    inclusion.tree_size == size
64        && size.checked_sub(1) == Some(inclusion.leaf_index)
65        && inclusion.verify_hash(leaf, root)
66}