feat(flashblock): Enable eth_getTransactionByHash support for flashblock (#19954)

Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
Francis Li
2025-11-25 00:31:24 -08:00
committed by GitHub
parent e57fe4510c
commit ba862da221
2 changed files with 78 additions and 37 deletions

View File

@@ -7,14 +7,16 @@ use futures::StreamExt;
use op_alloy_consensus::{transaction::OpTransactionInfo, OpTransaction};
use reth_chain_state::CanonStateSubscriptions;
use reth_optimism_primitives::DepositReceipt;
use reth_primitives_traits::{BlockBody, Recovered, SignedTransaction, WithEncoded};
use reth_primitives_traits::{
BlockBody, Recovered, SignedTransaction, SignerRecoverable, WithEncoded,
};
use reth_rpc_eth_api::{
helpers::{spec::SignersForRpc, EthTransactions, LoadReceipt, LoadTransaction},
helpers::{spec::SignersForRpc, EthTransactions, LoadReceipt, LoadTransaction, SpawnBlocking},
try_into_op_tx_info, EthApiTypes as _, FromEthApiError, FromEvmError, RpcConvert, RpcNodeCore,
RpcReceipt, TxInfoMapper,
};
use reth_rpc_eth_types::EthApiError;
use reth_storage_api::{errors::ProviderError, ReceiptProvider};
use reth_rpc_eth_types::{EthApiError, TransactionSource};
use reth_storage_api::{errors::ProviderError, ProviderTx, ReceiptProvider, TransactionsProvider};
use reth_transaction_pool::{
AddedTransactionOutcome, PoolPooledTx, PoolTransaction, TransactionOrigin, TransactionPool,
};
@@ -179,6 +181,53 @@ where
OpEthApiError: FromEvmError<N::Evm>,
Rpc: RpcConvert<Primitives = N::Primitives, Error = OpEthApiError>,
{
async fn transaction_by_hash(
&self,
hash: B256,
) -> Result<Option<TransactionSource<ProviderTx<Self::Provider>>>, Self::Error> {
// 1. Try to find the transaction on disk (historical blocks)
if let Some((tx, meta)) = self
.spawn_blocking_io(move |this| {
this.provider()
.transaction_by_hash_with_meta(hash)
.map_err(Self::Error::from_eth_err)
})
.await?
{
let transaction = tx
.try_into_recovered_unchecked()
.map_err(|_| EthApiError::InvalidTransactionSignature)?;
return Ok(Some(TransactionSource::Block {
transaction,
index: meta.index,
block_hash: meta.block_hash,
block_number: meta.block_number,
base_fee: meta.base_fee,
}));
}
// 2. check flashblocks (sequencer preconfirmations)
if let Ok(Some(pending_block)) = self.pending_flashblock().await &&
let Some(indexed_tx) = pending_block.block().find_indexed(hash)
{
let meta = indexed_tx.meta();
return Ok(Some(TransactionSource::Block {
transaction: indexed_tx.recovered_tx().cloned(),
index: meta.index,
block_hash: meta.block_hash,
block_number: meta.block_number,
base_fee: meta.base_fee,
}));
}
// 3. check local pool
if let Some(tx) = self.pool().get(&hash).map(|tx| tx.transaction.clone_into_consensus()) {
return Ok(Some(TransactionSource::Pool(tx)));
}
Ok(None)
}
}
impl<N, Rpc> OpEthApi<N, Rpc>

View File

@@ -609,45 +609,37 @@ pub trait LoadTransaction: SpawnBlocking + FullEthApiTypes + RpcNodeCoreExt {
> + Send {
async move {
// Try to find the transaction on disk
let mut resp = self
if let Some((tx, meta)) = self
.spawn_blocking_io(move |this| {
match this
.provider()
this.provider()
.transaction_by_hash_with_meta(hash)
.map_err(Self::Error::from_eth_err)?
{
None => Ok(None),
Some((tx, meta)) => {
// Note: we assume this transaction is valid, because it's mined (or
// part of pending block) and already. We don't need to
// check for pre EIP-2 because this transaction could be pre-EIP-2.
let transaction = tx
.try_into_recovered_unchecked()
.map_err(|_| EthApiError::InvalidTransactionSignature)?;
let tx = TransactionSource::Block {
transaction,
index: meta.index,
block_hash: meta.block_hash,
block_number: meta.block_number,
base_fee: meta.base_fee,
};
Ok(Some(tx))
}
}
.map_err(Self::Error::from_eth_err)
})
.await?;
.await?
{
// Note: we assume this transaction is valid, because it's mined (or
// part of pending block) and already. We don't need to
// check for pre EIP-2 because this transaction could be pre-EIP-2.
let transaction = tx
.try_into_recovered_unchecked()
.map_err(|_| EthApiError::InvalidTransactionSignature)?;
if resp.is_none() {
// tx not found on disk, check pool
if let Some(tx) =
self.pool().get(&hash).map(|tx| tx.transaction.clone().into_consensus())
{
resp = Some(TransactionSource::Pool(tx.into()));
}
return Ok(Some(TransactionSource::Block {
transaction,
index: meta.index,
block_hash: meta.block_hash,
block_number: meta.block_number,
base_fee: meta.base_fee,
}));
}
Ok(resp)
// tx not found on disk, check pool
if let Some(tx) = self.pool().get(&hash).map(|tx| tx.transaction.clone_into_consensus())
{
return Ok(Some(TransactionSource::Pool(tx.into())));
}
Ok(None)
}
}