mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
Make it compilable (still bunch to fix!)
This commit is contained in:
32
src/node/evm/assembler.rs
Normal file
32
src/node/evm/assembler.rs
Normal file
@ -0,0 +1,32 @@
|
||||
use crate::{
|
||||
node::evm::config::{HlBlockExecutorFactory, HlEvmConfig},
|
||||
HlBlock, HlBlockBody,
|
||||
};
|
||||
use alloy_consensus::{Block, Header};
|
||||
use reth_evm::{
|
||||
block::BlockExecutionError,
|
||||
execute::{BlockAssembler, BlockAssemblerInput},
|
||||
};
|
||||
|
||||
impl BlockAssembler<HlBlockExecutorFactory> for HlEvmConfig {
|
||||
type Block = HlBlock;
|
||||
|
||||
fn assemble_block(
|
||||
&self,
|
||||
input: BlockAssemblerInput<'_, '_, HlBlockExecutorFactory, Header>,
|
||||
) -> Result<Self::Block, BlockExecutionError> {
|
||||
let Block { header, body: inner } = self.block_assembler.assemble_block(input)?;
|
||||
Ok(HlBlock {
|
||||
header,
|
||||
body: HlBlockBody {
|
||||
inner,
|
||||
// HACK: we're setting sidecars to `None` here but ideally we should somehow get
|
||||
// them from the payload builder.
|
||||
//
|
||||
// Payload building is out of scope of reth-bsc for now, so this is not critical
|
||||
sidecars: None,
|
||||
read_precompile_calls: None,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
316
src/node/evm/config.rs
Normal file
316
src/node/evm/config.rs
Normal file
@ -0,0 +1,316 @@
|
||||
use super::{executor::HlBlockExecutor, factory::HlEvmFactory};
|
||||
use crate::{
|
||||
chainspec::HlChainSpec,
|
||||
evm::{spec::HlSpecId, transaction::HlTxEnv},
|
||||
hardforks::HlHardforks,
|
||||
HlPrimitives,
|
||||
};
|
||||
use alloy_consensus::{BlockHeader, Header, TxReceipt};
|
||||
use alloy_primitives::{Log, U256};
|
||||
use reth_chainspec::{EthChainSpec, EthereumHardforks, Hardforks};
|
||||
use reth_ethereum_forks::EthereumHardfork;
|
||||
use reth_evm::{
|
||||
block::{BlockExecutorFactory, BlockExecutorFor},
|
||||
eth::{receipt_builder::ReceiptBuilder, EthBlockExecutionCtx},
|
||||
ConfigureEvm, EvmEnv, EvmFactory, ExecutionCtxFor, FromRecoveredTx, FromTxWithEncoded,
|
||||
IntoTxEnv, NextBlockEnvAttributes,
|
||||
};
|
||||
use reth_evm_ethereum::{EthBlockAssembler, RethReceiptBuilder};
|
||||
use reth_primitives::{BlockTy, HeaderTy, SealedBlock, SealedHeader, TransactionSigned};
|
||||
use reth_revm::State;
|
||||
use revm::{
|
||||
context::{BlockEnv, CfgEnv, TxEnv},
|
||||
context_interface::block::BlobExcessGasAndPrice,
|
||||
primitives::hardfork::SpecId,
|
||||
Inspector,
|
||||
};
|
||||
use std::{borrow::Cow, convert::Infallible, sync::Arc};
|
||||
|
||||
/// Ethereum-related EVM configuration.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct HlEvmConfig {
|
||||
/// Inner [`HlBlockExecutorFactory`].
|
||||
pub executor_factory:
|
||||
HlBlockExecutorFactory<RethReceiptBuilder, Arc<HlChainSpec>, HlEvmFactory>,
|
||||
/// Ethereum block assembler.
|
||||
pub block_assembler: EthBlockAssembler<HlChainSpec>,
|
||||
}
|
||||
|
||||
impl HlEvmConfig {
|
||||
/// Creates a new Ethereum EVM configuration with the given chain spec.
|
||||
pub fn new(chain_spec: Arc<HlChainSpec>) -> Self {
|
||||
Self::hl(chain_spec)
|
||||
}
|
||||
|
||||
/// Creates a new Ethereum EVM configuration.
|
||||
pub fn hl(chain_spec: Arc<HlChainSpec>) -> Self {
|
||||
Self::new_with_evm_factory(chain_spec, HlEvmFactory::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl HlEvmConfig {
|
||||
/// Creates a new Ethereum EVM configuration with the given chain spec and EVM factory.
|
||||
pub fn new_with_evm_factory(chain_spec: Arc<HlChainSpec>, evm_factory: HlEvmFactory) -> Self {
|
||||
Self {
|
||||
block_assembler: EthBlockAssembler::new(chain_spec.clone()),
|
||||
executor_factory: HlBlockExecutorFactory::new(
|
||||
RethReceiptBuilder::default(),
|
||||
chain_spec,
|
||||
evm_factory,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the chain spec associated with this configuration.
|
||||
pub const fn chain_spec(&self) -> &Arc<HlChainSpec> {
|
||||
self.executor_factory.spec()
|
||||
}
|
||||
}
|
||||
|
||||
/// Ethereum block executor factory.
|
||||
#[derive(Debug, Clone, Default, Copy)]
|
||||
pub struct HlBlockExecutorFactory<
|
||||
R = RethReceiptBuilder,
|
||||
Spec = Arc<HlChainSpec>,
|
||||
EvmFactory = HlEvmFactory,
|
||||
> {
|
||||
/// Receipt builder.
|
||||
receipt_builder: R,
|
||||
/// Chain specification.
|
||||
spec: Spec,
|
||||
/// EVM factory.
|
||||
evm_factory: EvmFactory,
|
||||
}
|
||||
|
||||
impl<R, Spec, EvmFactory> HlBlockExecutorFactory<R, Spec, EvmFactory> {
|
||||
/// Creates a new [`HlBlockExecutorFactory`] with the given spec, [`EvmFactory`], and
|
||||
/// [`ReceiptBuilder`].
|
||||
pub const fn new(receipt_builder: R, spec: Spec, evm_factory: EvmFactory) -> Self {
|
||||
Self {
|
||||
receipt_builder,
|
||||
spec,
|
||||
evm_factory,
|
||||
}
|
||||
}
|
||||
|
||||
/// Exposes the receipt builder.
|
||||
pub const fn receipt_builder(&self) -> &R {
|
||||
&self.receipt_builder
|
||||
}
|
||||
|
||||
/// Exposes the chain specification.
|
||||
pub const fn spec(&self) -> &Spec {
|
||||
&self.spec
|
||||
}
|
||||
}
|
||||
|
||||
impl<R, Spec, EvmF> BlockExecutorFactory for HlBlockExecutorFactory<R, Spec, EvmF>
|
||||
where
|
||||
R: ReceiptBuilder<Transaction = TransactionSigned, Receipt: TxReceipt<Log = Log>>,
|
||||
Spec: EthereumHardforks + HlHardforks + EthChainSpec + Hardforks + Clone,
|
||||
EvmF: EvmFactory<Tx: FromRecoveredTx<TransactionSigned> + FromTxWithEncoded<TransactionSigned>>,
|
||||
R::Transaction: From<TransactionSigned> + Clone,
|
||||
Self: 'static,
|
||||
HlTxEnv<TxEnv>: IntoTxEnv<<EvmF as EvmFactory>::Tx>,
|
||||
{
|
||||
type EvmFactory = EvmF;
|
||||
type ExecutionCtx<'a> = EthBlockExecutionCtx<'a>;
|
||||
type Transaction = TransactionSigned;
|
||||
type Receipt = R::Receipt;
|
||||
|
||||
fn evm_factory(&self) -> &Self::EvmFactory {
|
||||
&self.evm_factory
|
||||
}
|
||||
|
||||
fn create_executor<'a, DB, I>(
|
||||
&'a self,
|
||||
evm: <Self::EvmFactory as EvmFactory>::Evm<&'a mut State<DB>, I>,
|
||||
ctx: Self::ExecutionCtx<'a>,
|
||||
) -> impl BlockExecutorFor<'a, Self, DB, I>
|
||||
where
|
||||
DB: alloy_evm::Database + 'a,
|
||||
I: Inspector<<Self::EvmFactory as EvmFactory>::Context<&'a mut State<DB>>> + 'a,
|
||||
{
|
||||
HlBlockExecutor::new(evm, ctx, self.spec().clone(), self.receipt_builder())
|
||||
}
|
||||
}
|
||||
|
||||
const EIP1559_INITIAL_BASE_FEE: u64 = 0;
|
||||
|
||||
impl ConfigureEvm for HlEvmConfig
|
||||
where
|
||||
Self: Send + Sync + Unpin + Clone + 'static,
|
||||
{
|
||||
type Primitives = HlPrimitives;
|
||||
type Error = Infallible;
|
||||
type NextBlockEnvCtx = NextBlockEnvAttributes;
|
||||
type BlockExecutorFactory = HlBlockExecutorFactory;
|
||||
type BlockAssembler = Self;
|
||||
|
||||
fn block_executor_factory(&self) -> &Self::BlockExecutorFactory {
|
||||
&self.executor_factory
|
||||
}
|
||||
|
||||
fn block_assembler(&self) -> &Self::BlockAssembler {
|
||||
self
|
||||
}
|
||||
|
||||
fn evm_env(&self, header: &Header) -> EvmEnv<HlSpecId> {
|
||||
let blob_params = self.chain_spec().blob_params_at_timestamp(header.timestamp);
|
||||
let spec = revm_spec_by_timestamp_and_block_number(
|
||||
self.chain_spec().clone(),
|
||||
header.timestamp(),
|
||||
header.number(),
|
||||
);
|
||||
|
||||
// configure evm env based on parent block
|
||||
let mut cfg_env = CfgEnv::new()
|
||||
.with_chain_id(self.chain_spec().chain().id())
|
||||
.with_spec(spec);
|
||||
|
||||
if let Some(blob_params) = &blob_params {
|
||||
cfg_env.set_blob_max_count(blob_params.max_blob_count);
|
||||
}
|
||||
|
||||
// derive the EIP-4844 blob fees from the header's `excess_blob_gas` and the current
|
||||
// blobparams
|
||||
let blob_excess_gas_and_price =
|
||||
header
|
||||
.excess_blob_gas
|
||||
.zip(blob_params)
|
||||
.map(|(excess_blob_gas, params)| {
|
||||
let blob_gasprice = params.calc_blob_fee(excess_blob_gas);
|
||||
BlobExcessGasAndPrice {
|
||||
excess_blob_gas,
|
||||
blob_gasprice,
|
||||
}
|
||||
});
|
||||
|
||||
let eth_spec = spec.into_eth_spec();
|
||||
|
||||
let block_env = BlockEnv {
|
||||
number: header.number(),
|
||||
beneficiary: header.beneficiary(),
|
||||
timestamp: header.timestamp(),
|
||||
difficulty: if eth_spec >= SpecId::MERGE {
|
||||
U256::ZERO
|
||||
} else {
|
||||
header.difficulty()
|
||||
},
|
||||
prevrandao: if eth_spec >= SpecId::MERGE {
|
||||
header.mix_hash()
|
||||
} else {
|
||||
None
|
||||
},
|
||||
gas_limit: header.gas_limit(),
|
||||
basefee: header.base_fee_per_gas().unwrap_or_default(),
|
||||
blob_excess_gas_and_price,
|
||||
};
|
||||
|
||||
EvmEnv { cfg_env, block_env }
|
||||
}
|
||||
|
||||
fn next_evm_env(
|
||||
&self,
|
||||
parent: &Header,
|
||||
attributes: &Self::NextBlockEnvCtx,
|
||||
) -> Result<EvmEnv<HlSpecId>, Self::Error> {
|
||||
// ensure we're not missing any timestamp based hardforks
|
||||
let spec_id = revm_spec_by_timestamp_and_block_number(
|
||||
self.chain_spec().clone(),
|
||||
attributes.timestamp,
|
||||
parent.number() + 1,
|
||||
);
|
||||
|
||||
// configure evm env based on parent block
|
||||
let cfg_env = CfgEnv::new()
|
||||
.with_chain_id(self.chain_spec().chain().id())
|
||||
.with_spec(spec_id);
|
||||
|
||||
// if the parent block did not have excess blob gas (i.e. it was pre-cancun), but it is
|
||||
// cancun now, we need to set the excess blob gas to the default value(0)
|
||||
let blob_excess_gas_and_price = parent
|
||||
.maybe_next_block_excess_blob_gas(
|
||||
self.chain_spec()
|
||||
.blob_params_at_timestamp(attributes.timestamp),
|
||||
)
|
||||
.or_else(|| (spec_id.into_eth_spec().is_enabled_in(SpecId::CANCUN)).then_some(0))
|
||||
.map(|gas| BlobExcessGasAndPrice::new(gas, false));
|
||||
|
||||
let mut basefee = parent.next_block_base_fee(
|
||||
self.chain_spec()
|
||||
.base_fee_params_at_timestamp(attributes.timestamp),
|
||||
);
|
||||
|
||||
let mut gas_limit = U256::from(parent.gas_limit);
|
||||
|
||||
// If we are on the London fork boundary, we need to multiply the parent's gas limit by the
|
||||
// elasticity multiplier to get the new gas limit.
|
||||
if self
|
||||
.chain_spec()
|
||||
.inner
|
||||
.fork(EthereumHardfork::London)
|
||||
.transitions_at_block(parent.number + 1)
|
||||
{
|
||||
let elasticity_multiplier = self
|
||||
.chain_spec()
|
||||
.base_fee_params_at_timestamp(attributes.timestamp)
|
||||
.elasticity_multiplier;
|
||||
|
||||
// multiply the gas limit by the elasticity multiplier
|
||||
gas_limit *= U256::from(elasticity_multiplier);
|
||||
|
||||
// set the base fee to the initial base fee from the EIP-1559 spec
|
||||
basefee = Some(EIP1559_INITIAL_BASE_FEE)
|
||||
}
|
||||
|
||||
let block_env = BlockEnv {
|
||||
number: parent.number() + 1,
|
||||
beneficiary: attributes.suggested_fee_recipient,
|
||||
timestamp: attributes.timestamp,
|
||||
difficulty: U256::ZERO,
|
||||
prevrandao: Some(attributes.prev_randao),
|
||||
gas_limit: attributes.gas_limit,
|
||||
// calculate basefee based on parent block's gas usage
|
||||
basefee: basefee.unwrap_or_default(),
|
||||
// calculate excess gas based on parent block's blob gas usage
|
||||
blob_excess_gas_and_price,
|
||||
};
|
||||
|
||||
Ok(EvmEnv { cfg_env, block_env })
|
||||
}
|
||||
|
||||
fn context_for_block<'a>(
|
||||
&self,
|
||||
block: &'a SealedBlock<BlockTy<Self::Primitives>>,
|
||||
) -> ExecutionCtxFor<'a, Self> {
|
||||
EthBlockExecutionCtx {
|
||||
parent_hash: block.header().parent_hash,
|
||||
parent_beacon_block_root: block.header().parent_beacon_block_root,
|
||||
ommers: &block.body().ommers,
|
||||
withdrawals: block.body().withdrawals.as_ref().map(Cow::Borrowed),
|
||||
}
|
||||
}
|
||||
|
||||
fn context_for_next_block(
|
||||
&self,
|
||||
parent: &SealedHeader<HeaderTy<Self::Primitives>>,
|
||||
attributes: Self::NextBlockEnvCtx,
|
||||
) -> ExecutionCtxFor<'_, Self> {
|
||||
EthBlockExecutionCtx {
|
||||
parent_hash: parent.hash(),
|
||||
parent_beacon_block_root: attributes.parent_beacon_block_root,
|
||||
ommers: &[],
|
||||
withdrawals: attributes.withdrawals.map(Cow::Owned),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Map the latest active hardfork at the given timestamp or block number to a [`HlSpecId`].
|
||||
pub fn revm_spec_by_timestamp_and_block_number(
|
||||
chain_spec: impl HlHardforks,
|
||||
timestamp: u64,
|
||||
block_number: u64,
|
||||
) -> HlSpecId {
|
||||
HlSpecId::V1
|
||||
}
|
||||
211
src/node/evm/executor.rs
Normal file
211
src/node/evm/executor.rs
Normal file
@ -0,0 +1,211 @@
|
||||
use super::patch::patch_mainnet_after_tx;
|
||||
use crate::{evm::transaction::HlTxEnv, hardforks::HlHardforks};
|
||||
use alloy_consensus::{Transaction, TxReceipt};
|
||||
use alloy_eips::{eip7685::Requests, Encodable2718};
|
||||
use alloy_evm::{block::ExecutableTx, eth::receipt_builder::ReceiptBuilderCtx};
|
||||
use alloy_primitives::Address;
|
||||
use reth_chainspec::{EthChainSpec, EthereumHardforks, Hardforks};
|
||||
use reth_evm::{
|
||||
block::{BlockValidationError, CommitChanges},
|
||||
eth::{receipt_builder::ReceiptBuilder, EthBlockExecutionCtx},
|
||||
execute::{BlockExecutionError, BlockExecutor},
|
||||
Database, Evm, FromRecoveredTx, FromTxWithEncoded, IntoTxEnv, OnStateHook, RecoveredTx,
|
||||
};
|
||||
use reth_primitives::TransactionSigned;
|
||||
use reth_provider::BlockExecutionResult;
|
||||
use reth_revm::State;
|
||||
use revm::{
|
||||
context::{
|
||||
result::{ExecutionResult, ResultAndState},
|
||||
TxEnv,
|
||||
},
|
||||
state::Bytecode,
|
||||
Database as _, DatabaseCommit,
|
||||
};
|
||||
|
||||
fn is_system_transaction(tx: &TransactionSigned) -> bool {
|
||||
let Some(gas_price) = tx.gas_price() else {
|
||||
return false;
|
||||
};
|
||||
gas_price == 0
|
||||
}
|
||||
|
||||
pub struct HlBlockExecutor<'a, EVM, Spec, R: ReceiptBuilder>
|
||||
where
|
||||
Spec: EthChainSpec,
|
||||
{
|
||||
/// Reference to the specification object.
|
||||
spec: Spec,
|
||||
/// Inner EVM.
|
||||
evm: EVM,
|
||||
/// Gas used in the block.
|
||||
gas_used: u64,
|
||||
/// Receipts of executed transactions.
|
||||
receipts: Vec<R::Receipt>,
|
||||
/// System txs
|
||||
system_txs: Vec<R::Transaction>,
|
||||
/// Receipt builder.
|
||||
receipt_builder: R,
|
||||
/// System contracts used to trigger fork specific logic.
|
||||
// system_contracts: SystemContract<Spec>,
|
||||
/// Context for block execution.
|
||||
_ctx: EthBlockExecutionCtx<'a>,
|
||||
}
|
||||
|
||||
impl<'a, DB, EVM, Spec, R: ReceiptBuilder> HlBlockExecutor<'a, EVM, Spec, R>
|
||||
where
|
||||
DB: Database + 'a,
|
||||
EVM: Evm<
|
||||
DB = &'a mut State<DB>,
|
||||
Tx: FromRecoveredTx<R::Transaction>
|
||||
+ FromRecoveredTx<TransactionSigned>
|
||||
+ FromTxWithEncoded<TransactionSigned>,
|
||||
>,
|
||||
Spec: EthereumHardforks + HlHardforks + EthChainSpec + Hardforks + Clone,
|
||||
R: ReceiptBuilder<Transaction = TransactionSigned, Receipt: TxReceipt>,
|
||||
<R as ReceiptBuilder>::Transaction: Unpin + From<TransactionSigned>,
|
||||
<EVM as alloy_evm::Evm>::Tx: FromTxWithEncoded<<R as ReceiptBuilder>::Transaction>,
|
||||
HlTxEnv<TxEnv>: IntoTxEnv<<EVM as alloy_evm::Evm>::Tx>,
|
||||
R::Transaction: Into<TransactionSigned>,
|
||||
{
|
||||
/// Creates a new HlBlockExecutor.
|
||||
pub fn new(
|
||||
evm: EVM,
|
||||
_ctx: EthBlockExecutionCtx<'a>,
|
||||
spec: Spec,
|
||||
receipt_builder: R,
|
||||
// system_contracts: SystemContract<Spec>,
|
||||
) -> Self {
|
||||
Self {
|
||||
spec,
|
||||
evm,
|
||||
gas_used: 0,
|
||||
receipts: vec![],
|
||||
system_txs: vec![],
|
||||
receipt_builder,
|
||||
// system_contracts,
|
||||
_ctx,
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes the genesis contracts
|
||||
fn deploy_genesis_contracts(
|
||||
&mut self,
|
||||
beneficiary: Address,
|
||||
) -> Result<(), BlockExecutionError> {
|
||||
todo!("Deploy WETH, System contract");
|
||||
// Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, DB, E, Spec, R> BlockExecutor for HlBlockExecutor<'a, E, Spec, R>
|
||||
where
|
||||
DB: Database + 'a,
|
||||
E: Evm<
|
||||
DB = &'a mut State<DB>,
|
||||
Tx: FromRecoveredTx<R::Transaction>
|
||||
+ FromRecoveredTx<TransactionSigned>
|
||||
+ FromTxWithEncoded<TransactionSigned>,
|
||||
>,
|
||||
Spec: EthereumHardforks + HlHardforks + EthChainSpec + Hardforks,
|
||||
R: ReceiptBuilder<Transaction = TransactionSigned, Receipt: TxReceipt>,
|
||||
<R as ReceiptBuilder>::Transaction: Unpin + From<TransactionSigned>,
|
||||
<E as alloy_evm::Evm>::Tx: FromTxWithEncoded<<R as ReceiptBuilder>::Transaction>,
|
||||
HlTxEnv<TxEnv>: IntoTxEnv<<E as alloy_evm::Evm>::Tx>,
|
||||
R::Transaction: Into<TransactionSigned>,
|
||||
{
|
||||
type Transaction = TransactionSigned;
|
||||
type Receipt = R::Receipt;
|
||||
type Evm = E;
|
||||
|
||||
fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError> {
|
||||
// If first block deploy genesis contracts
|
||||
if self.evm.block().number == 1 {
|
||||
self.deploy_genesis_contracts(self.evm.block().beneficiary)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn execute_transaction_with_commit_condition(
|
||||
&mut self,
|
||||
_tx: impl ExecutableTx<Self>,
|
||||
_f: impl FnOnce(&ExecutionResult<<Self::Evm as Evm>::HaltReason>) -> CommitChanges,
|
||||
) -> Result<Option<u64>, BlockExecutionError> {
|
||||
Ok(Some(0))
|
||||
}
|
||||
|
||||
fn execute_transaction_with_result_closure(
|
||||
&mut self,
|
||||
tx: impl ExecutableTx<Self>
|
||||
+ IntoTxEnv<<E as alloy_evm::Evm>::Tx>
|
||||
+ RecoveredTx<TransactionSigned>,
|
||||
f: impl for<'b> FnOnce(&'b ExecutionResult<<E as alloy_evm::Evm>::HaltReason>),
|
||||
) -> Result<u64, BlockExecutionError> {
|
||||
// Check if it's a system transaction
|
||||
// let signer = tx.signer();
|
||||
// let is_system_transaction = is_system_transaction(tx.tx());
|
||||
|
||||
let block_available_gas = self.evm.block().gas_limit - self.gas_used;
|
||||
if tx.tx().gas_limit() > block_available_gas {
|
||||
return Err(
|
||||
BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas {
|
||||
transaction_gas_limit: tx.tx().gas_limit(),
|
||||
block_available_gas,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
let result_and_state = self
|
||||
.evm
|
||||
.transact(tx)
|
||||
.map_err(|err| BlockExecutionError::evm(err, tx.tx().trie_hash()))?;
|
||||
let ResultAndState { result, mut state } = result_and_state;
|
||||
f(&result);
|
||||
let gas_used = result.gas_used();
|
||||
self.gas_used += gas_used;
|
||||
self.receipts
|
||||
.push(self.receipt_builder.build_receipt(ReceiptBuilderCtx {
|
||||
tx: tx.tx(),
|
||||
evm: &self.evm,
|
||||
result,
|
||||
state: &state,
|
||||
cumulative_gas_used: self.gas_used,
|
||||
}));
|
||||
|
||||
// apply patches after
|
||||
patch_mainnet_after_tx(
|
||||
self.evm.block().number,
|
||||
self.receipts.len() as u64,
|
||||
is_system_transaction(tx.tx()),
|
||||
&mut state,
|
||||
)?;
|
||||
|
||||
self.evm.db_mut().commit(state);
|
||||
|
||||
Ok(gas_used)
|
||||
}
|
||||
|
||||
fn finish(
|
||||
mut self,
|
||||
) -> Result<(Self::Evm, BlockExecutionResult<R::Receipt>), BlockExecutionError> {
|
||||
Ok((
|
||||
self.evm,
|
||||
BlockExecutionResult {
|
||||
receipts: self.receipts,
|
||||
requests: Requests::default(),
|
||||
gas_used: self.gas_used,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn set_state_hook(&mut self, _hook: Option<Box<dyn OnStateHook>>) {}
|
||||
|
||||
fn evm_mut(&mut self) -> &mut Self::Evm {
|
||||
&mut self.evm
|
||||
}
|
||||
|
||||
fn evm(&self) -> &Self::Evm {
|
||||
&self.evm
|
||||
}
|
||||
}
|
||||
74
src/node/evm/factory.rs
Normal file
74
src/node/evm/factory.rs
Normal file
@ -0,0 +1,74 @@
|
||||
use super::HlEvm;
|
||||
use crate::evm::{
|
||||
api::{
|
||||
builder::HlBuilder,
|
||||
ctx::{HlContext, DefaultHl},
|
||||
},
|
||||
precompiles::HlPrecompiles,
|
||||
spec::HlSpecId,
|
||||
transaction::HlTxEnv,
|
||||
};
|
||||
use reth_evm::{precompiles::PrecompilesMap, EvmEnv, EvmFactory};
|
||||
use reth_revm::{Context, Database};
|
||||
use revm::{
|
||||
context::{
|
||||
result::{EVMError, HaltReason},
|
||||
TxEnv,
|
||||
},
|
||||
inspector::NoOpInspector,
|
||||
Inspector,
|
||||
};
|
||||
|
||||
/// Factory producing [`HlEvm`].
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
#[non_exhaustive]
|
||||
pub struct HlEvmFactory;
|
||||
|
||||
impl EvmFactory for HlEvmFactory {
|
||||
type Evm<DB: Database<Error: Send + Sync + 'static>, I: Inspector<HlContext<DB>>> =
|
||||
HlEvm<DB, I, Self::Precompiles>;
|
||||
type Context<DB: Database<Error: Send + Sync + 'static>> = HlContext<DB>;
|
||||
type Tx = HlTxEnv<TxEnv>;
|
||||
type Error<DBError: core::error::Error + Send + Sync + 'static> = EVMError<DBError>;
|
||||
type HaltReason = HaltReason;
|
||||
type Spec = HlSpecId;
|
||||
type Precompiles = PrecompilesMap;
|
||||
|
||||
fn create_evm<DB: Database<Error: Send + Sync + 'static>>(
|
||||
&self,
|
||||
db: DB,
|
||||
input: EvmEnv<HlSpecId>,
|
||||
) -> Self::Evm<DB, NoOpInspector> {
|
||||
let precompiles = HlPrecompiles::new(input.cfg_env.spec).precompiles();
|
||||
HlEvm {
|
||||
inner: Context::hl()
|
||||
.with_block(input.block_env)
|
||||
.with_cfg(input.cfg_env)
|
||||
.with_db(db)
|
||||
.build_hl_with_inspector(NoOpInspector {})
|
||||
.with_precompiles(PrecompilesMap::from_static(precompiles)),
|
||||
inspect: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn create_evm_with_inspector<
|
||||
DB: Database<Error: Send + Sync + 'static>,
|
||||
I: Inspector<Self::Context<DB>>,
|
||||
>(
|
||||
&self,
|
||||
db: DB,
|
||||
input: EvmEnv<HlSpecId>,
|
||||
inspector: I,
|
||||
) -> Self::Evm<DB, I> {
|
||||
let precompiles = HlPrecompiles::new(input.cfg_env.spec).precompiles();
|
||||
HlEvm {
|
||||
inner: Context::hl()
|
||||
.with_block(input.block_env)
|
||||
.with_cfg(input.cfg_env)
|
||||
.with_db(db)
|
||||
.build_hl_with_inspector(inspector)
|
||||
.with_precompiles(PrecompilesMap::from_static(precompiles)),
|
||||
inspect: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
165
src/node/evm/mod.rs
Normal file
165
src/node/evm/mod.rs
Normal file
@ -0,0 +1,165 @@
|
||||
use crate::{
|
||||
evm::{
|
||||
api::{ctx::HlContext, HlEvmInner},
|
||||
precompiles::HlPrecompiles,
|
||||
spec::HlSpecId,
|
||||
transaction::{HlTxEnv, HlTxTr},
|
||||
},
|
||||
node::HlNode,
|
||||
};
|
||||
use alloy_primitives::{Address, Bytes};
|
||||
use config::HlEvmConfig;
|
||||
use reth::{
|
||||
api::FullNodeTypes,
|
||||
builder::{components::ExecutorBuilder, BuilderContext},
|
||||
};
|
||||
use reth_evm::{Evm, EvmEnv};
|
||||
use revm::{
|
||||
context::{
|
||||
result::{EVMError, HaltReason, ResultAndState},
|
||||
BlockEnv, TxEnv,
|
||||
},
|
||||
handler::{instructions::EthInstructions, PrecompileProvider},
|
||||
interpreter::{interpreter::EthInterpreter, InterpreterResult},
|
||||
Context, Database, ExecuteEvm, InspectEvm, Inspector,
|
||||
};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
mod assembler;
|
||||
pub mod config;
|
||||
mod executor;
|
||||
mod factory;
|
||||
mod patch;
|
||||
|
||||
/// HL EVM implementation.
|
||||
///
|
||||
/// This is a wrapper type around the `revm` evm with optional [`Inspector`] (tracing)
|
||||
/// support. [`Inspector`] support is configurable at runtime because it's part of the underlying
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct HlEvm<DB: Database, I, P = HlPrecompiles> {
|
||||
pub inner: HlEvmInner<HlContext<DB>, I, EthInstructions<EthInterpreter, HlContext<DB>>, P>,
|
||||
pub inspect: bool,
|
||||
}
|
||||
|
||||
impl<DB: Database, I, P> HlEvm<DB, I, P> {
|
||||
/// Provides a reference to the EVM context.
|
||||
pub const fn ctx(&self) -> &HlContext<DB> {
|
||||
&self.inner.0.ctx
|
||||
}
|
||||
|
||||
/// Provides a mutable reference to the EVM context.
|
||||
pub fn ctx_mut(&mut self) -> &mut HlContext<DB> {
|
||||
&mut self.inner.0.ctx
|
||||
}
|
||||
}
|
||||
|
||||
impl<DB: Database, I, P> Deref for HlEvm<DB, I, P> {
|
||||
type Target = HlContext<DB>;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.ctx()
|
||||
}
|
||||
}
|
||||
|
||||
impl<DB: Database, I, P> DerefMut for HlEvm<DB, I, P> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.ctx_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<DB, I, P> Evm for HlEvm<DB, I, P>
|
||||
where
|
||||
DB: Database,
|
||||
I: Inspector<HlContext<DB>>,
|
||||
P: PrecompileProvider<HlContext<DB>, Output = InterpreterResult>,
|
||||
<DB as revm::Database>::Error: std::marker::Send + std::marker::Sync + 'static,
|
||||
{
|
||||
type DB = DB;
|
||||
type Tx = HlTxEnv<TxEnv>;
|
||||
type Error = EVMError<DB::Error>;
|
||||
type HaltReason = HaltReason;
|
||||
type Spec = HlSpecId;
|
||||
type Precompiles = P;
|
||||
type Inspector = I;
|
||||
|
||||
fn chain_id(&self) -> u64 {
|
||||
self.cfg.chain_id
|
||||
}
|
||||
|
||||
fn block(&self) -> &BlockEnv {
|
||||
&self.block
|
||||
}
|
||||
|
||||
fn transact_raw(
|
||||
&mut self,
|
||||
tx: Self::Tx,
|
||||
) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
|
||||
if self.inspect {
|
||||
self.inner.set_tx(tx);
|
||||
self.inner.inspect_replay()
|
||||
} else if tx.is_system_transaction() {
|
||||
self.inner.set_tx(tx);
|
||||
self.inner.inspect_replay()
|
||||
} else {
|
||||
self.inner.transact(tx)
|
||||
}
|
||||
}
|
||||
|
||||
fn transact_system_call(
|
||||
&mut self,
|
||||
_caller: Address,
|
||||
_contract: Address,
|
||||
_data: Bytes,
|
||||
) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn db_mut(&mut self) -> &mut Self::DB {
|
||||
&mut self.journaled_state.database
|
||||
}
|
||||
|
||||
fn finish(self) -> (Self::DB, EvmEnv<Self::Spec>) {
|
||||
let Context { block: block_env, cfg: cfg_env, journaled_state, .. } = self.inner.0.ctx;
|
||||
|
||||
(journaled_state.database, EvmEnv { block_env, cfg_env })
|
||||
}
|
||||
|
||||
fn set_inspector_enabled(&mut self, enabled: bool) {
|
||||
self.inspect = enabled;
|
||||
}
|
||||
|
||||
fn precompiles_mut(&mut self) -> &mut Self::Precompiles {
|
||||
&mut self.inner.0.precompiles
|
||||
}
|
||||
|
||||
fn inspector_mut(&mut self) -> &mut Self::Inspector {
|
||||
&mut self.inner.0.inspector
|
||||
}
|
||||
|
||||
fn precompiles(&self) -> &Self::Precompiles {
|
||||
&self.inner.0.precompiles
|
||||
}
|
||||
|
||||
fn inspector(&self) -> &Self::Inspector {
|
||||
&self.inner.0.inspector
|
||||
}
|
||||
}
|
||||
|
||||
/// A regular hl evm and executor builder.
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
#[non_exhaustive]
|
||||
pub struct HlExecutorBuilder;
|
||||
|
||||
impl<Node> ExecutorBuilder<Node> for HlExecutorBuilder
|
||||
where
|
||||
Node: FullNodeTypes<Types = HlNode>,
|
||||
{
|
||||
type EVM = HlEvmConfig;
|
||||
|
||||
async fn build_evm(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::EVM> {
|
||||
let evm_config = HlEvmConfig::hl(ctx.chain_spec());
|
||||
Ok(evm_config)
|
||||
}
|
||||
}
|
||||
145
src/node/evm/patch.rs
Normal file
145
src/node/evm/patch.rs
Normal file
@ -0,0 +1,145 @@
|
||||
use alloy_primitives::{address, b256, Address, B256, U256};
|
||||
use reth_evm::block::BlockExecutionError;
|
||||
use reth_primitives_traits::SignedTransaction;
|
||||
use reth_revm::{db::states::StorageSlot, State};
|
||||
use revm::{primitives::HashMap, state::Account, Database};
|
||||
use std::{str::FromStr, sync::LazyLock};
|
||||
use tracing::trace;
|
||||
|
||||
/// Applies storage patches to the state after a transaction is executed.
|
||||
/// See https://github.com/hyperliquid-dex/hyper-evm-sync/commit/39047242b6260f7764527a2f5057dd9c3a75aa89 for more details.
|
||||
static MAINNET_PATCHES_AFTER_TX: &[(u64, u64, bool, Address)] = &[
|
||||
(
|
||||
1_467_569,
|
||||
0,
|
||||
false,
|
||||
address!("0x33f6fe38c55cb100ce27b3138e5d2d041648364f"),
|
||||
),
|
||||
(
|
||||
1_467_631,
|
||||
0,
|
||||
false,
|
||||
address!("0x33f6fe38c55cb100ce27b3138e5d2d041648364f"),
|
||||
),
|
||||
(
|
||||
1_499_313,
|
||||
2,
|
||||
false,
|
||||
address!("0xe27bfc0a812b38927ff646f24af9149f45deb550"),
|
||||
),
|
||||
(
|
||||
1_499_406,
|
||||
0,
|
||||
false,
|
||||
address!("0xe27bfc0a812b38927ff646f24af9149f45deb550"),
|
||||
),
|
||||
(
|
||||
1_499_685,
|
||||
0,
|
||||
false,
|
||||
address!("0xfee3932b75a87e86930668a6ab3ed43b404c8a30"),
|
||||
),
|
||||
(
|
||||
1_514_843,
|
||||
0,
|
||||
false,
|
||||
address!("0x723e5fbbeed025772a91240fd0956a866a41a603"),
|
||||
),
|
||||
(
|
||||
1_514_936,
|
||||
0,
|
||||
false,
|
||||
address!("0x723e5fbbeed025772a91240fd0956a866a41a603"),
|
||||
),
|
||||
(
|
||||
1_530_529,
|
||||
2,
|
||||
false,
|
||||
address!("0xa694e8fd8f4a177dd23636d838e9f1fb2138d87a"),
|
||||
),
|
||||
(
|
||||
1_530_622,
|
||||
2,
|
||||
false,
|
||||
address!("0xa694e8fd8f4a177dd23636d838e9f1fb2138d87a"),
|
||||
),
|
||||
(
|
||||
1_530_684,
|
||||
3,
|
||||
false,
|
||||
address!("0xa694e8fd8f4a177dd23636d838e9f1fb2138d87a"),
|
||||
),
|
||||
(
|
||||
1_530_777,
|
||||
3,
|
||||
false,
|
||||
address!("0xa694e8fd8f4a177dd23636d838e9f1fb2138d87a"),
|
||||
),
|
||||
(
|
||||
1_530_839,
|
||||
2,
|
||||
false,
|
||||
address!("0x692a343fc401a7755f8fc2facf61af426adaf061"),
|
||||
),
|
||||
(
|
||||
1_530_901,
|
||||
0,
|
||||
false,
|
||||
address!("0xfd9716f16596715ce765dabaee11787870e04b8a"),
|
||||
),
|
||||
(
|
||||
1_530_994,
|
||||
3,
|
||||
false,
|
||||
address!("0xfd9716f16596715ce765dabaee11787870e04b8a"),
|
||||
),
|
||||
(
|
||||
1_531_056,
|
||||
4,
|
||||
false,
|
||||
address!("0xdc67c2b8349ca20f58760e08371fc9271e82b5a4"),
|
||||
),
|
||||
(
|
||||
1_531_149,
|
||||
0,
|
||||
false,
|
||||
address!("0xdc67c2b8349ca20f58760e08371fc9271e82b5a4"),
|
||||
),
|
||||
(
|
||||
1_531_211,
|
||||
3,
|
||||
false,
|
||||
address!("0xdc67c2b8349ca20f58760e08371fc9271e82b5a4"),
|
||||
),
|
||||
(
|
||||
1_531_366,
|
||||
1,
|
||||
false,
|
||||
address!("0x9a90a517d27a9e60e454c96fefbbe94ff244ed6f"),
|
||||
),
|
||||
];
|
||||
|
||||
pub(crate) fn patch_mainnet_after_tx(
|
||||
block_number: u64,
|
||||
tx_index: u64,
|
||||
is_system_tx: bool,
|
||||
changes: &mut HashMap<Address, Account>,
|
||||
) -> Result<(), BlockExecutionError> {
|
||||
if MAINNET_PATCHES_AFTER_TX.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let first = MAINNET_PATCHES_AFTER_TX.first().unwrap().0;
|
||||
let last = MAINNET_PATCHES_AFTER_TX.last().unwrap().0;
|
||||
if first > block_number || last < block_number {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for (block_num, idx, is_system, address) in MAINNET_PATCHES_AFTER_TX {
|
||||
if block_number == *block_num && tx_index == *idx && is_system_tx == *is_system {
|
||||
changes.remove(address);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user