-
Notifications
You must be signed in to change notification settings - Fork 2.4k
feat(flashblock): Enable eth_getTransactionByHash support for flashblock #19954
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
58cd0d4
1dcb716
de6f424
dd13356
11bd072
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did not choose to add a new FlashBlock source type, current Block type seem to represent the situation well |
||
| 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> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reworked the function a bit to avoid unnecessary nested loops and improve readability |
||
| .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?; | ||
|
|
||
| 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())); | ||
| } | ||
| .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)?; | ||
|
|
||
| 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) | ||
| } | ||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: Steps 1 and 3 duplicate logic from the default trait implementation rather than extracting to a helper method. This keeps the override self-contained and avoids adding a helper method to the trait API that would only exist to support this one override.
Also we check flashblocks first before checking pool (in case people are using nodes with txpool gossip enabled to serve rpc requests)