From 4e2883c70788aa7190a2fd3815cb544da3f3a767 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 14 Dec 2022 19:33:19 -0700 Subject: [PATCH] Move CombinedTree to incrementalmerkletree::testing --- bridgetree/src/testing.rs | 300 ++------------------------- incrementalmerkletree/src/testing.rs | 285 +++++++++++++++++++++++++ 2 files changed, 301 insertions(+), 284 deletions(-) diff --git a/bridgetree/src/testing.rs b/bridgetree/src/testing.rs index d87a37f..9403142 100644 --- a/bridgetree/src/testing.rs +++ b/bridgetree/src/testing.rs @@ -1,303 +1,35 @@ #[cfg(test)] pub(crate) mod tests { use proptest::prelude::*; - use std::collections::BTreeSet; use std::fmt::Debug; use crate::BridgeTree; use incrementalmerkletree::{ testing::{ - append_str, arb_operation, check_operations, complete_tree::CompleteTree, unmark, - witness, Operation::*, SipHashable, Tree, + arb_operation, check_operations, check_rewind_remove_mark, + check_rewind_remove_mark_consistency, complete_tree::CompleteTree, CombinedTree, + SipHashable, }, - Hashable, Position, + Hashable, }; - // - // Types and utilities for cross-verification property tests - // - - #[derive(Clone)] - pub struct CombinedTree { - inefficient: CompleteTree, - efficient: BridgeTree, - } - - impl CombinedTree { - pub fn new() -> Self { - CombinedTree { - inefficient: CompleteTree::new(DEPTH.into(), 100), - efficient: BridgeTree::new(100), - } - } - } - - impl Tree for CombinedTree { - fn append(&mut self, value: &H) -> bool { - let a = self.inefficient.append(value); - let b = self.efficient.append(value); - assert_eq!(a, b); - a - } - - fn root(&self, checkpoint_depth: usize) -> Option { - let a = self.inefficient.root(checkpoint_depth); - let b = self.efficient.root(checkpoint_depth); - assert_eq!(a, b); - a - } - - fn current_position(&self) -> Option { - let a = self.inefficient.current_position(); - let b = self.efficient.current_position(); - assert_eq!(a, b); - a - } - - fn current_leaf(&self) -> Option<&H> { - let a = self.inefficient.current_leaf(); - let b = self.efficient.current_leaf(); - assert_eq!(a, b); - a - } - - fn get_marked_leaf(&self, position: Position) -> Option<&H> { - let a = self.inefficient.get_marked_leaf(position); - let b = self.efficient.get_marked_leaf(position); - assert_eq!(a, b); - a - } - - fn mark(&mut self) -> Option { - let a = self.inefficient.mark(); - let b = self.efficient.mark(); - assert_eq!(a, b); - let apos = self.inefficient.marked_positions(); - let bpos = self.efficient.marked_positions(); - assert_eq!(apos, bpos); - a - } - - fn marked_positions(&self) -> BTreeSet { - let a = self.inefficient.marked_positions(); - let b = self.efficient.marked_positions(); - assert_eq!(a, b); - a - } - - fn witness(&self, position: Position, as_of_root: &H) -> Option> { - let a = self.inefficient.witness(position, as_of_root); - let b = self.efficient.witness(position, as_of_root); - assert_eq!(a, b); - a - } - - fn remove_mark(&mut self, position: Position) -> bool { - let a = self.inefficient.remove_mark(position); - let b = self.efficient.remove_mark(position); - assert_eq!(a, b); - a - } - - fn checkpoint(&mut self) { - self.inefficient.checkpoint(); - self.efficient.checkpoint(); - } - - fn rewind(&mut self) -> bool { - let a = self.inefficient.rewind(); - let b = self.efficient.rewind(); - assert_eq!(a, b); - a - } + fn new_combined_tree( + max_checkpoints: usize, + ) -> CombinedTree, BridgeTree> { + CombinedTree::new( + CompleteTree::new(4, max_checkpoints), + BridgeTree::::new(max_checkpoints), + ) } #[test] - fn test_witness_consistency() { - let samples = vec![ - // Reduced examples - vec![ - append_str("a"), - append_str("b"), - Checkpoint, - Mark, - witness(0, 1), - ], - vec![ - append_str("c"), - append_str("d"), - Mark, - Checkpoint, - witness(1, 1), - ], - vec![ - append_str("e"), - Checkpoint, - Mark, - append_str("f"), - witness(0, 1), - ], - vec![ - append_str("g"), - Mark, - Checkpoint, - unmark(0), - append_str("h"), - witness(0, 0), - ], - vec![ - append_str("i"), - Checkpoint, - Mark, - unmark(0), - append_str("j"), - witness(0, 0), - ], - vec![ - append_str("i"), - Mark, - append_str("j"), - Checkpoint, - append_str("k"), - witness(0, 1), - ], - vec![ - append_str("l"), - Checkpoint, - Mark, - Checkpoint, - append_str("m"), - Checkpoint, - witness(0, 2), - ], - vec![Checkpoint, append_str("n"), Mark, witness(0, 1)], - vec![ - append_str("a"), - Mark, - Checkpoint, - unmark(0), - Checkpoint, - append_str("b"), - witness(0, 1), - ], - vec![ - append_str("a"), - Mark, - append_str("b"), - unmark(0), - Checkpoint, - witness(0, 0), - ], - vec![ - append_str("a"), - Mark, - Checkpoint, - unmark(0), - Checkpoint, - Rewind, - append_str("b"), - witness(0, 0), - ], - vec![ - append_str("a"), - Mark, - Checkpoint, - Checkpoint, - Rewind, - append_str("a"), - unmark(0), - witness(0, 1), - ], - // Unreduced examples - vec![ - append_str("o"), - append_str("p"), - Mark, - append_str("q"), - Checkpoint, - unmark(1), - witness(1, 1), - ], - vec![ - append_str("r"), - append_str("s"), - append_str("t"), - Mark, - Checkpoint, - unmark(2), - Checkpoint, - witness(2, 2), - ], - vec![ - append_str("u"), - Mark, - append_str("v"), - append_str("w"), - Checkpoint, - unmark(0), - append_str("x"), - Checkpoint, - Checkpoint, - witness(0, 3), - ], - ]; - - for (i, sample) in samples.iter().enumerate() { - let tree = CombinedTree::::new(); - let result = check_operations(tree, 4, sample); - assert!( - matches!(result, Ok(())), - "Reference/Test mismatch at index {}: {:?}", - i, - result - ); - } + fn test_rewind_remove_mark() { + check_rewind_remove_mark(new_combined_tree); } - // These check_operations tests cover errors where the test framework itself previously did not - // correctly handle chain state restoration. #[test] fn test_rewind_remove_mark_consistency() { - let samples = vec![ - vec![append_str("x"), Checkpoint, Mark, Rewind, unmark(0)], - vec![ - append_str("d"), - Checkpoint, - Mark, - unmark(0), - Rewind, - unmark(0), - ], - vec![ - append_str("o"), - Checkpoint, - Mark, - Checkpoint, - unmark(0), - Rewind, - Rewind, - ], - vec![ - append_str("s"), - Mark, - append_str("m"), - Checkpoint, - unmark(0), - Rewind, - unmark(0), - unmark(0), - ], - ]; - for (i, sample) in samples.iter().enumerate() { - let tree = CombinedTree::::new(); - let result = check_operations(tree, 4, sample); - assert!( - matches!(result, Ok(())), - "Reference/Test mismatch at index {}: {:?}", - i, - result - ); - } + check_rewind_remove_mark_consistency(new_combined_tree); } proptest! { @@ -310,7 +42,7 @@ pub(crate) mod tests { 1..100 ) ) { - let tree = CombinedTree::::new(); + let tree = new_combined_tree(100); check_operations(tree, 4, &ops)?; } @@ -321,7 +53,7 @@ pub(crate) mod tests { 1..100 ) ) { - let tree = CombinedTree::::new(); + let tree = new_combined_tree(100); check_operations(tree, 4, &ops)?; } } diff --git a/incrementalmerkletree/src/testing.rs b/incrementalmerkletree/src/testing.rs index ab5b221..d9e6743 100644 --- a/incrementalmerkletree/src/testing.rs +++ b/incrementalmerkletree/src/testing.rs @@ -1,4 +1,7 @@ +//! Traits and types used to permit comparison testing between tree implementations. + use core::fmt::Debug; +use core::marker::PhantomData; use proptest::prelude::*; use std::collections::BTreeSet; @@ -384,6 +387,106 @@ pub fn compute_root_from_witness(value: H, position: Position, path cur } +// +// Types and utilities for cross-verification property tests +// + +#[derive(Clone)] +pub struct CombinedTree, E: Tree> { + inefficient: I, + efficient: E, + _phantom: PhantomData, +} + +impl, E: Tree> CombinedTree { + pub fn new(inefficient: I, efficient: E) -> Self { + CombinedTree { + inefficient, + efficient, + _phantom: PhantomData, + } + } +} + +impl, E: Tree> Tree for CombinedTree { + fn append(&mut self, value: &H) -> bool { + let a = self.inefficient.append(value); + let b = self.efficient.append(value); + assert_eq!(a, b); + a + } + + fn root(&self, checkpoint_depth: usize) -> Option { + let a = self.inefficient.root(checkpoint_depth); + let b = self.efficient.root(checkpoint_depth); + assert_eq!(a, b); + a + } + + fn current_position(&self) -> Option { + let a = self.inefficient.current_position(); + let b = self.efficient.current_position(); + assert_eq!(a, b); + a + } + + fn current_leaf(&self) -> Option<&H> { + let a = self.inefficient.current_leaf(); + let b = self.efficient.current_leaf(); + assert_eq!(a, b); + a + } + + fn get_marked_leaf(&self, position: Position) -> Option<&H> { + let a = self.inefficient.get_marked_leaf(position); + let b = self.efficient.get_marked_leaf(position); + assert_eq!(a, b); + a + } + + fn mark(&mut self) -> Option { + let a = self.inefficient.mark(); + let b = self.efficient.mark(); + assert_eq!(a, b); + let apos = self.inefficient.marked_positions(); + let bpos = self.efficient.marked_positions(); + assert_eq!(apos, bpos); + a + } + + fn marked_positions(&self) -> BTreeSet { + let a = self.inefficient.marked_positions(); + let b = self.efficient.marked_positions(); + assert_eq!(a, b); + a + } + + fn witness(&self, position: Position, as_of_root: &H) -> Option> { + let a = self.inefficient.witness(position, as_of_root); + let b = self.efficient.witness(position, as_of_root); + assert_eq!(a, b); + a + } + + fn remove_mark(&mut self, position: Position) -> bool { + let a = self.inefficient.remove_mark(position); + let b = self.efficient.remove_mark(position); + assert_eq!(a, b); + a + } + + fn checkpoint(&mut self) { + self.inefficient.checkpoint(); + self.efficient.checkpoint(); + } + + fn rewind(&mut self) -> bool { + let a = self.inefficient.rewind(); + let b = self.efficient.rewind(); + assert_eq!(a, b); + a + } +} // // Shared example tests // @@ -763,6 +866,188 @@ pub fn check_rewind_remove_mark, F: Fn(usize) -> T>(new_tree: F) } } +pub fn check_witness_consistency, F: Fn(usize) -> T>(new_tree: F) { + let samples = vec![ + // Reduced examples + vec![ + append_str("a"), + append_str("b"), + Checkpoint, + Mark, + witness(0, 1), + ], + vec![ + append_str("c"), + append_str("d"), + Mark, + Checkpoint, + witness(1, 1), + ], + vec![ + append_str("e"), + Checkpoint, + Mark, + append_str("f"), + witness(0, 1), + ], + vec![ + append_str("g"), + Mark, + Checkpoint, + unmark(0), + append_str("h"), + witness(0, 0), + ], + vec![ + append_str("i"), + Checkpoint, + Mark, + unmark(0), + append_str("j"), + witness(0, 0), + ], + vec![ + append_str("i"), + Mark, + append_str("j"), + Checkpoint, + append_str("k"), + witness(0, 1), + ], + vec![ + append_str("l"), + Checkpoint, + Mark, + Checkpoint, + append_str("m"), + Checkpoint, + witness(0, 2), + ], + vec![Checkpoint, append_str("n"), Mark, witness(0, 1)], + vec![ + append_str("a"), + Mark, + Checkpoint, + unmark(0), + Checkpoint, + append_str("b"), + witness(0, 1), + ], + vec![ + append_str("a"), + Mark, + append_str("b"), + unmark(0), + Checkpoint, + witness(0, 0), + ], + vec![ + append_str("a"), + Mark, + Checkpoint, + unmark(0), + Checkpoint, + Rewind, + append_str("b"), + witness(0, 0), + ], + vec![ + append_str("a"), + Mark, + Checkpoint, + Checkpoint, + Rewind, + append_str("a"), + unmark(0), + witness(0, 1), + ], + // Unreduced examples + vec![ + append_str("o"), + append_str("p"), + Mark, + append_str("q"), + Checkpoint, + unmark(1), + witness(1, 1), + ], + vec![ + append_str("r"), + append_str("s"), + append_str("t"), + Mark, + Checkpoint, + unmark(2), + Checkpoint, + witness(2, 2), + ], + vec![ + append_str("u"), + Mark, + append_str("v"), + append_str("w"), + Checkpoint, + unmark(0), + append_str("x"), + Checkpoint, + Checkpoint, + witness(0, 3), + ], + ]; + + for (i, sample) in samples.iter().enumerate() { + let result = check_operations(new_tree(100), 4, sample); + assert!( + matches!(result, Ok(())), + "Reference/Test mismatch at index {}: {:?}", + i, + result + ); + } +} + +pub fn check_rewind_remove_mark_consistency, F: Fn(usize) -> T>(new_tree: F) { + let samples = vec![ + vec![append_str("x"), Checkpoint, Mark, Rewind, unmark(0)], + vec![ + append_str("d"), + Checkpoint, + Mark, + unmark(0), + Rewind, + unmark(0), + ], + vec![ + append_str("o"), + Checkpoint, + Mark, + Checkpoint, + unmark(0), + Rewind, + Rewind, + ], + vec![ + append_str("s"), + Mark, + append_str("m"), + Checkpoint, + unmark(0), + Rewind, + unmark(0), + unmark(0), + ], + ]; + for (i, sample) in samples.iter().enumerate() { + let result = check_operations(new_tree(100), 4, sample); + assert!( + matches!(result, Ok(())), + "Reference/Test mismatch at index {}: {:?}", + i, + result + ); + } +} + #[cfg(test)] pub(crate) mod tests { use crate::{