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, ¤t));
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, ¤t), (sibling_id, &sibling)),
390 Greater => D::node_digest((sibling_id, &sibling), (current_id, ¤t)),
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}