use super::*;
impl<N: Network, P: ProgramStorage<N>> VM<N, P> {
#[inline]
pub fn verify(&self, transaction: &Transaction<N>) -> bool {
match transaction.to_root() {
Ok(root) => {
if *transaction.id() != root {
warn!("Incorrect transaction ID ({})", transaction.id());
return false;
}
}
Err(error) => {
warn!("Failed to compute the Merkle root of the transaction: {error}\n{transaction}");
return false;
}
};
if has_duplicates(transaction.transition_ids()) {
warn!("Found duplicate transition in the transactions list");
return false;
}
if has_duplicates(transaction.transition_public_keys()) {
warn!("Found duplicate transition public keys in the transactions list");
return false;
}
if has_duplicates(transaction.serial_numbers()) {
warn!("Found duplicate serial numbers in the transactions list");
return false;
}
if has_duplicates(transaction.commitments()) {
warn!("Found duplicate commitments in the transactions list");
return false;
}
if has_duplicates(transaction.nonces()) {
warn!("Found duplicate nonces in the transactions list");
return false;
}
match transaction {
Transaction::Deploy(_, deployment, additional_fee) => {
if let Err(error) = Transaction::check_deployment_size(deployment) {
warn!("Invalid transaction size (deployment): {error}");
return false;
}
self.verify_deployment(deployment)
&& self.verify_additional_fee(additional_fee)
}
Transaction::Execute(_, execution, additional_fee) => {
if let Err(error) = Transaction::check_execution_size(execution) {
warn!("Invalid transaction size (execution): {error}");
return false;
}
let check_additional_fee = match additional_fee {
Some(additional_fee) => self.verify_additional_fee(additional_fee),
None => true,
};
self.verify_execution(execution)
&& check_additional_fee
}
}
}
#[inline]
fn verify_deployment(&self, deployment: &Deployment<N>) -> bool {
macro_rules! logic {
($process:expr, $network:path, $aleo:path) => {{
let task = || {
let deployment = cast_ref!(&deployment as Deployment<$network>);
let rng = &mut rand::thread_rng();
$process.verify_deployment::<$aleo, _>(&deployment, rng)
};
task()
}};
}
match process!(self, logic) {
Ok(()) => true,
Err(error) => {
warn!("Deployment verification failed: {error}");
false
}
}
}
#[inline]
fn verify_execution(&self, execution: &Execution<N>) -> bool {
macro_rules! logic {
($process:expr, $network:path, $aleo:path) => {{
let task = || {
let execution = cast_ref!(&execution as Execution<$network>);
$process.verify_execution(execution)
};
task()
}};
}
match process!(self, logic) {
Ok(()) => true,
Err(error) => {
warn!("Execution verification failed: {error}");
false
}
}
}
#[inline]
fn verify_additional_fee(&self, additional_fee: &AdditionalFee<N>) -> bool {
macro_rules! logic {
($process:expr, $network:path, $aleo:path) => {{
let task = || {
let additional_fee = cast_ref!(&additional_fee as AdditionalFee<$network>);
$process.verify_additional_fee(additional_fee)
};
task()
}};
}
match process!(self, logic) {
Ok(()) => true,
Err(error) => {
warn!("Additional fee verification failed: {error}");
false
}
}
}
}
#[cfg(test)]
mod tests {
use crate::ledger::vm::test_helpers::sample_program;
use snarkvm_utilities::test_crypto_rng;
#[test]
fn test_verify() {
let vm = crate::ledger::vm::test_helpers::sample_vm();
let deployment_transaction = crate::ledger::vm::test_helpers::sample_deployment_transaction();
assert!(vm.verify(&deployment_transaction));
let execution_transaction = crate::ledger::vm::test_helpers::sample_execution_transaction();
assert!(vm.verify(&execution_transaction));
}
#[test]
fn test_verify_deployment() {
let rng = &mut test_crypto_rng();
let vm = crate::ledger::vm::test_helpers::sample_vm();
let program = sample_program();
let deployment = vm.deploy(&program, rng).unwrap();
assert!(vm.verify_deployment(&deployment));
}
}