unc_vm_runner/
runner.rs

1use crate::errors::ContractPrecompilatonResult;
2use crate::logic::errors::{CacheError, CompilationError, VMRunnerError};
3use crate::logic::types::PromiseResult;
4use crate::logic::{CompiledContractCache, External, VMContext, VMOutcome};
5use crate::ContractCode;
6use unc_parameters::vm::{Config, VMKind};
7use unc_parameters::RuntimeFeesConfig;
8
9/// Returned by VM::run method.
10///
11/// `VMRunnerError` means unc-infra.is buggy or the data base has been corrupted.
12/// We are unable to produce a deterministic result. The correct action usually
13/// is to crash or maybe ban a peer and/or send a challenge.
14///
15/// A `VMOutcome` is a graceful completion of a VM execution. It can also contain
16/// an guest error message in the `aborted` field. But these are not errors in
17/// the real sense, those are just reasons why execution failed at some point.
18/// Such as when a smart contract code panics.
19/// Note that the fact that `VMOutcome` contains is tracked on the blockchain.
20/// All validators must produce an error deterministically or all should succeed.
21/// (See also `PartialExecutionStatus`.)
22/// Similarly, the gas values on `VMOutcome` must be the exact same on all
23/// validators, even when a guest error occurs, or else their state will diverge.
24pub(crate) type VMResult<T = VMOutcome> = Result<T, VMRunnerError>;
25
26/// Validate and run the specified contract.
27///
28/// This is the entry point for executing a UNC protocol contract. Before the
29/// entry point (as specified by the `method_name` argument) of the contract
30/// code is executed, the contract will be validated (see
31/// [`crate::prepare::prepare_contract`]), instrumented (e.g. for gas
32/// accounting), and linked with the externs specified via the `ext` argument.
33///
34/// [`VMContext::input`] will be passed to the contract entrypoint as an
35/// argument.
36///
37/// The contract will be executed with the default VM implementation for the
38/// current protocol version.
39///
40/// The gas cost for contract preparation will be subtracted by the VM
41/// implementation.
42pub fn run(
43    code: &ContractCode,
44    method_name: &str,
45    ext: &mut dyn External,
46    context: VMContext,
47    wasm_config: &Config,
48    fees_config: &RuntimeFeesConfig,
49    promise_results: &[PromiseResult],
50    cache: Option<&dyn CompiledContractCache>,
51) -> VMResult {
52    let vm_kind = wasm_config.vm_kind;
53    let span = tracing::debug_span!(
54        target: "vm",
55        "run",
56        "code.len" = code.code().len(),
57        %method_name,
58        ?vm_kind,
59        burnt_gas = tracing::field::Empty,
60    )
61    .entered();
62
63    let runtime = vm_kind
64        .runtime(wasm_config.clone())
65        .unwrap_or_else(|| panic!("the {vm_kind:?} runtime has not been enabled at compile time"));
66
67    let outcome =
68        runtime.run(code, method_name, ext, context, fees_config, promise_results, cache)?;
69
70    span.record("burnt_gas", &outcome.burnt_gas);
71    Ok(outcome)
72}
73
74pub trait VM {
75    /// Validate and run the specified contract.
76    ///
77    /// This is the entry point for executing a UNC protocol contract. Before
78    /// the entry point (as specified by the `method_name` argument) of the
79    /// contract code is executed, the contract will be validated (see
80    /// [`crate::prepare::prepare_contract`]), instrumented (e.g. for gas
81    /// accounting), and linked with the externs specified via the `ext`
82    /// argument.
83    ///
84    /// [`VMContext::input`] will be passed to the contract entrypoint as an
85    /// argument.
86    ///
87    /// The gas cost for contract preparation will be subtracted by the VM
88    /// implementation.
89    fn run(
90        &self,
91        code: &ContractCode,
92        method_name: &str,
93        ext: &mut dyn External,
94        context: VMContext,
95        fees_config: &RuntimeFeesConfig,
96        promise_results: &[PromiseResult],
97        cache: Option<&dyn CompiledContractCache>,
98    ) -> VMResult;
99
100    /// Precompile a WASM contract to a VM specific format and store the result
101    /// into the `cache`.
102    ///
103    /// Further calls to [`Self::run`] or [`Self::precompile`] with the same
104    /// `code`, `cache` and [`Config`] may reuse the results of this
105    /// precompilation step.
106    fn precompile(
107        &self,
108        code: &ContractCode,
109        cache: &dyn CompiledContractCache,
110    ) -> Result<Result<ContractPrecompilatonResult, CompilationError>, CacheError>;
111}
112
113pub trait VMKindExt {
114    /// Make a [`VM`] for this [`VMKind`].
115    ///
116    /// This is not intended to be used by code other than internal tools like
117    /// the estimator.
118    fn runtime(&self, config: Config) -> Option<Box<dyn VM>>;
119}
120
121impl VMKindExt for VMKind {
122    #[allow(unused_variables)]
123    fn runtime(&self, config: Config) -> Option<Box<dyn VM>> {
124        match self {
125            #[cfg(all(feature = "wasmer0_vm", target_arch = "x86_64"))]
126            Self::Wasmer0 => Some(Box::new(crate::wasmer_runner::Wasmer0VM::new(config))),
127            #[cfg(feature = "wasmtime_vm")]
128            Self::Wasmtime => Some(Box::new(crate::wasmtime_runner::WasmtimeVM::new(config))),
129            #[cfg(all(feature = "wasmer2_vm", target_arch = "x86_64"))]
130            Self::Wasmer2 => Some(Box::new(crate::wasmer2_runner::Wasmer2VM::new(config))),
131            #[cfg(all(feature = "unc_vm", target_arch = "x86_64"))]
132            Self::UncVm => Some(Box::new(crate::unc_vm_runner::UncVM::new(config))),
133            #[allow(unreachable_patterns)] // reachable when some of the VMs are disabled.
134            _ => None,
135        }
136    }
137}