mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat(rpc): dedup rpc getTxBySenderAndNonce (#10600)
Signed-off-by: jsvisa <delweng@gmail.com>
This commit is contained in:
@ -813,7 +813,8 @@ where
|
||||
WithOtherFields<reth_rpc_types::Transaction>,
|
||||
reth_rpc_types::Block<WithOtherFields<reth_rpc_types::Transaction>>,
|
||||
reth_rpc_types::AnyTransactionReceipt,
|
||||
> + TraceExt,
|
||||
> + TraceExt
|
||||
+ EthTransactions,
|
||||
{
|
||||
let otterscan_api = self.otterscan_api();
|
||||
self.modules.insert(RethRpcModule::Ots, otterscan_api.into_rpc().into());
|
||||
@ -917,7 +918,8 @@ where
|
||||
WithOtherFields<reth_rpc_types::Transaction>,
|
||||
reth_rpc_types::Block<WithOtherFields<reth_rpc_types::Transaction>>,
|
||||
reth_rpc_types::AnyTransactionReceipt,
|
||||
> + TraceExt,
|
||||
> + TraceExt
|
||||
+ EthTransactions,
|
||||
{
|
||||
let eth_api = self.eth_api().clone();
|
||||
OtterscanApi::new(eth_api)
|
||||
|
||||
@ -3,26 +3,22 @@
|
||||
|
||||
use alloy_dyn_abi::TypedData;
|
||||
use alloy_json_rpc::RpcObject;
|
||||
use jsonrpsee::{core::RpcResult, proc_macros::rpc, types::ErrorObjectOwned};
|
||||
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
|
||||
use reth_primitives::{
|
||||
transaction::AccessListResult, Address, BlockId, BlockNumberOrTag, Bytes, B256, B64, U256, U64,
|
||||
};
|
||||
use reth_rpc_eth_types::{utils::binary_search, EthApiError};
|
||||
use reth_rpc_server_types::{result::internal_rpc_err, ToRpcResult};
|
||||
use reth_rpc_types::{
|
||||
serde_helpers::JsonStorageKey,
|
||||
simulate::{SimBlock, SimulatedBlock},
|
||||
state::{EvmOverrides, StateOverride},
|
||||
BlockOverrides, BlockTransactions, Bundle, EIP1186AccountProofResponse, EthCallResponse,
|
||||
FeeHistory, Header, Index, StateContext, SyncStatus, TransactionRequest, Work,
|
||||
BlockOverrides, Bundle, EIP1186AccountProofResponse, EthCallResponse, FeeHistory, Header,
|
||||
Index, StateContext, SyncStatus, TransactionRequest, Work,
|
||||
};
|
||||
use reth_transaction_pool::{PoolTransaction, TransactionPool};
|
||||
use tracing::trace;
|
||||
|
||||
use crate::{
|
||||
helpers::{
|
||||
EthApiSpec, EthBlocks, EthCall, EthFees, EthState, EthTransactions, FullEthApi, LoadState,
|
||||
},
|
||||
helpers::{EthApiSpec, EthBlocks, EthCall, EthFees, EthState, EthTransactions, FullEthApi},
|
||||
RpcBlock, RpcReceipt, RpcTransaction,
|
||||
};
|
||||
|
||||
@ -562,55 +558,8 @@ where
|
||||
nonce: U64,
|
||||
) -> RpcResult<Option<RpcTransaction<T::NetworkTypes>>> {
|
||||
trace!(target: "rpc::eth", ?sender, ?nonce, "Serving eth_getTransactionBySenderAndNonce");
|
||||
let nonce = nonce.to::<u64>();
|
||||
|
||||
// Check the pool first
|
||||
if let Some(tx) = LoadState::pool(self).get_transaction_by_sender_and_nonce(sender, nonce) {
|
||||
let transaction = tx.transaction.clone().into_consensus();
|
||||
return Ok(Some(reth_rpc_types_compat::transaction::from_recovered(transaction)))
|
||||
}
|
||||
|
||||
// Check if the sender is a contract
|
||||
if self.get_code(sender, None).await?.len() > 0 {
|
||||
return Ok(None)
|
||||
}
|
||||
|
||||
let highest = EthState::transaction_count(self, sender, None).await?.saturating_to::<u64>();
|
||||
|
||||
// If the nonce is higher or equal to the highest nonce, the transaction is pending or not
|
||||
// exists.
|
||||
if nonce >= highest {
|
||||
return Ok(None)
|
||||
}
|
||||
|
||||
// perform a binary search over the block range to find the block in which the sender's
|
||||
// nonce reached the requested nonce.
|
||||
let num = binary_search::<_, _, ErrorObjectOwned>(
|
||||
1,
|
||||
self.block_number()?.saturating_to(),
|
||||
|mid| {
|
||||
async move {
|
||||
let mid_nonce = EthState::transaction_count(self, sender, Some(mid.into()))
|
||||
.await?
|
||||
.saturating_to::<u64>();
|
||||
|
||||
// The `transaction_count` returns the `nonce` after the transaction was
|
||||
// executed, which is the state of the account after the block, and we need to
|
||||
// find the transaction whose nonce is the pre-state, so
|
||||
// need to compare with `nonce`(no equal).
|
||||
Ok(mid_nonce > nonce)
|
||||
}
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
let Some(BlockTransactions::Full(transactions)) =
|
||||
self.block_by_number(num.into(), true).await?.map(|block| block.transactions)
|
||||
else {
|
||||
return Err(EthApiError::UnknownBlockNumber.into());
|
||||
};
|
||||
|
||||
Ok(transactions.into_iter().find(|tx| *tx.from == *sender && tx.nonce == nonce))
|
||||
Ok(EthTransactions::get_transaction_by_sender_and_nonce(self, sender, nonce.to(), true)
|
||||
.await?)
|
||||
}
|
||||
|
||||
/// Handler for: `eth_getTransactionReceipt`
|
||||
|
||||
@ -42,14 +42,7 @@ pub trait EthState: LoadState + SpawnBlocking {
|
||||
address: Address,
|
||||
block_id: Option<BlockId>,
|
||||
) -> impl Future<Output = Result<Bytes, Self::Error>> + Send {
|
||||
self.spawn_blocking_io(move |this| {
|
||||
Ok(this
|
||||
.state_at_block_id_or_latest(block_id)?
|
||||
.account_code(address)
|
||||
.map_err(Self::Error::from_eth_err)?
|
||||
.unwrap_or_default()
|
||||
.original_bytes())
|
||||
})
|
||||
LoadState::get_code(self, address, block_id)
|
||||
}
|
||||
|
||||
/// Returns balance of given account, at given blocknumber.
|
||||
@ -295,4 +288,23 @@ pub trait LoadState: EthApiTypes {
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns code of given account, at the given identifier.
|
||||
fn get_code(
|
||||
&self,
|
||||
address: Address,
|
||||
block_id: Option<BlockId>,
|
||||
) -> impl Future<Output = Result<Bytes, Self::Error>> + Send
|
||||
where
|
||||
Self: SpawnBlocking,
|
||||
{
|
||||
self.spawn_blocking_io(move |this| {
|
||||
Ok(this
|
||||
.state_at_block_id_or_latest(block_id)?
|
||||
.account_code(address)
|
||||
.map_err(Self::Error::from_eth_err)?
|
||||
.unwrap_or_default()
|
||||
.original_bytes())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,9 +7,10 @@ use reth_primitives::{
|
||||
Address, BlockId, Bytes, Receipt, SealedBlockWithSenders, TransactionMeta, TransactionSigned,
|
||||
TxHash, TxKind, B256, U256,
|
||||
};
|
||||
use reth_provider::{BlockReaderIdExt, ReceiptProvider, TransactionsProvider};
|
||||
use reth_provider::{BlockNumReader, BlockReaderIdExt, ReceiptProvider, TransactionsProvider};
|
||||
use reth_rpc_eth_types::{
|
||||
utils::recover_raw_transaction, EthApiError, EthStateCache, SignError, TransactionSource,
|
||||
utils::{binary_search, recover_raw_transaction},
|
||||
EthApiError, EthStateCache, SignError, TransactionSource,
|
||||
};
|
||||
use reth_rpc_types::{
|
||||
transaction::{
|
||||
@ -18,13 +19,14 @@ use reth_rpc_types::{
|
||||
},
|
||||
AnyTransactionReceipt, TransactionInfo, TransactionRequest, TypedTransactionRequest,
|
||||
};
|
||||
use reth_rpc_types_compat::transaction::from_recovered_with_block_context;
|
||||
use reth_rpc_types_compat::transaction::{from_recovered, from_recovered_with_block_context};
|
||||
use reth_transaction_pool::{PoolTransaction, TransactionOrigin, TransactionPool};
|
||||
|
||||
use crate::{FromEthApiError, IntoEthApiError, RpcTransaction};
|
||||
|
||||
use super::{
|
||||
Call, EthApiSpec, EthSigner, LoadBlock, LoadFee, LoadPendingBlock, LoadReceipt, SpawnBlocking,
|
||||
Call, EthApiSpec, EthSigner, LoadBlock, LoadFee, LoadPendingBlock, LoadReceipt, LoadState,
|
||||
SpawnBlocking,
|
||||
};
|
||||
|
||||
/// Transaction related functions for the [`EthApiServer`](crate::EthApiServer) trait in
|
||||
@ -212,6 +214,81 @@ pub trait EthTransactions: LoadTransaction {
|
||||
}
|
||||
}
|
||||
|
||||
/// Find a transaction by sender's address and nonce.
|
||||
fn get_transaction_by_sender_and_nonce(
|
||||
&self,
|
||||
sender: Address,
|
||||
nonce: u64,
|
||||
include_pending: bool,
|
||||
) -> impl Future<Output = Result<Option<RpcTransaction<Self::NetworkTypes>>, Self::Error>> + Send
|
||||
where
|
||||
Self: LoadBlock + LoadState,
|
||||
{
|
||||
async move {
|
||||
// Check the pool first
|
||||
if include_pending {
|
||||
if let Some(tx) =
|
||||
LoadState::pool(self).get_transaction_by_sender_and_nonce(sender, nonce)
|
||||
{
|
||||
let transaction = tx.transaction.clone().into_consensus();
|
||||
return Ok(Some(from_recovered(transaction)));
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the sender is a contract
|
||||
if self.get_code(sender, None).await?.len() > 0 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let highest = self.transaction_count(sender, None).await?.saturating_to::<u64>();
|
||||
|
||||
// If the nonce is higher or equal to the highest nonce, the transaction is pending or
|
||||
// not exists.
|
||||
if nonce >= highest {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let Ok(high) = LoadBlock::provider(self).best_block_number() else {
|
||||
return Err(EthApiError::UnknownBlockNumber.into());
|
||||
};
|
||||
|
||||
// Perform a binary search over the block range to find the block in which the sender's
|
||||
// nonce reached the requested nonce.
|
||||
let num = binary_search::<_, _, Self::Error>(1, high, |mid| async move {
|
||||
let mid_nonce =
|
||||
self.transaction_count(sender, Some(mid.into())).await?.saturating_to::<u64>();
|
||||
|
||||
Ok(mid_nonce > nonce)
|
||||
})
|
||||
.await?;
|
||||
|
||||
self.block_with_senders(num.into())
|
||||
.await?
|
||||
.and_then(|block| {
|
||||
let block_hash = block.hash();
|
||||
let block_number = block.number;
|
||||
let base_fee_per_gas = block.base_fee_per_gas;
|
||||
|
||||
block
|
||||
.into_transactions_ecrecovered()
|
||||
.enumerate()
|
||||
.find(|(_, tx)| tx.signer() == sender && tx.nonce() == nonce)
|
||||
.map(|(index, tx)| {
|
||||
let tx_info = TransactionInfo {
|
||||
hash: Some(tx.hash()),
|
||||
block_hash: Some(block_hash),
|
||||
block_number: Some(block_number),
|
||||
base_fee: base_fee_per_gas.map(u128::from),
|
||||
index: Some(index as u64),
|
||||
};
|
||||
from_recovered_with_block_context(tx, tx_info)
|
||||
})
|
||||
})
|
||||
.ok_or(EthApiError::UnknownBlockNumber.into())
|
||||
.map(Some)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get transaction, as raw bytes, by [`BlockId`] and index of transaction within that block.
|
||||
///
|
||||
/// Returns `Ok(None)` if the block does not exist, or index is out of range.
|
||||
|
||||
@ -4,7 +4,10 @@ use async_trait::async_trait;
|
||||
use jsonrpsee::{core::RpcResult, types::ErrorObjectOwned};
|
||||
use reth_primitives::{Address, BlockNumberOrTag, TxHash, B256, U256};
|
||||
use reth_rpc_api::{EthApiServer, OtterscanServer};
|
||||
use reth_rpc_eth_api::{helpers::TraceExt, EthApiTypes, RpcBlock, RpcReceipt, RpcTransaction};
|
||||
use reth_rpc_eth_api::{
|
||||
helpers::{EthTransactions, TraceExt},
|
||||
EthApiTypes, RpcBlock, RpcReceipt, RpcTransaction,
|
||||
};
|
||||
use reth_rpc_eth_types::{utils::binary_search, EthApiError};
|
||||
use reth_rpc_server_types::result::internal_rpc_err;
|
||||
use reth_rpc_types::{
|
||||
@ -75,6 +78,7 @@ where
|
||||
TransactionResponse = WithOtherFields<reth_rpc_types::Transaction>,
|
||||
>,
|
||||
> + TraceExt
|
||||
+ EthTransactions
|
||||
+ 'static,
|
||||
{
|
||||
/// Handler for `{ots,erigon}_getHeaderByNumber`
|
||||
@ -84,7 +88,9 @@ where
|
||||
|
||||
/// Handler for `ots_hasCode`
|
||||
async fn has_code(&self, address: Address, block_number: Option<u64>) -> RpcResult<bool> {
|
||||
self.eth.get_code(address, block_number.map(Into::into)).await.map(|code| !code.is_empty())
|
||||
EthApiServer::get_code(&self.eth, address, block_number.map(Into::into))
|
||||
.await
|
||||
.map(|code| !code.is_empty())
|
||||
}
|
||||
|
||||
/// Handler for `ots_getApiLevel`
|
||||
@ -282,51 +288,11 @@ where
|
||||
sender: Address,
|
||||
nonce: u64,
|
||||
) -> RpcResult<Option<TxHash>> {
|
||||
// Check if the sender is a contract
|
||||
if self.has_code(sender, None).await? {
|
||||
return Ok(None)
|
||||
}
|
||||
|
||||
let highest =
|
||||
EthApiServer::transaction_count(&self.eth, sender, None).await?.saturating_to::<u64>();
|
||||
|
||||
// If the nonce is higher or equal to the highest nonce, the transaction is pending or not
|
||||
// exists.
|
||||
if nonce >= highest {
|
||||
return Ok(None)
|
||||
}
|
||||
|
||||
// perform a binary search over the block range to find the block in which the sender's
|
||||
// nonce reached the requested nonce.
|
||||
let num = binary_search::<_, _, ErrorObjectOwned>(
|
||||
1,
|
||||
self.eth.block_number()?.saturating_to(),
|
||||
|mid| {
|
||||
async move {
|
||||
let mid_nonce =
|
||||
EthApiServer::transaction_count(&self.eth, sender, Some(mid.into()))
|
||||
.await?
|
||||
.saturating_to::<u64>();
|
||||
|
||||
// The `transaction_count` returns the `nonce` after the transaction was
|
||||
// executed, which is the state of the account after the block, and we need to
|
||||
// find the transaction whose nonce is the pre-state, so
|
||||
// need to compare with `nonce`(no equal).
|
||||
Ok(mid_nonce > nonce)
|
||||
}
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
let Some(BlockTransactions::Full(transactions)) =
|
||||
self.eth.block_by_number(num.into(), true).await?.map(|block| block.transactions)
|
||||
else {
|
||||
return Err(EthApiError::UnknownBlockNumber.into());
|
||||
};
|
||||
|
||||
Ok(transactions
|
||||
.into_iter()
|
||||
.find(|tx| *tx.from == *sender && tx.nonce == nonce)
|
||||
Ok(self
|
||||
.eth
|
||||
.get_transaction_by_sender_and_nonce(sender, nonce, false)
|
||||
.await
|
||||
.map_err(|e| e.into())?
|
||||
.map(|tx| tx.hash))
|
||||
}
|
||||
|
||||
@ -341,7 +307,9 @@ where
|
||||
self.eth.block_number()?.saturating_to(),
|
||||
|mid| {
|
||||
Box::pin(async move {
|
||||
Ok(!self.eth.get_code(address, Some(mid.into())).await?.is_empty())
|
||||
Ok(!EthApiServer::get_code(&self.eth, address, Some(mid.into()))
|
||||
.await?
|
||||
.is_empty())
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user