use super::*;
impl<A: Aleo> StatePath<A> {
pub fn verify(&self, is_global: &Boolean<A>, local_state_root: &Field<A>) -> Boolean<A> {
let check_transition_path = A::verify_merkle_path_bhp(
&self.transition_path,
self.transaction_leaf.id(),
&self.transition_leaf.to_bits_le(),
) & self.transition_leaf.variant().is_equal(&U16::constant(console::U16::new(3)));
let check_transaction_path = A::verify_merkle_path_bhp(
&self.transaction_path,
&self.transaction_id,
&self.transaction_leaf.to_bits_le(),
) & self.transaction_leaf.variant().is_equal(&U8::one());
let check_transactions_path = A::verify_merkle_path_bhp(
&self.transactions_path,
self.header_leaf.id(),
&self.transaction_id.to_bits_le(),
);
let check_header_path =
A::verify_merkle_path_bhp(&self.header_path, &self.header_root, &self.header_leaf.to_bits_le())
& self.header_leaf.index().is_equal(&U8::one());
let block_hash_preimage = self
.previous_block_hash
.to_bits_le()
.into_iter()
.chain(self.header_root.to_bits_le().into_iter())
.collect::<Vec<_>>();
let check_block_hash = A::hash_bhp1024(&block_hash_preimage).is_equal(&self.block_hash);
let check_state_root =
A::verify_merkle_path_bhp(&self.block_path, &self.global_state_root, &self.block_hash.to_bits_le());
let check_transition_and_transaction_path = check_transition_path & check_transaction_path;
let check_local = &check_transition_and_transaction_path & local_state_root.is_equal(&self.transaction_id);
let check_global = check_transition_and_transaction_path
& check_transactions_path
& check_header_path
& check_block_hash
& check_state_root;
Boolean::ternary(is_global, &check_global, &check_local)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Circuit;
use snarkvm_utilities::rand::{TestRng, Uniform};
type CurrentNetwork = <Circuit as Environment>::Network;
const ITERATIONS: usize = 20;
fn check_verify_global(
mode: Mode,
is_global: bool,
num_constants: u64,
num_public: u64,
num_private: u64,
num_constraints: u64,
) -> Result<()> {
let rng = &mut TestRng::default();
for i in 0..ITERATIONS {
let console_state_path =
console::state_path::test_helpers::sample_global_state_path::<CurrentNetwork>(None, rng).unwrap();
let local_state_root = console::Field::rand(rng);
console_state_path.verify(true, local_state_root).unwrap();
Circuit::scope(format!("Verify global state path {mode} (is_global: {is_global})"), || {
let circuit_is_global = Boolean::new(mode, is_global);
let circuit_local_state_root = Field::new(mode, local_state_root);
let circuit_state_path = StatePath::<Circuit>::new(mode, console_state_path.clone());
let is_valid = circuit_state_path.verify(&circuit_is_global, &circuit_local_state_root);
match is_global {
true => assert!(is_valid.eject_value()),
false => assert!(!is_valid.eject_value()),
}
assert!(Circuit::is_satisfied());
if i > 0 {
assert_scope!(num_constants, num_public, num_private, num_constraints);
}
});
Circuit::reset();
}
Ok(())
}
fn check_verify_local(
mode: Mode,
is_global: bool,
is_valid_local_root: bool,
num_constants: u64,
num_public: u64,
num_private: u64,
num_constraints: u64,
) -> Result<()> {
let rng = &mut TestRng::default();
for i in 0..ITERATIONS {
let console_state_path =
console::state_path::test_helpers::sample_local_state_path::<CurrentNetwork>(None, rng).unwrap();
let local_state_root = **console_state_path.transaction_id();
console_state_path.verify(false, local_state_root).unwrap();
Circuit::scope(
format!(
"Verify local state path {mode} (is_global: {is_global}, is_valid_local_root: {is_valid_local_root})"
),
|| {
let circuit_is_global = Boolean::new(mode, is_global);
let circuit_local_state_root = if is_valid_local_root {
Field::new(mode, local_state_root)
} else {
Field::new(mode, console::Field::rand(rng))
};
let circuit_state_path = StatePath::<Circuit>::new(mode, console_state_path.clone());
let is_valid = circuit_state_path.verify(&circuit_is_global, &circuit_local_state_root);
match (is_global, is_valid_local_root) {
(false, true) => assert!(is_valid.eject_value()),
_ => assert!(!is_valid.eject_value()),
}
assert!(Circuit::is_satisfied());
if i > 0 {
assert_scope!(num_constants, num_public, num_private, num_constraints);
}
},
);
Circuit::reset();
}
Ok(())
}
#[test]
fn test_state_path_verify_global_constant() -> Result<()> {
check_verify_global(Mode::Constant, true, 101535, 1, 2, 3)?;
check_verify_global(Mode::Constant, false, 101535, 1, 2, 3)
}
#[test]
fn test_state_path_verify_global_public() -> Result<()> {
check_verify_global(Mode::Public, true, 26595, 453, 86212, 86732)?;
check_verify_global(Mode::Public, false, 26595, 453, 86212, 86732)
}
#[test]
fn test_state_path_verify_global_private() -> Result<()> {
check_verify_global(Mode::Private, true, 26595, 1, 86664, 86732)?;
check_verify_global(Mode::Private, false, 26595, 1, 86664, 86732)
}
#[test]
fn test_state_path_verify_local_constant() -> Result<()> {
check_verify_local(Mode::Constant, false, true, 101535, 1, 2, 3)?;
check_verify_local(Mode::Constant, false, false, 101535, 1, 2, 3)?;
check_verify_local(Mode::Constant, true, true, 101535, 1, 2, 3)?;
check_verify_local(Mode::Constant, true, false, 101535, 1, 2, 3)
}
#[test]
fn test_state_path_verify_local_public() -> Result<()> {
check_verify_local(Mode::Public, false, true, 26595, 453, 86212, 86732)?;
check_verify_local(Mode::Public, false, false, 26595, 453, 86212, 86732)?;
check_verify_local(Mode::Public, true, true, 26595, 453, 86212, 86732)?;
check_verify_local(Mode::Public, true, false, 26595, 453, 86212, 86732)
}
#[test]
fn test_state_path_verify_local_private() -> Result<()> {
check_verify_local(Mode::Private, false, true, 26595, 1, 86664, 86732)?;
check_verify_local(Mode::Private, false, false, 26595, 1, 86664, 86732)?;
check_verify_local(Mode::Private, true, true, 26595, 1, 86664, 86732)?;
check_verify_local(Mode::Private, true, false, 26595, 1, 86664, 86732)
}
}