cfx_executor/executive/
execution_outcome.rs

1use super::executed::Executed;
2use crate::unwrap_or_return;
3use cfx_types::{Address, H256, U256, U512};
4use cfx_vm_types as vm;
5use primitives::{
6    log_entry::build_bloom, receipt::StorageChange, LogEntry, Receipt,
7    SignedTransaction, TransactionStatus,
8};
9use solidity_abi::string_revert_reason_decode;
10
11#[derive(Debug)]
12pub enum ExecutionOutcome {
13    NotExecutedDrop(TxDropError),
14    NotExecutedToReconsiderPacking(ToRepackError),
15    ExecutionErrorBumpNonce(ExecutionError, Executed),
16    Finished(Executed),
17}
18use vm::Spec;
19use ExecutionOutcome::*;
20
21#[derive(Debug)]
22pub enum ToRepackError {
23    /// Returned when transaction nonce does not match state nonce.
24    InvalidNonce {
25        /// Nonce expected.
26        expected: U256,
27        /// Nonce found.
28        got: U256,
29    },
30
31    /// Epoch height out of bound.
32    /// The transaction was correct in the block where it's packed, but
33    /// falls into the error when in the epoch to execute.
34    EpochHeightOutOfBound {
35        block_height: u64,
36        set: u64,
37        transaction_epoch_bound: u64,
38    },
39
40    /// Returned when cost of transaction (value + gas_price * gas) exceeds
41    /// current sponsor balance.
42    NotEnoughCashFromSponsor {
43        /// Minimum required gas cost.
44        required_gas_cost: U512,
45        /// Actual balance of gas sponsor.
46        gas_sponsor_balance: U512,
47        /// Minimum required storage collateral cost.
48        required_storage_cost: U256,
49        /// Actual balance of storage sponsor.
50        storage_sponsor_balance: U256,
51    },
52
53    /// Returned when a non-sponsored transaction's sender does not exist yet.
54    SenderDoesNotExist,
55
56    NotEnoughBaseFee {
57        expected: U256,
58        got: U256,
59    },
60
61    // For align_evm test only
62    NotEnoughBalance {
63        expected: U512,
64        got: U256,
65    },
66}
67
68#[derive(Debug)]
69pub enum TxDropError {
70    /// The account nonce in world-state is larger than tx nonce
71    OldNonce(U256, U256),
72
73    /// The recipient of current tx is in invalid address field.
74    /// Although it can be verified in tx packing,
75    /// by spec doc, it is checked in execution.
76    InvalidRecipientAddress(Address),
77
78    /// Not enough gas limit for large transacton, only for estimation
79    NotEnoughGasLimit { expected: U256, got: U256 },
80
81    /// The EOA sender with contract code is forbidded by CIP-152
82    SenderWithCode(Address),
83}
84
85#[derive(Debug, PartialEq)]
86pub enum ExecutionError {
87    /// Returned when cost of transaction (value + gas_price * gas) exceeds
88    /// current sender balance.
89    NotEnoughCash {
90        /// Minimum required balance.
91        required: U512,
92        /// Actual balance.
93        got: U512,
94        /// Actual gas cost. This should be min(gas_fee, balance).
95        actual_gas_cost: U256,
96        /// Maximum storage limit cost.
97        max_storage_limit_cost: U256,
98    },
99    NonceOverflow(Address),
100    VmError(vm::Error),
101}
102
103impl ExecutionOutcome {
104    #[inline]
105    pub fn make_receipt(
106        self, accumulated_gas_used: &mut U256, spec: &Spec,
107    ) -> Receipt {
108        *accumulated_gas_used += self.gas_used();
109
110        let gas_fee = self.gas_fee();
111        let gas_sponsor_paid = self.gas_sponsor_paid();
112        let storage_sponsor_paid = self.storage_sponsor_paid();
113
114        let tx_outcome_status = self.outcome_status();
115        let transaction_logs = self.transaction_logs();
116        let storage_collateralized = self.storage_collateralized();
117        let storage_released = self.storage_released();
118
119        let burnt_fee = self.burnt_fee(spec);
120
121        let log_bloom = build_bloom(&transaction_logs);
122
123        Receipt::new(
124            tx_outcome_status,
125            *accumulated_gas_used,
126            gas_fee,
127            gas_sponsor_paid,
128            transaction_logs,
129            log_bloom,
130            storage_sponsor_paid,
131            storage_collateralized,
132            storage_released,
133            burnt_fee,
134        )
135    }
136
137    #[inline]
138    pub fn into_success_executed(self) -> Option<Executed> {
139        match self {
140            ExecutionOutcome::Finished(executed) => Some(executed),
141            _ => None,
142        }
143    }
144
145    #[inline]
146    pub fn try_as_success_executed(&self) -> Option<&Executed> {
147        match self {
148            ExecutionOutcome::Finished(executed) => Some(executed),
149            _ => None,
150        }
151    }
152
153    #[inline]
154    pub fn try_as_executed(&self) -> Option<&Executed> {
155        match self {
156            NotExecutedDrop(_) | NotExecutedToReconsiderPacking(_) => None,
157            ExecutionErrorBumpNonce(_, executed) | Finished(executed) => {
158                Some(executed)
159            }
160        }
161    }
162
163    pub fn try_into_executed(self) -> Option<Executed> {
164        match self {
165            NotExecutedDrop(_) | NotExecutedToReconsiderPacking(_) => None,
166            ExecutionErrorBumpNonce(_, executed) | Finished(executed) => {
167                Some(executed)
168            }
169        }
170    }
171
172    #[inline]
173    pub fn gas_fee(&self) -> U256 {
174        let executed = unwrap_or_return!(self.try_as_executed());
175        executed.fee
176    }
177
178    #[inline]
179    pub fn gas_used(&self) -> U256 {
180        let executed = unwrap_or_return!(self.try_as_executed());
181        executed.gas_used
182    }
183
184    #[inline]
185    pub fn gas_sponsor_paid(&self) -> bool {
186        let executed = unwrap_or_return!(self.try_as_executed());
187        executed.gas_sponsor_paid
188    }
189
190    #[inline]
191    pub fn storage_sponsor_paid(&self) -> bool {
192        let executed = unwrap_or_return!(self.try_as_executed());
193        executed.storage_sponsor_paid
194    }
195
196    #[inline]
197    pub fn transaction_logs(&self) -> Vec<LogEntry> {
198        let executed = unwrap_or_return!(self.try_as_success_executed());
199        executed.logs.clone()
200    }
201
202    #[inline]
203    pub fn storage_collateralized(&self) -> Vec<StorageChange> {
204        let executed = unwrap_or_return!(self.try_as_success_executed());
205        executed.storage_collateralized.clone()
206    }
207
208    #[inline]
209    pub fn storage_released(&self) -> Vec<StorageChange> {
210        let executed = unwrap_or_return!(self.try_as_success_executed());
211        executed.storage_released.clone()
212    }
213
214    #[inline]
215    pub fn burnt_fee(&self, spec: &Spec) -> Option<U256> {
216        if let Some(e) = self.try_as_executed() {
217            e.burnt_fee
218        } else if spec.cip1559 {
219            Some(U256::zero())
220        } else {
221            None
222        }
223    }
224
225    #[inline]
226    pub fn consider_repacked(&self) -> bool {
227        matches!(self, NotExecutedToReconsiderPacking(_))
228    }
229
230    #[inline]
231    pub fn error_message(&self) -> String {
232        match self {
233            NotExecutedDrop(_) | NotExecutedToReconsiderPacking(_) => {
234                "tx not executed".into()
235            }
236            ExecutionErrorBumpNonce(error, executed) => {
237                if error == &ExecutionError::VmError(vm::Error::Reverted) {
238                    let revert_reason =
239                        string_revert_reason_decode(&executed.output);
240                    format!("Vm reverted, {}", revert_reason)
241                } else {
242                    format!("{:?}", error)
243                }
244            }
245            Finished(_) => "".into(),
246        }
247    }
248
249    #[inline]
250    pub fn outcome_status(&self) -> TransactionStatus {
251        match self {
252            NotExecutedDrop(_) | NotExecutedToReconsiderPacking(_) => {
253                TransactionStatus::Skipped
254            }
255            ExecutionErrorBumpNonce(_, _) => TransactionStatus::Failure,
256            Finished(_) => TransactionStatus::Success,
257        }
258    }
259
260    #[inline]
261    pub fn log(&self, tx: &SignedTransaction, block_hash: &H256) {
262        match self {
263            ExecutionOutcome::NotExecutedDrop(e) => {
264                trace!(
265                    "tx not executed, not to reconsider packing: \
266                     transaction={:?},err={:?}",
267                    tx,
268                    e
269                );
270            }
271            ExecutionOutcome::NotExecutedToReconsiderPacking(e) => {
272                trace!(
273                    "tx not executed, to reconsider packing: \
274                     transaction={:?}, err={:?}",
275                    tx,
276                    e
277                );
278            }
279            ExecutionOutcome::ExecutionErrorBumpNonce(error, _) => {
280                debug!(
281                    "tx execution error: err={:?}, transaction={:?}",
282                    error, tx
283                );
284            }
285            ExecutionOutcome::Finished(executed) => {
286                trace!("tx executed successfully: result={:?}, transaction={:?}, in block {:?}", executed, tx, block_hash);
287            }
288        }
289    }
290}