warg_transparency/log/mod.rs
1//! Immutable Log w/ Inclusion and Consistency Proofs
2//!
3//! The main trait in this module is [`VerifiableLog`],
4//! which defines the API of a log where inclusion
5//! and consistency can be verified.
6//!
7//! Implementations:
8//! * [`InOrderLog`] -
9//!
10//! The only implementation in this module is ,
11//! which is a [`VerifiableLog`] whose contents are structured
12//! using binary in-order interval numbering as described in
13//! [Dat - Distributed Dataset Synchronization and Versioning][2].
14
15mod node;
16/// Logic for constructing and validating proofs
17mod proof;
18mod proof_bundle;
19mod sparse_data;
20mod stack_log;
21mod vec_log;
22
23use warg_crypto::{
24 hash::{Hash, SupportedDigest},
25 VisitBytes,
26};
27
28pub use node::{Node, Side};
29pub use proof::{
30 ConsistencyProof, ConsistencyProofError, InclusionProof, InclusionProofError,
31 InclusionProofWalk,
32};
33pub use proof_bundle::ProofBundle;
34pub use proof_bundle::ProofBundle as LogProofBundle;
35pub use stack_log::StackLog;
36pub use vec_log::VecLog;
37
38/// A [merkle tree][0] log data type based on [DAT][1].
39/// where the merkle tree computation is conformant to
40/// [RFC 6962 - Certificate Transparency][2]. This allows
41/// you to efficiently append data and then verify that
42/// it the log is consistent over time and contains a
43/// given entry.
44///
45/// It represents its data using binary in-order interval numbering.
46/// This means that all of the leaf and balanced branch nodes of the tree
47/// are stored in one contiguous array using a particular indexing scheme.
48///
49/// ## Example
50/// ```text
51/// 0 X \
52/// 1 X
53/// 2 X / \
54/// 3 X
55/// 4 X \ /
56/// 5 X
57/// 6 X /
58/// ```
59///
60/// ## Properties
61/// This has various convenient properties for traversing the structure.
62/// * The height of a node is the number of trailing ones in its index.
63/// * For the above reason, leaves always have even indices.
64/// * The side (left/right) of a node can be computed from its index.
65/// * The distance between parent/child indices is a simple function of height.
66///
67/// [0]: https://en.wikipedia.org/wiki/Merkle_tree
68/// [1]: https://www.researchgate.net/publication/326120012_Dat_-_Distributed_Dataset_Synchronization_And_Versioning
69/// [2]: https://www.rfc-editor.org/rfc/rfc6962
70pub trait LogBuilder<D, V>
71where
72 D: SupportedDigest,
73 V: VisitBytes,
74{
75 /// Get the checkpoint (hash and length) of the log at this point.
76 fn checkpoint(&self) -> Checkpoint<D>;
77
78 /// Push a new entry into the log.
79 fn push(&mut self, entry: &V) -> Node;
80}
81
82/// A point in the history of a log, represented by its length
83#[derive(Debug, Clone, PartialOrd, Ord)]
84pub struct Checkpoint<D>
85where
86 D: SupportedDigest,
87{
88 root: Hash<D>,
89 length: usize,
90}
91
92impl<D> Checkpoint<D>
93where
94 D: SupportedDigest,
95{
96 /// The root hash of the log at this checkpoint
97 pub fn root(&self) -> Hash<D> {
98 self.root.clone()
99 }
100
101 /// The length of the log at this checkpoint
102 pub fn length(&self) -> usize {
103 self.length
104 }
105}
106
107impl<D> Eq for Checkpoint<D> where D: SupportedDigest {}
108
109impl<D> PartialEq for Checkpoint<D>
110where
111 D: SupportedDigest,
112{
113 fn eq(&self, other: &Self) -> bool {
114 self.root == other.root && self.length == other.length
115 }
116}
117
118/// A collection of hash data
119pub trait LogData<D, V>
120where
121 D: SupportedDigest,
122 V: VisitBytes,
123{
124 /// Does this hash exist in the collection?
125 fn has_hash(&self, node: Node) -> bool;
126
127 /// Get the hash for a given node
128 /// None if node does not yet exist
129 fn hash_for(&self, node: Node) -> Option<Hash<D>>;
130
131 /// Construct an inclusion proof for this log
132 fn prove_inclusion(&self, leaf: Node, log_length: usize) -> InclusionProof<D, V> {
133 InclusionProof::new(leaf, log_length)
134 }
135
136 /// Construct a consistency proof for this log
137 fn prove_consistency(&self, old_length: usize, new_length: usize) -> ConsistencyProof<D, V> {
138 ConsistencyProof::new(old_length, new_length)
139 }
140}
141
142/// Compute the hash for an empty tree using a given Digest algorithm.
143pub(crate) fn hash_empty<D: SupportedDigest>() -> Hash<D> {
144 Hash::of(())
145}
146
147/// Compute the hash for a leaf in a tree using a given Digest algorithm.
148pub(crate) fn hash_leaf<D: SupportedDigest>(data: impl VisitBytes) -> Hash<D> {
149 let input = (0u8, data);
150 Hash::of(&input)
151}
152
153/// Compute the hash for a branch in a tree using a given Digest algorithm.
154pub(crate) fn hash_branch<D: SupportedDigest>(
155 left: impl VisitBytes,
156 right: impl VisitBytes,
157) -> Hash<D> {
158 let input = (1u8, left, right);
159 Hash::of(&input)
160}