precompile attempt

This commit is contained in:
sprites0
2025-06-19 21:08:38 -04:00
parent 821bf7a775
commit 4449ad55ab
8 changed files with 295 additions and 78 deletions

View File

@ -15,18 +15,7 @@ impl BlockAssembler<HlBlockExecutorFactory> for HlEvmConfig {
&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,
},
})
let HlBlock { header, body } = self.block_assembler.assemble_block(input)?;
Ok(HlBlock { header, body })
}
}

View File

@ -3,20 +3,28 @@ use crate::{
chainspec::HlChainSpec,
evm::{spec::HlSpecId, transaction::HlTxEnv},
hardforks::HlHardforks,
HlPrimitives,
node::types::ReadPrecompileMap,
HlBlock, HlBlockBody, HlPrimitives,
};
use alloy_consensus::{BlockHeader, Header, TxReceipt};
use alloy_consensus::{BlockHeader, Header, Transaction as _, TxReceipt, EMPTY_OMMER_ROOT_HASH};
use alloy_eips::merge::BEACON_NONCE;
use alloy_primitives::{Log, U256};
use reth_chainspec::{EthChainSpec, EthereumHardforks, Hardforks};
use reth_ethereum_forks::EthereumHardfork;
use reth_ethereum_primitives::BlockBody;
use reth_evm::{
block::{BlockExecutorFactory, BlockExecutorFor},
block::{BlockExecutionError, BlockExecutorFactory, BlockExecutorFor},
eth::{receipt_builder::ReceiptBuilder, EthBlockExecutionCtx},
execute::{BlockAssembler, BlockAssemblerInput},
ConfigureEvm, EvmEnv, EvmFactory, ExecutionCtxFor, FromRecoveredTx, FromTxWithEncoded,
IntoTxEnv, NextBlockEnvAttributes,
};
use reth_evm_ethereum::{EthBlockAssembler, RethReceiptBuilder};
use reth_primitives::{BlockTy, HeaderTy, SealedBlock, SealedHeader, TransactionSigned};
use reth_primitives::{
logs_bloom, BlockTy, HeaderTy, Receipt, SealedBlock, SealedHeader, TransactionSigned,
};
use reth_primitives_traits::proofs;
use reth_provider::BlockExecutionResult;
use reth_revm::State;
use revm::{
context::{BlockEnv, CfgEnv, TxEnv},
@ -26,6 +34,139 @@ use revm::{
};
use std::{borrow::Cow, convert::Infallible, sync::Arc};
#[derive(Debug, Clone)]
pub struct HlBlockAssembler {
inner: EthBlockAssembler<HlChainSpec>,
}
impl<F> BlockAssembler<F> for HlBlockAssembler
where
F: for<'a> BlockExecutorFactory<
ExecutionCtx<'a> = HlBlockExecutionCtx<'a>,
Transaction = TransactionSigned,
Receipt = Receipt,
>,
{
type Block = HlBlock;
fn assemble_block(
&self,
input: BlockAssemblerInput<'_, '_, F>,
) -> Result<Self::Block, BlockExecutionError> {
// TODO: Copy of EthBlockAssembler::assemble_block
let inner = &self.inner;
let BlockAssemblerInput {
evm_env,
execution_ctx: ctx,
parent,
transactions,
output:
BlockExecutionResult {
receipts,
requests,
gas_used,
},
state_root,
..
} = input;
let timestamp = evm_env.block_env.timestamp;
let transactions_root = proofs::calculate_transaction_root(&transactions);
let receipts_root = Receipt::calculate_receipt_root_no_memo(receipts);
let logs_bloom = logs_bloom(receipts.iter().flat_map(|r| r.logs()));
let withdrawals = inner
.chain_spec
.is_shanghai_active_at_timestamp(timestamp)
.then(|| {
ctx.ctx
.withdrawals
.map(|w| w.into_owned())
.unwrap_or_default()
});
let withdrawals_root = withdrawals
.as_deref()
.map(|w| proofs::calculate_withdrawals_root(w));
let requests_hash = inner
.chain_spec
.is_prague_active_at_timestamp(timestamp)
.then(|| requests.requests_hash());
let mut excess_blob_gas = None;
let mut blob_gas_used = None;
// only determine cancun fields when active
if inner.chain_spec.is_cancun_active_at_timestamp(timestamp) {
blob_gas_used = Some(
transactions
.iter()
.map(|tx| tx.blob_gas_used().unwrap_or_default())
.sum(),
);
excess_blob_gas = if inner
.chain_spec
.is_cancun_active_at_timestamp(parent.timestamp)
{
parent.maybe_next_block_excess_blob_gas(
inner.chain_spec.blob_params_at_timestamp(timestamp),
)
} else {
// for the first post-fork block, both parent.blob_gas_used and
// parent.excess_blob_gas are evaluated as 0
Some(alloy_eips::eip7840::BlobParams::cancun().next_block_excess_blob_gas(0, 0))
};
}
let header = Header {
parent_hash: ctx.ctx.parent_hash,
ommers_hash: EMPTY_OMMER_ROOT_HASH,
beneficiary: evm_env.block_env.beneficiary,
state_root,
transactions_root,
receipts_root,
withdrawals_root,
logs_bloom,
timestamp,
mix_hash: evm_env.block_env.prevrandao.unwrap_or_default(),
nonce: BEACON_NONCE.into(),
base_fee_per_gas: Some(evm_env.block_env.basefee),
number: evm_env.block_env.number,
gas_limit: evm_env.block_env.gas_limit,
difficulty: evm_env.block_env.difficulty,
gas_used: *gas_used,
extra_data: inner.extra_data.clone(),
parent_beacon_block_root: ctx.ctx.parent_beacon_block_root,
blob_gas_used,
excess_blob_gas,
requests_hash,
};
let read_precompile_calls = ctx.read_precompile_calls.clone().into();
Ok(Self::Block {
header,
body: HlBlockBody {
inner: BlockBody {
transactions,
ommers: Default::default(),
withdrawals,
},
sidecars: None,
read_precompile_calls: Some(read_precompile_calls),
},
})
}
}
impl HlBlockAssembler {
pub fn new(chain_spec: Arc<HlChainSpec>) -> Self {
Self {
inner: EthBlockAssembler::new(chain_spec),
}
}
}
/// Ethereum-related EVM configuration.
#[derive(Debug, Clone)]
pub struct HlEvmConfig {
@ -33,7 +174,7 @@ pub struct HlEvmConfig {
pub executor_factory:
HlBlockExecutorFactory<RethReceiptBuilder, Arc<HlChainSpec>, HlEvmFactory>,
/// Ethereum block assembler.
pub block_assembler: EthBlockAssembler<HlChainSpec>,
pub block_assembler: HlBlockAssembler,
}
impl HlEvmConfig {
@ -52,7 +193,7 @@ 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()),
block_assembler: HlBlockAssembler::new(chain_spec.clone()),
executor_factory: HlBlockExecutorFactory::new(
RethReceiptBuilder::default(),
chain_spec,
@ -104,6 +245,12 @@ impl<R, Spec, EvmFactory> HlBlockExecutorFactory<R, Spec, EvmFactory> {
}
}
#[derive(Debug, Clone)]
pub struct HlBlockExecutionCtx<'a> {
ctx: EthBlockExecutionCtx<'a>,
read_precompile_calls: ReadPrecompileMap,
}
impl<R, Spec, EvmF> BlockExecutorFactory for HlBlockExecutorFactory<R, Spec, EvmF>
where
R: ReceiptBuilder<Transaction = TransactionSigned, Receipt: TxReceipt<Log = Log>>,
@ -114,7 +261,7 @@ where
HlTxEnv<TxEnv>: IntoTxEnv<<EvmF as EvmFactory>::Tx>,
{
type EvmFactory = EvmF;
type ExecutionCtx<'a> = EthBlockExecutionCtx<'a>;
type ExecutionCtx<'a> = HlBlockExecutionCtx<'a>;
type Transaction = TransactionSigned;
type Receipt = R::Receipt;
@ -284,11 +431,18 @@ where
&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),
HlBlockExecutionCtx {
ctx: 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),
},
read_precompile_calls: block
.body()
.read_precompile_calls
.clone()
.map_or(ReadPrecompileMap::default(), |calls| calls.into()),
}
}
@ -297,11 +451,15 @@ where
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),
HlBlockExecutionCtx {
ctx: EthBlockExecutionCtx {
parent_hash: parent.hash(),
parent_beacon_block_root: attributes.parent_beacon_block_root,
ommers: &[],
withdrawals: attributes.withdrawals.map(Cow::Owned),
},
// TODO: hacky, double check if this is correct
read_precompile_calls: ReadPrecompileMap::default(),
}
}
}

