use super::*;
use snarkvm_console_algorithms::{BHP512, BHP1024, Poseidon};
use snarkvm_console_types::prelude::Console;
type CurrentEnvironment = Console;
const DEPTH: u8 = 8;
fn check_state_round_trip<
E: Environment,
LH: LeafHash<Hash = PH::Hash>,
PH: PathHash<Hash = Field<E>>,
const DEPTH: u8,
>(
leaf_hasher: &LH,
path_hasher: &PH,
leaves: &[LH::Leaf],
) -> Result<()> {
let merkle_tree = MerkleTree::<E, LH, PH, DEPTH>::new(leaf_hasher, path_hasher, leaves)?;
let serialized = bincode::serialize(&merkle_tree.to_state())?;
let state: MerkleTreeState<E> = bincode::deserialize(&serialized)?;
let recreated = MerkleTree::<E, LH, PH, DEPTH>::from_state(leaf_hasher, path_hasher, state)?;
assert_eq!(merkle_tree.root(), recreated.root());
assert_eq!(merkle_tree.tree(), recreated.tree());
assert_eq!(merkle_tree.number_of_leaves, recreated.number_of_leaves);
assert_eq!(merkle_tree.empty_hash, recreated.empty_hash);
for (leaf_index, leaf) in leaves.iter().enumerate() {
let proof = recreated.prove(leaf_index, leaf)?;
assert!(proof.verify(leaf_hasher, path_hasher, recreated.root(), leaf));
}
let serialized_owned = bincode::serialize(&merkle_tree.to_state().into_owned())?;
assert_eq!(serialized, serialized_owned);
let owned_state: MerkleTreeState<E> = bincode::deserialize(&serialized_owned)?;
let from_owned = MerkleTree::<E, LH, PH, DEPTH>::from_state(leaf_hasher, path_hasher, owned_state)?;
assert_eq!(recreated.root(), from_owned.root());
assert_eq!(recreated.tree(), from_owned.tree());
let outliving_state = merkle_tree.to_state().into_owned();
drop(merkle_tree);
let from_outliving = MerkleTree::<E, LH, PH, DEPTH>::from_state(leaf_hasher, path_hasher, outliving_state)?;
assert_eq!(recreated.root(), from_outliving.root());
let twice_owned = from_outliving.to_state().into_owned().into_owned();
assert_eq!(serialized, bincode::serialize(&twice_owned)?);
Ok(())
}
#[test]
fn test_state_round_trip_bhp() -> Result<()> {
let mut rng = TestRng::default();
let leaf_hasher = BHP1024::<CurrentEnvironment>::setup("MerkleTreeTest0")?;
let path_hasher = BHP512::<CurrentEnvironment>::setup("MerkleTreeTest1")?;
for num_leaves in [0, 1, 2, 3, 4, 7, 8, 100] {
let leaves: Vec<Vec<bool>> =
(0..num_leaves).map(|_| Field::<CurrentEnvironment>::rand(&mut rng).to_bits_le()).collect();
check_state_round_trip::<CurrentEnvironment, _, _, DEPTH>(&leaf_hasher, &path_hasher, &leaves)?;
}
Ok(())
}
#[test]
fn test_state_round_trip_poseidon() -> Result<()> {
let mut rng = TestRng::default();
let leaf_hasher = Poseidon::<CurrentEnvironment, 4>::setup("MerkleTreeTest0")?;
let path_hasher = Poseidon::<CurrentEnvironment, 2>::setup("MerkleTreeTest1")?;
for num_leaves in [0, 1, 2, 3, 4, 7, 8, 100] {
let leaves: Vec<Vec<Field<CurrentEnvironment>>> =
(0..num_leaves).map(|_| vec![Field::<CurrentEnvironment>::rand(&mut rng)]).collect();
check_state_round_trip::<CurrentEnvironment, _, _, DEPTH>(&leaf_hasher, &path_hasher, &leaves)?;
}
Ok(())
}
#[test]
fn test_state_round_trip_after_append() -> Result<()> {
let mut rng = TestRng::default();
let leaf_hasher = BHP1024::<CurrentEnvironment>::setup("MerkleTreeTest0")?;
let path_hasher = BHP512::<CurrentEnvironment>::setup("MerkleTreeTest1")?;
let mut merkle_tree = MerkleTree::<CurrentEnvironment, _, _, DEPTH>::new(&leaf_hasher, &path_hasher, &[])?;
for _ in 0..20 {
let leaf = Field::<CurrentEnvironment>::rand(&mut rng).to_bits_le();
merkle_tree.append(&[leaf])?;
let serialized = bincode::serialize(&merkle_tree.to_state())?;
let state: MerkleTreeState<CurrentEnvironment> = bincode::deserialize(&serialized)?;
let recreated = MerkleTree::<CurrentEnvironment, _, _, DEPTH>::from_state(&leaf_hasher, &path_hasher, state)?;
assert_eq!(merkle_tree.root(), recreated.root());
assert_eq!(merkle_tree.tree(), recreated.tree());
}
Ok(())
}
#[test]
fn test_state_does_not_contain_the_hashers() -> Result<()> {
let mut rng = TestRng::default();
let leaf_hasher = BHP1024::<CurrentEnvironment>::setup("MerkleTreeTest0")?;
let path_hasher = BHP512::<CurrentEnvironment>::setup("MerkleTreeTest1")?;
let leaf_hasher_size = bincode::serialize(&leaf_hasher)?.len();
let path_hasher_size = bincode::serialize(&path_hasher)?.len();
let leaves: Vec<Vec<bool>> = (0..10).map(|_| Field::<CurrentEnvironment>::rand(&mut rng).to_bits_le()).collect();
let merkle_tree = MerkleTree::<CurrentEnvironment, _, _, DEPTH>::new(&leaf_hasher, &path_hasher, &leaves)?;
let size = bincode::serialize(&merkle_tree.to_state())?.len();
let tree_size = merkle_tree.tree().len() * Field::<CurrentEnvironment>::size_in_bytes();
assert!(size < 2 * tree_size, "the cached state ({size} B) is disproportionate to the tree ({tree_size} B)");
assert!(size < leaf_hasher_size.min(path_hasher_size));
Ok(())
}
#[test]
fn test_state_rejects_a_mismatched_path_hasher() -> Result<()> {
let mut rng = TestRng::default();
let leaf_hasher = BHP1024::<CurrentEnvironment>::setup("MerkleTreeTest0")?;
let path_hasher = BHP512::<CurrentEnvironment>::setup("MerkleTreeTest1")?;
let other_path_hasher = BHP512::<CurrentEnvironment>::setup("MerkleTreeTest2")?;
let leaves: Vec<Vec<bool>> = (0..10).map(|_| Field::<CurrentEnvironment>::rand(&mut rng).to_bits_le()).collect();
let merkle_tree = MerkleTree::<CurrentEnvironment, _, _, DEPTH>::new(&leaf_hasher, &path_hasher, &leaves)?;
assert!(
MerkleTree::<CurrentEnvironment, _, _, DEPTH>::from_state(
&leaf_hasher,
&other_path_hasher,
merkle_tree.to_state()
)
.is_err()
);
Ok(())
}
#[test]
fn test_state_rejects_a_corrupted_tree() -> Result<()> {
let mut rng = TestRng::default();
let leaf_hasher = BHP1024::<CurrentEnvironment>::setup("MerkleTreeTest0")?;
let path_hasher = BHP512::<CurrentEnvironment>::setup("MerkleTreeTest1")?;
let leaves: Vec<Vec<bool>> = (0..10).map(|_| Field::<CurrentEnvironment>::rand(&mut rng).to_bits_le()).collect();
let merkle_tree = MerkleTree::<CurrentEnvironment, _, _, DEPTH>::new(&leaf_hasher, &path_hasher, &leaves)?;
let mut state = merkle_tree.to_state();
state.tree.to_mut()[0] = Field::<CurrentEnvironment>::rand(&mut rng);
assert!(MerkleTree::<CurrentEnvironment, _, _, DEPTH>::from_state(&leaf_hasher, &path_hasher, state).is_err());
let mut state = merkle_tree.to_state();
state.tree.to_mut().pop();
assert!(MerkleTree::<CurrentEnvironment, _, _, DEPTH>::from_state(&leaf_hasher, &path_hasher, state).is_err());
Ok(())
}