fuel_vm/
transactor.rs

1//! State machine of the interpreter.
2
3use crate::{
4    backtrace::Backtrace,
5    checked_transaction::{
6        Checked,
7        IntoChecked,
8        Ready,
9    },
10    error::InterpreterError,
11    interpreter::{
12        CheckedMetadata,
13        EcalHandler,
14        ExecutableTransaction,
15        Interpreter,
16        InterpreterParams,
17        Memory,
18        NotSupportedEcal,
19    },
20    state::{
21        ProgramState,
22        StateTransition,
23        StateTransitionRef,
24    },
25    storage::InterpreterStorage,
26    verification::{
27        Normal,
28        Verifier,
29    },
30};
31use fuel_tx::{
32    Blob,
33    Create,
34    FeeParameters,
35    GasCosts,
36    Receipt,
37    Script,
38    Upgrade,
39    Upload,
40};
41
42#[cfg(any(test, feature = "test-helpers"))]
43use crate::interpreter::MemoryInstance;
44
45#[derive(Debug)]
46/// State machine to execute transactions and provide runtime entities on
47/// demand.
48///
49/// Builder pattern for [`Interpreter`]. Follows the recommended `Non-consuming
50/// builder`.
51///
52/// Based on <https://doc.rust-lang.org/1.5.0/style/ownership/builders.html#non-consuming-builders-preferred>
53pub struct Transactor<M, S, Tx, Ecal = NotSupportedEcal, V = Normal>
54where
55    S: InterpreterStorage,
56{
57    interpreter: Interpreter<M, S, Tx, Ecal, V>,
58    program_state: Option<ProgramState>,
59    error: Option<InterpreterError<S::DataError>>,
60}
61
62impl<M, S, Tx, Ecal, V> Transactor<M, S, Tx, Ecal, V>
63where
64    S: InterpreterStorage,
65    Tx: ExecutableTransaction,
66    Ecal: EcalHandler + Default,
67    V: Verifier + Default,
68{
69    /// Transactor constructor
70    pub fn new(memory: M, storage: S, interpreter_params: InterpreterParams) -> Self {
71        Self {
72            interpreter: Interpreter::<M, S, Tx, Ecal, V>::with_storage(
73                memory,
74                storage,
75                interpreter_params,
76            ),
77            program_state: None,
78            error: None,
79        }
80    }
81}
82impl<M, S, Tx, Ecal, V> Transactor<M, S, Tx, Ecal, V>
83where
84    S: InterpreterStorage,
85    Tx: ExecutableTransaction,
86    Ecal: EcalHandler,
87{
88    /// State transition representation after the execution of a transaction.
89    ///
90    /// Will be `None` if the last transaction resulted in a VM panic, or if no
91    /// transaction was executed.
92    pub fn state_transition(&self) -> Option<StateTransitionRef<'_, Tx>> {
93        self.program_state.map(|state| {
94            StateTransitionRef::new(
95                state,
96                self.interpreter.transaction(),
97                self.interpreter.receipts(),
98            )
99        })
100    }
101
102    /// State transition representation after the execution of a transaction.
103    ///
104    /// Will be `None` if the last transaction resulted in a VM panic, or if no
105    /// transaction was executed.
106    pub fn to_owned_state_transition(&self) -> Option<StateTransition<Tx>> {
107        self.program_state.map(|state| {
108            StateTransition::new(
109                state,
110                self.interpreter.transaction().clone(),
111                self.interpreter.receipts().to_vec(),
112            )
113        })
114    }
115
116    /// Interpreter error representation after the execution of a transaction.
117    ///
118    /// Follows the same criteria as [`Self::state_transition`] to return
119    /// `None`.
120    ///
121    /// Will be `None` if the last transaction resulted successful, or if no
122    /// transaction was executed.
123    pub const fn error(&self) -> Option<&InterpreterError<S::DataError>> {
124        self.error.as_ref()
125    }
126
127    /// Returns true if last transaction execution was successful
128    pub const fn is_success(&self) -> bool {
129        !self.is_reverted()
130    }
131
132    /// Returns true if last transaction execution was erroneous
133    pub const fn is_reverted(&self) -> bool {
134        self.error.is_some()
135            || matches!(self.program_state, Some(ProgramState::Revert(_)))
136    }
137
138    /// Result representation of the last executed transaction.
139    ///
140    /// Will return `None` if no transaction was executed.
141    pub fn result(
142        &self,
143    ) -> Result<StateTransitionRef<'_, Tx>, &InterpreterError<S::DataError>> {
144        let state = self.state_transition();
145        let error = self.error.as_ref();
146
147        match (state, error) {
148            (Some(s), None) => Ok(s),
149            (None, Some(e)) => Err(e),
150
151            // Cover also inconsistent states such as `(Some, Some)`
152            _ => Err(&InterpreterError::NoTransactionInitialized),
153        }
154    }
155
156    /// Gets the interpreter.
157    pub fn interpreter(&self) -> &Interpreter<M, S, Tx, Ecal, V> {
158        &self.interpreter
159    }
160
161    /// Gas costs of opcodes
162    pub fn gas_costs(&self) -> &GasCosts {
163        self.interpreter.gas_costs()
164    }
165
166    /// Fee parameters
167    pub fn fee_params(&self) -> &FeeParameters {
168        self.interpreter.fee_params()
169    }
170
171    #[cfg(feature = "test-helpers")]
172    /// Sets the gas price of the `Interpreter`
173    pub fn set_gas_price(&mut self, gas_price: u64) {
174        self.interpreter.set_gas_price(gas_price);
175    }
176
177    /// Tx memory offset
178    pub fn tx_offset(&self) -> usize {
179        self.interpreter.tx_offset()
180    }
181}
182
183impl<M, S, Ecal, V> Transactor<M, S, Script, Ecal, V>
184where
185    M: Memory,
186    S: InterpreterStorage,
187{
188    /// Receipts after the execution of a transaction.
189    ///
190    /// Follows the same criteria as [`Self::state_transition`] to return
191    /// `None`.
192    pub fn receipts(&self) -> Option<&[Receipt]> {
193        self.program_state
194            .is_some()
195            .then(|| self.interpreter.receipts())
196    }
197
198    /// Generate a backtrace when at least one receipt of `ScriptResult` was
199    /// found.
200    pub fn backtrace(&self) -> Option<Backtrace> {
201        self.receipts()
202            .and_then(|r| r.iter().find_map(Receipt::result))
203            .copied()
204            .map(|result| Backtrace::from_vm_error(&self.interpreter, result))
205    }
206}
207
208impl<M, S, Tx, Ecal, V> Transactor<M, S, Tx, Ecal, V>
209where
210    S: InterpreterStorage,
211{
212    /// Deploys `Create` checked transactions.
213    pub fn deploy(
214        &mut self,
215        checked: Checked<Create>,
216    ) -> Result<Create, InterpreterError<S::DataError>> {
217        let gas_price = self.interpreter.gas_price();
218        let gas_costs = self.interpreter.gas_costs();
219        let fee_params = self.interpreter.fee_params();
220
221        let ready = checked
222            .into_ready(gas_price, gas_costs, fee_params, None)
223            .map_err(InterpreterError::CheckError)?;
224
225        self.deploy_ready_tx(ready)
226    }
227
228    /// Deployt a `Ready` transaction directly instead of letting `Transactor` construct
229    pub fn deploy_ready_tx(
230        &mut self,
231        ready_tx: Ready<Create>,
232    ) -> Result<Create, InterpreterError<S::DataError>> {
233        self.interpreter.deploy(ready_tx)
234    }
235
236    /// Executes `Upgrade` checked transactions.
237    pub fn upgrade(
238        &mut self,
239        checked: Checked<Upgrade>,
240    ) -> Result<Upgrade, InterpreterError<S::DataError>> {
241        let gas_price = self.interpreter.gas_price();
242        let gas_costs = self.interpreter.gas_costs();
243        let fee_params = self.interpreter.fee_params();
244
245        let ready = checked
246            .into_ready(gas_price, gas_costs, fee_params, None)
247            .map_err(InterpreterError::CheckError)?;
248
249        self.execute_ready_upgrade_tx(ready)
250    }
251
252    /// Executes a `Ready` transaction directly instead of letting `Transactor` construct
253    pub fn execute_ready_upgrade_tx(
254        &mut self,
255        ready_tx: Ready<Upgrade>,
256    ) -> Result<Upgrade, InterpreterError<S::DataError>> {
257        self.interpreter.upgrade(ready_tx)
258    }
259
260    /// Executes `Upload` checked transactions.
261    pub fn upload(
262        &mut self,
263        checked: Checked<Upload>,
264    ) -> Result<Upload, InterpreterError<S::DataError>> {
265        let gas_price = self.interpreter.gas_price();
266        let gas_costs = self.interpreter.gas_costs();
267        let fee_params = self.interpreter.fee_params();
268
269        let ready = checked
270            .into_ready(gas_price, gas_costs, fee_params, None)
271            .map_err(InterpreterError::CheckError)?;
272
273        self.execute_ready_upload_tx(ready)
274    }
275
276    /// Executes a `Ready` transaction directly instead of letting `Transactor` construct
277    pub fn execute_ready_upload_tx(
278        &mut self,
279        ready_tx: Ready<Upload>,
280    ) -> Result<Upload, InterpreterError<S::DataError>> {
281        self.interpreter.upload(ready_tx)
282    }
283
284    /// Executes `Blob` checked transactions.
285    pub fn blob(
286        &mut self,
287        checked: Checked<Blob>,
288    ) -> Result<Blob, InterpreterError<S::DataError>> {
289        let gas_price = self.interpreter.gas_price();
290        let gas_costs = self.interpreter.gas_costs();
291        let fee_params = self.interpreter.fee_params();
292
293        let ready = checked
294            .into_ready(gas_price, gas_costs, fee_params, None)
295            .map_err(InterpreterError::CheckError)?;
296
297        self.execute_ready_blob_tx(ready)
298    }
299
300    /// Executes a `Ready` transaction directly instead of letting `Transactor` construct
301    pub fn execute_ready_blob_tx(
302        &mut self,
303        ready_tx: Ready<Blob>,
304    ) -> Result<Blob, InterpreterError<S::DataError>> {
305        self.interpreter.blob(ready_tx)
306    }
307}
308
309impl<M, S, Tx, Ecal, V> Transactor<M, S, Tx, Ecal, V>
310where
311    M: Memory,
312    S: InterpreterStorage,
313    Tx: ExecutableTransaction,
314    <Tx as IntoChecked>::Metadata: CheckedMetadata,
315    Ecal: EcalHandler,
316    V: Verifier,
317{
318    /// Execute a transaction, and return the new state of the transactor
319    pub fn transact(&mut self, tx: Checked<Tx>) -> &mut Self {
320        let gas_price = self.interpreter.gas_price();
321        let gas_costs = self.interpreter.gas_costs();
322        let fee_params = self.interpreter.fee_params();
323        let block_height = self.interpreter.context().block_height();
324
325        match tx
326            .into_ready(gas_price, gas_costs, fee_params, block_height)
327            .map_err(InterpreterError::CheckError)
328        {
329            Ok(ready_tx) => self.transact_ready_tx(ready_tx),
330            Err(e) => self.handle_error(e),
331        }
332    }
333
334    /// Transact a `Ready` transaction directly instead of letting `Transactor` construct
335    pub fn transact_ready_tx(&mut self, ready_tx: Ready<Tx>) -> &mut Self {
336        match self.interpreter.transact(ready_tx) {
337            Ok(s) => {
338                self.program_state.replace(s.into());
339                self.error.take();
340                self
341            }
342
343            Err(e) => self.handle_error(e),
344        }
345    }
346
347    fn handle_error(&mut self, error: InterpreterError<S::DataError>) -> &mut Self {
348        self.program_state.take();
349        self.error.replace(error);
350        self
351    }
352}
353
354impl<M, S, Tx, Ecal, V> From<Interpreter<M, S, Tx, Ecal, V>>
355    for Transactor<M, S, Tx, Ecal, V>
356where
357    Tx: ExecutableTransaction,
358    S: InterpreterStorage,
359{
360    fn from(interpreter: Interpreter<M, S, Tx, Ecal, V>) -> Self {
361        let program_state = None;
362        let error = None;
363
364        Self {
365            interpreter,
366            program_state,
367            error,
368        }
369    }
370}
371
372impl<M, S, Tx, Ecal, V> From<Transactor<M, S, Tx, Ecal, V>>
373    for Interpreter<M, S, Tx, Ecal, V>
374where
375    Tx: ExecutableTransaction,
376    S: InterpreterStorage,
377{
378    fn from(transactor: Transactor<M, S, Tx, Ecal, V>) -> Self {
379        transactor.interpreter
380    }
381}
382
383impl<M, S, Tx, Ecal, V> AsRef<Interpreter<M, S, Tx, Ecal, V>>
384    for Transactor<M, S, Tx, Ecal, V>
385where
386    Tx: ExecutableTransaction,
387    S: InterpreterStorage,
388    Ecal: EcalHandler,
389{
390    fn as_ref(&self) -> &Interpreter<M, S, Tx, Ecal, V> {
391        &self.interpreter
392    }
393}
394
395impl<M, S, Tx, Ecal, V> AsRef<S> for Transactor<M, S, Tx, Ecal, V>
396where
397    Tx: ExecutableTransaction,
398    S: InterpreterStorage,
399{
400    fn as_ref(&self) -> &S {
401        self.interpreter.as_ref()
402    }
403}
404
405impl<M, S, Tx, Ecal, V> AsMut<S> for Transactor<M, S, Tx, Ecal, V>
406where
407    Tx: ExecutableTransaction,
408    S: InterpreterStorage,
409{
410    fn as_mut(&mut self) -> &mut S {
411        self.interpreter.as_mut()
412    }
413}
414
415#[cfg(feature = "test-helpers")]
416impl<S, Tx, Ecal, V> Default for Transactor<MemoryInstance, S, Tx, Ecal, V>
417where
418    S: InterpreterStorage + Default,
419    Tx: ExecutableTransaction,
420    Ecal: EcalHandler + Default,
421    V: Verifier + Default,
422{
423    fn default() -> Self {
424        Self::new(
425            MemoryInstance::new(),
426            S::default(),
427            InterpreterParams::default(),
428        )
429    }
430}