diff --git a/Cargo.lock b/Cargo.lock index fc0a411e..208c4d93 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -56,6 +56,7 @@ dependencies = [ "getrandom", "pkg-config", "rust-embed", + "sled", "tempfile", "wasmer", ] @@ -463,12 +464,10 @@ name = "common" version = "0.1.0" dependencies = [ "acvm", - "blake2", "dirs 3.0.2", "futures-util", "indicatif", "reqwest", - "sled", "tokio", ] diff --git a/acvm_backend_barretenberg/Cargo.toml b/acvm_backend_barretenberg/Cargo.toml index e05e0646..8f11f7dc 100644 --- a/acvm_backend_barretenberg/Cargo.toml +++ b/acvm_backend_barretenberg/Cargo.toml @@ -24,6 +24,7 @@ getrandom = { version = "0.2", optional = true } pkg-config = "0.3" [dev-dependencies] +sled = "0.34.6" tempfile = "*" [features] diff --git a/acvm_backend_barretenberg/src/acvm_interop/pwg.rs b/acvm_backend_barretenberg/src/acvm_interop/pwg.rs index 74a35083..4952ab6e 100644 --- a/acvm_backend_barretenberg/src/acvm_interop/pwg.rs +++ b/acvm_backend_barretenberg/src/acvm_interop/pwg.rs @@ -48,9 +48,14 @@ impl PartialWitnessGenerator for Barretenberg { .map(|input| witness_to_value(initial_witness, input.witness)) .collect(); - let valid_proof = merkle::check_membership(self, hash_path?, root, index, leaf); - - let result = if valid_proof { + let computed_merkle_root = merkle::compute_merkle_root( + |left, right| self.compress_native(left, right), + hash_path?, + index, + leaf, + ); + + let result = if &computed_merkle_root == root { FieldElement::one() } else { FieldElement::zero() diff --git a/acvm_backend_barretenberg/src/acvm_interop/pwg/merkle.rs b/acvm_backend_barretenberg/src/acvm_interop/pwg/merkle.rs index a33f168f..642d8948 100644 --- a/acvm_backend_barretenberg/src/acvm_interop/pwg/merkle.rs +++ b/acvm_backend_barretenberg/src/acvm_interop/pwg/merkle.rs @@ -1,131 +1,36 @@ -use crate::{pedersen::Pedersen, Barretenberg}; -use common::{acvm::FieldElement, merkle::PathHasher}; +use common::acvm::FieldElement; -impl PathHasher for Barretenberg { - fn hash(&self, left: &FieldElement, right: &FieldElement) -> FieldElement { - self.compress_native(left, right) - } - - fn new() -> Self { - Barretenberg::new() - } -} - -// TODO: alter this method so that it only processes one hash per level rather than overriding -// the one of leaves for each level of the hash path -pub(super) fn check_membership( - path_hasher: &impl PathHasher, +pub(super) fn compute_merkle_root( + hash_func: impl Fn(&FieldElement, &FieldElement) -> FieldElement, hash_path: Vec<&FieldElement>, - root: &FieldElement, index: &FieldElement, leaf: &FieldElement, -) -> bool { - let mut index_bits = index.bits(); +) -> FieldElement { + let mut index_bits: Vec = index.bits(); index_bits.reverse(); - let mut current = *leaf; - - for (i, path_elem) in hash_path.into_iter().enumerate() { - let path_bit = index_bits[i]; - let (hash_left, hash_right) = if !path_bit { - (current, *path_elem) - } else { - (*path_elem, current) - }; - current = path_hasher.hash(&hash_left, &hash_right); - } - - ¤t == root + assert!( + hash_path.len() <= index_bits.len(), + "hash path exceeds max depth of tree" + ); + index_bits.into_iter().zip(hash_path.into_iter()).fold( + *leaf, + |current_node, (path_bit, path_elem)| { + let (left, right) = if !path_bit { + (¤t_node, path_elem) + } else { + (path_elem, ¤t_node) + }; + hash_func(left, right) + }, + ) } #[cfg(test)] mod tests { - use super::*; - use common::merkle::{MerkleTree, MessageHasher}; - - #[test] - fn basic_interop_initial_root() { - use tempfile::tempdir; - let temp_dir = tempdir().unwrap(); - // Test that the initial root is computed correctly - let tree: MerkleTree = MerkleTree::new(3, &temp_dir); - // Copied from barretenberg by copying the stdout from MemoryTree - let expected_hex = "04ccfbbb859b8605546e03dcaf41393476642859ff7f99446c054b841f0e05c8"; - assert_eq!(tree.root().to_hex(), expected_hex) - } - #[test] - fn basic_interop_hashpath() { - use tempfile::tempdir; - let temp_dir = tempdir().unwrap(); - // Test that the hashpath is correct - let tree: MerkleTree = MerkleTree::new(3, &temp_dir); - - let path = tree.get_hash_path(0); - - let expected_hash_path = vec![ - ( - "1cdcf02431ba623767fe389337d011df1048dcc24b98ed81cec97627bab454a0", - "1cdcf02431ba623767fe389337d011df1048dcc24b98ed81cec97627bab454a0", - ), - ( - "0b5e9666e7323ce925c28201a97ddf4144ac9d148448ed6f49f9008719c1b85b", - "0b5e9666e7323ce925c28201a97ddf4144ac9d148448ed6f49f9008719c1b85b", - ), - ( - "22ec636f8ad30ef78c42b7fe2be4a4cacf5a445cfb5948224539f59a11d70775", - "22ec636f8ad30ef78c42b7fe2be4a4cacf5a445cfb5948224539f59a11d70775", - ), - ]; - - for (got, expected_segment) in path.into_iter().zip(expected_hash_path) { - assert_eq!(got.0.to_hex().as_str(), expected_segment.0); - assert_eq!(got.1.to_hex().as_str(), expected_segment.1) - } - } - - #[test] - fn basic_interop_update() { - // Test that computing the HashPath is correct - use tempfile::tempdir; - let temp_dir = tempdir().unwrap(); - let mut tree: MerkleTree = MerkleTree::new(3, &temp_dir); - - tree.update_message(0, &[0; 64]); - tree.update_message(1, &[1; 64]); - tree.update_message(2, &[2; 64]); - tree.update_message(3, &[3; 64]); - tree.update_message(4, &[4; 64]); - tree.update_message(5, &[5; 64]); - tree.update_message(6, &[6; 64]); - let root = tree.update_message(7, &[7; 64]); - - assert_eq!( - "0ef8e14db4762ebddadb23b2225f93ca200a4c9bd37130b4d028c971bbad16b5", - root.to_hex() - ); - - let path = tree.get_hash_path(2); - - let expected_hash_path = vec![ - ( - "06c2335d6f7acb84bbc7d0892cefebb7ca31169a89024f24814d5785e0d05324", - "12dc36b01cbd8a6248b04e08f0ec91aa6d11a91f030b4a7b1460281859942185", - ), - ( - "1f399ea0d6aaf602c7cbcb6ae8cda0e6b6487836c017163888ed4fd38b548389", - "220dd1b310caa4a6af755b4c893d956c48f31642b487164b258f2973aac2c28f", - ), - ( - "25cbb3084647221ffcb535945bb65bd70e0809834dc7a6d865a3f2bb046cdc29", - "2cc463fc8c9a4eda416f3e490876672f644708dd0330a915f6835d8396fa8f20", - ), - ]; - - for (got, expected_segment) in path.into_iter().zip(expected_hash_path) { - assert_eq!(got.0.to_hex().as_str(), expected_segment.0); - assert_eq!(got.1.to_hex().as_str(), expected_segment.1) - } - } + use crate::merkle::{MerkleTree, MessageHasher}; + use crate::{pedersen::Pedersen, Barretenberg}; + use common::acvm::FieldElement; #[test] fn test_check_membership() { @@ -208,8 +113,15 @@ mod tests { hash_path_ref.push(hash); } let hash_path_ref = hash_path_ref.iter().collect(); - let is_leaf_in_tree = - super::check_membership(&Barretenberg::new(), hash_path_ref, &root, &index, &leaf); + + let bb = Barretenberg::new(); + let computed_merkle_root = super::compute_merkle_root( + |left, right| bb.compress_native(left, right), + hash_path_ref, + &index, + &leaf, + ); + let is_leaf_in_tree = root == computed_merkle_root; assert_eq!( is_leaf_in_tree, test_vector.result, @@ -255,9 +167,14 @@ mod tests { hash_path_ref.push(hash); } let hash_path_ref = hash_path_ref.iter().collect(); - let is_leaf_in_tree = - super::check_membership(&Barretenberg::new(), hash_path_ref, &root, &index, &leaf); + let bb = Barretenberg::new(); + let computed_merkle_root = super::compute_merkle_root( + |left, right| bb.compress_native(left, right), + hash_path_ref, + &index, + &leaf, + ); - assert!(is_leaf_in_tree) + assert_eq!(root, computed_merkle_root) } } diff --git a/acvm_backend_barretenberg/src/lib.rs b/acvm_backend_barretenberg/src/lib.rs index 9ce54c92..7f748ced 100644 --- a/acvm_backend_barretenberg/src/lib.rs +++ b/acvm_backend_barretenberg/src/lib.rs @@ -12,6 +12,8 @@ mod acvm_interop; mod composer; #[cfg(all(feature = "native", test))] mod crs; +#[cfg(test)] +mod merkle; mod pedersen; mod pippenger; mod scalar_mul; diff --git a/common/src/merkle.rs b/acvm_backend_barretenberg/src/merkle.rs similarity index 65% rename from common/src/merkle.rs rename to acvm_backend_barretenberg/src/merkle.rs index 40105356..be912a5f 100644 --- a/common/src/merkle.rs +++ b/acvm_backend_barretenberg/src/merkle.rs @@ -1,14 +1,25 @@ -use acvm::FieldElement; +use crate::{pedersen::Pedersen, Barretenberg}; +use common::acvm::FieldElement; use std::{convert::TryInto, path::Path}; // Hashes the leaves up the path, on the way to the root -pub trait PathHasher { +pub(crate) trait PathHasher { fn new() -> Self; fn hash(&self, left: &FieldElement, right: &FieldElement) -> FieldElement; } +impl PathHasher for Barretenberg { + fn hash(&self, left: &FieldElement, right: &FieldElement) -> FieldElement { + self.compress_native(left, right) + } + + fn new() -> Self { + Barretenberg::new() + } +} + // Hashes the message into a leaf -pub trait MessageHasher { +pub(crate) trait MessageHasher { fn new() -> Self; fn hash(&mut self, msg: &[u8]) -> FieldElement; } @@ -36,15 +47,16 @@ impl MessageHasher for blake2::Blake2s { // // With sparse merkle, one can update at any index -pub(crate) type HashPath = Vec<(FieldElement, FieldElement)>; +type HashPath = Vec<(FieldElement, FieldElement)>; -pub fn flatten_path(path: Vec<(FieldElement, FieldElement)>) -> Vec { +#[allow(dead_code)] +fn flatten_path(path: Vec<(FieldElement, FieldElement)>) -> Vec { path.into_iter() .flat_map(|(left, right)| std::iter::once(left).chain(std::iter::once(right))) .collect() } -pub struct MerkleTree { +pub(crate) struct MerkleTree { depth: u32, total_size: u32, db: sled::Db, @@ -95,6 +107,7 @@ fn insert_preimage(db: &mut sled::Db, index: u32, value: Vec) { tree.insert(index.to_be_bytes(), value).unwrap(); } +#[allow(dead_code)] fn fetch_preimage(db: &sled::Db, index: usize) -> Vec { let tree = db.open_tree("preimages").unwrap(); @@ -122,6 +135,7 @@ fn insert_hash(db: &mut sled::Db, index: u32, hash: FieldElement) { .unwrap(); } +#[allow(dead_code)] fn find_hash_from_value(db: &sled::Db, leaf_value: &FieldElement) -> Option { let tree = db.open_tree("hashes").unwrap(); @@ -137,7 +151,8 @@ fn find_hash_from_value(db: &sled::Db, leaf_value: &FieldElement) -> Option MerkleTree { - pub fn from_path>( + #[allow(dead_code)] + pub(crate) fn from_path>( path: P, barretenberg: PH, msg_hasher: MH, @@ -159,7 +174,8 @@ impl MerkleTree { msg_hasher, } } - pub fn new>(depth: u32, path: P) -> MerkleTree { + + pub(crate) fn new>(depth: u32, path: P) -> MerkleTree { let barretenberg = PH::new(); let mut msg_hasher = MH::new(); @@ -213,7 +229,7 @@ impl MerkleTree { } } - pub fn get_hash_path(&self, mut index: usize) -> HashPath { + pub(crate) fn get_hash_path(&self, mut index: usize) -> HashPath { let mut path = HashPath::with_capacity(self.depth as usize); let mut offset = 0usize; @@ -231,7 +247,7 @@ impl MerkleTree { path } /// Updates the message at index and computes the new tree root - pub fn update_message(&mut self, index: usize, new_message: &[u8]) -> FieldElement { + pub(crate) fn update_message(&mut self, index: usize, new_message: &[u8]) -> FieldElement { let current = self.msg_hasher.hash(new_message); insert_preimage(&mut self.db, index as u32, new_message.to_vec()); @@ -250,19 +266,25 @@ impl MerkleTree { } } - pub fn find_index_from_leaf(&self, leaf_value: &FieldElement) -> Option { + #[allow(dead_code)] + pub(crate) fn find_index_from_leaf(&self, leaf_value: &FieldElement) -> Option { let index = find_hash_from_value(&self.db, leaf_value); index.map(|val| val as usize) } + #[allow(dead_code)] // TODO: this gets updated to be -1 on the latest barretenberg branch - pub fn find_index_for_empty_leaf(&self) -> usize { + pub(crate) fn find_index_for_empty_leaf(&self) -> usize { let index = fetch_empty_index(&self.db); index as usize } /// Update the element at index and compute the new tree root - pub fn update_leaf(&mut self, mut index: usize, mut current: FieldElement) -> FieldElement { + pub(crate) fn update_leaf( + &mut self, + mut index: usize, + mut current: FieldElement, + ) -> FieldElement { // Note that this method does not update the list of messages [preimages]| // use `update_message` to do this self.check_if_index_valid_and_increment(index); @@ -286,15 +308,104 @@ impl MerkleTree { insert_root(&mut self.db, current); current } + + #[allow(dead_code)] /// Gets a message at `index`. This is not the leaf - pub fn get_message_at_index(&self, index: usize) -> Vec { + pub(crate) fn get_message_at_index(&self, index: usize) -> Vec { fetch_preimage(&self.db, index) } - pub fn root(&self) -> FieldElement { + pub(crate) fn root(&self) -> FieldElement { fetch_root(&self.db) } - pub fn depth(&self) -> u32 { + + #[allow(dead_code)] + pub(crate) fn depth(&self) -> u32 { self.depth } } + +#[test] +fn basic_interop_initial_root() { + use tempfile::tempdir; + let temp_dir = tempdir().unwrap(); + // Test that the initial root is computed correctly + let tree: MerkleTree = MerkleTree::new(3, &temp_dir); + // Copied from barretenberg by copying the stdout from MemoryTree + let expected_hex = "04ccfbbb859b8605546e03dcaf41393476642859ff7f99446c054b841f0e05c8"; + assert_eq!(tree.root().to_hex(), expected_hex) +} + +#[test] +fn basic_interop_hashpath() { + use tempfile::tempdir; + let temp_dir = tempdir().unwrap(); + // Test that the hashpath is correct + let tree: MerkleTree = MerkleTree::new(3, &temp_dir); + + let path = tree.get_hash_path(0); + + let expected_hash_path = vec![ + ( + "1cdcf02431ba623767fe389337d011df1048dcc24b98ed81cec97627bab454a0", + "1cdcf02431ba623767fe389337d011df1048dcc24b98ed81cec97627bab454a0", + ), + ( + "0b5e9666e7323ce925c28201a97ddf4144ac9d148448ed6f49f9008719c1b85b", + "0b5e9666e7323ce925c28201a97ddf4144ac9d148448ed6f49f9008719c1b85b", + ), + ( + "22ec636f8ad30ef78c42b7fe2be4a4cacf5a445cfb5948224539f59a11d70775", + "22ec636f8ad30ef78c42b7fe2be4a4cacf5a445cfb5948224539f59a11d70775", + ), + ]; + + for (got, expected_segment) in path.into_iter().zip(expected_hash_path) { + assert_eq!(got.0.to_hex().as_str(), expected_segment.0); + assert_eq!(got.1.to_hex().as_str(), expected_segment.1) + } +} + +#[test] +fn basic_interop_update() { + // Test that computing the HashPath is correct + use tempfile::tempdir; + let temp_dir = tempdir().unwrap(); + let mut tree: MerkleTree = MerkleTree::new(3, &temp_dir); + + tree.update_message(0, &[0; 64]); + tree.update_message(1, &[1; 64]); + tree.update_message(2, &[2; 64]); + tree.update_message(3, &[3; 64]); + tree.update_message(4, &[4; 64]); + tree.update_message(5, &[5; 64]); + tree.update_message(6, &[6; 64]); + let root = tree.update_message(7, &[7; 64]); + + assert_eq!( + "0ef8e14db4762ebddadb23b2225f93ca200a4c9bd37130b4d028c971bbad16b5", + root.to_hex() + ); + + let path = tree.get_hash_path(2); + + let expected_hash_path = vec![ + ( + "06c2335d6f7acb84bbc7d0892cefebb7ca31169a89024f24814d5785e0d05324", + "12dc36b01cbd8a6248b04e08f0ec91aa6d11a91f030b4a7b1460281859942185", + ), + ( + "1f399ea0d6aaf602c7cbcb6ae8cda0e6b6487836c017163888ed4fd38b548389", + "220dd1b310caa4a6af755b4c893d956c48f31642b487164b258f2973aac2c28f", + ), + ( + "25cbb3084647221ffcb535945bb65bd70e0809834dc7a6d865a3f2bb046cdc29", + "2cc463fc8c9a4eda416f3e490876672f644708dd0330a915f6835d8396fa8f20", + ), + ]; + + for (got, expected_segment) in path.into_iter().zip(expected_hash_path) { + assert_eq!(got.0.to_hex().as_str(), expected_segment.0); + assert_eq!(got.1.to_hex().as_str(), expected_segment.1) + } +} diff --git a/common/Cargo.toml b/common/Cargo.toml index b4285e87..52ac264e 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -12,8 +12,6 @@ publish = false [dependencies] acvm = { version = "0.9.0", features = ["bn254"] } -sled = "0.34.6" -blake2 = "0.9.1" dirs = { version = "3.0", optional = true } reqwest = { version = "0.11.16", optional = true, default-features = false, features = ["stream", "rustls-tls"] } tokio = { version = "1.0", optional = true } diff --git a/common/src/lib.rs b/common/src/lib.rs index 9084ec93..e2919745 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -5,7 +5,6 @@ pub mod barretenberg_structures; #[cfg(feature = "std")] pub mod crs; -pub mod merkle; // Re-export acvm pub use acvm;