zebra/zebra-chain/src/sprout/tests/tree.rs

167 lines
5.4 KiB
Rust

//! Tests for Sprout Note Commitment Trees.
use std::sync::Arc;
use color_eyre::eyre;
use eyre::Result;
use hex::FromHex;
use crate::{
block::Block,
parameters::{Network, NetworkUpgrade},
serialization::ZcashDeserializeInto,
sprout::{commitment::NoteCommitment, tests::test_vectors, tree},
};
/// Tests if empty roots are generated correctly.
#[test]
fn empty_roots() {
let _init_guard = zebra_test::init();
for i in 0..tree::EMPTY_ROOTS.len() {
assert_eq!(
hex::encode(tree::EMPTY_ROOTS[i]),
// The test vector is in reversed order.
test_vectors::HEX_EMPTY_ROOTS[usize::from(tree::MERKLE_DEPTH) - i]
);
}
}
/// Tests if we have the right unused (empty) leaves.
#[test]
fn empty_leaf() {
assert_eq!(
tree::NoteCommitmentTree::uncommitted(),
test_vectors::EMPTY_LEAF
);
}
/// Tests if we can build the tree correctly.
#[test]
fn incremental_roots() {
let _init_guard = zebra_test::init();
let mut leaves = vec![];
let mut incremental_tree = tree::NoteCommitmentTree::default();
for (i, cm) in test_vectors::COMMITMENTS.iter().enumerate() {
let bytes = <[u8; 32]>::from_hex(cm).unwrap();
let cm = NoteCommitment::from(bytes);
// Test if we can append a new note commitment to the tree.
let _ = incremental_tree.append(cm);
let incremental_root_hash = incremental_tree.hash();
assert_eq!(hex::encode(incremental_root_hash), test_vectors::ROOTS[i]);
// The root hashes should match.
let incremental_root = incremental_tree.root();
assert_eq!(<[u8; 32]>::from(incremental_root), incremental_root_hash);
// Test if the note commitments are counted correctly.
assert_eq!(incremental_tree.count(), (i + 1) as u64);
// Test if we can build the tree from a vector of note commitments
// instead of appending only one note commitment to the tree.
leaves.push(cm);
let ad_hoc_tree = tree::NoteCommitmentTree::from(leaves.clone());
let ad_hoc_root_hash = ad_hoc_tree.hash();
assert_eq!(hex::encode(ad_hoc_root_hash), test_vectors::ROOTS[i]);
// The root hashes should match.
let ad_hoc_root = ad_hoc_tree.root();
assert_eq!(<[u8; 32]>::from(ad_hoc_root), ad_hoc_root_hash);
// Test if the note commitments are counted correctly.
assert_eq!(ad_hoc_tree.count(), (i + 1) as u64);
}
}
#[test]
fn incremental_roots_with_blocks() -> Result<()> {
incremental_roots_with_blocks_for_network(Network::Mainnet)?;
incremental_roots_with_blocks_for_network(Network::Testnet)?;
Ok(())
}
fn incremental_roots_with_blocks_for_network(network: Network) -> Result<()> {
// Load the test data.
let (blocks, sprout_roots, next_height) = network.block_sprout_roots_height();
// Load the Genesis height.
let genesis_height = NetworkUpgrade::Genesis
.activation_height(network)
.unwrap()
.0;
// Load the Genesis block.
let genesis_block = Arc::new(
blocks
.get(&genesis_height)
.expect("test vector exists")
.zcash_deserialize_into::<Block>()
.expect("block is structurally valid"),
);
// Build an empty note commitment tree.
let mut note_commitment_tree = tree::NoteCommitmentTree::default();
// Add note commitments from Genesis to the tree.
for transaction in genesis_block.transactions.iter() {
for sprout_note_commitment in transaction.sprout_note_commitments() {
note_commitment_tree
.append(*sprout_note_commitment)
.expect("we should not be able to fill up the tree");
}
}
// Load the Genesis note commitment tree root.
let genesis_anchor = tree::Root::from(
**sprout_roots
.get(&genesis_height)
.expect("test vector exists"),
);
// Check if the root of the note commitment tree of Genesis is correct.
assert_eq!(genesis_anchor, note_commitment_tree.root());
// Load the first block after Genesis that contains a JoinSplit transaction
// so that we can add new note commitments to the tree.
let next_block = Arc::new(
blocks
.get(&(genesis_height + next_height))
.expect("test vector exists")
.zcash_deserialize_into::<Block>()
.expect("block is structurally valid"),
);
// Load the note commitment tree root of `next_block`.
let next_block_anchor = tree::Root::from(
**sprout_roots
.get(&(genesis_height + next_height))
.expect("test vector exists"),
);
// Add the note commitments from `next_block` to the tree.
let mut appended_count = 0;
for transaction in next_block.transactions.iter() {
for sprout_note_commitment in transaction.sprout_note_commitments() {
note_commitment_tree
.append(*sprout_note_commitment)
.expect("test vector is correct");
appended_count += 1;
}
}
// We also want to make sure that sprout_note_commitments() is returning
// the commitments in the right order. But this will only be actually tested
// if there are more than one note commitment in a block.
assert!(appended_count > 1);
// Check if the root of `next_block` is correct.
assert_eq!(next_block_anchor, note_commitment_tree.root());
Ok(())
}