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