Skip to main content

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