essential_vm/
lib.rs

1//! The essential VM implementation.
2//!
3//! ## Reading State
4//!
5//! The primary entrypoint for this crate is the [`Vm` type][Vm].
6//!
7//! The `Vm` allows for executing arbitrary [essential ASM][asm] ops.
8//! The primary use-case is executing [`Program`][essential_types::predicate::Program]s
9//! that make up a [`Predicate`][essential_types::predicate::Predicate]'s program graph
10//! during [`Solution`][essential_types::solution::Solution] validation.
11//!
12//! ## Executing Ops
13//!
14//! There are two primary methods available for executing operations:
15//!
16//! - [`Vm::exec_ops`]
17//! - [`Vm::exec_bytecode`]
18//!
19//! Each have slightly different performance implications, so be sure to read
20//! the docs before selecting a method.
21//!
22//! ## Execution Future
23//!
24//! The `Vm::exec_*` functions all return `Future`s that not only yield on
25//! async operations, but yield based on a user-specified gas limit too. See the
26//! [`ExecFuture`] docs for further details on the implementation.
27#![deny(missing_docs, unsafe_code)]
28
29pub use access::Access;
30pub use cached::LazyCache;
31#[doc(inline)]
32pub use essential_asm::{self as asm, Op};
33pub use essential_types as types;
34#[doc(inline)]
35pub use memory::Memory;
36#[doc(inline)]
37pub use op_access::OpAccess;
38#[doc(inline)]
39pub use repeat::Repeat;
40#[doc(inline)]
41pub use stack::Stack;
42#[doc(inline)]
43pub use state_read::StateRead;
44#[doc(inline)]
45pub use state_read::StateReads;
46#[doc(inline)]
47pub use total_control_flow::ProgramControlFlow;
48#[doc(inline)]
49pub use vm::Vm;
50
51mod access;
52mod alu;
53pub mod bytecode;
54mod cached;
55mod crypto;
56pub mod error;
57mod memory;
58mod op_access;
59mod pred;
60mod repeat;
61mod sets;
62mod stack;
63mod state_read;
64pub mod sync;
65mod total_control_flow;
66mod vm;
67
68#[cfg(test)]
69pub(crate) mod utils {
70    use crate::{StateRead, StateReads};
71
72    pub struct EmptyState;
73    impl StateRead for EmptyState {
74        type Error = String;
75
76        fn key_range(
77            &self,
78            _contract_addr: essential_types::ContentAddress,
79            _key: essential_types::Key,
80            _num_values: usize,
81        ) -> Result<Vec<Vec<essential_asm::Word>>, Self::Error> {
82            Ok(vec![])
83        }
84    }
85
86    impl StateReads for EmptyState {
87        type Error = String;
88        type Pre = Self;
89        type Post = Self;
90
91        fn pre(&self) -> &Self::Pre {
92            self
93        }
94
95        fn post(&self) -> &Self::Post {
96            self
97        }
98    }
99}
100
101/// Shorthand for the `BytecodeMapped` type representing a mapping to/from [`Op`]s.
102pub type BytecodeMapped<Bytes = Vec<u8>> = bytecode::BytecodeMapped<Op, Bytes>;
103/// Shorthand for the `BytecodeMappedSlice` type for mapping [`Op`]s.
104pub type BytecodeMappedSlice<'a> = bytecode::BytecodeMappedSlice<'a, Op>;
105
106/// Unit used to measure gas.
107pub type Gas = u64;
108
109/// Gas limits.
110#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
111pub struct GasLimit {
112    /// The amount that may be spent synchronously until the execution future should yield.
113    pub per_yield: Gas,
114    /// The total amount of gas that may be spent.
115    pub total: Gas,
116}
117
118/// A mapping from an operation to its gas cost.
119pub trait OpGasCost {
120    /// The gas cost associated with the given op.
121    fn op_gas_cost(&self, op: &Op) -> Gas;
122}
123
124impl GasLimit {
125    /// The default value used for the `per_yield` limit.
126    // TODO: Adjust this to match recommended poll time limit on supported validator
127    // hardware.
128    pub const DEFAULT_PER_YIELD: Gas = 4_096;
129
130    /// Unlimited gas limit with default gas-per-yield.
131    pub const UNLIMITED: Self = Self {
132        per_yield: Self::DEFAULT_PER_YIELD,
133        total: Gas::MAX,
134    };
135}
136
137impl<F> OpGasCost for F
138where
139    F: Fn(&Op) -> Gas,
140{
141    fn op_gas_cost(&self, op: &Op) -> Gas {
142        (*self)(op)
143    }
144}
145
146/// Trace the operation at the given program counter.
147///
148/// In the success case, also emits the resulting stack.
149///
150/// In the error case, emits a debug log with the error.
151#[cfg(feature = "tracing")]
152pub(crate) fn trace_op_res<OA, T, E>(
153    oa: &OA,
154    pc: usize,
155    stack: &Stack,
156    memory: &Memory,
157    op_res: &Result<T, E>,
158) where
159    OA: OpAccess,
160    OA::Op: core::fmt::Debug,
161    E: core::fmt::Display,
162{
163    let op = oa
164        .op_access(pc)
165        .expect("must exist as retrieved previously")
166        .expect("must exist as retrieved previously");
167    let pc_op = format!("0x{:02X}: {op:?}", pc);
168    match op_res {
169        Ok(_) => {
170            tracing::trace!("{pc_op}\n  ├── {:?}\n  └── {:?}", stack, memory)
171        }
172        Err(ref err) => {
173            tracing::trace!("{pc_op}");
174            tracing::debug!("{err}");
175        }
176    }
177}