use alloc::sync::Arc;
use miden_core::deferred::{DeferredRoot, DeferredState, DeferredStateWire};
use miden_verifier::{ExecutionClaim, PrecompileStatus, VerificationOutcome, Verifier};
use crate::errors::TransactionVerifierError;
use crate::transaction::{ProvenTransaction, TransactionKernel};
use crate::vm::ProgramInfo;
pub struct TransactionVerifier {
tx_program_info: ProgramInfo,
proof_security_level: u32,
}
impl TransactionVerifier {
pub fn new(proof_security_level: u32) -> Self {
let tx_program_info = TransactionKernel::program_info();
Self { tx_program_info, proof_security_level }
}
pub fn verify(
&self,
transaction: &ProvenTransaction,
) -> Result<VerificationOutcome, TransactionVerifierError> {
if matches!(transaction.proof().precompile(), PrecompileStatus::Proven(_)) {
return Err(TransactionVerifierError::TransactionProofContainsPrecompiles);
}
let stack_inputs = TransactionKernel::build_input_stack(
transaction.account_id(),
transaction.account_update().initial_state_commitment(),
transaction.input_notes().commitment(),
transaction.ref_block_commitment(),
transaction.ref_block_num(),
);
let stack_outputs = TransactionKernel::build_output_stack(
transaction.account_update().final_state_commitment(),
transaction.account_update().account_patch_commitment(),
transaction.output_notes().commitment(),
transaction.expiration_block_num(),
);
let claim = ExecutionClaim::from_program_info(
self.tx_program_info.clone(),
stack_inputs,
stack_outputs,
);
let outcome = Verifier::new()
.verify(&claim, transaction.proof())
.map_err(TransactionVerifierError::TransactionVerificationFailed)?;
let proof_security_level = outcome.vm_security_parameters().conjectured_security_level();
if let PrecompileStatus::Deferred(precompile) = transaction.proof().precompile() {
let expected_root = outcome
.outstanding_precompile_root()
.expect("a verified deferred proof must have an outstanding precompile root");
validate_deferred_witness(precompile, expected_root)?;
}
if proof_security_level < self.proof_security_level {
return Err(TransactionVerifierError::InsufficientProofSecurityLevel {
actual: proof_security_level,
expected_minimum: self.proof_security_level,
});
}
Ok(outcome)
}
}
fn validate_deferred_witness(
witness: &DeferredStateWire,
expected_root: DeferredRoot,
) -> Result<(), TransactionVerifierError> {
let state = DeferredState::from_wire(Arc::new(miden_precompiles::registry()), witness)
.map_err(TransactionVerifierError::InvalidTransactionPrecompileWitness)?;
let actual_root = state.root();
if actual_root != expected_root {
return Err(TransactionVerifierError::TransactionPrecompileRootMismatch {
expected: expected_root,
actual: actual_root,
});
}
Ok(())
}
#[cfg(test)]
mod tests {
use miden_core::Word;
use miden_core::deferred::DeferredStateWire;
use super::validate_deferred_witness;
use crate::errors::TransactionVerifierError;
#[test]
fn rejects_a_deferred_witness_with_the_wrong_root() {
let expected_root = Word::from([1_u32, 2, 3, 4]);
let error =
validate_deferred_witness(&DeferredStateWire::default(), expected_root).unwrap_err();
assert!(matches!(
error,
TransactionVerifierError::TransactionPrecompileRootMismatch { .. }
));
}
}