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}