merkle_log/
lib.rs

1//! An implementation of the "Merkle Tree-Structured Log" defined in the blog
2//! post [Transparent Logs for Skeptical Clients].
3//!
4//! [Transparent Logs for Skeptical Clients]: https://research.swtch.com/tlog
5
6#![cfg_attr(not(feature = "std"), no_std)]
7extern crate alloc;
8
9mod error;
10mod treeid;
11mod util;
12
13pub use error::Error;
14pub use treeid::TreeID;
15pub use util::{Digest, MemoryStore, Store};
16
17use crate::{maybestd::*, util::Either};
18
19#[cfg(not(feature = "std"))]
20pub(crate) mod maybestd {
21    extern crate alloc;
22
23    pub use alloc::{
24        collections::{BTreeMap, BTreeSet},
25        vec::Vec,
26    };
27    pub use core::{iter, marker::PhantomData};
28    pub use core2::io::{self, BufRead, Read, Write};
29}
30
31#[cfg(feature = "std")]
32pub(crate) mod maybestd {
33    pub use core2::io::{self, BufRead, Read, Write};
34    pub use std::{
35        collections::{BTreeMap, BTreeSet},
36        iter,
37        marker::PhantomData,
38        vec::Vec,
39    };
40}
41
42/// Type alias for nodes in the merkle tree.
43pub trait Node: AsRef<[u8]> + Copy + Eq {}
44impl<N> Node for N where N: AsRef<[u8]> + Copy + Eq {}
45
46/// Type alias for a [`BTreeMap`] containing leaf and tree nodes.
47///
48/// [`BTreeMap`]: crate::maybestd::BTreeMap
49pub type Proof<N> = BTreeMap<TreeID, N>;
50
51/// A [Merkle Tree-Structured Log] is a potentially unbalanced merkle tree
52/// containing the entries of an append-only log (maximum `2^63 + 1` entries).
53///
54/// It extends the functionality of a traditional merkle tree by allowing for:
55/// - continually appending new entries (even when the length of the log is not
56/// a power of two)
57/// - providing proofs that a previous log head is a prefix of (contained
58/// within) the current log.
59///
60/// ## Example
61/// ```rust
62/// use merkle_log::{MemoryStore, MerkleLog, Store};
63/// use digest::Output;
64/// use sha2::Sha256;
65///
66/// let mut store = MemoryStore::default();
67///
68/// // first entry
69/// let entry = b"hello";
70/// let mut log = MerkleLog::<Sha256, [u8; 32]>::new(&entry);
71/// let initial_head = *log.head();
72/// let initial_log = log.clone();
73/// store.set_leaf(log.head_id(), initial_head).unwrap();
74///
75/// // second entry
76/// let entry = b"world";
77/// log.append(entry, &mut store).unwrap();
78///
79/// // prove existence of initial entry by its digest
80/// let proof = log.prove(0, &store).unwrap();
81/// assert!(log.verify(0, &initial_head, &proof).unwrap());
82/// ```
83///
84/// [Merkle Tree-Structured Log]: https://research.swtch.com/tlog#merkle_tree-structured_log
85#[derive(Clone, Debug)]
86#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
87#[cfg_attr(
88    feature = "borsh",
89    derive(borsh::BorshDeserialize, borsh::BorshSerialize)
90)]
91pub struct MerkleLog<D: Digest<N>, N: Node> {
92    /// The digest of the log's head.
93    head: N,
94    /// The merkle root of the tree in which this entry is the head.
95    root: N,
96    /// The index of the log's head.
97    index: u64,
98    /// The underlying digest used by this log.
99    #[cfg_attr(feature = "serde", serde(skip))]
100    #[cfg_attr(feature = "borsh", borsh(skip))]
101    _digest: PhantomData<D>,
102}
103
104impl<D, N> MerkleLog<D, N>
105where
106    D: Digest<N>,
107    N: Node,
108{
109    /// Creates a new [`MerkleLog`] from the first log entry.
110    ///
111    /// [`MerkleLog`]: crate::MerkleLog
112    #[inline]
113    pub fn new(entry: impl AsRef<[u8]>) -> Self {
114        let head = D::leaf_digest(entry.as_ref());
115        Self {
116            index: 0,
117            head,
118            root: head,
119            _digest: PhantomData,
120        }
121    }
122
123    /// The size of the log.
124    #[inline(always)]
125    pub const fn len(&self) -> u64 {
126        self.index + 1
127    }
128
129    /// The [`Node`] of the current head.
130    ///
131    /// [`Node`]: crate::Node
132    #[inline(always)]
133    pub const fn head(&self) -> &N {
134        &self.head
135    }
136
137    /// The unique [`TreeID`] of the current head.
138    ///
139    /// [`TreeID`]: crate::TreeID
140    #[inline(always)]
141    pub const fn head_id(&self) -> TreeID {
142        TreeID::leaf(self.index)
143    }
144
145    /// The merkle root [`Node`] of the log.
146    ///
147    /// [`Node`]: crate::Node
148    #[inline(always)]
149    pub const fn root(&self) -> &N {
150        &self.root
151    }
152
153    /// The unique [`TreeID`] of the current root.
154    ///
155    /// [`TreeID`]: crate::TreeID
156    #[inline(always)]
157    pub const fn root_id(&self) -> TreeID {
158        TreeID::first(self.root_height())
159    }
160
161    /// The unique [`TreeID`] of the current tree root.
162    ///
163    /// [`TreeID`]: crate::TreeID
164    #[inline(always)]
165    pub const fn root_height(&self) -> u8 {
166        TreeID::root_height(self.len())
167    }
168
169    /// Produces the [`TreeID`]s whose values are required to produce a valid
170    /// proof of inclusion for a particular leaf entry in the log, starting from
171    /// the head.
172    ///
173    /// ## Examples
174    /// ```rust
175    /// use merkle_log::{MemoryStore, MerkleLog, Store, TreeID};
176    /// use digest::Output;
177    /// use sha2::Sha256;
178    ///
179    /// let mut store = MemoryStore::default();
180    ///
181    /// let entry = b"hello";
182    /// let mut log = MerkleLog::<Sha256, [u8; 32]>::new(&entry);
183    /// store.set_leaf(log.head_id(), *log.head()).unwrap();
184    ///
185    /// log.append(&entry, &mut store).unwrap(); // new size 2
186    /// log.append(&entry, &mut store).unwrap(); // new size 3
187    /// assert_eq!(log.proving_ids(1).collect::<Vec<_>>(), &[TreeID::from(0), TreeID::from(4)]);
188    ///
189    /// log.append(&entry, &mut store).unwrap(); // new size 4
190    /// assert_eq!(log.proving_ids(1).collect::<Vec<_>>(), &[TreeID::from(0), TreeID::from(5)]);
191    /// assert_eq!(log.proving_ids(2).collect::<Vec<_>>(), &[TreeID::from(6), TreeID::from(1)]);
192    /// ```
193    ///
194    /// [`TreeID`]: crate::TreeID
195    pub fn proving_ids(&self, entry_index: u64) -> impl Iterator<Item = TreeID> {
196        let len = self.len();
197        let entry_id = TreeID::leaf(entry_index);
198
199        // if balanced, use traditional merkle tree proof creation
200        if len.is_power_of_two() {
201            return Either::Left(entry_id.proving_ids(self.root_height()));
202        }
203
204        Either::Right(TreeID::subroot_ids(len).flat_map(move |subroot_id| {
205            if subroot_id.spans(&entry_id) {
206                Either::Left(entry_id.proving_ids(subroot_id.height()))
207            } else {
208                Either::Right(iter::once(subroot_id))
209            }
210        }))
211    }
212
213    /// Creates a proof that an entry is contained within the current log.
214    pub fn prove<S: Store<N>>(&self, entry_index: u64, store: &S) -> Result<Proof<N>, Error> {
215        store.get_many(self.proving_ids(entry_index))
216    }
217
218    /// Verifies a proof asserting that the `entry_node` exists at `entry_index`
219    /// within the current log.
220    pub fn verify(
221        &self,
222        entry_index: u64,
223        entry_node: &N,
224        proof: &Proof<N>,
225    ) -> Result<bool, Error> {
226        let len = self.len();
227        let entry_id = TreeID::leaf(entry_index);
228
229        if entry_index > self.index {
230            // check if out-of-bounds
231            return Err(Error::OutOfBounds);
232        } else if len == 1 {
233            // verifying a length-1 log
234            // index should be 0 and entry_node should be the log's root
235            return Ok(entry_index == self.index
236                && entry_node == &self.head
237                && entry_node == &self.root);
238        }
239
240        // if balanced, use traditional merkle tree verification
241        if len.is_power_of_two() {
242            let root = Self::root_hash(entry_id, entry_node, self.root_height(), proof)?;
243            return Ok(root == self.root);
244        }
245
246        // otherwise
247        // compute subroots, join them from right to left
248        let head_id = self.head_id();
249        let mut subroots = TreeID::subroot_ids(len)
250            .filter_map(|subroot_id| match subroot_id {
251                _ if &head_id == &subroot_id => Some((head_id, self.head)),
252                _ if subroot_id.spans(&entry_id) => {
253                    Self::root_hash(entry_id, entry_node, subroot_id.height(), proof)
254                        .ok()
255                        .map(|subroot| (subroot_id, subroot))
256                }
257                _ => proof
258                    .get(&subroot_id)
259                    .copied()
260                    .map(|subroot| (subroot_id, subroot)),
261            })
262            .collect::<Vec<(TreeID, N)>>()
263            .into_iter()
264            .rev();
265
266        let first_subroot = subroots.next();
267        let (_, root) = subroots
268            .fold(first_subroot, |root, (subroot_id, subroot)| {
269                root.map(|(root_id, root)| D::node_digest((subroot_id, &subroot), (root_id, &root)))
270                    .map(|root| (subroot_id.parent(), root))
271            })
272            .ok_or_else(|| Error::ProofError("failed to compute root digest"))?;
273
274        Ok(root == self.root)
275    }
276
277    /// Produces the [`TreeID`]s whose values are required to append the next
278    /// entry to log.
279    /// See [`TreeID::appending_ids`] for additional doctests.
280    ///
281    /// ## Examples
282    /// ```rust
283    /// use merkle_log::{MemoryStore, MerkleLog, Store, TreeID};
284    /// use digest::Output;
285    /// use sha2::Sha256;
286    ///
287    /// let mut store = MemoryStore::default();
288    ///
289    /// let entry = b"hello";
290    /// let mut log = MerkleLog::<Sha256, [u8; 32]>::new(&entry);
291    /// store.set_leaf(log.head_id(), *log.head()).unwrap();
292    /// assert_eq!(log.appending_ids().collect::<Vec<_>>(), &[TreeID::from(0)]);
293    ///
294    /// log.append(&entry, &mut store).unwrap(); // new size 2
295    /// assert_eq!(log.appending_ids().collect::<Vec<_>>(), &[TreeID::from(1)]);
296    ///
297    /// log.append(&entry, &mut store).unwrap(); // new size 3
298    /// assert_eq!(log.appending_ids().collect::<Vec<_>>(), &[TreeID::from(1), TreeID::from(4)]);
299    ///
300    /// log.append(&entry, &mut store).unwrap(); // new size 4
301    /// assert_eq!(log.appending_ids().collect::<Vec<_>>(), &[TreeID::from(3)]);
302    /// ```
303    ///
304    /// [`TreeID`]: crate::TreeID
305    #[inline]
306    pub fn appending_ids(&self) -> impl Iterator<Item = TreeID> {
307        TreeID::appending_ids(self.len() + 1)
308    }
309
310    /// Appends a new entry to the log, returning the new permanent [`Node`]s to
311    /// store.
312    ///
313    /// ## Examples
314    /// ```rust
315    /// use merkle_log::{MerkleLog, MemoryStore, Store, TreeID};
316    /// use digest::Output;
317    /// use sha2::Sha256;
318    ///
319    /// let mut store = MemoryStore::default();
320    ///
321    /// let mut entry = b"hello";
322    /// let mut log = MerkleLog::<Sha256, [u8; 32]>::new(&entry);
323    /// store.set_leaf(log.head_id(), *log.head()).unwrap();
324    /// assert_eq!(log.len(), 1);
325    /// assert_eq!(log.head_id(), TreeID::from(0));
326    /// assert_eq!(log.head(), store.get(&log.head_id()).unwrap());
327    ///
328    /// log.append(b"world", &mut store).unwrap();
329    /// assert_eq!(log.len(), 2);
330    /// assert_eq!(log.head_id(), TreeID::from(2));
331    /// assert_eq!(log.root(), store.get(&TreeID::from(1)).unwrap());
332    /// ```
333    ///
334    /// [`Node`]: crate::Node
335    pub fn append<S: Store<N>>(
336        &mut self,
337        entry: impl AsRef<[u8]>,
338        store: &mut S,
339    ) -> Result<(), Error> {
340        let new_index = self.index + 1;
341        let new_head_id = TreeID::leaf(new_index);
342        let new_head = D::leaf_digest(entry.as_ref());
343
344        let mut current = new_head;
345        let mut current_id = new_head_id;
346        let mut new_nodes = BTreeMap::new();
347
348        for subroot_id in self
349            .appending_ids()
350            .collect::<Vec<TreeID>>()
351            .into_iter()
352            .rev()
353        {
354            let subroot = store.get(&subroot_id)?;
355            current_id = current_id.parent();
356            current = D::node_digest((subroot_id, &subroot), (current_id, &current));
357
358            if current_id == subroot_id.parent() {
359                new_nodes.insert(current_id, current);
360            }
361        }
362
363        new_nodes.insert(new_head_id, new_head);
364        store.set_many(new_nodes.into_iter())?;
365
366        self.index = new_index;
367        self.head = new_head;
368        self.root = current;
369        Ok(())
370    }
371
372    /// Computes the root hash of a balanced merkle tree, starting from the
373    /// `leaf_id` node.
374    pub(crate) fn root_hash<S: Store<N>>(
375        leaf_id: TreeID,
376        leaf_node: &N,
377        height: u8,
378        in_store: &S,
379    ) -> Result<N, Error> {
380        use core::cmp::Ordering::*;
381
382        let mut current_id = leaf_id;
383        let mut current = *leaf_node;
384        for _ in 0..height {
385            let sibling_id = current_id.sibling();
386            let sibling = in_store.get(&sibling_id)?;
387
388            current = match current_id.cmp(&sibling_id) {
389                Less => D::node_digest((current_id, &current), (sibling_id, &sibling)),
390                Greater => D::node_digest((sibling_id, &sibling), (current_id, &current)),
391                _ => unreachable!(),
392            };
393            current_id = current_id.parent();
394        }
395
396        Ok(current)
397    }
398}
399
400impl<D: Digest<N> + Clone, N: Node> Copy for MerkleLog<D, N> {}
401impl<D: Digest<N>, N: Node> PartialEq for MerkleLog<D, N> {
402    fn eq(&self, other: &Self) -> bool {
403        self.head == other.head
404            && self.root == other.root
405            && self.index == other.index
406            && self._digest == other._digest
407    }
408}
409impl<D: Digest<N>, N: Node> Eq for MerkleLog<D, N> {}
410
411#[cfg(test)]
412mod tests {
413    use super::*;
414    use sha2::Sha256;
415
416    type TestLog = MerkleLog<Sha256, [u8; 32]>;
417    type MemStore = MemoryStore<[u8; 32]>;
418
419    // reference trees
420    // proving (2), providing [0] w/ static {1}:
421    //         x
422    //    1      \
423    // [0]  (2)   4
424    //
425    // proving (2), providing [0, 5, 9] w/ static {3, 9}:
426    //                   7
427    //         3                  x
428    //     1       [5]      [9]     \
429    // [0]  (2)   4   6    8   10    12
430    //
431    // proving (26), providing [7, 19, 21, 24] with static {7, 19}:
432    //                               15
433    //              [7]                                   \
434    //       3               11              [19]          |
435    //   1       5       9       13      17       [21]     25
436    // 0   2   4   6   8  10   12  14  16  18    20  22 [24](26)
437
438    fn new() -> (MemStore, TestLog) {
439        let mut store = MemStore::default();
440        let log = TestLog::new(&"hello world");
441        let log_head = *log.head();
442        store.set_leaf(log.head_id(), log_head).unwrap();
443        (store, log)
444    }
445
446    #[test]
447    fn creation() {
448        let (_, log) = new();
449        assert_eq!(log.head_id(), TreeID::from(0));
450        assert_eq!(log.len(), 1);
451        assert_eq!(log.head(), log.root());
452    }
453
454    #[test]
455    fn prove_and_verify() {
456        let (mut store, mut log) = new();
457        let proof = log.prove(0, &store).unwrap();
458        assert!(log.verify(0, log.head(), &proof).expect("failed to verify"));
459
460        for idx in 1..=128u64 {
461            let mut entry = alloc::string::String::new();
462            core::fmt::write(&mut entry, format_args!("hello world x{}", idx))
463                .expect("failed to generate entry");
464
465            log.append(&entry, &mut store)
466                .expect("failed to append entry to log and store");
467            assert_eq!(log.len(), idx + 1);
468
469            let proof = log
470                .prove(idx, &store)
471                .expect("failed to generate inclusion proof from log and store");
472            assert!(
473                log.verify(idx, log.head(), &proof)
474                    .expect("failed to verify"),
475                "failed verification for log of length {}",
476                idx + 1
477            );
478        }
479    }
480}