From d2d945edcb7cd67ac08fc4af572636ef1b03eb2b Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 6 Apr 2023 17:07:00 -0600 Subject: [PATCH] Expose `MerklePath` type without a feature flag. --- incrementalmerkletree/src/lib.rs | 68 ++++++++++++++++++++++++++++ incrementalmerkletree/src/witness.rs | 64 ++++---------------------- 2 files changed, 76 insertions(+), 56 deletions(-) diff --git a/incrementalmerkletree/src/lib.rs b/incrementalmerkletree/src/lib.rs index 236b4e7..36c08d1 100644 --- a/incrementalmerkletree/src/lib.rs +++ b/incrementalmerkletree/src/lib.rs @@ -464,6 +464,53 @@ impl<'a> From<&'a Address> for Option { } } +/// A path from a position in a particular commitment tree to the root of that tree. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MerklePath { + path_elems: Vec, + position: Position, +} + +impl MerklePath { + /// Constructs a Merkle path directly from a path and position. + #[allow(clippy::result_unit_err)] + pub fn from_parts(path_elems: Vec, position: Position) -> Result { + if path_elems.len() == usize::from(DEPTH) { + Ok(MerklePath { + path_elems, + position, + }) + } else { + Err(()) + } + } + + pub fn path_elems(&self) -> &[H] { + &self.path_elems + } + + pub fn position(&self) -> Position { + self.position + } +} + +impl MerklePath { + /// Returns the root of the tree corresponding to this path applied to `leaf`. + pub fn root(&self, leaf: H) -> H { + self.path_elems + .iter() + .enumerate() + .fold(leaf, |root, (i, h)| { + let level = Level(i as u8); + if self.position.0 >> i & 0x1 == 0 { + H::combine(level, &root, h) + } else { + H::combine(level, h, &root) + } + }) + } +} + /// A trait describing the operations that make a type suitable for use as /// a leaf or node value in a merkle tree. pub trait Hashable: Sized + core::fmt::Debug { @@ -480,6 +527,8 @@ pub trait Hashable: Sized + core::fmt::Debug { #[cfg(test)] pub(crate) mod tests { + use crate::MerklePath; + use super::{Address, Level, Position, Source}; use core::ops::Range; use either::Either; @@ -663,4 +712,23 @@ pub(crate) mod tests { Either::Left(Address::from_parts(Level(5), 1)) ); } + + #[test] + fn merkle_path_root() { + let path: MerklePath = MerklePath::from_parts( + vec!["a".to_string(), "cd".to_string(), "efgh".to_string()], + Position(1), + ) + .unwrap(); + + assert_eq!(path.root("b".to_string()), "abcdefgh".to_string()); + + let path: MerklePath = MerklePath::from_parts( + vec!["d".to_string(), "ab".to_string(), "efgh".to_string()], + Position(2), + ) + .unwrap(); + + assert_eq!(path.root("c".to_string()), "abcdefgh".to_string()); + } } diff --git a/incrementalmerkletree/src/witness.rs b/incrementalmerkletree/src/witness.rs index 88a9abd..1d3ac8b 100644 --- a/incrementalmerkletree/src/witness.rs +++ b/incrementalmerkletree/src/witness.rs @@ -3,57 +3,9 @@ use std::iter::repeat; use crate::{ frontier::{CommitmentTree, PathFiller}, - Hashable, Level, + Hashable, Level, MerklePath, Position, }; -/// A path from a position in a particular commitment tree to the root of that tree. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct MerklePath { - auth_path: Vec<(H, bool)>, - position: u64, -} - -impl MerklePath { - /// Constructs a Merkle path directly from a path and position. - #[allow(clippy::result_unit_err)] - pub fn from_parts(auth_path: Vec<(H, bool)>, position: u64) -> Result { - if auth_path.len() == usize::from(DEPTH) { - Ok(MerklePath { - auth_path, - position, - }) - } else { - Err(()) - } - } - - pub fn auth_path(&self) -> &[(H, bool)] { - &self.auth_path - } - - pub fn position(&self) -> u64 { - self.position - } -} - -impl MerklePath { - /// Returns the root of the tree corresponding to this path applied to `leaf`. - pub fn root(&self, leaf: H) -> H { - self.auth_path - .iter() - .enumerate() - .fold(leaf, |root, (i, (p, leaf_is_on_right))| { - let level = u8::try_from(i) - .expect("Parents list length may not exceed what is representable by an u8") - .into(); - match leaf_is_on_right { - false => H::combine(level, &root, p), - true => H::combine(level, p, &root), - } - }) - } -} - /// An updatable witness to a path from a position in a particular [`CommitmentTree`]. /// /// Appending the same commitments in the same order to both the original @@ -131,8 +83,8 @@ impl IncrementalWitness { } /// Returns the position of the witnessed leaf node in the commitment tree. - pub fn position(&self) -> usize { - self.tree.size() - 1 + pub fn position(&self) -> Position { + Position::from(self.tree.size() - 1) } /// Finds the next "depth" of an unfilled subtree. @@ -233,9 +185,9 @@ impl IncrementalWitness { if let Some(node) = &self.tree.left { if self.tree.right.is_some() { - auth_path.push((node.clone(), true)); + auth_path.push(node.clone()); } else { - auth_path.push((filler.next(0.into()), false)); + auth_path.push(filler.next(0.into())); } } else { // Can't create an authentication path for the beginning of the tree @@ -251,13 +203,13 @@ impl IncrementalWitness { .enumerate() { auth_path.push(match p { - Some(node) => (node.clone(), true), - None => (filler.next(Level::from((i + 1) as u8)), false), + Some(node) => node.clone(), + None => filler.next(Level::from((i + 1) as u8)), }); } assert_eq!(auth_path.len(), usize::from(depth)); - MerklePath::from_parts(auth_path, self.position() as u64).ok() + MerklePath::from_parts(auth_path, self.position()).ok() } }