Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions crates/rpc/rpc-eth-api/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,25 @@ pub trait EthApi<TxReq: RpcObject, T: RpcObject, B: RpcObject, R: RpcObject, H:
#[method(name = "getHeaderByHash")]
async fn header_by_hash(&self, hash: B256) -> RpcResult<Option<H>>;

/// 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<Option<H>>;

/// 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<Option<B>>;

/// `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")]
Expand Down Expand Up @@ -470,6 +489,7 @@ where
full: bool,
) -> RpcResult<Option<RpcBlock<T::NetworkTypes>>> {
trace!(target: "rpc::eth", ?number, ?full, "Serving eth_getBlockByNumber");
check_pending_tag(&number)?;
Ok(EthBlocks::rpc_block(self, number.into(), full).await?)
}

Expand All @@ -485,6 +505,7 @@ where
number: BlockNumberOrTag,
) -> RpcResult<Option<U256>> {
trace!(target: "rpc::eth", ?number, "Serving eth_getBlockTransactionCountByNumber");
check_pending_tag(&number)?;
Ok(EthBlocks::block_transaction_count(self, number.into()).await?.map(U256::from))
}

Expand All @@ -505,6 +526,7 @@ where
number: BlockNumberOrTag,
) -> RpcResult<Option<U256>> {
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())))
Expand All @@ -519,6 +541,7 @@ where
block_id: BlockId,
) -> RpcResult<Option<Vec<RpcReceipt<T::NetworkTypes>>>> {
trace!(target: "rpc::eth", ?block_id, "Serving eth_getBlockReceipts");
check_block_id_pending_tag(&block_id)?;
Ok(EthBlocks::block_receipts(self, block_id).await?)
}

Expand All @@ -539,6 +562,7 @@ where
index: Index,
) -> RpcResult<Option<RpcBlock<T::NetworkTypes>>> {
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?)
}

Expand Down Expand Up @@ -597,6 +621,7 @@ where
index: Index,
) -> RpcResult<Option<Bytes>> {
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(),
Expand All @@ -612,6 +637,7 @@ where
index: Index,
) -> RpcResult<Option<RpcTransaction<T::NetworkTypes>>> {
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?)
}
Expand All @@ -631,6 +657,7 @@ where
number: BlockNumberOrTag,
) -> RpcResult<Option<Vec<RpcTransaction<T::NetworkTypes>>>> {
trace!(target: "rpc::eth", ?number, "Serving eth_getTransactionsByBlockNumber");
check_pending_tag(&number)?;
Ok(EthTransactions::transactions_by_block_id(self, number.into()).await?)
}

Expand Down Expand Up @@ -670,6 +697,9 @@ where
/// Handler for: `eth_getBalance`
async fn balance(&self, address: Address, block_number: Option<BlockId>) -> RpcResult<U256> {
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?)
}

Expand All @@ -681,6 +711,9 @@ where
block_number: Option<BlockId>,
) -> RpcResult<B256> {
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?)
}

Expand All @@ -691,12 +724,18 @@ where
block_number: Option<BlockId>,
) -> RpcResult<U256> {
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<BlockId>) -> RpcResult<Bytes> {
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?)
}

Expand All @@ -706,6 +745,7 @@ where
block_number: BlockNumberOrTag,
) -> RpcResult<Option<RpcHeader<T::NetworkTypes>>> {
trace!(target: "rpc::eth", ?block_number, "Serving eth_getHeaderByNumber");
check_pending_tag(&block_number)?;
Ok(EthBlocks::rpc_block_header(self, block_number.into()).await?)
}

Expand All @@ -715,13 +755,28 @@ where
Ok(EthBlocks::rpc_block_header(self, hash.into()).await?)
}

/// Handler for: `eth_getFinalizedHeader`
async fn finalized_header(&self, verified_validator_num: u64) -> RpcResult<Option<RpcHeader<T::NetworkTypes>>> {
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<Option<RpcBlock<T::NetworkTypes>>> {
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,
payload: SimulatePayload<RpcTxReq<T::NetworkTypes>>,
block_number: Option<BlockId>,
) -> RpcResult<Vec<SimulatedBlock<RpcBlock<T::NetworkTypes>>>> {
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?)
}
Expand All @@ -735,6 +790,9 @@ where
block_overrides: Option<Box<BlockOverrides>>,
) -> RpcResult<Bytes> {
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,
Expand Down Expand Up @@ -763,6 +821,9 @@ where
state_override: Option<StateOverride>,
) -> RpcResult<AccessListResult> {
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?)
}

Expand All @@ -774,6 +835,9 @@ where
state_override: Option<StateOverride>,
) -> RpcResult<U256> {
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,
Expand All @@ -796,6 +860,7 @@ where
block: BlockId,
) -> RpcResult<Option<alloy_rpc_types_eth::Account>> {
trace!(target: "rpc::eth", "Serving eth_getAccount");
check_block_id_pending_tag(&block)?;
Ok(EthState::get_account(self, address, block).await?)
}

Expand Down Expand Up @@ -827,6 +892,7 @@ where
reward_percentiles: Option<Vec<f64>>,
) -> RpcResult<FeeHistory> {
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?)
}

Expand Down Expand Up @@ -904,6 +970,9 @@ where
block_number: Option<BlockId>,
) -> RpcResult<EIP1186AccountProofResponse> {
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?)
}

Expand All @@ -914,6 +983,26 @@ where
block: BlockId,
) -> RpcResult<alloy_rpc_types_eth::AccountInfo> {
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(()),
}
}
57 changes: 49 additions & 8 deletions crates/rpc/rpc-eth-api/src/helpers/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<N, E> = Result<Option<Vec<RpcReceipt<N>>>, E>;
Expand Down Expand Up @@ -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)
},
Expand All @@ -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<Output = Result<Option<RpcHeader<Self::NetworkTypes>>, 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<Output = Result<Option<RpcBlock<Self::NetworkTypes>>, 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
Expand Down Expand Up @@ -263,9 +302,11 @@ pub trait EthBlocks:
let block =
alloy_consensus::Block::<alloy_consensus::TxEnvelope, _>::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,
Expand Down
Loading