use super::*;
impl<N: Network> StatePath<N> {
pub fn verify(&self, is_global: bool, local_state_root: Field<N>) -> Result<()> {
ensure!(self.transition_leaf.variant() == 3, "Transition leaf variant must be 3 (Input::Record)");
ensure!(
N::verify_merkle_path_bhp(
&self.transition_path,
&self.transaction_leaf.id(),
&self.transition_leaf.to_bits_le()
),
"'{}' (an input or output ID) does not belong to '{}' (a function or transition)",
self.transition_leaf.id(),
self.transaction_leaf.id()
);
ensure!(self.transaction_leaf.variant() == 1, "Transaction leaf variant must be 1 (Transaction::Execution)");
ensure!(
N::verify_merkle_path_bhp(
&self.transaction_path,
&self.transaction_id,
&self.transaction_leaf.to_bits_le()
),
"'{}' (a function or transition) does not belong to transaction '{}'",
self.transaction_leaf.id(),
self.transaction_id
);
if is_global {
ensure!(self.header_leaf.index() == 1, "Header leaf index must be 1 (Header::transactions_root)");
ensure!(
N::verify_merkle_path_bhp(
&self.transactions_path,
&self.header_leaf.id(),
&self.transaction_id.to_bits_le()
),
"Transaction '{}' does not belong to '{}' (a header leaf)",
self.transaction_id,
self.header_leaf
);
ensure!(
N::verify_merkle_path_bhp(&self.header_path, &self.header_root, &self.header_leaf.to_bits_le()),
"'{}' (a header leaf) does not belong to '{}' (a block header)",
self.header_leaf,
self.block_hash
);
let preimage = (*self.previous_block_hash).to_bits_le().into_iter().chain(self.header_root.to_bits_le());
ensure!(
*self.block_hash == N::hash_bhp1024(&preimage.collect::<Vec<_>>())?,
"Block hash '{}' is incorrect. Double-check the previous block hash and block header root.",
self.block_hash
);
ensure!(
N::verify_merkle_path_bhp(&self.block_path, &self.global_state_root, &self.block_hash.to_bits_le()),
"'{}' (a block hash) does not belong to '{}' (a global state root)",
self.block_hash,
self.global_state_root
);
} else {
ensure!(
*self.transaction_id == local_state_root,
"'{}' (a decoded transaction ID) does not match the '{local_state_root}' (a local state root)",
*self.transaction_id
);
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use snarkvm_console_network::{prelude::TestRng, Testnet3};
type CurrentNetwork = Testnet3;
const ITERATIONS: usize = 100;
#[test]
fn test_verify_global() {
let rng = &mut TestRng::default();
for _ in 0..ITERATIONS {
let state_path =
crate::state_path::test_helpers::sample_global_state_path::<CurrentNetwork>(None, rng).unwrap();
let local_state_root = Field::rand(rng);
state_path.verify(true, local_state_root).unwrap();
state_path.verify(false, local_state_root).unwrap_err();
}
}
#[test]
fn test_verify_local() {
let rng = &mut TestRng::default();
for _ in 0..ITERATIONS {
let state_path =
crate::state_path::test_helpers::sample_local_state_path::<CurrentNetwork>(None, rng).unwrap();
let local_state_root = **state_path.transaction_id();
state_path.verify(false, local_state_root).unwrap();
state_path.verify(false, Field::rand(rng)).unwrap_err();
state_path.verify(true, local_state_root).unwrap_err();
state_path.verify(true, Field::rand(rng)).unwrap_err();
}
}
#[test]
fn test_verify_new_local() {
let rng = &mut TestRng::default();
for _ in 0..ITERATIONS {
let state_path =
crate::state_path::test_helpers::sample_local_state_path::<CurrentNetwork>(None, rng).unwrap();
let new_local_state_path = StatePath::new_local(
state_path.global_state_root(),
*state_path.transaction_id(),
state_path.transaction_path().clone(),
*state_path.transaction_leaf(),
state_path.transition_path().clone(),
*state_path.transition_leaf(),
)
.unwrap();
let local_state_root = **new_local_state_path.transaction_id();
new_local_state_path.verify(false, local_state_root).unwrap();
new_local_state_path.verify(false, Field::rand(rng)).unwrap_err();
new_local_state_path.verify(true, local_state_root).unwrap_err();
new_local_state_path.verify(true, Field::rand(rng)).unwrap_err();
}
}
}