From bcdf4d933dcb602b656a2f0ebc44c90cd88820a5 Mon Sep 17 00:00:00 2001 From: sprites0 <199826320+sprites0@users.noreply.github.com> Date: Mon, 6 Oct 2025 06:20:47 +0000 Subject: [PATCH] feat(breaking): Use HlHeader for HlPrimitives --- src/addons/hl_node_compliance.rs | 6 ++-- src/chainspec/mod.rs | 12 +++----- src/evm/api/patch.rs | 26 +--------------- src/node/consensus/reth_copy.rs | 10 +++---- src/node/evm/config.rs | 12 ++++++-- src/node/primitives/header.rs | 51 ++++++++++++++++++++------------ src/node/types/reth_compat.rs | 9 +++++- 7 files changed, 62 insertions(+), 64 deletions(-) diff --git a/src/addons/hl_node_compliance.rs b/src/addons/hl_node_compliance.rs index 0331926bd..60deeb6f8 100644 --- a/src/addons/hl_node_compliance.rs +++ b/src/addons/hl_node_compliance.rs @@ -23,7 +23,7 @@ use jsonrpsee::{PendingSubscriptionSink, SubscriptionMessage, SubscriptionSink, use jsonrpsee_core::{RpcResult, async_trait}; use jsonrpsee_types::{ErrorObject, error::INTERNAL_ERROR_CODE}; use reth::{api::FullNodeComponents, builder::rpc::RpcContext, tasks::TaskSpawner}; -use reth_primitives_traits::{BlockBody as _, SignedTransaction}; +use reth_primitives_traits::SignedTransaction; use reth_provider::{BlockIdReader, BlockReader, BlockReaderIdExt, ReceiptProvider}; use reth_rpc::{EthFilter, EthPubSub, RpcTypes, eth::pubsub::SubscriptionSerializeError}; use reth_rpc_eth_api::{ @@ -579,9 +579,9 @@ async fn adjust_transaction_receipt( // This function assumes that `block_id` is already validated by the caller. fn system_tx_count_for_block(eth_api: &Eth, block_id: BlockId) -> usize { let provider = eth_api.provider(); - let block = provider.block_by_id(block_id).unwrap().unwrap(); + let header = provider.header_by_id(block_id).unwrap().unwrap(); - block.body.transactions().iter().filter(|tx| tx.is_system_transaction()).count() + header.extras.system_tx_count.try_into().unwrap() } #[async_trait] diff --git a/src/chainspec/mod.rs b/src/chainspec/mod.rs index 14c3a1d61..19ae4bdc0 100644 --- a/src/chainspec/mod.rs +++ b/src/chainspec/mod.rs @@ -1,7 +1,7 @@ pub mod hl; pub mod parser; -use crate::{hardforks::HlHardforks, node::primitives::HlHeader}; +use crate::{hardforks::HlHardforks, node::primitives::{header::HlHeaderExtras, HlHeader}}; use alloy_eips::eip7840::BlobParams; use alloy_genesis::Genesis; use alloy_primitives::{Address, B256, U256}; @@ -127,14 +127,10 @@ impl HlChainSpec { _ => unreachable!("Unreachable since ChainSpecParser won't return other chains"), } } - + fn new(inner: ChainSpec) -> Self { - let genesis_header = HlHeader { - inner: inner.genesis_header().clone(), - logs_bloom_with_system_txs: Default::default(), - system_tx_count: 0, - read_precompile_calls: Default::default(), - }; + let genesis_header = + HlHeader { inner: inner.genesis_header().clone(), extras: HlHeaderExtras::default() }; Self { inner, genesis_header } } } diff --git a/src/evm/api/patch.rs b/src/evm/api/patch.rs index 62c85ac48..f333d845d 100644 --- a/src/evm/api/patch.rs +++ b/src/evm/api/patch.rs @@ -7,36 +7,12 @@ use alloy_primitives::keccak256; use revm::{ context::Host, interpreter::{ - InstructionContext, InterpreterTypes, as_u64_saturated, interpreter_types::StackTr, + _count, InstructionContext, InterpreterTypes, as_u64_saturated, interpreter_types::StackTr, popn_top, }, primitives::{BLOCK_HASH_HISTORY, U256}, }; -#[doc(hidden)] -#[macro_export] -#[collapse_debuginfo(yes)] -macro_rules! _count { - (@count) => { 0 }; - (@count $head:tt $($tail:tt)*) => { 1 + _count!(@count $($tail)*) }; - ($($arg:tt)*) => { _count!(@count $($arg)*) }; -} - -/// Pops n values from the stack and returns the top value. Fails the instruction if n values can't -/// be popped. -#[macro_export] -#[collapse_debuginfo(yes)] -macro_rules! popn_top { - ([ $($x:ident),* ], $top:ident, $interpreter:expr $(,$ret:expr)? ) => { - // Workaround for https://github.com/rust-lang/rust/issues/144329. - if $interpreter.stack.len() < (1 + $crate::_count!($($x)*)) { - $interpreter.halt_underflow(); - return $($ret)?; - } - let ([$( $x ),*], $top) = unsafe { $interpreter.stack.popn_top().unwrap_unchecked() }; - }; -} - /// Implements the BLOCKHASH instruction. /// /// Gets the hash of one of the 256 most recent complete blocks. diff --git a/src/node/consensus/reth_copy.rs b/src/node/consensus/reth_copy.rs index f4934dfed..1adf4c8dd 100644 --- a/src/node/consensus/reth_copy.rs +++ b/src/node/consensus/reth_copy.rs @@ -1,21 +1,21 @@ //! Copy of reth codebase. +use crate::HlBlock; use alloy_consensus::{BlockHeader, TxReceipt, proofs::calculate_receipt_root}; use alloy_eips::eip7685::Requests; use alloy_primitives::{B256, Bloom}; use reth::consensus::ConsensusError; use reth_chainspec::EthereumHardforks; use reth_primitives::{GotExpected, RecoveredBlock, gas_spent_by_transactions}; -use reth_primitives_traits::{Block, Receipt as ReceiptTrait}; +use reth_primitives_traits::Receipt as ReceiptTrait; -pub fn validate_block_post_execution( - block: &RecoveredBlock, +pub fn validate_block_post_execution( + block: &RecoveredBlock, chain_spec: &ChainSpec, receipts: &[R], requests: &Requests, ) -> Result<(), ConsensusError> where - B: Block, R: ReceiptTrait, ChainSpec: EthereumHardforks, { @@ -42,7 +42,7 @@ where receipts.iter().filter(|&r| r.cumulative_gas_used() != 0).cloned().collect::>(); if let Err(error) = verify_receipts( block.header().receipts_root(), - block.header().logs_bloom(), + block.header().inner.logs_bloom(), &receipts_for_root, ) { tracing::debug!(%error, ?receipts, "receipts verification failed"); diff --git a/src/node/evm/config.rs b/src/node/evm/config.rs index c5e406b38..dcf226ec8 100644 --- a/src/node/evm/config.rs +++ b/src/node/evm/config.rs @@ -1,11 +1,15 @@ use super::{executor::HlBlockExecutor, factory::HlEvmFactory}; use crate::{ - chainspec::HlChainSpec, evm::{spec::HlSpecId, transaction::HlTxEnv}, hardforks::HlHardforks, node::{ + HlBlock, HlBlockBody, HlHeader, HlPrimitives, + chainspec::HlChainSpec, + evm::{spec::HlSpecId, transaction::HlTxEnv}, + hardforks::HlHardforks, + node::{ evm::{executor::is_system_transaction, receipt_builder::RethReceiptBuilder}, primitives::{BlockBody, TransactionSigned}, rpc::engine_api::validator::HlExecutionData, types::HlExtras, - }, HlBlock, HlBlockBody, HlHeader, HlPrimitives + }, }; use alloy_consensus::{BlockHeader, EMPTY_OMMER_ROOT_HASH, Header, Transaction as _, TxReceipt}; use alloy_eips::{Encodable2718, merge::BEACON_NONCE}; @@ -132,7 +136,9 @@ where excess_blob_gas, requests_hash, }; - let header = HlHeader::from_ethereum_header(header, receipts); + let system_tx_count = + transactions.iter().filter(|t| is_system_transaction(t)).count() as u64; + let header = HlHeader::from_ethereum_header(header, receipts, system_tx_count); Ok(Self::Block { header, diff --git a/src/node/primitives/header.rs b/src/node/primitives/header.rs index b0c356ae3..3734ba2e5 100644 --- a/src/node/primitives/header.rs +++ b/src/node/primitives/header.rs @@ -9,8 +9,6 @@ use reth_primitives_traits::{BlockHeader, InMemorySize, serde_bincode_compat::Rl use reth_rpc_convert::transaction::FromConsensusHeader; use serde::{Deserialize, Serialize}; -use crate::node::types::HlExtras; - /// The header type of this node /// /// This type extends the regular ethereum header with an extension. @@ -33,22 +31,25 @@ pub struct HlHeader { /// The regular eth header #[as_ref] #[deref] - #[serde(flatten)] pub inner: Header, /// The extended header fields that is not part of the block hash + pub extras: HlHeaderExtras, +} + +#[derive( + Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, RlpEncodable, RlpDecodable, Hash, +)] +pub struct HlHeaderExtras { pub logs_bloom_with_system_txs: Bloom, pub system_tx_count: u64, - pub read_precompile_calls: HlExtras, } + impl HlHeader { - pub(crate) fn from_ethereum_header(header: Header, receipts: &[EthereumReceipt]) -> HlHeader { + pub(crate) fn from_ethereum_header(header: Header, receipts: &[EthereumReceipt], system_tx_count: u64) -> HlHeader { let logs_bloom = logs_bloom(receipts.iter().flat_map(|r| &r.logs)); - let system_tx_count = receipts.iter().filter(|r| r.cumulative_gas_used == 0).count() as u64; HlHeader { inner: header, - logs_bloom_with_system_txs: logs_bloom, - system_tx_count, - read_precompile_calls: Default::default(), + extras: HlHeaderExtras { logs_bloom_with_system_txs: logs_bloom, system_tx_count }, } } } @@ -101,7 +102,7 @@ impl alloy_consensus::BlockHeader for HlHeader { } fn logs_bloom(&self) -> Bloom { - self.inner.logs_bloom() + self.extras.logs_bloom_with_system_txs } fn difficulty(&self) -> U256 { @@ -155,14 +156,21 @@ impl alloy_consensus::BlockHeader for HlHeader { fn extra_data(&self) -> &Bytes { self.inner.extra_data() } + + fn is_empty(&self) -> bool { + self.extras.system_tx_count == 0 && self.inner.is_empty() + } } impl InMemorySize for HlHeader { fn size(&self) -> usize { - self.inner.size() - + self.logs_bloom_with_system_txs.data().len() - + self.system_tx_count.size() - + self.read_precompile_calls.size() + self.inner.size() + self.extras.size() + } +} + +impl InMemorySize for HlHeaderExtras { + fn size(&self) -> usize { + self.logs_bloom_with_system_txs.data().len() + self.system_tx_count.size() } } @@ -171,15 +179,20 @@ impl reth_codecs::Compact for HlHeader { where B: alloy_rlp::bytes::BufMut + AsMut<[u8]>, { - // It's too tedious to implement to_compact for every field, so use rmp_serde to serialize - // This also helps the struct to be upgradable in future thanks to serde's flexibility. + // Because Header ends with extra_data which is `Bytes`, we can't use to_compact for extras, + // because Compact trait requires the Bytes field to be placed at the end of the struct. + // Bytes::from_compact just reads all trailing data as the Bytes field. + // + // Hence we need to use other form of serialization, since extra headers are not Compact-compatible. + // We just treat all header fields as rmp-serialized one `Bytes` field. let result: Bytes = rmp_serde::to_vec(&self).unwrap().into(); result.to_compact(buf) } - fn from_compact(buf: &[u8], identifier: usize) -> (Self, &[u8]) { - let header: HlHeader = rmp_serde::from_slice(&buf[identifier..]).unwrap(); - (header, &buf[identifier + buf.len()..]) + fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) { + let (bytes, remaining) = Bytes::from_compact(buf, len); + let header: HlHeader = rmp_serde::from_slice(&bytes).unwrap(); + (header, remaining) } } diff --git a/src/node/types/reth_compat.rs b/src/node/types/reth_compat.rs index 7978767ef..11e93a89e 100644 --- a/src/node/types/reth_compat.rs +++ b/src/node/types/reth_compat.rs @@ -120,6 +120,11 @@ impl SealedBlock { let mut merged_txs = vec![]; merged_txs.extend(system_txs.iter().map(|tx| system_tx_to_reth_transaction(tx, chain_id))); merged_txs.extend(self.body.transactions.iter().map(|tx| tx.to_reth_transaction())); + + let mut merged_receipts = vec![]; + merged_receipts.extend(system_txs.iter().map(|tx| tx.receipt.clone().unwrap().into())); + merged_receipts.extend(receipts.into_iter().map(From::from)); + let block_body = HlBlockBody { inner: reth_primitives::BlockBody { transactions: merged_txs, @@ -131,10 +136,12 @@ impl SealedBlock { highest_precompile_address, }; + let system_tx_count = system_txs.len() as u64; HlBlock { header: HlHeader::from_ethereum_header( self.header.header.clone(), - &receipts.into_iter().map(From::from).collect::>(), + &merged_receipts, + system_tx_count, ), body: block_body, }