fuel_vm/
verification.rs

1//! Stategies for verifying the correctness of the VM execution.
2//! The default strategy, [`Normal`], simply returns an error on failed verification.
3//! Alternative strategy, [`AttemptContinue`], continues execution and collects multiple
4//! errors.
5
6use alloc::{
7    collections::BTreeSet,
8    vec::Vec,
9};
10
11use fuel_tx::{
12    ContractId,
13    PanicReason,
14};
15
16use crate::{
17    error::PanicOrBug,
18    interpreter::PanicContext,
19};
20
21/// Do not allow outside implementations for the Verifier, so that it's not a breaking
22/// change to modify it.
23trait Seal {}
24
25/// What to do when verification fails.
26#[allow(private_bounds)] // For selaed trait
27pub trait Verifier
28where
29    Self: Sized + Seal,
30{
31    /// Handle an error after a contract is missing from the inputs
32    #[allow(private_interfaces)] // PanicContext is an internal type, so this isn't callable by external code
33    fn check_contract_in_inputs(
34        &mut self,
35        panic_context: &mut PanicContext,
36        input_contracts: &BTreeSet<ContractId>,
37        contract_id: &ContractId,
38    ) -> Result<(), PanicOrBug>;
39}
40
41/// The default verification strategy.
42/// Performs the standard verification checks and panics on failure.
43#[derive(Debug, Copy, Clone, Default)]
44pub struct Normal;
45
46impl Verifier for Normal
47where
48    Self: Sized,
49{
50    #[allow(private_interfaces)]
51    fn check_contract_in_inputs(
52        &mut self,
53        panic_context: &mut PanicContext,
54        input_contracts: &BTreeSet<ContractId>,
55        contract_id: &ContractId,
56    ) -> Result<(), PanicOrBug> {
57        if input_contracts.contains(contract_id) {
58            Ok(())
59        } else {
60            *panic_context = PanicContext::ContractId(*contract_id);
61            Err(PanicReason::ContractNotInInputs.into())
62        }
63    }
64}
65
66impl Seal for Normal {}
67
68/// With some subset of errors it's possible to continue execution,
69/// allowing the collection of multiple errors during a single run.
70#[derive(Debug, Clone, Default)]
71#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
72#[non_exhaustive]
73pub struct AttemptContinue {
74    /// Contracts that were called but not in the inputs
75    pub missing_contract_inputs: Vec<ContractId>,
76}
77
78impl Verifier for AttemptContinue
79where
80    Self: Sized,
81{
82    #[allow(private_interfaces)]
83    fn check_contract_in_inputs(
84        &mut self,
85        _panic_context: &mut PanicContext,
86        input_contracts: &BTreeSet<ContractId>,
87        contract_id: &ContractId,
88    ) -> Result<(), PanicOrBug> {
89        if !input_contracts.contains(contract_id) {
90            self.missing_contract_inputs.push(*contract_id);
91        }
92        Ok(())
93    }
94}
95
96impl Seal for AttemptContinue {}