Skip to main content

fluentbase_runtime/
runtime.rs

1//! Execution mode dispatcher for rWasm-based runtimes.
2//!
3//! This module defines a thin abstraction over different execution backends
4//! (contract vs system runtimes). The goal is to provide a uniform interface
5//! for driving execution, memory access, fuel accounting and context handling,
6//! while allowing the underlying runtime to differ.
7//!
8//! From the executor’s point of view, `ExecutionMode` behaves like a tagged
9//! union with identical operational semantics.
10
11use crate::RuntimeContext;
12use rwasm::TrapCode;
13
14mod contract_runtime;
15pub use contract_runtime::ContractRuntime;
16
17mod system_runtime;
18pub use system_runtime::SystemRuntime;
19
20/// Represents the active execution mode.
21///
22/// Execution can happen either in a *contract* context (user-deployed code)
23/// or in a *system* context (privileged runtimes, precompiles, delegated VMs).
24///
25/// Both modes expose the same operational surface:
26/// execution control, memory access, fuel accounting, and context access.
27/// This enum acts as a dynamic dispatcher between them.
28#[allow(clippy::large_enum_variant)]
29pub enum ExecutionMode {
30    /// Contract-level execution runtime.
31    ///
32    /// Used for regular smart contracts executed in a constrained,
33    /// user-controlled environment.
34    Contract(ContractRuntime),
35
36    /// System-level execution runtime.
37    ///
38    /// Used for privileged or delegated runtimes (e.g., EVM/SVM/Wasm system
39    /// runtimes) that may have different invariants or capabilities.
40    System(SystemRuntime),
41}
42
43impl ExecutionMode {
44    /// Executes the runtime until it either finishes or traps.
45    ///
46    /// This is the initial entry point for execution. Any runtime-specific
47    /// exit conditions (normal return, trap, host call) are translated
48    /// into a `TrapCode` if execution cannot continue.
49    pub fn execute(&mut self) -> Result<(), TrapCode> {
50        match self {
51            ExecutionMode::Contract(runtime) => runtime.execute(),
52            ExecutionMode::System(runtime) => runtime.execute(),
53        }
54    }
55
56    /// Resumes execution after an external interruption.
57    ///
58    /// Typically used after handling a host call or delegated execution,
59    /// passing back the exit code and the amount of fuel consumed by
60    /// the external component.
61    pub fn resume(&mut self, exit_code: i32, fuel_consumed: u64) -> Result<(), TrapCode> {
62        match self {
63            ExecutionMode::Contract(runtime) => runtime.resume(exit_code, fuel_consumed),
64            ExecutionMode::System(runtime) => runtime.resume(exit_code, fuel_consumed),
65        }
66    }
67
68    /// Writes data into the runtime linear memory.
69    ///
70    /// The write is bounds-checked according to the runtime’s memory model.
71    /// Any violation (out-of-bounds, unmapped memory, etc.) results in a trap.
72    pub fn memory_write(&mut self, offset: usize, data: &[u8]) -> Result<(), TrapCode> {
73        match self {
74            ExecutionMode::Contract(runtime) => runtime.memory_write(offset, data),
75            ExecutionMode::System(runtime) => runtime.memory_write(offset, data),
76        }
77    }
78
79    /// Reads data from the runtime linear memory.
80    ///
81    /// The provided buffer is filled with bytes starting at `offset`.
82    /// Traps if the read exceeds the accessible memory range.
83    pub fn memory_read(&mut self, offset: usize, buffer: &mut [u8]) -> Result<(), TrapCode> {
84        match self {
85            ExecutionMode::Contract(runtime) => runtime.memory_read(offset, buffer),
86            ExecutionMode::System(runtime) => runtime.memory_read(offset, buffer),
87        }
88    }
89
90    /// Returns the remaining execution fuel, if fuel metering is enabled.
91    ///
92    /// Some runtimes may choose not to expose fuel accounting; in that case
93    /// `None` is returned.
94    pub fn remaining_fuel(&self) -> Option<u64> {
95        match self {
96            ExecutionMode::Contract(runtime) => runtime.remaining_fuel(),
97            ExecutionMode::System(runtime) => runtime.remaining_fuel(),
98        }
99    }
100
101    /// Provides mutable access to the underlying `RuntimeContext`.
102    ///
103    /// This is the only sanctioned way to mutate execution context shared
104    /// between the executor and the runtime (e.g., call depth, logs, gas
105    /// accounting, environment data).
106    ///
107    /// The closure-based API prevents leaking mutable references outside
108    /// the execution boundary.
109    pub fn context_mut(&mut self) -> &mut RuntimeContext {
110        match self {
111            ExecutionMode::Contract(runtime) => runtime.context_mut(),
112            ExecutionMode::System(runtime) => runtime.context_mut(),
113        }
114    }
115
116    /// Provides immutable access to the underlying `RuntimeContext`.
117    ///
118    /// Intended for inspection and read-only queries without allowing
119    /// mutation of consensus-critical state.
120    pub fn context(&self) -> &RuntimeContext {
121        match self {
122            ExecutionMode::Contract(runtime) => runtime.context(),
123            ExecutionMode::System(runtime) => runtime.context(),
124        }
125    }
126}