use super::executed::Executed;
use crate::unwrap_or_return;
use cfx_types::{Address, H256, U256, U512};
use cfx_vm_types as vm;
use primitives::{
log_entry::build_bloom, receipt::StorageChange, LogEntry, Receipt,
SignedTransaction, TransactionStatus,
};
use solidity_abi::string_revert_reason_decode;
#[derive(Debug)]
pub enum ExecutionOutcome {
NotExecutedDrop(TxDropError),
NotExecutedToReconsiderPacking(ToRepackError),
ExecutionErrorBumpNonce(ExecutionError, Executed),
Finished(Executed),
}
use vm::Spec;
use ExecutionOutcome::*;
#[derive(Debug)]
pub enum ToRepackError {
InvalidNonce {
expected: U256,
got: U256,
},
EpochHeightOutOfBound {
block_height: u64,
set: u64,
transaction_epoch_bound: u64,
},
NotEnoughCashFromSponsor {
required_gas_cost: U512,
gas_sponsor_balance: U512,
required_storage_cost: U256,
storage_sponsor_balance: U256,
},
SenderDoesNotExist,
NotEnoughBaseFee {
expected: U256,
got: U256,
},
}
#[derive(Debug)]
pub enum TxDropError {
OldNonce(U256, U256),
InvalidRecipientAddress(Address),
NotEnoughGasLimit { expected: U256, got: U256 },
}
#[derive(Debug, PartialEq)]
pub enum ExecutionError {
NotEnoughCash {
required: U512,
got: U512,
actual_gas_cost: U256,
max_storage_limit_cost: U256,
},
VmError(vm::Error),
}
impl ExecutionOutcome {
#[inline]
pub fn make_receipt(
self, accumulated_gas_used: &mut U256, spec: &Spec,
) -> Receipt {
*accumulated_gas_used += self.gas_used();
let gas_fee = self.gas_fee();
let gas_sponsor_paid = self.gas_sponsor_paid();
let storage_sponsor_paid = self.storage_sponsor_paid();
let tx_outcome_status = self.outcome_status();
let transaction_logs = self.transaction_logs();
let storage_collateralized = self.storage_collateralized();
let storage_released = self.storage_released();
let burnt_fee = self.burnt_fee(spec);
let log_bloom = build_bloom(&transaction_logs);
Receipt::new(
tx_outcome_status,
*accumulated_gas_used,
gas_fee,
gas_sponsor_paid,
transaction_logs,
log_bloom,
storage_sponsor_paid,
storage_collateralized,
storage_released,
burnt_fee,
)
}
#[inline]
pub fn into_success_executed(self) -> Option<Executed> {
match self {
ExecutionOutcome::Finished(executed) => Some(executed),
_ => None,
}
}
#[inline]
pub fn try_as_success_executed(&self) -> Option<&Executed> {
match self {
ExecutionOutcome::Finished(executed) => Some(executed),
_ => None,
}
}
#[inline]
pub fn try_as_executed(&self) -> Option<&Executed> {
match self {
NotExecutedDrop(_) | NotExecutedToReconsiderPacking(_) => None,
ExecutionErrorBumpNonce(_, executed) | Finished(executed) => {
Some(executed)
}
}
}
#[inline]
pub fn gas_fee(&self) -> U256 {
let executed = unwrap_or_return!(self.try_as_executed());
executed.fee
}
#[inline]
pub fn gas_used(&self) -> U256 {
let executed = unwrap_or_return!(self.try_as_executed());
executed.gas_used
}
#[inline]
pub fn gas_sponsor_paid(&self) -> bool {
let executed = unwrap_or_return!(self.try_as_executed());
executed.gas_sponsor_paid
}
#[inline]
pub fn storage_sponsor_paid(&self) -> bool {
let executed = unwrap_or_return!(self.try_as_executed());
executed.storage_sponsor_paid
}
#[inline]
pub fn transaction_logs(&self) -> Vec<LogEntry> {
let executed = unwrap_or_return!(self.try_as_success_executed());
executed.logs.clone()
}
#[inline]
pub fn storage_collateralized(&self) -> Vec<StorageChange> {
let executed = unwrap_or_return!(self.try_as_success_executed());
executed.storage_collateralized.clone()
}
#[inline]
pub fn storage_released(&self) -> Vec<StorageChange> {
let executed = unwrap_or_return!(self.try_as_success_executed());
executed.storage_released.clone()
}
#[inline]
pub fn burnt_fee(&self, spec: &Spec) -> Option<U256> {
if let Some(e) = self.try_as_executed() {
e.burnt_fee
} else if spec.cip1559 {
Some(U256::zero())
} else {
None
}
}
#[inline]
pub fn consider_repacked(&self) -> bool {
matches!(self, NotExecutedToReconsiderPacking(_))
}
#[inline]
pub fn error_message(&self) -> String {
match self {
NotExecutedDrop(_) | NotExecutedToReconsiderPacking(_) => {
"tx not executed".into()
}
ExecutionErrorBumpNonce(error, executed) => {
if error == &ExecutionError::VmError(vm::Error::Reverted) {
let revert_reason =
string_revert_reason_decode(&executed.output);
format!("Vm reverted, {}", revert_reason)
} else {
format!("{:?}", error)
}
}
Finished(_) => "".into(),
}
}
#[inline]
pub fn outcome_status(&self) -> TransactionStatus {
match self {
NotExecutedDrop(_) | NotExecutedToReconsiderPacking(_) => {
TransactionStatus::Skipped
}
ExecutionErrorBumpNonce(_, _) => TransactionStatus::Failure,
Finished(_) => TransactionStatus::Success,
}
}
#[inline]
pub fn log(&self, tx: &SignedTransaction, block_hash: &H256) {
match self {
ExecutionOutcome::NotExecutedDrop(e) => {
trace!(
"tx not executed, not to reconsider packing: \
transaction={:?},err={:?}",
tx,
e
);
}
ExecutionOutcome::NotExecutedToReconsiderPacking(e) => {
trace!(
"tx not executed, to reconsider packing: \
transaction={:?}, err={:?}",
tx,
e
);
}
ExecutionOutcome::ExecutionErrorBumpNonce(error, _) => {
debug!(
"tx execution error: err={:?}, transaction={:?}",
error, tx
);
}
ExecutionOutcome::Finished(executed) => {
trace!("tx executed successfully: result={:?}, transaction={:?}, in block {:?}", executed, tx, block_hash);
}
}
}
}