revm_handler/
post_execution.rs

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