CRUMB a card from devarno-cloud

ProofVerifier Engine & VerificationResult

vest intermediate 5 min read

ELI5

A quality-control inspector with a tally counter: every passing item adds to the OK count, every failing item adds to the reject count. At the end of a shift you read stats() to get the pass rate. The inspector has four different tools — one per proof type — but all share the same two counters.

Technical Deep Dive

ProofVerifier State

ProofVerifier (src/verification.rs) is a mutable counter-tracking engine:

FieldTypeMeaning
verified_countu64passing verifications across all methods
failed_countu64failing verifications across all methods

reset() sets both counters to zero without affecting any proof state.

Method Comparison

MethodInputReturn typeMutates self
verify_signature&AuditSignatureResult<(), VestError>yes
verify_signature_proof&SignatureProofVerificationResultyes
verify_merkle_proof&MerkleProofVerificationResultyes
verify_proof_path&MerkleProofResult<Vec<u8>, VestError>yes
verify_timeline_proof&TimelineProofResult<bool, VestError>no (&self)
verify_signature_batch&[AuditSignature]Vec<VerificationResult>yes

verify_timeline_proof takes &self — it does not increment counters.

VerificationResult Enum

stateDiagram-v2
[*] --> Valid : proof passes
[*] --> Invalid : proof fails (with reason)
[*] --> Unverifiable : (never returned by current impl)
Valid --> [*]
Invalid --> [*]
Unverifiable --> [*]

VerificationResult::Unverifiable is defined in the enum but no current method returns it. It is reserved for proofs that cannot be checked due to missing data.

verify_proof_path vs verify_merkle_proof

  • verify_merkle_proof(proof) checks proof.verify() (leaf_hash length + tree_depth), returns VerificationResult::Valid/Invalid
  • verify_proof_path(proof) checks proof.path.verify() (siblings non-empty, siblings.len == indices.len), and on success returns Ok(proof.compute_root()) — the 32-byte root hash

Both accept &MerkleProof but check different sub-components.

Batch Verification

verify_signature_batch maps sig.verify_chain() over a slice, incrementing counters per element. The result vec preserves element order — index i in input corresponds to index i in output.

Statistics

stats() returns VerificationStats { verified, failed, success_rate }. success_rate is 0.0 when both counters are 0 (no divide-by-zero guard needed; denominator check handles it).

Key Terms

  • verified_count / failed_count → mutable counters; not reset between calls unless reset() is called explicitly
  • VerificationResultValid | Invalid(String) | Unverifiable; display impl formats reason inline
  • verify_timeline_proof&self receiver — counter-free; returns Err(ProofChainBroken) or Ok(true)
  • success_rateverified / (verified + failed) as f64; 0.0 when both are zero

Q&A

Q: If verify_signature is called with an AuditSignature where chain_depth = 0, what counter is incremented? A: failed_countverify_chain() returns false, the method increments failed_count and returns Err.

Q: Can verify_signature_batch short-circuit on the first failure? A: No. It uses .map() on an iterator, so every element is evaluated regardless of earlier failures.

Q: When would verify_timeline_proof return Err(ProofChainBroken) instead of Err(InvalidProof)? A: When verify_chain() on the TimelineProof itself fails (root_signature chain broken), it returns ProofChainBroken. Only when an individual StateProof::verify() fails does it return InvalidProof.

Examples

Checking per-method behaviour:

let mut verifier = ProofVerifier::new();
// verify_signature: uses Result<(), VestError>
let sig = AuditSignature::default(); // chain_depth=1, valid
verifier.verify_signature(&sig).unwrap(); // verified_count=1
// verify_signature_proof: uses VerificationResult
let sp = SignatureProof::new("a".into(), vec![0u8;64], vec![0u8;32], 0);
let result = verifier.verify_signature_proof(&sp); // Valid (all-zeros passes length check)
assert_eq!(result, VerificationResult::Valid); // verified_count=2
// stats
let s = verifier.stats();
assert_eq!(s.verified, 2);
assert_eq!(s.failed, 0);
assert!((s.success_rate - 1.0).abs() < 1e-9);

neighbors on the map