diff --git a/crypto/crypto/src/merkle_tree/backends/field_element.rs b/crypto/crypto/src/merkle_tree/backends/field_element.rs index 738ae349e..d5d5c32d7 100644 --- a/crypto/crypto/src/merkle_tree/backends/field_element.rs +++ b/crypto/crypto/src/merkle_tree/backends/field_element.rs @@ -70,138 +70,3 @@ where P::hash(left, right) } } - -#[cfg(test)] -mod tests { - use alloc::vec::Vec; - use math::field::{element::FieldElement, goldilocks::GoldilocksField}; - use sha3::{Keccak256, Keccak512, Sha3_256, Sha3_512}; - - use crate::merkle_tree::{backends::field_element::FieldElementBackend, merkle::MerkleTree}; - - type F = GoldilocksField; - type FE = FieldElement; - - #[test] - fn hash_data_field_element_backend_works_with_keccak_256() { - let values: Vec = (1..6).map(FE::from).collect(); - let merkle_tree = - MerkleTree::>::build(&values).unwrap(); - let proof = merkle_tree.get_proof_by_pos(0).unwrap(); - assert!(proof.verify::>( - &merkle_tree.root, - 0, - &values[0] - )); - } - - #[test] - fn hash_data_field_element_backend_works_with_sha3_256() { - let values: Vec = (1..6).map(FE::from).collect(); - let merkle_tree = - MerkleTree::>::build(&values).unwrap(); - let proof = merkle_tree.get_proof_by_pos(0).unwrap(); - assert!(proof.verify::>( - &merkle_tree.root, - 0, - &values[0] - )); - } - - #[test] - fn hash_data_field_element_backend_works_with_keccak_512() { - let values: Vec = (1..6).map(FE::from).collect(); - let merkle_tree = - MerkleTree::>::build(&values).unwrap(); - let proof = merkle_tree.get_proof_by_pos(0).unwrap(); - assert!(proof.verify::>( - &merkle_tree.root, - 0, - &values[0] - )); - } - - #[test] - fn hash_data_field_element_backend_works_with_sha3_512() { - let values: Vec = (1..6).map(FE::from).collect(); - let merkle_tree = - MerkleTree::>::build(&values).unwrap(); - let proof = merkle_tree.get_proof_by_pos(0).unwrap(); - assert!(proof.verify::>( - &merkle_tree.root, - 0, - &values[0] - )); - } - - /// Tests batch proof with a real cryptographic hash (Keccak256). - /// This verifies that proof ordering works correctly with non-commutative hashes. - #[test] - fn batch_proof_with_keccak256_verifies_sparse_leaves() { - let values: Vec = (1..=16).map(FE::from).collect(); - let merkle_tree = - MerkleTree::>::build(&values).unwrap(); - - // Test with sparse leaves across different subtrees - let pos_list: Vec = vec![1, 8, 9, 15]; - let batch_proof = merkle_tree.get_batch_proof(&pos_list).unwrap(); - let leaves_to_verify: Vec = pos_list.iter().map(|&i| values[i]).collect(); - - assert!( - batch_proof.verify::>( - &merkle_tree.root, - &pos_list, - &leaves_to_verify, - 16 - ), - "Batch proof verification failed with Keccak256" - ); - } - - /// Tests batch proof with adjacent leaves using real hash. - #[test] - fn batch_proof_with_keccak256_verifies_adjacent_leaves() { - let values: Vec = (1..=8).map(FE::from).collect(); - let merkle_tree = - MerkleTree::>::build(&values).unwrap(); - - // Adjacent leaves (siblings) - let pos_list: Vec = vec![0, 1]; - let batch_proof = merkle_tree.get_batch_proof(&pos_list).unwrap(); - let leaves_to_verify: Vec = pos_list.iter().map(|&i| values[i]).collect(); - - assert!( - batch_proof.verify::>( - &merkle_tree.root, - &pos_list, - &leaves_to_verify, - 8 - ), - "Batch proof verification failed for adjacent leaves" - ); - } - - /// Tests that batch proof fails with wrong values using real hash. - #[test] - fn batch_proof_with_keccak256_fails_with_wrong_values() { - let values: Vec = (1..=8).map(FE::from).collect(); - let merkle_tree = - MerkleTree::>::build(&values).unwrap(); - - let pos_list: Vec = vec![0, 1]; - let batch_proof = merkle_tree.get_batch_proof(&pos_list).unwrap(); - - // Use wrong values - let wrong_values: Vec = vec![FE::from(999), FE::from(998)]; - - assert!( - !batch_proof.verify::>( - &merkle_tree.root, - &pos_list, - &wrong_values, - 8 - ), - "Batch proof should fail with wrong values" - ); - } -} diff --git a/crypto/crypto/src/merkle_tree/backends/field_element_vector.rs b/crypto/crypto/src/merkle_tree/backends/field_element_vector.rs index 1023b3a03..25ba807c6 100644 --- a/crypto/crypto/src/merkle_tree/backends/field_element_vector.rs +++ b/crypto/crypto/src/merkle_tree/backends/field_element_vector.rs @@ -144,127 +144,3 @@ where P::hash(left, right) } } - -#[cfg(test)] -mod tests { - use math::field::{element::FieldElement, goldilocks::GoldilocksField}; - use sha2::Sha512; - use sha3::{Keccak256, Keccak512, Sha3_256, Sha3_512}; - - use crate::merkle_tree::{ - backends::field_element_vector::FieldElementVectorBackend, merkle::MerkleTree, - }; - - type F = GoldilocksField; - type FE = FieldElement; - - #[test] - fn hash_data_field_element_backend_works_with_sha3_256() { - let values = [ - vec![FE::from(2u64), FE::from(11u64)], - vec![FE::from(3u64), FE::from(14u64)], - vec![FE::from(4u64), FE::from(7u64)], - vec![FE::from(5u64), FE::from(3u64)], - vec![FE::from(6u64), FE::from(5u64)], - vec![FE::from(7u64), FE::from(16u64)], - vec![FE::from(8u64), FE::from(19u64)], - vec![FE::from(9u64), FE::from(21u64)], - ]; - let merkle_tree = - MerkleTree::>::build(&values).unwrap(); - let proof = merkle_tree.get_proof_by_pos(0).unwrap(); - assert!(proof.verify::>( - &merkle_tree.root, - 0, - &values[0] - )); - } - - #[test] - fn hash_data_field_element_backend_works_with_keccak256() { - let values = [ - vec![FE::from(2u64), FE::from(11u64)], - vec![FE::from(3u64), FE::from(14u64)], - vec![FE::from(4u64), FE::from(7u64)], - vec![FE::from(5u64), FE::from(3u64)], - vec![FE::from(6u64), FE::from(5u64)], - vec![FE::from(7u64), FE::from(16u64)], - vec![FE::from(8u64), FE::from(19u64)], - vec![FE::from(9u64), FE::from(21u64)], - ]; - let merkle_tree = - MerkleTree::>::build(&values).unwrap(); - let proof = merkle_tree.get_proof_by_pos(0).unwrap(); - assert!(proof.verify::>( - &merkle_tree.root, - 0, - &values[0] - )); - } - - #[test] - fn hash_data_field_element_backend_works_with_sha3_512() { - let values = [ - vec![FE::from(2u64), FE::from(11u64)], - vec![FE::from(3u64), FE::from(14u64)], - vec![FE::from(4u64), FE::from(7u64)], - vec![FE::from(5u64), FE::from(3u64)], - vec![FE::from(6u64), FE::from(5u64)], - vec![FE::from(7u64), FE::from(16u64)], - vec![FE::from(8u64), FE::from(19u64)], - vec![FE::from(9u64), FE::from(21u64)], - ]; - let merkle_tree = - MerkleTree::>::build(&values).unwrap(); - let proof = merkle_tree.get_proof_by_pos(0).unwrap(); - assert!(proof.verify::>( - &merkle_tree.root, - 0, - &values[0] - )); - } - - #[test] - fn hash_data_field_element_backend_works_with_keccak512() { - let values = [ - vec![FE::from(2u64), FE::from(11u64)], - vec![FE::from(3u64), FE::from(14u64)], - vec![FE::from(4u64), FE::from(7u64)], - vec![FE::from(5u64), FE::from(3u64)], - vec![FE::from(6u64), FE::from(5u64)], - vec![FE::from(7u64), FE::from(16u64)], - vec![FE::from(8u64), FE::from(19u64)], - vec![FE::from(9u64), FE::from(21u64)], - ]; - let merkle_tree = - MerkleTree::>::build(&values).unwrap(); - let proof = merkle_tree.get_proof_by_pos(0).unwrap(); - assert!(proof.verify::>( - &merkle_tree.root, - 0, - &values[0] - )); - } - - #[test] - fn hash_data_field_element_backend_works_with_sha2_512() { - let values = [ - vec![FE::from(2u64), FE::from(11u64)], - vec![FE::from(3u64), FE::from(14u64)], - vec![FE::from(4u64), FE::from(7u64)], - vec![FE::from(5u64), FE::from(3u64)], - vec![FE::from(6u64), FE::from(5u64)], - vec![FE::from(7u64), FE::from(16u64)], - vec![FE::from(8u64), FE::from(19u64)], - vec![FE::from(9u64), FE::from(21u64)], - ]; - let merkle_tree = - MerkleTree::>::build(&values).unwrap(); - let proof = merkle_tree.get_proof_by_pos(0).unwrap(); - assert!(proof.verify::>( - &merkle_tree.root, - 0, - &values[0] - )); - } -} diff --git a/crypto/crypto/src/merkle_tree/merkle.rs b/crypto/crypto/src/merkle_tree/merkle.rs index 4ea0e5411..8ce08ddd2 100644 --- a/crypto/crypto/src/merkle_tree/merkle.rs +++ b/crypto/crypto/src/merkle_tree/merkle.rs @@ -57,7 +57,7 @@ pub struct MerkleTree { nodes: Vec, #[cfg(feature = "disk-spill")] #[cfg_attr(feature = "serde", serde(skip))] - mmap_backing: Option, + pub(crate) mmap_backing: Option, } // `mmap_backing` is `#[serde(skip)]` and `spill_nodes_to_disk` empties `nodes`, @@ -352,39 +352,3 @@ where Ok(()) } } - -#[cfg(all(test, feature = "serde", feature = "disk-spill"))] -mod disk_spill_serde_tests { - use super::*; - use crate::merkle_tree::backends::field_element::FieldElementBackend; - use math::field::{element::FieldElement, goldilocks::GoldilocksField}; - use sha3::Keccak256; - - type F = GoldilocksField; - type FE = FieldElement; - type Backend = FieldElementBackend; - - /// Serializing a spilled MerkleTree must produce identical bytes to - /// serializing the same tree before spilling, and round-trip back to an - /// equal tree. - #[test] - fn test_serialize_spilled_merkle_tree_matches_unspilled() { - let values: Vec = (1..17).map(FE::from).collect(); - let unspilled = MerkleTree::::build(&values).expect("build merkle tree"); - let unspilled_bytes = bincode::serialize(&unspilled).expect("serialize unspilled"); - - let mut spilled = MerkleTree::::build(&values).expect("build merkle tree"); - spilled.spill_nodes_to_disk().expect("spill_nodes_to_disk"); - let spilled_bytes = bincode::serialize(&spilled).expect("serialize spilled"); - - assert_eq!( - spilled_bytes, unspilled_bytes, - "spilled and unspilled trees must serialize to identical bytes" - ); - - let restored: MerkleTree = - bincode::deserialize(&spilled_bytes).expect("deserialize spilled bytes"); - assert!(restored.mmap_backing.is_none()); - assert_eq!(restored.root, unspilled.root); - } -} diff --git a/crypto/crypto/src/tests/field_element_tests.rs b/crypto/crypto/src/tests/field_element_tests.rs new file mode 100644 index 000000000..2df6687a7 --- /dev/null +++ b/crypto/crypto/src/tests/field_element_tests.rs @@ -0,0 +1,118 @@ +//! Tests for the field-element Merkle tree backend. + +use alloc::vec::Vec; +use math::field::{element::FieldElement, goldilocks::GoldilocksField}; +use sha3::{Keccak256, Keccak512, Sha3_256, Sha3_512}; + +use crate::merkle_tree::{backends::field_element::FieldElementBackend, merkle::MerkleTree}; + +type F = GoldilocksField; +type FE = FieldElement; + +#[test] +fn hash_data_field_element_backend_works_with_keccak_256() { + let values: Vec = (1..6).map(FE::from).collect(); + let merkle_tree = MerkleTree::>::build(&values).unwrap(); + let proof = merkle_tree.get_proof_by_pos(0).unwrap(); + assert!(proof.verify::>( + &merkle_tree.root, + 0, + &values[0] + )); +} + +#[test] +fn hash_data_field_element_backend_works_with_sha3_256() { + let values: Vec = (1..6).map(FE::from).collect(); + let merkle_tree = MerkleTree::>::build(&values).unwrap(); + let proof = merkle_tree.get_proof_by_pos(0).unwrap(); + assert!(proof.verify::>(&merkle_tree.root, 0, &values[0])); +} + +#[test] +fn hash_data_field_element_backend_works_with_keccak_512() { + let values: Vec = (1..6).map(FE::from).collect(); + let merkle_tree = MerkleTree::>::build(&values).unwrap(); + let proof = merkle_tree.get_proof_by_pos(0).unwrap(); + assert!(proof.verify::>( + &merkle_tree.root, + 0, + &values[0] + )); +} + +#[test] +fn hash_data_field_element_backend_works_with_sha3_512() { + let values: Vec = (1..6).map(FE::from).collect(); + let merkle_tree = MerkleTree::>::build(&values).unwrap(); + let proof = merkle_tree.get_proof_by_pos(0).unwrap(); + assert!(proof.verify::>(&merkle_tree.root, 0, &values[0])); +} + +/// Tests batch proof with a real cryptographic hash (Keccak256). +/// This verifies that proof ordering works correctly with non-commutative hashes. +#[test] +fn batch_proof_with_keccak256_verifies_sparse_leaves() { + let values: Vec = (1..=16).map(FE::from).collect(); + let merkle_tree = MerkleTree::>::build(&values).unwrap(); + + // Test with sparse leaves across different subtrees + let pos_list: Vec = vec![1, 8, 9, 15]; + let batch_proof = merkle_tree.get_batch_proof(&pos_list).unwrap(); + let leaves_to_verify: Vec = pos_list.iter().map(|&i| values[i]).collect(); + + assert!( + batch_proof.verify::>( + &merkle_tree.root, + &pos_list, + &leaves_to_verify, + 16 + ), + "Batch proof verification failed with Keccak256" + ); +} + +/// Tests batch proof with adjacent leaves using real hash. +#[test] +fn batch_proof_with_keccak256_verifies_adjacent_leaves() { + let values: Vec = (1..=8).map(FE::from).collect(); + let merkle_tree = MerkleTree::>::build(&values).unwrap(); + + // Adjacent leaves (siblings) + let pos_list: Vec = vec![0, 1]; + let batch_proof = merkle_tree.get_batch_proof(&pos_list).unwrap(); + let leaves_to_verify: Vec = pos_list.iter().map(|&i| values[i]).collect(); + + assert!( + batch_proof.verify::>( + &merkle_tree.root, + &pos_list, + &leaves_to_verify, + 8 + ), + "Batch proof verification failed for adjacent leaves" + ); +} + +/// Tests that batch proof fails with wrong values using real hash. +#[test] +fn batch_proof_with_keccak256_fails_with_wrong_values() { + let values: Vec = (1..=8).map(FE::from).collect(); + let merkle_tree = MerkleTree::>::build(&values).unwrap(); + + let pos_list: Vec = vec![0, 1]; + let batch_proof = merkle_tree.get_batch_proof(&pos_list).unwrap(); + + // Use wrong values + let wrong_values: Vec = vec![FE::from(999), FE::from(998)]; + + assert!( + !batch_proof.verify::>( + &merkle_tree.root, + &pos_list, + &wrong_values, + 8 + ), + "Batch proof should fail with wrong values" + ); +} diff --git a/crypto/crypto/src/tests/field_element_vector_tests.rs b/crypto/crypto/src/tests/field_element_vector_tests.rs new file mode 100644 index 000000000..145e3f463 --- /dev/null +++ b/crypto/crypto/src/tests/field_element_vector_tests.rs @@ -0,0 +1,122 @@ +//! Tests for the field-element-vector Merkle tree backend. + +use math::field::{element::FieldElement, goldilocks::GoldilocksField}; +use sha2::Sha512; +use sha3::{Keccak256, Keccak512, Sha3_256, Sha3_512}; + +use crate::merkle_tree::{ + backends::field_element_vector::FieldElementVectorBackend, merkle::MerkleTree, +}; + +type F = GoldilocksField; +type FE = FieldElement; + +#[test] +fn hash_data_field_element_backend_works_with_sha3_256() { + let values = [ + vec![FE::from(2u64), FE::from(11u64)], + vec![FE::from(3u64), FE::from(14u64)], + vec![FE::from(4u64), FE::from(7u64)], + vec![FE::from(5u64), FE::from(3u64)], + vec![FE::from(6u64), FE::from(5u64)], + vec![FE::from(7u64), FE::from(16u64)], + vec![FE::from(8u64), FE::from(19u64)], + vec![FE::from(9u64), FE::from(21u64)], + ]; + let merkle_tree = + MerkleTree::>::build(&values).unwrap(); + let proof = merkle_tree.get_proof_by_pos(0).unwrap(); + assert!(proof.verify::>( + &merkle_tree.root, + 0, + &values[0] + )); +} + +#[test] +fn hash_data_field_element_backend_works_with_keccak256() { + let values = [ + vec![FE::from(2u64), FE::from(11u64)], + vec![FE::from(3u64), FE::from(14u64)], + vec![FE::from(4u64), FE::from(7u64)], + vec![FE::from(5u64), FE::from(3u64)], + vec![FE::from(6u64), FE::from(5u64)], + vec![FE::from(7u64), FE::from(16u64)], + vec![FE::from(8u64), FE::from(19u64)], + vec![FE::from(9u64), FE::from(21u64)], + ]; + let merkle_tree = + MerkleTree::>::build(&values).unwrap(); + let proof = merkle_tree.get_proof_by_pos(0).unwrap(); + assert!(proof.verify::>( + &merkle_tree.root, + 0, + &values[0] + )); +} + +#[test] +fn hash_data_field_element_backend_works_with_sha3_512() { + let values = [ + vec![FE::from(2u64), FE::from(11u64)], + vec![FE::from(3u64), FE::from(14u64)], + vec![FE::from(4u64), FE::from(7u64)], + vec![FE::from(5u64), FE::from(3u64)], + vec![FE::from(6u64), FE::from(5u64)], + vec![FE::from(7u64), FE::from(16u64)], + vec![FE::from(8u64), FE::from(19u64)], + vec![FE::from(9u64), FE::from(21u64)], + ]; + let merkle_tree = + MerkleTree::>::build(&values).unwrap(); + let proof = merkle_tree.get_proof_by_pos(0).unwrap(); + assert!(proof.verify::>( + &merkle_tree.root, + 0, + &values[0] + )); +} + +#[test] +fn hash_data_field_element_backend_works_with_keccak512() { + let values = [ + vec![FE::from(2u64), FE::from(11u64)], + vec![FE::from(3u64), FE::from(14u64)], + vec![FE::from(4u64), FE::from(7u64)], + vec![FE::from(5u64), FE::from(3u64)], + vec![FE::from(6u64), FE::from(5u64)], + vec![FE::from(7u64), FE::from(16u64)], + vec![FE::from(8u64), FE::from(19u64)], + vec![FE::from(9u64), FE::from(21u64)], + ]; + let merkle_tree = + MerkleTree::>::build(&values).unwrap(); + let proof = merkle_tree.get_proof_by_pos(0).unwrap(); + assert!(proof.verify::>( + &merkle_tree.root, + 0, + &values[0] + )); +} + +#[test] +fn hash_data_field_element_backend_works_with_sha2_512() { + let values = [ + vec![FE::from(2u64), FE::from(11u64)], + vec![FE::from(3u64), FE::from(14u64)], + vec![FE::from(4u64), FE::from(7u64)], + vec![FE::from(5u64), FE::from(3u64)], + vec![FE::from(6u64), FE::from(5u64)], + vec![FE::from(7u64), FE::from(16u64)], + vec![FE::from(8u64), FE::from(19u64)], + vec![FE::from(9u64), FE::from(21u64)], + ]; + let merkle_tree = + MerkleTree::>::build(&values).unwrap(); + let proof = merkle_tree.get_proof_by_pos(0).unwrap(); + assert!(proof.verify::>( + &merkle_tree.root, + 0, + &values[0] + )); +} diff --git a/crypto/crypto/src/tests/merkle_tests.rs b/crypto/crypto/src/tests/merkle_tests.rs index 18f853083..ae71d0c51 100644 --- a/crypto/crypto/src/tests/merkle_tests.rs +++ b/crypto/crypto/src/tests/merkle_tests.rs @@ -136,3 +136,39 @@ fn batch_proof_len_is_expected_for_long_pos_list() { let batch_proof = merkle_tree.get_batch_proof(&pos_list).unwrap(); assert_eq!(batch_proof.path.len(), 2); } + +#[cfg(all(test, feature = "serde", feature = "disk-spill"))] +mod disk_spill_serde_tests { + use crate::merkle_tree::backends::field_element::FieldElementBackend; + use crate::merkle_tree::merkle::*; + use math::field::{element::FieldElement, goldilocks::GoldilocksField}; + use sha3::Keccak256; + + type F = GoldilocksField; + type FE = FieldElement; + type Backend = FieldElementBackend; + + /// Serializing a spilled MerkleTree must produce identical bytes to + /// serializing the same tree before spilling, and round-trip back to an + /// equal tree. + #[test] + fn test_serialize_spilled_merkle_tree_matches_unspilled() { + let values: Vec = (1..17).map(FE::from).collect(); + let unspilled = MerkleTree::::build(&values).expect("build merkle tree"); + let unspilled_bytes = bincode::serialize(&unspilled).expect("serialize unspilled"); + + let mut spilled = MerkleTree::::build(&values).expect("build merkle tree"); + spilled.spill_nodes_to_disk().expect("spill_nodes_to_disk"); + let spilled_bytes = bincode::serialize(&spilled).expect("serialize spilled"); + + assert_eq!( + spilled_bytes, unspilled_bytes, + "spilled and unspilled trees must serialize to identical bytes" + ); + + let restored: MerkleTree = + bincode::deserialize(&spilled_bytes).expect("deserialize spilled bytes"); + assert!(restored.mmap_backing.is_none()); + assert_eq!(restored.root, unspilled.root); + } +} diff --git a/crypto/crypto/src/tests/mod.rs b/crypto/crypto/src/tests/mod.rs index 9b9952e7f..96bf36e92 100644 --- a/crypto/crypto/src/tests/mod.rs +++ b/crypto/crypto/src/tests/mod.rs @@ -1,4 +1,6 @@ pub mod default_transcript_tests; +pub mod field_element_tests; +pub mod field_element_vector_tests; pub mod merkle_proof_tests; pub mod merkle_tests; pub mod merkle_utils_tests;