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