revm_handler/
post_execution.rs

1use crate::FrameResult;
2use context_interface::{
3    journaled_state::JournalTr,
4    result::{ExecutionResult, HaltReason, HaltReasonTr},
5    Block, Cfg, ContextTr, Database, LocalContextTr, Transaction,
6};
7use interpreter::{Gas, InitialAndFloorGas, SuccessOrHalt};
8use primitives::{hardfork::SpecId, U256};
9
10/// Ensures minimum gas floor is spent according to EIP-7623.
11pub fn eip7623_check_gas_floor(gas: &mut Gas, init_and_floor_gas: InitialAndFloorGas) {
12    // EIP-7623: Increase calldata cost
13    // spend at least a gas_floor amount of gas.
14    if gas.spent_sub_refunded() < init_and_floor_gas.floor_gas {
15        gas.set_spent(init_and_floor_gas.floor_gas);
16        // clear refund
17        gas.set_refund(0);
18    }
19}
20
21/// Calculates and applies gas refunds based on the specification.
22pub fn refund(spec: SpecId, gas: &mut Gas, eip7702_refund: i64) {
23    gas.record_refund(eip7702_refund);
24    // Calculate gas refund for transaction.
25    // If spec is set to london, it will decrease the maximum refund amount to 5th part of
26    // gas spend. (Before london it was 2th part of gas spend)
27    gas.set_final_refund(spec.is_enabled_in(SpecId::LONDON));
28}
29
30/// Reimburses the caller for unused gas.
31#[inline]
32pub fn reimburse_caller<CTX: ContextTr>(
33    context: &mut CTX,
34    gas: &Gas,
35    additional_refund: U256,
36) -> Result<(), <CTX::Db as Database>::Error> {
37    let basefee = context.block().basefee() as u128;
38    let caller = context.tx().caller();
39    let effective_gas_price = context.tx().effective_gas_price(basefee);
40
41    // Return balance of not spend gas.
42    context
43        .journal_mut()
44        .load_account_mut(caller)?
45        .incr_balance(
46            U256::from(
47                effective_gas_price
48                    .saturating_mul((gas.remaining() + gas.refunded() as u64) as u128),
49            ) + additional_refund,
50        );
51
52    Ok(())
53}
54
55/// Rewards the beneficiary with transaction fees.
56#[inline]
57pub fn reward_beneficiary<CTX: ContextTr>(
58    context: &mut CTX,
59    gas: &Gas,
60) -> Result<(), <CTX::Db as Database>::Error> {
61    let (block, tx, cfg, journal, _, _) = context.all_mut();
62    let basefee = block.basefee() as u128;
63    let effective_gas_price = tx.effective_gas_price(basefee);
64
65    // Transfer fee to coinbase/beneficiary.
66    // EIP-1559 discard basefee for coinbase transfer. Basefee amount of gas is discarded.
67    let coinbase_gas_price = if cfg.spec().into().is_enabled_in(SpecId::LONDON) {
68        effective_gas_price.saturating_sub(basefee)
69    } else {
70        effective_gas_price
71    };
72
73    // reward beneficiary
74    journal
75        .load_account_mut(block.beneficiary())?
76        .incr_balance(U256::from(coinbase_gas_price * gas.used() as u128));
77
78    Ok(())
79}
80
81/// Calculate last gas spent and transform internal reason to external.
82///
83/// TODO make Journal FinalOutput more generic.
84pub fn output<CTX: ContextTr<Journal: JournalTr>, HALTREASON: HaltReasonTr>(
85    context: &mut CTX,
86    // TODO, make this more generic and nice.
87    // FrameResult should be a generic that returns gas and interpreter result.
88    result: FrameResult,
89) -> ExecutionResult<HALTREASON> {
90    // Used gas with refund calculated.
91    let gas_refunded = result.gas().refunded() as u64;
92    let gas_used = result.gas().used();
93    let output = result.output();
94    let instruction_result = result.into_interpreter_result();
95
96    // take logs from journal.
97    let logs = context.journal_mut().take_logs();
98
99    match SuccessOrHalt::<HALTREASON>::from(instruction_result.result) {
100        SuccessOrHalt::Success(reason) => ExecutionResult::Success {
101            reason,
102            gas_used,
103            gas_refunded,
104            logs,
105            output,
106        },
107        SuccessOrHalt::Revert => ExecutionResult::Revert {
108            gas_used,
109            output: output.into_data(),
110        },
111        SuccessOrHalt::Halt(reason) => {
112            // Bubble up precompile errors from context when available
113            if matches!(
114                instruction_result.result,
115                interpreter::InstructionResult::PrecompileError
116            ) {
117                if let Some(message) = context.local_mut().take_precompile_error_context() {
118                    return ExecutionResult::Halt {
119                        reason: HALTREASON::from(HaltReason::PrecompileErrorWithContext(message)),
120                        gas_used,
121                    };
122                }
123            }
124            ExecutionResult::Halt { reason, gas_used }
125        }
126        // Only two internal return flags.
127        flag @ (SuccessOrHalt::FatalExternalError | SuccessOrHalt::Internal(_)) => {
128            panic!(
129                "Encountered unexpected internal return flag: {flag:?} with instruction result: {instruction_result:?}"
130            )
131        }
132    }
133}