View File

@ -1,13 +1,13 @@
use super::config::HlBlockExecutionCtx;
use super::patch::patch_mainnet_after_tx;
use crate::{evm::transaction::HlTxEnv, hardforks::HlHardforks};
use crate::{evm::transaction::HlTxEnv, hardforks::HlHardforks, node::types::ReadPrecompileCalls};
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},
eth::receipt_builder::ReceiptBuilder,
execute::{BlockExecutionError, BlockExecutor},
Database, Evm, FromRecoveredTx, FromTxWithEncoded, IntoTxEnv, OnStateHook, RecoveredTx,
};
@ -19,8 +19,7 @@ use revm::{
result::{ExecutionResult, ResultAndState},
TxEnv,
},
state::Bytecode,
Database as _, DatabaseCommit,
DatabaseCommit,
};
fn is_system_transaction(tx: &TransactionSigned) -> bool {
@ -44,12 +43,12 @@ where
receipts: Vec<R::Receipt>,
/// System txs
system_txs: Vec<R::Transaction>,
/// Read precompile calls
read_precompile_calls: ReadPrecompileCalls,
/// Receipt builder.
receipt_builder: R,
/// System contracts used to trigger fork specific logic.
// system_contracts: SystemContract<Spec>,
/// Context for block execution.
_ctx: EthBlockExecutionCtx<'a>,
ctx: HlBlockExecutionCtx<'a>,
}
impl<'a, DB, EVM, Spec, R: ReceiptBuilder> HlBlockExecutor<'a, EVM, Spec, R>
@ -69,33 +68,19 @@ where
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 {
pub fn new(evm: EVM, ctx: HlBlockExecutionCtx<'a>, spec: Spec, receipt_builder: R) -> Self {
Self {
spec,
evm,
gas_used: 0,
receipts: vec![],
system_txs: vec![],
read_precompile_calls: ReadPrecompileCalls::default(),
receipt_builder,
// system_contracts,
_ctx,
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>
@ -119,11 +104,6 @@ where
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(())
}
@ -186,9 +166,7 @@ where
Ok(gas_used)
}
fn finish(
mut self,
) -> Result<(Self::Evm, BlockExecutionResult<R::Receipt>), BlockExecutionError> {
fn finish(self) -> Result<(Self::Evm, BlockExecutionResult<R::Receipt>), BlockExecutionError> {
Ok((
self.evm,
BlockExecutionResult {

View File

@ -1,12 +1,15 @@
use super::HlEvm;
use crate::evm::{
api::{
builder::HlBuilder,
ctx::{HlContext, DefaultHl},
use crate::{
evm::{
api::{
builder::HlBuilder,
ctx::{DefaultHl, HlContext},
},
precompiles::HlPrecompiles,
spec::HlSpecId,
transaction::HlTxEnv,
},
precompiles::HlPrecompiles,
spec::HlSpecId,
transaction::HlTxEnv,
node::types::ReadPrecompileMap,
};
use reth_evm::{precompiles::PrecompilesMap, EvmEnv, EvmFactory};
use reth_revm::{Context, Database};
@ -39,7 +42,8 @@ impl EvmFactory for HlEvmFactory {
db: DB,
input: EvmEnv<HlSpecId>,
) -> Self::Evm<DB, NoOpInspector> {
let precompiles = HlPrecompiles::new(input.cfg_env.spec).precompiles();
let precompiles =
HlPrecompiles::new(input.cfg_env.spec, ReadPrecompileMap::default()).precompiles();
HlEvm {
inner: Context::hl()
.with_block(input.block_env)
@ -60,7 +64,8 @@ impl EvmFactory for HlEvmFactory {
input: EvmEnv<HlSpecId>,
inspector: I,
) -> Self::Evm<DB, I> {
let precompiles = HlPrecompiles::new(input.cfg_env.spec).precompiles();
let precompiles =
HlPrecompiles::new(input.cfg_env.spec, ReadPrecompileMap::default()).precompiles();
HlEvm {
inner: Context::hl()
.with_block(input.block_env)

View File

@ -121,7 +121,12 @@ where
}
fn finish(self) -> (Self::DB, EvmEnv<Self::Spec>) {
let Context { block: block_env, cfg: cfg_env, journaled_state, .. } = self.inner.0.ctx;
let Context {
block: block_env,
cfg: cfg_env,
journaled_state,
..
} = self.inner.0.ctx;
(journaled_state.database, EvmEnv { block_env, cfg_env })
}

View File

@ -379,6 +379,7 @@ mod tests {
withdrawals: None,
},
sidecars: None,
read_precompile_calls: None,
},
};
let new_block = HlNewBlock(NewBlock { block, td: U128::from(1) });

View File

@ -6,12 +6,34 @@ use alloy_primitives::{Address, Bytes, Log};
use alloy_rlp::{Decodable, Encodable, RlpDecodable, RlpEncodable};
use bytes::BufMut;
use reth_primitives::{SealedBlock, Transaction};
use revm::primitives::HashMap;
use serde::{Deserialize, Serialize};
pub type ReadPrecompileCall = (Address, Vec<(ReadPrecompileInput, ReadPrecompileResult)>);
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Default)]
pub struct ReadPrecompileCalls(pub Vec<ReadPrecompileCall>);
pub type ReadPrecompileMap = HashMap<Address, HashMap<ReadPrecompileInput, ReadPrecompileResult>>;
impl From<ReadPrecompileCalls> for ReadPrecompileMap {
fn from(calls: ReadPrecompileCalls) -> Self {
calls
.0
.into_iter()
.map(|(address, calls)| (address, calls.into_iter().collect()))
.collect()
}
}
impl From<ReadPrecompileMap> for ReadPrecompileCalls {
fn from(map: ReadPrecompileMap) -> Self {
Self(
map.into_iter()
.map(|(address, calls)| (address, calls.into_iter().collect()))
.collect(),
)
}
}
impl Encodable for ReadPrecompileCalls {
fn encode(&self, out: &mut dyn BufMut) {