diff --git a/crates/rpc/rpc-eth-api/src/core.rs b/crates/rpc/rpc-eth-api/src/core.rs index 8cf2ccd58..f6dbef79e 100644 --- a/crates/rpc/rpc-eth-api/src/core.rs +++ b/crates/rpc/rpc-eth-api/src/core.rs @@ -232,6 +232,25 @@ pub trait EthApi RpcResult>; + /// Returns the finalized block header. + /// + /// The `verified_validator_num` parameter is provided for BSC compatibility but is not used + /// in standard Ethereum. The finalized block is determined by the Beacon Chain consensus + /// (Casper FFG) and requires 2/3+ validator attestations. + #[method(name = "getFinalizedHeader")] + async fn finalized_header(&self, verified_validator_num: u64) -> RpcResult>; + + /// Returns the finalized block. + /// + /// The `verified_validator_num` parameter is provided for BSC compatibility but is not used + /// in standard Ethereum. The finalized block is determined by the Beacon Chain consensus + /// (Casper FFG) and requires 2/3+ validator attestations. + /// + /// If `full` is true, the block object will contain all transaction objects, + /// otherwise it will only contain the transaction hashes. + #[method(name = "getFinalizedBlock")] + async fn finalized_block(&self, verified_validator_num: u64, full: bool) -> RpcResult>; + /// `eth_simulateV1` executes an arbitrary number of transactions on top of the requested state. /// The transactions are packed into individual blocks. Overrides can be provided. #[method(name = "simulateV1")] @@ -470,6 +489,7 @@ where full: bool, ) -> RpcResult>> { trace!(target: "rpc::eth", ?number, ?full, "Serving eth_getBlockByNumber"); + check_pending_tag(&number)?; Ok(EthBlocks::rpc_block(self, number.into(), full).await?) } @@ -485,6 +505,7 @@ where number: BlockNumberOrTag, ) -> RpcResult> { trace!(target: "rpc::eth", ?number, "Serving eth_getBlockTransactionCountByNumber"); + check_pending_tag(&number)?; Ok(EthBlocks::block_transaction_count(self, number.into()).await?.map(U256::from)) } @@ -505,6 +526,7 @@ where number: BlockNumberOrTag, ) -> RpcResult> { trace!(target: "rpc::eth", ?number, "Serving eth_getUncleCountByBlockNumber"); + check_pending_tag(&number)?; if let Some(block) = self.block_by_number(number, false).await? { Ok(Some(U256::from(block.uncles.len()))) @@ -519,6 +541,7 @@ where block_id: BlockId, ) -> RpcResult>>> { trace!(target: "rpc::eth", ?block_id, "Serving eth_getBlockReceipts"); + check_block_id_pending_tag(&block_id)?; Ok(EthBlocks::block_receipts(self, block_id).await?) } @@ -539,6 +562,7 @@ where index: Index, ) -> RpcResult>> { trace!(target: "rpc::eth", ?number, ?index, "Serving eth_getUncleByBlockNumberAndIndex"); + check_pending_tag(&number)?; Ok(EthBlocks::ommer_by_block_and_index(self, number.into(), index).await?) } @@ -597,6 +621,7 @@ where index: Index, ) -> RpcResult> { trace!(target: "rpc::eth", ?number, ?index, "Serving eth_getRawTransactionByBlockNumberAndIndex"); + check_pending_tag(&number)?; Ok(EthTransactions::raw_transaction_by_block_and_tx_index( self, number.into(), @@ -612,6 +637,7 @@ where index: Index, ) -> RpcResult>> { trace!(target: "rpc::eth", ?number, ?index, "Serving eth_getTransactionByBlockNumberAndIndex"); + check_pending_tag(&number)?; Ok(EthTransactions::transaction_by_block_and_tx_index(self, number.into(), index.into()) .await?) } @@ -631,6 +657,7 @@ where number: BlockNumberOrTag, ) -> RpcResult>>> { trace!(target: "rpc::eth", ?number, "Serving eth_getTransactionsByBlockNumber"); + check_pending_tag(&number)?; Ok(EthTransactions::transactions_by_block_id(self, number.into()).await?) } @@ -670,6 +697,9 @@ where /// Handler for: `eth_getBalance` async fn balance(&self, address: Address, block_number: Option) -> RpcResult { trace!(target: "rpc::eth", ?address, ?block_number, "Serving eth_getBalance"); + if let Some(ref block_id) = block_number { + check_block_id_pending_tag(block_id)?; + } Ok(EthState::balance(self, address, block_number).await?) } @@ -681,6 +711,9 @@ where block_number: Option, ) -> RpcResult { trace!(target: "rpc::eth", ?address, ?block_number, "Serving eth_getStorageAt"); + if let Some(ref block_id) = block_number { + check_block_id_pending_tag(block_id)?; + } Ok(EthState::storage_at(self, address, index, block_number).await?) } @@ -691,12 +724,18 @@ where block_number: Option, ) -> RpcResult { trace!(target: "rpc::eth", ?address, ?block_number, "Serving eth_getTransactionCount"); + if let Some(ref block_id) = block_number { + check_block_id_pending_tag(block_id)?; + } Ok(EthState::transaction_count(self, address, block_number).await?) } /// Handler for: `eth_getCode` async fn get_code(&self, address: Address, block_number: Option) -> RpcResult { trace!(target: "rpc::eth", ?address, ?block_number, "Serving eth_getCode"); + if let Some(ref block_id) = block_number { + check_block_id_pending_tag(block_id)?; + } Ok(EthState::get_code(self, address, block_number).await?) } @@ -706,6 +745,7 @@ where block_number: BlockNumberOrTag, ) -> RpcResult>> { trace!(target: "rpc::eth", ?block_number, "Serving eth_getHeaderByNumber"); + check_pending_tag(&block_number)?; Ok(EthBlocks::rpc_block_header(self, block_number.into()).await?) } @@ -715,6 +755,18 @@ where Ok(EthBlocks::rpc_block_header(self, hash.into()).await?) } + /// Handler for: `eth_getFinalizedHeader` + async fn finalized_header(&self, verified_validator_num: u64) -> RpcResult>> { + trace!(target: "rpc::eth", verified_validator_num, "Serving eth_getFinalizedHeader"); + Ok(EthBlocks::rpc_finalized_header(self, verified_validator_num).await?) + } + + /// Handler for: `eth_getFinalizedBlock` + async fn finalized_block(&self, verified_validator_num: u64, full: bool) -> RpcResult>> { + trace!(target: "rpc::eth", verified_validator_num, ?full, "Serving eth_getFinalizedBlock"); + Ok(EthBlocks::rpc_finalized_block(self, verified_validator_num, full).await?) + } + /// Handler for: `eth_simulateV1` async fn simulate_v1( &self, @@ -722,6 +774,9 @@ where block_number: Option, ) -> RpcResult>>> { trace!(target: "rpc::eth", ?block_number, "Serving eth_simulateV1"); + if let Some(ref block_id) = block_number { + check_block_id_pending_tag(block_id)?; + } let _permit = self.tracing_task_guard().clone().acquire_owned().await; Ok(EthCall::simulate_v1(self, payload, block_number).await?) } @@ -735,6 +790,9 @@ where block_overrides: Option>, ) -> RpcResult { trace!(target: "rpc::eth", ?request, ?block_number, ?state_overrides, ?block_overrides, "Serving eth_call"); + if let Some(ref block_id) = block_number { + check_block_id_pending_tag(block_id)?; + } Ok(EthCall::call( self, request, @@ -763,6 +821,9 @@ where state_override: Option, ) -> RpcResult { trace!(target: "rpc::eth", ?request, ?block_number, ?state_override, "Serving eth_createAccessList"); + if let Some(ref block_id) = block_number { + check_block_id_pending_tag(block_id)?; + } Ok(EthCall::create_access_list_at(self, request, block_number, state_override).await?) } @@ -774,6 +835,9 @@ where state_override: Option, ) -> RpcResult { trace!(target: "rpc::eth", ?request, ?block_number, "Serving eth_estimateGas"); + if let Some(ref block_id) = block_number { + check_block_id_pending_tag(block_id)?; + } Ok(EthCall::estimate_gas_at( self, request, @@ -796,6 +860,7 @@ where block: BlockId, ) -> RpcResult> { trace!(target: "rpc::eth", "Serving eth_getAccount"); + check_block_id_pending_tag(&block)?; Ok(EthState::get_account(self, address, block).await?) } @@ -827,6 +892,7 @@ where reward_percentiles: Option>, ) -> RpcResult { trace!(target: "rpc::eth", ?block_count, ?newest_block, ?reward_percentiles, "Serving eth_feeHistory"); + check_pending_tag(&newest_block)?; Ok(EthFees::fee_history(self, block_count.to(), newest_block, reward_percentiles).await?) } @@ -904,6 +970,9 @@ where block_number: Option, ) -> RpcResult { trace!(target: "rpc::eth", ?address, ?keys, ?block_number, "Serving eth_getProof"); + if let Some(ref block_id) = block_number { + check_block_id_pending_tag(block_id)?; + } Ok(EthState::get_proof(self, address, keys, block_number)?.await?) } @@ -914,6 +983,26 @@ where block: BlockId, ) -> RpcResult { trace!(target: "rpc::eth", "Serving eth_getAccountInfo"); + check_block_id_pending_tag(&block)?; Ok(EthState::get_account_info(self, address, block).await?) } } + +/// Helper function to check if BlockNumberOrTag is pending and return error if so +fn check_pending_tag(block_number: &BlockNumberOrTag) -> RpcResult<()> { + if matches!(block_number, BlockNumberOrTag::Pending) { + Err(internal_rpc_err("Unsupported pending tag")) + } else { + Ok(()) + } +} + +/// Helper function to check if BlockId contains pending tag and return error if so +fn check_block_id_pending_tag(block_id: &BlockId) -> RpcResult<()> { + match block_id { + BlockId::Number(BlockNumberOrTag::Pending) => { + Err(internal_rpc_err("Unsupported pending tag")) + } + _ => Ok(()), + } +} diff --git a/crates/rpc/rpc-eth-api/src/helpers/block.rs b/crates/rpc/rpc-eth-api/src/helpers/block.rs index e4756f900..584cce69a 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/block.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/block.rs @@ -6,7 +6,7 @@ use crate::{ RpcReceipt, }; use alloy_consensus::TxReceipt; -use alloy_eips::BlockId; +use alloy_eips::{BlockId, BlockNumberOrTag}; use alloy_rlp::Encodable; use alloy_rpc_types_eth::{Block, BlockTransactions, Index}; use futures::Future; @@ -15,10 +15,9 @@ use reth_primitives_traits::{ AlloyBlockHeader, RecoveredBlock, SealedHeader, SignedTransaction, TransactionMeta, }; use reth_rpc_convert::{transaction::ConvertReceiptInput, RpcConvert, RpcHeader}; -use reth_storage_api::{BlockIdReader, BlockReader, ProviderHeader, ProviderReceipt, ProviderTx}; +use reth_storage_api::{BlockIdReader, BlockReader, HeaderProvider, ProviderHeader, ProviderReceipt, ProviderTx}; use reth_transaction_pool::{PoolTransaction, TransactionPool}; use std::{borrow::Cow, sync::Arc}; -use reth_storage_api::HeaderProvider; /// Result type of the fetched block receipts. pub type BlockReceiptsResult = Result>>, E>; @@ -66,10 +65,10 @@ pub trait EthBlocks: full.into(), |tx, tx_info| self.tx_resp_builder().fill(tx, tx_info), |header, size| { - let header_hash = header.hash(); + let block_number = header.number(); let td = self .provider() - .header_td(&header_hash) + .header_td_by_number(block_number) .map_err(Self::Error::from_eth_err)?; self.tx_resp_builder().convert_header(header, size, td) }, @@ -78,6 +77,46 @@ pub trait EthBlocks: } } + /// Returns the finalized block header. + /// + /// The `verified_validator_num` parameter is provided for BSC compatibility but is ignored + /// in the implementation. In standard Ethereum, the finalized block is determined by the + /// Beacon Chain consensus (Casper FFG). + fn rpc_finalized_header( + &self, + _verified_validator_num: u64, + ) -> impl Future>, Self::Error>> + Send + where + Self: FullEthApiTypes, + { + async move { + // Simply delegate to rpc_block_header with Finalized tag + self.rpc_block_header(BlockNumberOrTag::Finalized.into()).await + } + } + + /// Returns the finalized block. + /// + /// The `verified_validator_num` parameter is provided for BSC compatibility but is ignored + /// in the implementation. In standard Ethereum, the finalized block is determined by the + /// Beacon Chain consensus (Casper FFG). + /// + /// If `full` is true, the block object will contain all transaction objects, otherwise it will + /// only contain the transaction hashes. + fn rpc_finalized_block( + &self, + _verified_validator_num: u64, + full: bool, + ) -> impl Future>, Self::Error>> + Send + where + Self: FullEthApiTypes, + { + async move { + // Simply delegate to rpc_block with Finalized tag + self.rpc_block(BlockNumberOrTag::Finalized.into(), full).await + } + } + /// Returns the number transactions in the given block. /// /// Returns `None` if the block does not exist @@ -263,9 +302,11 @@ pub trait EthBlocks: let block = alloy_consensus::Block::::uncle(header); let size = block.length(); - let header = self - .tx_resp_builder() - .convert_header(SealedHeader::new_unhashed(block.header), size, None)?; + let header = self.tx_resp_builder().convert_header( + SealedHeader::new_unhashed(block.header), + size, + None, + )?; Ok(Block { uncles: vec![], header, diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index 43c642260..08d2a6f01 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -80,6 +80,20 @@ impl DebugApi where Eth: EthApiTypes + TraceExt + 'static, { + /// Handles BSC system transactions by disabling block gas limit validation. + /// + /// BSC system transactions are identified by: + /// 1. gas_limit == u64::MAX / 2 + /// 2. caller == block beneficiary (coinbase) + fn handle_bsc_system_transaction( + evm_env: &mut EvmEnvFor, + tx_env: &TxEnvFor, + ) { + if tx_env.gas_limit() == u64::MAX / 2 && tx_env.caller() == evm_env.block_env.beneficiary { + evm_env.cfg_env.disable_block_gas_limit = true; + } + } + /// Acquires a permit to execute a tracing call. async fn acquire_trace_permit(&self) -> Result { self.inner.blocking_task_guard.clone().acquire_owned().await @@ -269,7 +283,7 @@ where opts: GethDebugTracingCallOptions, ) -> Result { let at = block_id.unwrap_or_default(); - let GethDebugTracingCallOptions { tracing_options, state_overrides, block_overrides } = + let GethDebugTracingCallOptions { tracing_options, state_overrides, block_overrides, .. } = opts; let overrides = EvmOverrides::new(state_overrides, block_overrides.map(Box::new)); let GethDebugTracingOptions { config, tracer, tracer_config, .. } = tracing_options; @@ -283,7 +297,8 @@ where let mut inspector = FourByteInspector::default(); let inspector = self .eth_api() - .spawn_with_call_at(call, at, overrides, move |db, evm_env, tx_env| { + .spawn_with_call_at(call, at, overrides, move |db, mut evm_env, tx_env| { + Self::handle_bsc_system_transaction(&mut evm_env, &tx_env); this.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?; Ok(inspector) }) @@ -301,7 +316,9 @@ where let frame = self .eth_api() - .spawn_with_call_at(call, at, overrides, move |db, evm_env, tx_env| { + .spawn_with_call_at(call, at, overrides, move |db, mut evm_env, tx_env| { + Self::handle_bsc_system_transaction(&mut evm_env, &tx_env); + let gas_limit = tx_env.gas_limit(); let res = this.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?; @@ -324,11 +341,13 @@ where let frame = self .eth_api() - .spawn_with_call_at(call, at, overrides, move |db, evm_env, tx_env| { + .spawn_with_call_at(call, at, overrides, move |db, mut evm_env, tx_env| { // wrapper is hack to get around 'higher-ranked lifetime error', // see let db = db.0; + Self::handle_bsc_system_transaction(&mut evm_env, &tx_env); + let gas_limit = tx_env.gas_limit(); let res = this.eth_api().inspect( &mut *db, @@ -358,7 +377,7 @@ where let frame = self .inner .eth_api - .spawn_with_call_at(call, at, overrides, move |db, evm_env, tx_env| { + .spawn_with_call_at(call, at, overrides, move |db, mut evm_env, tx_env| { // wrapper is hack to get around 'higher-ranked lifetime error', see // let db = db.0; @@ -371,6 +390,8 @@ where index: None, }; + Self::handle_bsc_system_transaction(&mut evm_env, &tx_env); + let res = this.eth_api().inspect( &mut *db, evm_env, @@ -397,7 +418,9 @@ where let frame: FlatCallFrame = self .inner .eth_api - .spawn_with_call_at(call, at, overrides, move |db, evm_env, tx_env| { + .spawn_with_call_at(call, at, overrides, move |db, mut evm_env, tx_env| { + Self::handle_bsc_system_transaction(&mut evm_env, &tx_env); + let gas_limit = tx_env.gas_limit(); this.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?; let tx_info = TransactionInfo::default(); @@ -719,7 +742,7 @@ where fn trace_transaction( &self, opts: &GethDebugTracingOptions, - evm_env: EvmEnvFor, + mut evm_env: EvmEnvFor, tx_env: TxEnvFor, db: &mut StateCacheDb<'_>, transaction_context: Option, @@ -744,6 +767,7 @@ where GethDebugTracerType::BuiltInTracer(tracer) => match tracer { GethDebugBuiltInTracerType::FourByteTracer => { let mut inspector = FourByteInspector::default(); + Self::handle_bsc_system_transaction(&mut evm_env, &tx_env); let res = self.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?; return Ok((FourByteFrame::from(&inspector).into(), res.state)) } @@ -759,6 +783,8 @@ where )) }); + Self::handle_bsc_system_transaction(&mut evm_env, &tx_env); + let gas_limit = tx_env.gas_limit(); let res = self.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?; @@ -781,6 +807,9 @@ where TracingInspectorConfig::from_geth_prestate_config(&prestate_config), ) }); + + Self::handle_bsc_system_transaction(&mut evm_env, &tx_env); + let gas_limit = tx_env.gas_limit(); let res = self.eth_api().inspect(&mut *db, evm_env, tx_env, &mut inspector)?; @@ -805,6 +834,8 @@ where let mut inspector = MuxInspector::try_from_config(mux_config) .map_err(Eth::Error::from_eth_err)?; + Self::handle_bsc_system_transaction(&mut evm_env, &tx_env); + let res = self.eth_api().inspect(&mut *db, evm_env, tx_env, &mut inspector)?; let frame = inspector @@ -822,6 +853,8 @@ where TracingInspectorConfig::from_flat_call_config(&flat_call_config), ); + Self::handle_bsc_system_transaction(&mut evm_env, &tx_env); + let gas_limit = tx_env.gas_limit(); let res = self.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?; let frame: FlatCallFrame = inspector diff --git a/crates/storage/provider/src/providers/consistent.rs b/crates/storage/provider/src/providers/consistent.rs index f617c3f6f..82feddf16 100644 --- a/crates/storage/provider/src/providers/consistent.rs +++ b/crates/storage/provider/src/providers/consistent.rs @@ -671,7 +671,7 @@ impl HeaderProvider for ConsistentProvider { } fn header_td_by_number(&self, number: BlockNumber) -> ProviderResult> { - let number = if self.head_block.as_ref().map(|b| b.block_on_chain(number.into())).is_some() + let number = if self.head_block.as_ref().and_then(|b| b.block_on_chain(number.into())).is_some() { // If the block exists in memory, we should return a TD for it. //