fuel_core_interfaces/
executor.rs

1use crate::{
2    common::{
3        fuel_tx::{
4            CheckError,
5            TxId,
6            UtxoId,
7        },
8        fuel_types::{
9            Bytes32,
10            ContractId,
11            MessageId,
12        },
13        fuel_vm::backtrace::Backtrace,
14    },
15    db::KvStoreError,
16    model::{
17        BlockId,
18        FuelBlock,
19        PartialFuelBlock,
20    },
21    txpool::TransactionStatus,
22};
23use fuel_vm::fuel_tx::Transaction;
24use std::error::Error as StdError;
25use thiserror::Error;
26
27/// Starting point for executing a block.
28/// Production starts with a [`PartialFuelBlock`].
29/// Validation starts with a full [`FuelBlock`].
30pub type ExecutionBlock = ExecutionTypes<PartialFuelBlock, FuelBlock>;
31
32/// Execution wrapper with only a single type.
33pub type ExecutionType<T> = ExecutionTypes<T, T>;
34
35#[derive(Debug, Clone, Copy)]
36/// Execution wrapper where the types
37/// depend on the type of execution.
38pub enum ExecutionTypes<P, V> {
39    /// Production mode where P is being produced.
40    Production(P),
41    /// Validation mode where V is being checked.
42    Validation(V),
43}
44
45/// The result of transactions execution.
46#[derive(Debug)]
47pub struct ExecutionResult {
48    /// Created block during the execution of transactions. It contains only valid transactions.
49    pub block: FuelBlock,
50    /// The list of skipped transactions with corresponding errors. Those transactions were
51    /// not included in the block and didn't affect the state of the blockchain.
52    pub skipped_transactions: Vec<(Transaction, Error)>,
53    /// The status of the transactions execution included into the block.
54    pub tx_status: Vec<TransactionExecutionStatus>,
55}
56
57#[derive(Debug, Clone)]
58pub struct TransactionExecutionStatus {
59    pub id: Bytes32,
60    pub status: TransactionStatus,
61}
62
63/// The uncommitted result of transactions execution with database transaction.
64/// The caller should commit the result by itself.
65#[derive(Debug)]
66pub struct UncommittedResult<DbTransaction> {
67    /// The execution result.
68    result: ExecutionResult,
69    /// The database transaction with not committed state.
70    database_transaction: DbTransaction,
71}
72
73impl<DbTransaction> UncommittedResult<DbTransaction> {
74    /// Create a new instance of `UncommittedResult`.
75    pub fn new(result: ExecutionResult, database_transaction: DbTransaction) -> Self {
76        Self {
77            result,
78            database_transaction,
79        }
80    }
81
82    /// Returns a reference to the `ExecutionResult`.
83    pub fn result(&self) -> &ExecutionResult {
84        &self.result
85    }
86
87    /// Return the result and database transaction.
88    ///
89    /// The service can unpack the `UncommittedResult`, apply some changes and pack it again into
90    /// `UncommittedResult`. Because `commit` of the database transaction consumes `self`,
91    /// after committing it is not possible create `UncommittedResult`.
92    pub fn into(self) -> (ExecutionResult, DbTransaction) {
93        (self.result, self.database_transaction)
94    }
95
96    /// Discards the database transaction and returns only the result of execution.
97    pub fn into_result(self) -> ExecutionResult {
98        self.result
99    }
100
101    /// Discards the result and return database transaction.
102    pub fn into_transaction(self) -> DbTransaction {
103        self.database_transaction
104    }
105}
106
107#[derive(Debug, Clone, Copy)]
108/// The kind of execution.
109pub enum ExecutionKind {
110    /// Producing a block.
111    Production,
112    /// Validating a block.
113    Validation,
114}
115
116#[derive(Debug, Error)]
117#[non_exhaustive]
118pub enum TransactionValidityError {
119    #[error("Coin input was already spent")]
120    CoinAlreadySpent(UtxoId),
121    #[error("Coin has not yet reached maturity")]
122    CoinHasNotMatured(UtxoId),
123    #[error("The specified coin doesn't exist")]
124    CoinDoesNotExist(UtxoId),
125    #[error("The specified message was already spent")]
126    MessageAlreadySpent(MessageId),
127    #[error(
128        "Message is not yet spendable, as it's DA height is newer than this block allows"
129    )]
130    MessageSpendTooEarly(MessageId),
131    #[error("The specified message doesn't exist")]
132    MessageDoesNotExist(MessageId),
133    #[error("Contract output index isn't valid: {0:#x}")]
134    InvalidContractInputIndex(UtxoId),
135    #[error("The transaction must have at least one coin or message input type: {0:#x}")]
136    NoCoinOrMessageInput(TxId),
137    #[error("The transaction contains predicate inputs which aren't enabled: {0:#x}")]
138    PredicateExecutionDisabled(TxId),
139    #[error(
140        "The transaction contains a predicate which failed to validate: TransactionId({0:#x})"
141    )]
142    InvalidPredicate(TxId),
143    #[error("Transaction validity: {0:#?}")]
144    Validation(#[from] CheckError),
145    #[error("Datastore error occurred")]
146    DataStoreError(Box<dyn StdError + Send + Sync>),
147}
148
149impl From<KvStoreError> for TransactionValidityError {
150    fn from(e: KvStoreError) -> Self {
151        Self::DataStoreError(Box::new(e))
152    }
153}
154
155#[derive(Error, Debug)]
156#[non_exhaustive]
157pub enum Error {
158    #[error("Transaction id was already used: {0:#x}")]
159    TransactionIdCollision(Bytes32),
160    #[error("output already exists")]
161    OutputAlreadyExists,
162    #[error("The computed fee caused an integer overflow")]
163    FeeOverflow,
164    #[error("Not supported transaction: {0:?}")]
165    NotSupportedTransaction(Box<Transaction>),
166    #[error("The first transaction in the block is not `Mint` - coinbase.")]
167    CoinbaseIsNotFirstTransaction,
168    #[error("Coinbase should have one output.")]
169    CoinbaseSeveralOutputs,
170    #[error("Coinbase outputs is invalid.")]
171    CoinbaseOutputIsInvalid,
172    #[error("Coinbase amount mismatches with expected.")]
173    CoinbaseAmountMismatch,
174    #[error("Invalid transaction: {0}")]
175    TransactionValidity(#[from] TransactionValidityError),
176    #[error("corrupted block state")]
177    CorruptedBlockState(Box<dyn StdError + Send + Sync>),
178    #[error("Transaction({transaction_id:#x}) execution error: {error:?}")]
179    VmExecution {
180        error: fuel_vm::prelude::InterpreterError,
181        transaction_id: Bytes32,
182    },
183    #[error(transparent)]
184    InvalidTransaction(#[from] CheckError),
185    #[error("Execution error with backtrace")]
186    Backtrace(Box<Backtrace>),
187    #[error("Transaction doesn't match expected result: {transaction_id:#x}")]
188    InvalidTransactionOutcome { transaction_id: Bytes32 },
189    #[error("Transaction root is invalid")]
190    InvalidTransactionRoot,
191    #[error("The amount of charged fees is invalid")]
192    InvalidFeeAmount,
193    #[error("Block id is invalid")]
194    InvalidBlockId,
195    #[error("No matching utxo for contract id ${0:#x}")]
196    ContractUtxoMissing(ContractId),
197}
198
199impl ExecutionBlock {
200    /// Get the hash of the full [`FuelBlock`] if validating.
201    pub fn id(&self) -> Option<BlockId> {
202        match self {
203            ExecutionTypes::Production(_) => None,
204            ExecutionTypes::Validation(v) => Some(v.id()),
205        }
206    }
207
208    /// Get the transaction root from the full [`FuelBlock`] if validating.
209    pub fn txs_root(&self) -> Option<Bytes32> {
210        match self {
211            ExecutionTypes::Production(_) => None,
212            ExecutionTypes::Validation(v) => Some(v.header().transactions_root),
213        }
214    }
215}
216
217impl<P, V> ExecutionTypes<P, V> {
218    /// Map the production type if producing.
219    pub fn map_p<Q, F>(self, f: F) -> ExecutionTypes<Q, V>
220    where
221        F: FnOnce(P) -> Q,
222    {
223        match self {
224            ExecutionTypes::Production(p) => ExecutionTypes::Production(f(p)),
225            ExecutionTypes::Validation(v) => ExecutionTypes::Validation(v),
226        }
227    }
228
229    /// Map the validation type if validating.
230    pub fn map_v<W, F>(self, f: F) -> ExecutionTypes<P, W>
231    where
232        F: FnOnce(V) -> W,
233    {
234        match self {
235            ExecutionTypes::Production(p) => ExecutionTypes::Production(p),
236            ExecutionTypes::Validation(v) => ExecutionTypes::Validation(f(v)),
237        }
238    }
239
240    /// Get a reference version of the inner type.
241    pub fn as_ref(&self) -> ExecutionTypes<&P, &V> {
242        match *self {
243            ExecutionTypes::Production(ref p) => ExecutionTypes::Production(p),
244            ExecutionTypes::Validation(ref v) => ExecutionTypes::Validation(v),
245        }
246    }
247
248    /// Get a mutable reference version of the inner type.
249    pub fn as_mut(&mut self) -> ExecutionTypes<&mut P, &mut V> {
250        match *self {
251            ExecutionTypes::Production(ref mut p) => ExecutionTypes::Production(p),
252            ExecutionTypes::Validation(ref mut v) => ExecutionTypes::Validation(v),
253        }
254    }
255
256    /// Get the kind of execution.
257    pub fn to_kind(&self) -> ExecutionKind {
258        match self {
259            ExecutionTypes::Production(_) => ExecutionKind::Production,
260            ExecutionTypes::Validation(_) => ExecutionKind::Validation,
261        }
262    }
263}
264
265impl<T> ExecutionType<T> {
266    /// Map the wrapped type.
267    pub fn map<U, F>(self, f: F) -> ExecutionType<U>
268    where
269        F: FnOnce(T) -> U,
270    {
271        match self {
272            ExecutionTypes::Production(p) => ExecutionTypes::Production(f(p)),
273            ExecutionTypes::Validation(v) => ExecutionTypes::Validation(f(v)),
274        }
275    }
276
277    /// Filter and map the inner type.
278    pub fn filter_map<U, F>(self, f: F) -> Option<ExecutionType<U>>
279    where
280        F: FnOnce(T) -> Option<U>,
281    {
282        match self {
283            ExecutionTypes::Production(p) => f(p).map(ExecutionTypes::Production),
284            ExecutionTypes::Validation(v) => f(v).map(ExecutionTypes::Validation),
285        }
286    }
287
288    /// Get the inner type.
289    pub fn into_inner(self) -> T {
290        match self {
291            ExecutionTypes::Production(t) | ExecutionTypes::Validation(t) => t,
292        }
293    }
294
295    /// Split into the execution kind and the inner type.
296    pub fn split(self) -> (ExecutionKind, T) {
297        let kind = self.to_kind();
298        (kind, self.into_inner())
299    }
300}
301
302impl ExecutionKind {
303    /// Wrap a type in this execution kind.
304    pub fn wrap<T>(self, t: T) -> ExecutionType<T> {
305        match self {
306            ExecutionKind::Production => ExecutionTypes::Production(t),
307            ExecutionKind::Validation => ExecutionTypes::Validation(t),
308        }
309    }
310}
311
312impl From<Backtrace> for Error {
313    fn from(e: Backtrace) -> Self {
314        Error::Backtrace(Box::new(e))
315    }
316}
317
318impl From<KvStoreError> for Error {
319    fn from(e: KvStoreError) -> Self {
320        Error::CorruptedBlockState(Box::new(e))
321    }
322}
323
324impl From<crate::db::Error> for Error {
325    fn from(e: crate::db::Error) -> Self {
326        Error::CorruptedBlockState(Box::new(e))
327    }
328}
329
330impl<T> core::ops::Deref for ExecutionType<T> {
331    type Target = T;
332
333    fn deref(&self) -> &Self::Target {
334        match self {
335            ExecutionTypes::Production(p) => p,
336            ExecutionTypes::Validation(v) => v,
337        }
338    }
339}
340
341impl<T> core::ops::DerefMut for ExecutionType<T> {
342    fn deref_mut(&mut self) -> &mut Self::Target {
343        match self {
344            ExecutionTypes::Production(p) => p,
345            ExecutionTypes::Validation(v) => v,
346        }
347    }
348}