From 7da823e4ab8b36de264239203fe5e1bf8f6c3640 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Wed, 23 Oct 2024 21:50:46 -0700 Subject: [PATCH] added abort test comparison for lazy and cascade --- crates/trees/src/cascading/mod.rs | 2 +- examples/abort/main.rs | 162 ++++++++++++++++++++++++++++++ examples/abort_test/main.rs | 67 ------------ src/util.rs | 4 +- 4 files changed, 165 insertions(+), 70 deletions(-) create mode 100644 examples/abort/main.rs delete mode 100644 examples/abort_test/main.rs diff --git a/crates/trees/src/cascading/mod.rs b/crates/trees/src/cascading/mod.rs index 9e36f4f..5852624 100644 --- a/crates/trees/src/cascading/mod.rs +++ b/crates/trees/src/cascading/mod.rs @@ -405,7 +405,7 @@ where for subtree_power in first_subtree_power..=last_subtree_power { // We have a special case for subtree_power = 0 // because the subtree is completely empty. - // This represents the very borrow left of the tree. + // This represents the very bottom left of the tree. // parent_index represents the index of the parent node of the subtree. // It is the power of two on the left most branch of the tree. let parent_index = if subtree_power == 0 { diff --git a/examples/abort/main.rs b/examples/abort/main.rs new file mode 100644 index 0000000..09317b2 --- /dev/null +++ b/examples/abort/main.rs @@ -0,0 +1,162 @@ +use color_eyre::Result; +use hasher::Hasher; +use itertools::Itertools; +use poseidon::Poseidon; +use rand::Rng; +use ruint::aliases::U256; +use std::{env, process::Stdio}; +use storage::MmapVec; +use trees::cascading::CascadingMerkleTree; +use trees::lazy::LazyMerkleTree; + +static FILE_PATH: &str = "target/debug/examples/abort.mmap"; +static BIN_PATH: &str = "target/debug/examples/abort"; +static ITERATIONS: usize = 20; +static INITIAL_LEAVES: usize = 10; + +/// A test that interupts writes to the mmap merkle trees +/// to simulate a crash, and to check if restoring the tree +/// is successful +/// +/// Run this binary with no arguments to run the tests +/// `RUSTFLAGS="-C panic=abort" cargo run --example abort` +#[tokio::main] +async fn main() -> Result<()> { + let args: Vec = env::args().collect(); + + // initialize + if args.len() == 1 { + run()?; + } else if args.len() == 2 && args[1] == "cascade_restore" { + cascade_restore()?; + } else if args.len() == 2 && args[1] == "cascade_init" { + cascade_init()?; + } else if args.len() == 2 && args[1] == "lazy_restore" { + lazy_restore()?; + } else if args.len() == 2 && args[1] == "lazy_init" { + lazy_init()?; + } else { + panic!("invalid arguments"); + } + + Ok(()) +} + +fn run() -> Result<()> { + let cascade_failures = run_test("cascade")?; + let lazy_failures = run_test("lazy")?; + + println!("\nAll Tests Complete!"); + println!("Cascade failure rate: {cascade_failures}/{ITERATIONS}"); + println!("Lazy failure rate: {lazy_failures}/{ITERATIONS}"); + + Ok(()) +} + +fn run_test(prefix: &str) -> Result { + let mut failures = 0u32; + println!("Running {prefix} test"); + for i in 0..ITERATIONS { + println!("\n{prefix} run #{i}"); + let output = std::process::Command::new(BIN_PATH) + .arg(format!("{prefix}_init")) + .stdout(Stdio::piped()) + .output()?; + let stdout = String::from_utf8(output.stdout)?; + print!("{}", stdout); + let stderr = String::from_utf8(output.stderr)?; + print!("{}", stderr); + + let output = std::process::Command::new(BIN_PATH) + .arg(format!("{prefix}_restore")) + .stdout(Stdio::piped()) + .output()?; + let stdout = String::from_utf8(output.stdout)?; + print!("{}", stdout); + let stderr = String::from_utf8(output.stderr)?; + if !stderr.is_empty() { + print!("{}", stderr); + failures += 1; + } + } + + println!("\n{prefix} test complete"); + Ok(failures) +} + +fn cascade_init() -> Result<()> { + let mmap_vec: MmapVec<::Hash> = + unsafe { MmapVec::create_from_path(FILE_PATH)? }; + + let leaves = vec![Default::default(); INITIAL_LEAVES]; + + let mut tree = CascadingMerkleTree::::new_with_leaves( + mmap_vec, + 30, + &Default::default(), + &leaves, + ); + + let _handle = tokio::spawn(async move { + for _ in 0..15 { + tree.push(U256::from(2)).unwrap(); + } + }); + + let mut rng = rand::thread_rng(); + let millis: u64 = rng.gen_range(0..50); + std::thread::sleep(std::time::Duration::from_millis(millis)); + + panic!(""); +} + +fn cascade_restore() -> Result<()> { + let file = std::fs::OpenOptions::new() + .read(true) + .write(true) + .open(FILE_PATH)?; + + let mmap_vec: MmapVec<::Hash> = unsafe { MmapVec::restore(file)? }; + let tree = CascadingMerkleTree::::restore(mmap_vec, 30, &Default::default())?; + println!("tree length: {}", tree.num_leaves()); + tree.validate()?; + + Ok(()) +} + +fn lazy_init() -> Result<()> { + let leaves = vec![Default::default(); INITIAL_LEAVES]; + + let mut tree = LazyMerkleTree::::new_mmapped_with_dense_prefix_with_init_values( + 30, + 13, + &Default::default(), + &leaves, + FILE_PATH, + )?; + + let _handle = std::thread::spawn(move || { + for i in INITIAL_LEAVES..(INITIAL_LEAVES + 15) { + tree = tree.update_with_mutation(i, &U256::from(2)); + } + }); + + let mut rng = rand::thread_rng(); + let millis: u64 = rng.gen_range(0..50); + std::thread::sleep(std::time::Duration::from_millis(millis)); + + panic!(""); +} + +fn lazy_restore() -> Result<()> { + let tree = LazyMerkleTree::::attempt_dense_mmap_restore( + 30, + 13, + &Default::default(), + FILE_PATH, + )?; + + let leaves = tree.leaves().take(20).collect_vec(); + println!("tree length: {leaves:?}"); + Ok(()) +} diff --git a/examples/abort_test/main.rs b/examples/abort_test/main.rs deleted file mode 100644 index c43e184..0000000 --- a/examples/abort_test/main.rs +++ /dev/null @@ -1,67 +0,0 @@ -use std::{env, process::Stdio}; - -use color_eyre::Result; - -use hasher::Hasher; -use storage::MmapVec; -use trees::cascading::CascadingMerkleTree; - -#[derive(Debug, Clone, PartialEq, Eq)] -struct TestHasher; -impl Hasher for TestHasher { - type Hash = usize; - - fn hash_node(left: &Self::Hash, right: &Self::Hash) -> Self::Hash { - left + right - } -} - -fn main() -> Result<()> { - let args: Vec = env::args().collect(); - - let tempfile = tempfile::tempfile()?; - let mmap_vec: MmapVec<::Hash> = - unsafe { MmapVec::restore(tempfile.try_clone()?)? }; - - // initialize - if args.len() == 1 { - println!("initializing\n"); - let leaves = vec![1; 1_000_000]; - let _ = CascadingMerkleTree::::new_with_leaves(mmap_vec, 30, &1, &leaves); - for i in 0..100 { - println!("running interation {}", i); - let output = std::process::Command::new("target/debug/examples/abort_test") - .arg("child") - .stdout(Stdio::piped()) - .output()?; - let stdout = String::from_utf8(output.stdout)?; - println!("stdout:\n{}", stdout); - let stderr = String::from_utf8(output.stderr)?; - println!("stderr:\n{}", stderr); - } - return Ok(()); - } - - drop(mmap_vec); - - let mmap_vec: MmapVec<::Hash> = unsafe { MmapVec::restore(tempfile)? }; - - println!("restoring"); - let mut tree = CascadingMerkleTree::::restore(mmap_vec, 30, &1)?; - tree.push(2).unwrap(); - - println!("tree length: {}", tree.num_leaves()); - - println!("validating"); - tree.validate()?; - - println!("spawning"); - std::thread::spawn(move || loop { - tree.push(2).unwrap(); - }); - std::thread::sleep(std::time::Duration::from_millis(1)); - - println!("aborting"); - panic!(); - // abort(); -} diff --git a/src/util.rs b/src/util.rs index 8078aa5..46afc6c 100644 --- a/src/util.rs +++ b/src/util.rs @@ -69,7 +69,7 @@ pub(crate) fn deserialize_bytes<'de, const N: usize, D: Deserializer<'de>>( ) -> Result<[u8; N], D::Error> { if deserializer.is_human_readable() { struct StrVisitor; - impl<'de, const N: usize> Visitor<'de> for StrVisitor { + impl Visitor<'_> for StrVisitor { type Value = [u8; N]; fn expecting(&self, formatter: &mut Formatter) -> FmtResult { @@ -86,7 +86,7 @@ pub(crate) fn deserialize_bytes<'de, const N: usize, D: Deserializer<'de>>( deserializer.deserialize_str(StrVisitor) } else { struct ByteVisitor; - impl<'de, const N: usize> Visitor<'de> for ByteVisitor { + impl Visitor<'_> for ByteVisitor { type Value = [u8; N]; fn expecting(&self, formatter: &mut Formatter) -> FmtResult {