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 three primary methods available for executing operations:
15//!
16//! - [`Vm::exec_ops`]
17//! - [`Vm::exec_bytecode`]
18//! - [`Vm::exec_bytecode_iter`]
19//!
20//! Each have slightly different performance implications, so be sure to read
21//! the docs before selecting a method.
22//!
23//! ## Execution Future
24//!
25//! The `Vm::exec_*` functions all return `Future`s that not only yield on
26//! async operations, but yield based on a user-specified gas limit too. See the
27//! [`ExecFuture`] docs for further details on the implementation.
28#![deny(missing_docs, unsafe_code)]
29
30pub use access::{mut_keys, mut_keys_set, mut_keys_slices, Access};
31pub use cached::LazyCache;
32#[doc(inline)]
33pub use essential_asm::{self as asm, Op};
34pub use essential_types as types;
35use essential_types::ContentAddress;
36#[doc(inline)]
37pub use future::ExecFuture;
38#[doc(inline)]
39pub use memory::Memory;
40#[doc(inline)]
41pub use op_access::OpAccess;
42#[doc(inline)]
43pub use repeat::Repeat;
44#[doc(inline)]
45pub use stack::Stack;
46#[doc(inline)]
47pub use state_read::StateRead;
48#[doc(inline)]
49pub use total_control_flow::ProgramControlFlow;
50#[doc(inline)]
51pub use vm::Vm;
52
53mod access;
54mod alu;
55pub mod bytecode;
56mod cached;
57mod crypto;
58pub mod error;
59mod future;
60mod memory;
61mod op_access;
62mod pred;
63mod repeat;
64mod sets;
65mod stack;
66mod state_read;
67pub mod sync;
68mod total_control_flow;
69mod vm;
70
71/// Shorthand for the `BytecodeMapped` type representing a mapping to/from [`Op`]s.
72pub type BytecodeMapped<Bytes = Vec<u8>> = bytecode::BytecodeMapped<Op, Bytes>;
73/// Shorthand for the `BytecodeMappedSlice` type for mapping [`Op`]s.
74pub type BytecodeMappedSlice<'a> = bytecode::BytecodeMappedSlice<'a, Op>;
75/// Shorthand for the `BytecodeMappedLazy` type for mapping [`Op`]s.
76pub type BytecodeMappedLazy<I> = bytecode::BytecodeMappedLazy<Op, I>;
77
78/// Unit used to measure gas.
79pub type Gas = u64;
80
81/// Gas limits.
82#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
83pub struct GasLimit {
84    /// The amount that may be spent synchronously until the execution future should yield.
85    pub per_yield: Gas,
86    /// The total amount of gas that may be spent.
87    pub total: Gas,
88}
89
90/// Distinguish between sync and async ops to ease `Future` implementation.
91#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
92pub(crate) enum OpKind {
93    /// Operations that yield immediately.
94    Sync(OpSync),
95    /// Operations returning a future.
96    Async(OpAsync),
97}
98
99/// The set of operations that are performed asynchronously.
100#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
101pub struct OpAsync(asm::StateRead);
102
103/// The set of operations that are performed synchronously.
104#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
105pub enum OpSync {
106    /// `[asm::Access]` operations.
107    Access(asm::Access),
108    /// `[asm::Alu]` operations.
109    Alu(asm::Alu),
110    /// `[asm::TotalControlFlow]` operations.
111    ControlFlow(asm::TotalControlFlow),
112    /// `[asm::Crypto]` operations.
113    Crypto(asm::Crypto),
114    /// `[asm::Memory]` operations.
115    Memory(asm::Memory),
116    /// `[asm::Pred]` operations.
117    Pred(asm::Pred),
118    /// `[asm::Stack]` operations.
119    Stack(asm::Stack),
120}
121
122/// A mapping from an operation to its gas cost.
123pub trait OpGasCost {
124    /// The gas cost associated with the given op.
125    fn op_gas_cost(&self, op: &Op) -> Gas;
126}
127
128impl GasLimit {
129    /// The default value used for the `per_yield` limit.
130    // TODO: Adjust this to match recommended poll time limit on supported validator
131    // hardware.
132    pub const DEFAULT_PER_YIELD: Gas = 4_096;
133
134    /// Unlimited gas limit with default gas-per-yield.
135    pub const UNLIMITED: Self = Self {
136        per_yield: Self::DEFAULT_PER_YIELD,
137        total: Gas::MAX,
138    };
139}
140
141impl From<Op> for OpKind {
142    fn from(op: Op) -> Self {
143        match op {
144            Op::Access(op) => OpKind::Sync(OpSync::Access(op)),
145            Op::Alu(op) => OpKind::Sync(OpSync::Alu(op)),
146            Op::Crypto(op) => OpKind::Sync(OpSync::Crypto(op)),
147            Op::Memory(op) => OpKind::Sync(OpSync::Memory(op)),
148            Op::Pred(op) => OpKind::Sync(OpSync::Pred(op)),
149            Op::Stack(op) => OpKind::Sync(OpSync::Stack(op)),
150            Op::StateRead(op) => OpKind::Async(OpAsync(op)),
151            Op::TotalControlFlow(op) => OpKind::Sync(OpSync::ControlFlow(op)),
152        }
153    }
154}
155
156impl<F> OpGasCost for F
157where
158    F: Fn(&Op) -> Gas,
159{
160    fn op_gas_cost(&self, op: &Op) -> Gas {
161        (*self)(op)
162    }
163}
164
165/// Trace the operation at the given program counter.
166///
167/// In the success case, also emits the resulting stack.
168///
169/// In the error case, emits a debug log with the error.
170#[cfg(feature = "tracing")]
171pub(crate) fn trace_op_res<OA, T, E>(
172    oa: &mut OA,
173    pc: usize,
174    stack: &Stack,
175    memory: &Memory,
176    op_res: Result<T, E>,
177) where
178    OA: OpAccess,
179    OA::Op: core::fmt::Debug,
180    E: core::fmt::Display,
181{
182    let op = oa
183        .op_access(pc)
184        .expect("must exist as retrieved previously")
185        .expect("must exist as retrieved previously");
186    let pc_op = format!("0x{:02X}: {op:?}", pc);
187    match op_res {
188        Ok(_) => {
189            tracing::trace!("{pc_op}\n  ├── {:?}\n  └── {:?}", stack, memory)
190        }
191        Err(ref err) => {
192            tracing::trace!("{pc_op}");
193            tracing::debug!("{err}");
194        }
195    }
196}