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 InvalidNonce {
25 expected: U256,
27 got: U256,
29 },
30
31 EpochHeightOutOfBound {
35 block_height: u64,
36 set: u64,
37 transaction_epoch_bound: u64,
38 },
39
40 NotEnoughCashFromSponsor {
43 required_gas_cost: U512,
45 gas_sponsor_balance: U512,
47 required_storage_cost: U256,
49 storage_sponsor_balance: U256,
51 },
52
53 SenderDoesNotExist,
55
56 NotEnoughBaseFee {
57 expected: U256,
58 got: U256,
59 },
60
61 NotEnoughBalance {
63 expected: U512,
64 got: U256,
65 },
66}
67
68#[derive(Debug)]
69pub enum TxDropError {
70 OldNonce(U256, U256),
72
73 InvalidRecipientAddress(Address),
77
78 NotEnoughGasLimit { expected: U256, got: U256 },
80
81 SenderWithCode(Address),
83}
84
85#[derive(Debug, PartialEq)]
86pub enum ExecutionError {
87 NotEnoughCash {
90 required: U512,
92 got: U512,
94 actual_gas_cost: U256,
96 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}