diff --git a/Cargo.lock b/Cargo.lock index d4d5ea8..a5caf16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6496,6 +6496,8 @@ dependencies = [ "reth-evm", "reth-evm-ethereum", "reth-node-ethereum", + "revm", + "serde_json", "tikv-jemallocator", ] diff --git a/Cargo.toml b/Cargo.toml index 6a5302e..d789b16 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,8 @@ eyre = "0.6.12" clap = { version = "4.5.6", features = ["derive"] } alloy-sol-macro = "0.7.6" alloy-sol-types = "0.7.6" +revm = "9.0.0" +serde_json = "1.0.117" [target.'cfg(unix)'.dependencies] tikv-jemallocator = { version = "0.5.0", optional = true } diff --git a/src/evm_config.rs b/src/evm_config.rs new file mode 100644 index 0000000..4174c0f --- /dev/null +++ b/src/evm_config.rs @@ -0,0 +1,112 @@ +use reth::{ + primitives::{Address, ChainSpec, Header, TransactionSigned, U256}, + revm::{ + inspector_handle_register, + interpreter::Gas, + primitives::{spec_to_generic, CfgEnvWithHandlerCfg, EVMError, Spec, SpecId, TxEnv}, + Context, Database, Evm, EvmBuilder, GetInspector, + }, +}; +use reth_evm::{ConfigureEvm, ConfigureEvmEnv}; +use reth_evm_ethereum::EthEvmConfig; +use revm::handler::mainnet::reward_beneficiary as reward_beneficiary_mainnet; +use std::sync::Arc; + +/// Reward beneficiary with gas fee. +#[inline] +pub fn reward_beneficiary( + context: &mut Context, + gas: &Gas, + collector_address: Address, +) -> Result<(), EVMError> { + reward_beneficiary_mainnet::(context, gas)?; + if SPEC::enabled(SpecId::LONDON) { + mint_basefee_to_collector_address::(context, gas, collector_address)?; + } + Ok(()) +} + +/// Mint basefee to eip1559 collector +#[inline] +pub fn mint_basefee_to_collector_address( + context: &mut Context, + gas: &Gas, + collector_address: Address, +) -> Result<(), EVMError> { + // TODO: Define a per-network collector address configurable via genesis file + let base_fee = context.evm.env.block.basefee; + let gas_used = U256::from(gas.spent() - gas.refunded() as u64); + + let (collector_account, _) = context + .evm + .inner + .journaled_state + .load_account(collector_address, &mut context.evm.inner.db)?; + + collector_account.mark_touch(); + collector_account.info.balance = collector_account + .info + .balance + .saturating_add(base_fee * gas_used); + + Ok(()) +} + +/// Custom EVM configuration +#[derive(Debug, Clone, Copy)] +pub struct GnosisEvmConfig { + pub collector_address: Address, +} + +impl ConfigureEvm for GnosisEvmConfig { + type DefaultExternalContext<'a> = (); + + fn evm<'a, DB: Database + 'a>(&self, db: DB) -> Evm<'a, Self::DefaultExternalContext<'a>, DB> { + let collector_address = self.collector_address; + EvmBuilder::default() + .with_db(db) + .append_handler_register_box(Box::new(move |h| { + spec_to_generic!(h.spec_id(), { + h.post_execution.reward_beneficiary = Arc::new(move |context, gas| { + reward_beneficiary::(context, gas, collector_address) + }); + }); + })) + .build() + } + + fn evm_with_inspector<'a, DB, I>(&self, db: DB, inspector: I) -> Evm<'a, I, DB> + where + DB: Database + 'a, + I: GetInspector, + { + let collector_address = self.collector_address; + EvmBuilder::default() + .with_db(db) + .with_external_context(inspector) + .append_handler_register_box(Box::new(move |h| { + spec_to_generic!(h.spec_id(), { + h.post_execution.reward_beneficiary = Arc::new(move |context, gas| { + reward_beneficiary::(context, gas, collector_address) + }); + }); + })) + .append_handler_register(inspector_handle_register) + .build() + } +} + +impl ConfigureEvmEnv for GnosisEvmConfig { + fn fill_tx_env(tx_env: &mut TxEnv, transaction: &TransactionSigned, sender: Address) { + EthEvmConfig::fill_tx_env(tx_env, transaction, sender) + } + + fn fill_cfg_env( + cfg_env: &mut CfgEnvWithHandlerCfg, + chain_spec: &ChainSpec, + header: &Header, + total_difficulty: U256, + ) { + EthEvmConfig::fill_cfg_env(cfg_env, chain_spec, header, total_difficulty); + } +} diff --git a/src/lib.rs b/src/lib.rs index 882ffdf..f13c2eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,6 @@ +use evm_config::GnosisEvmConfig; use execute::GnosisExecutorProvider; +use eyre::eyre; use reth::{ api::NodeTypes, builder::{ @@ -7,13 +9,13 @@ use reth::{ BuilderContext, Node, }, }; -use reth_evm_ethereum::EthEvmConfig; use reth_node_ethereum::{ node::{EthereumNetworkBuilder, EthereumPayloadBuilder, EthereumPoolBuilder}, EthEngineTypes, EthereumNode, }; mod ethereum; +mod evm_config; mod execute; mod gnosis; @@ -88,7 +90,7 @@ where Node: FullNodeTypes, { // Must implement ConfigureEvm; - type EVM = EthEvmConfig; + type EVM = GnosisEvmConfig; // Must implement BlockExecutorProvider; type Executor = GnosisExecutorProvider; @@ -97,7 +99,18 @@ where ctx: &BuilderContext, ) -> eyre::Result<(Self::EVM, Self::Executor)> { let chain_spec = ctx.chain_spec(); - let evm_config = EthEvmConfig::default(); + let collector_address = ctx + .config() + .chain + .genesis() + .config + .extra_fields + .get("eip1559collector") + .ok_or(eyre!("no eip1559collector field"))?; + + let evm_config = GnosisEvmConfig { + collector_address: serde_json::from_value(collector_address.clone())?, + }; let executor = GnosisExecutorProvider::new(chain_spec, evm_config); Ok((evm_config, executor))