chore(rpc): EthApiTypes trait for network specific error AT (#9523)

Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
Emilia Hane
2024-07-25 19:30:30 +02:00
committed by GitHub
parent f175f6ec9f
commit 0be2c17a9f
39 changed files with 878 additions and 478 deletions

2
Cargo.lock generated
View File

@ -7973,6 +7973,7 @@ version = "1.0.3"
dependencies = [
"alloy-primitives",
"jsonrpsee",
"jsonrpsee-types",
"parking_lot 0.12.3",
"reth-chainspec",
"reth-errors",
@ -8393,6 +8394,7 @@ dependencies = [
"dyn-clone",
"futures",
"jsonrpsee",
"jsonrpsee-types",
"parking_lot 0.12.3",
"reth-chainspec",
"reth-errors",

View File

@ -144,7 +144,6 @@ optimism = [
"reth-blockchain-tree/optimism",
"dep:reth-node-optimism",
"reth-node-core/optimism",
"reth-rpc-eth-types/optimism",
]
# no-op feature flag for switching between the `optimism` and default functionality in CI matrices

View File

@ -2,12 +2,9 @@ use alloy_consensus::TxEnvelope;
use alloy_network::eip2718::Decodable2718;
use reth::{
builder::{rpc::RpcRegistry, FullNodeComponents},
rpc::{
api::{
eth::helpers::{EthApiSpec, EthTransactions, TraceExt},
DebugApiServer,
},
server_types::eth::EthResult,
rpc::api::{
eth::helpers::{EthApiSpec, EthTransactions, TraceExt},
DebugApiServer,
},
};
use reth_primitives::{Bytes, B256};
@ -21,7 +18,7 @@ where
EthApi: EthApiSpec + EthTransactions + TraceExt,
{
/// Injects a raw transaction into the node tx pool via RPC server
pub async fn inject_tx(&mut self, raw_tx: Bytes) -> EthResult<B256> {
pub async fn inject_tx(&mut self, raw_tx: Bytes) -> Result<B256, EthApi::Error> {
let eth_api = self.inner.eth_api();
eth_api.send_raw_transaction(raw_tx).await
}

View File

@ -84,7 +84,6 @@ optimism = [
"reth-provider/optimism",
"reth-rpc-types-compat/optimism",
"reth-rpc-eth-api/optimism",
"reth-rpc-eth-types/optimism",
]

View File

@ -80,7 +80,6 @@ optimism = [
"reth-beacon-consensus/optimism",
"reth-revm/optimism",
"reth-auto-seal-consensus/optimism",
"reth-rpc-eth-types/optimism",
"reth-optimism-rpc/optimism"
]
test-utils = ["reth-node-builder/test-utils"]

View File

@ -38,6 +38,7 @@ tokio.workspace = true
# rpc
jsonrpsee.workspace = true
jsonrpsee-types.workspace = true
# misc
thiserror.workspace = true
@ -59,6 +60,5 @@ optimism = [
"reth-primitives/optimism",
"reth-provider/optimism",
"reth-rpc-eth-api/optimism",
"reth-rpc-eth-types/optimism",
"revm/optimism"
]

View File

@ -1,31 +1,85 @@
//! RPC errors specific to OP.
use jsonrpsee::types::ErrorObject;
use reth_primitives::revm_primitives::{InvalidTransaction, OptimismInvalidTransaction};
use reth_rpc_eth_api::AsEthApiError;
use reth_rpc_eth_types::EthApiError;
use reth_rpc_server_types::result::internal_rpc_err;
use reth_rpc_types::ToRpcError;
use reth_rpc_server_types::result::{internal_rpc_err, rpc_err};
use reth_rpc_types::error::EthRpcErrorCode;
/// Optimism specific errors, that extend [`EthApiError`].
#[derive(Debug, thiserror::Error)]
pub enum OpEthApiError {
/// L1 ethereum error.
#[error(transparent)]
Eth(#[from] EthApiError),
/// Thrown when calculating L1 gas fee.
#[error("failed to calculate l1 gas fee")]
L1BlockFeeError,
/// Thrown when calculating L1 gas used
#[error("failed to calculate l1 gas used")]
L1BlockGasError,
/// Wrapper for [`revm_primitives::InvalidTransaction`](InvalidTransaction).
#[error(transparent)]
InvalidTransaction(OptimismInvalidTransactionError),
}
impl ToRpcError for OpEthApiError {
fn to_rpc_error(&self) -> ErrorObject<'static> {
impl AsEthApiError for OpEthApiError {
fn as_err(&self) -> Option<&EthApiError> {
match self {
Self::L1BlockFeeError | Self::L1BlockGasError => internal_rpc_err(self.to_string()),
Self::Eth(err) => Some(err),
_ => None,
}
}
}
impl From<OpEthApiError> for EthApiError {
impl From<OpEthApiError> for jsonrpsee_types::error::ErrorObject<'static> {
fn from(err: OpEthApiError) -> Self {
Self::other(err)
match err {
OpEthApiError::Eth(err) => err.into(),
OpEthApiError::L1BlockFeeError | OpEthApiError::L1BlockGasError => {
internal_rpc_err(err.to_string())
}
OpEthApiError::InvalidTransaction(err) => err.into(),
}
}
}
/// Optimism specific invalid transaction errors
#[derive(thiserror::Error, Debug)]
pub enum OptimismInvalidTransactionError {
/// A deposit transaction was submitted as a system transaction post-regolith.
#[error("no system transactions allowed after regolith")]
DepositSystemTxPostRegolith,
/// A deposit transaction halted post-regolith
#[error("deposit transaction halted after regolith")]
HaltedDepositPostRegolith,
}
impl From<OptimismInvalidTransactionError> for jsonrpsee_types::error::ErrorObject<'static> {
fn from(err: OptimismInvalidTransactionError) -> Self {
match err {
OptimismInvalidTransactionError::DepositSystemTxPostRegolith |
OptimismInvalidTransactionError::HaltedDepositPostRegolith => {
rpc_err(EthRpcErrorCode::TransactionRejected.code(), err.to_string(), None)
}
}
}
}
impl TryFrom<InvalidTransaction> for OptimismInvalidTransactionError {
type Error = InvalidTransaction;
fn try_from(err: InvalidTransaction) -> Result<Self, Self::Error> {
match err {
InvalidTransaction::OptimismError(err) => match err {
OptimismInvalidTransaction::DepositSystemTxPostRegolith => {
Ok(Self::DepositSystemTxPostRegolith)
}
OptimismInvalidTransaction::HaltedDepositPostRegolith => {
Ok(Self::HaltedDepositPostRegolith)
}
},
_ => Err(err),
}
}
}

View File

@ -2,8 +2,11 @@
use reth_primitives::TransactionMeta;
use reth_provider::{BlockReaderIdExt, HeaderProvider};
use reth_rpc_eth_api::helpers::{EthApiSpec, EthBlocks, LoadBlock, LoadReceipt, LoadTransaction};
use reth_rpc_eth_types::{EthResult, EthStateCache, ReceiptBuilder};
use reth_rpc_eth_api::{
helpers::{EthApiSpec, EthBlocks, LoadBlock, LoadReceipt, LoadTransaction},
FromEthApiError,
};
use reth_rpc_eth_types::{EthStateCache, ReceiptBuilder};
use reth_rpc_types::{AnyTransactionReceipt, BlockId};
use crate::{op_receipt_fields, OpEthApi};
@ -19,7 +22,7 @@ where
async fn block_receipts(
&self,
block_id: BlockId,
) -> EthResult<Option<Vec<AnyTransactionReceipt>>>
) -> Result<Option<Vec<AnyTransactionReceipt>>, Self::Error>
where
Self: LoadReceipt,
{
@ -52,11 +55,13 @@ where
let optimism_tx_meta =
self.build_op_tx_meta(tx, l1_block_info.clone(), timestamp)?;
ReceiptBuilder::new(tx, meta, receipt, &receipts).map(|builder| {
op_receipt_fields(builder, tx, receipt, optimism_tx_meta).build()
})
ReceiptBuilder::new(tx, meta, receipt, &receipts)
.map(|builder| {
op_receipt_fields(builder, tx, receipt, optimism_tx_meta).build()
})
.map_err(Self::Error::from_eth_err)
})
.collect::<EthResult<Vec<_>>>();
.collect::<Result<Vec<_>, Self::Error>>();
return receipts.map(Some)
}

View File

@ -3,13 +3,22 @@ use reth_primitives::{
revm_primitives::{BlockEnv, OptimismFields, TxEnv},
Bytes,
};
use reth_rpc_eth_api::helpers::Call;
use reth_rpc_eth_types::EthResult;
use reth_rpc_eth_api::{
helpers::{Call, EthCall},
EthApiTypes, FromEthApiError,
};
use reth_rpc_eth_types::EthApiError;
use reth_rpc_types::TransactionRequest;
use crate::OpEthApi;
impl<Eth: Call> Call for OpEthApi<Eth> {
impl<Eth: EthCall> EthCall for OpEthApi<Eth> where EthApiError: From<Eth::Error> {}
impl<Eth> Call for OpEthApi<Eth>
where
Eth: Call + EthApiTypes,
EthApiError: From<Eth::Error>,
{
fn call_gas_limit(&self) -> u64 {
self.inner.call_gas_limit()
}
@ -22,8 +31,9 @@ impl<Eth: Call> Call for OpEthApi<Eth> {
&self,
block_env: &BlockEnv,
request: TransactionRequest,
) -> EthResult<TxEnv> {
let mut env = Eth::create_txn_env(&self.inner, block_env, request)?;
) -> Result<TxEnv, Self::Error> {
let mut env =
self.inner.create_txn_env(block_env, request).map_err(Self::Error::from_eth_err)?;
env.optimism = OptimismFields { enveloped_tx: Some(Bytes::new()), ..Default::default() };

View File

@ -18,10 +18,10 @@ use reth_provider::{BlockReaderIdExt, ChainSpecProvider, HeaderProvider, StatePr
use reth_rpc::eth::DevSigner;
use reth_rpc_eth_api::{
helpers::{
AddDevSigners, EthApiSpec, EthCall, EthFees, EthSigner, EthState, LoadFee, LoadState,
SpawnBlocking, Trace, UpdateRawTxForwarder,
AddDevSigners, EthApiSpec, EthFees, EthSigner, EthState, LoadFee, LoadState, SpawnBlocking,
Trace, UpdateRawTxForwarder,
},
RawTransactionForwarder,
EthApiTypes, RawTransactionForwarder,
};
use reth_rpc_eth_types::EthStateCache;
use reth_rpc_types::SyncStatus;
@ -29,6 +29,8 @@ use reth_tasks::{pool::BlockingTaskPool, TaskSpawner};
use reth_transaction_pool::TransactionPool;
use tokio::sync::{AcquireError, OwnedSemaphorePermit};
use crate::OpEthApiError;
/// OP-Reth `Eth` API implementation.
///
/// This type provides the functionality for handling `eth_` related requests.
@ -51,6 +53,13 @@ impl<Eth> OpEthApi<Eth> {
}
}
impl<Eth> EthApiTypes for OpEthApi<Eth>
where
Eth: Send + Sync,
{
type Error = OpEthApiError;
}
impl<Eth: EthApiSpec> EthApiSpec for OpEthApi<Eth> {
fn protocol_version(&self) -> impl Future<Output = RethResult<U64>> + Send {
self.inner.protocol_version()
@ -142,8 +151,6 @@ impl<Eth: EthState> EthState for OpEthApi<Eth> {
}
}
impl<Eth: EthCall> EthCall for OpEthApi<Eth> {}
impl<Eth: EthFees> EthFees for OpEthApi<Eth> {}
impl<Eth: Trace> Trace for OpEthApi<Eth> {

View File

@ -1,8 +1,11 @@
//! Loads and formats OP receipt RPC response.
use reth_primitives::{Receipt, TransactionMeta, TransactionSigned};
use reth_rpc_eth_api::helpers::{EthApiSpec, LoadReceipt, LoadTransaction};
use reth_rpc_eth_types::{EthApiError, EthResult, EthStateCache, ReceiptBuilder};
use reth_rpc_eth_api::{
helpers::{EthApiSpec, LoadReceipt, LoadTransaction},
FromEthApiError,
};
use reth_rpc_eth_types::{EthApiError, EthStateCache, ReceiptBuilder};
use reth_rpc_types::{AnyTransactionReceipt, OptimismTransactionReceiptFields};
use crate::{OpEthApi, OptimismTxMeta};
@ -21,17 +24,19 @@ where
tx: TransactionSigned,
meta: TransactionMeta,
receipt: Receipt,
) -> EthResult<AnyTransactionReceipt> {
) -> Result<AnyTransactionReceipt, Self::Error> {
let (block, receipts) = LoadReceipt::cache(self)
.get_block_and_receipts(meta.block_hash)
.await?
.ok_or(EthApiError::UnknownBlockNumber)?;
.await
.map_err(Self::Error::from_eth_err)?
.ok_or(Self::Error::from_eth_err(EthApiError::UnknownBlockNumber))?;
let block = block.unseal();
let l1_block_info = reth_evm_optimism::extract_l1_info(&block).ok();
let optimism_tx_meta = self.build_op_tx_meta(&tx, l1_block_info, block.timestamp)?;
let resp_builder = ReceiptBuilder::new(&tx, meta, &receipt, &receipts)?;
let resp_builder = ReceiptBuilder::new(&tx, meta, &receipt, &receipts)
.map_err(Self::Error::from_eth_err)?;
let resp_builder = op_receipt_fields(resp_builder, &tx, &receipt, optimism_tx_meta);
Ok(resp_builder.build())

View File

@ -7,9 +7,9 @@ use reth_primitives::TransactionSigned;
use reth_provider::{BlockReaderIdExt, TransactionsProvider};
use reth_rpc_eth_api::{
helpers::{EthApiSpec, EthSigner, EthTransactions, LoadTransaction},
RawTransactionForwarder,
EthApiTypes, RawTransactionForwarder,
};
use reth_rpc_eth_types::{EthResult, EthStateCache};
use reth_rpc_eth_types::EthStateCache;
use revm::L1BlockInfo;
use crate::{OpEthApi, OpEthApiError};
@ -79,7 +79,7 @@ where
tx: &TransactionSigned,
l1_block_info: Option<L1BlockInfo>,
block_timestamp: u64,
) -> EthResult<OptimismTxMeta> {
) -> Result<OptimismTxMeta, <Self as EthApiTypes>::Error> {
let Some(l1_block_info) = l1_block_info else { return Ok(OptimismTxMeta::default()) };
let (l1_fee, l1_data_gas) = if !tx.is_deposit() {

View File

@ -35,6 +35,7 @@ alloy-dyn-abi = { workspace = true, features = ["eip712"] }
# rpc
jsonrpsee = { workspace = true, features = ["server", "macros"] }
jsonrpsee-types.workspace = true
# async
async-trait.workspace = true
@ -53,5 +54,4 @@ optimism = [
"reth-primitives/optimism",
"revm/optimism",
"reth-provider/optimism",
"reth-rpc-eth-types/optimism"
]

View File

@ -334,7 +334,8 @@ pub trait EthApi {
#[async_trait::async_trait]
impl<T> EthApiServer for T
where
Self: FullEthApi,
T: FullEthApi,
jsonrpsee_types::error::ErrorObject<'static>: From<T::Error>,
{
/// Handler for: `eth_protocolVersion`
async fn protocol_version(&self) -> RpcResult<U64> {

View File

@ -5,10 +5,12 @@ use std::sync::Arc;
use futures::Future;
use reth_primitives::{BlockId, Receipt, SealedBlock, SealedBlockWithSenders, TransactionMeta};
use reth_provider::{BlockIdReader, BlockReader, BlockReaderIdExt, HeaderProvider};
use reth_rpc_eth_types::{EthApiError, EthResult, EthStateCache, ReceiptBuilder};
use reth_rpc_eth_types::{EthApiError, EthStateCache, ReceiptBuilder};
use reth_rpc_types::{AnyTransactionReceipt, Header, Index, RichBlock};
use reth_rpc_types_compat::block::{from_block, uncle_block_from_header};
use crate::FromEthApiError;
use super::{LoadPendingBlock, LoadReceipt, SpawnBlocking};
/// Block related functions for the [`EthApiServer`](crate::EthApiServer) trait in the
@ -23,7 +25,7 @@ pub trait EthBlocks: LoadBlock {
fn rpc_block_header(
&self,
block_id: BlockId,
) -> impl Future<Output = EthResult<Option<Header>>> + Send
) -> impl Future<Output = Result<Option<Header>, Self::Error>> + Send
where
Self: LoadPendingBlock + SpawnBlocking,
{
@ -38,7 +40,7 @@ pub trait EthBlocks: LoadBlock {
&self,
block_id: BlockId,
full: bool,
) -> impl Future<Output = EthResult<Option<RichBlock>>> + Send
) -> impl Future<Output = Result<Option<RichBlock>, Self::Error>> + Send
where
Self: LoadPendingBlock + SpawnBlocking,
{
@ -49,10 +51,11 @@ pub trait EthBlocks: LoadBlock {
};
let block_hash = block.hash();
let total_difficulty = EthBlocks::provider(self)
.header_td_by_number(block.number)?
.header_td_by_number(block.number)
.map_err(Self::Error::from_eth_err)?
.ok_or(EthApiError::UnknownBlockNumber)?;
let block =
from_block(block.unseal(), total_difficulty, full.into(), Some(block_hash))?;
let block = from_block(block.unseal(), total_difficulty, full.into(), Some(block_hash))
.map_err(Self::Error::from_eth_err)?;
Ok(Some(block.into()))
}
}
@ -63,19 +66,30 @@ pub trait EthBlocks: LoadBlock {
fn block_transaction_count(
&self,
block_id: BlockId,
) -> impl Future<Output = EthResult<Option<usize>>> + Send {
) -> impl Future<Output = Result<Option<usize>, Self::Error>> + Send {
async move {
if block_id.is_pending() {
// Pending block can be fetched directly without need for caching
return Ok(LoadBlock::provider(self).pending_block()?.map(|block| block.body.len()))
return Ok(LoadBlock::provider(self)
.pending_block()
.map_err(Self::Error::from_eth_err)?
.map(|block| block.body.len()))
}
let block_hash = match LoadBlock::provider(self).block_hash_for_id(block_id)? {
let block_hash = match LoadBlock::provider(self)
.block_hash_for_id(block_id)
.map_err(Self::Error::from_eth_err)?
{
Some(block_hash) => block_hash,
None => return Ok(None),
};
Ok(self.cache().get_block_transactions(block_hash).await?.map(|txs| txs.len()))
Ok(self
.cache()
.get_block_transactions(block_hash)
.await
.map_err(Self::Error::from_eth_err)?
.map(|txs| txs.len()))
}
}
@ -85,7 +99,7 @@ pub trait EthBlocks: LoadBlock {
fn block_receipts(
&self,
block_id: BlockId,
) -> impl Future<Output = EthResult<Option<Vec<AnyTransactionReceipt>>>> + Send
) -> impl Future<Output = Result<Option<Vec<AnyTransactionReceipt>>, Self::Error>> + Send
where
Self: LoadReceipt,
{
@ -116,8 +130,9 @@ pub trait EthBlocks: LoadBlock {
ReceiptBuilder::new(&tx, meta, receipt, &receipts)
.map(|builder| builder.build())
.map_err(Self::Error::from_eth_err)
})
.collect::<EthResult<Vec<_>>>();
.collect::<Result<Vec<_>, Self::Error>>();
return receipts.map(Some)
}
@ -129,19 +144,26 @@ pub trait EthBlocks: LoadBlock {
fn load_block_and_receipts(
&self,
block_id: BlockId,
) -> impl Future<Output = EthResult<Option<(SealedBlock, Arc<Vec<Receipt>>)>>> + Send
) -> impl Future<Output = Result<Option<(SealedBlock, Arc<Vec<Receipt>>)>, Self::Error>> + Send
where
Self: LoadReceipt,
{
async move {
if block_id.is_pending() {
return Ok(LoadBlock::provider(self)
.pending_block_and_receipts()?
.pending_block_and_receipts()
.map_err(Self::Error::from_eth_err)?
.map(|(sb, receipts)| (sb, Arc::new(receipts))))
}
if let Some(block_hash) = LoadBlock::provider(self).block_hash_for_id(block_id)? {
return Ok(LoadReceipt::cache(self).get_block_and_receipts(block_hash).await?)
if let Some(block_hash) = LoadBlock::provider(self)
.block_hash_for_id(block_id)
.map_err(Self::Error::from_eth_err)?
{
return LoadReceipt::cache(self)
.get_block_and_receipts(block_hash)
.await
.map_err(Self::Error::from_eth_err)
}
Ok(None)
@ -151,8 +173,11 @@ pub trait EthBlocks: LoadBlock {
/// Returns uncle headers of given block.
///
/// Returns an empty vec if there are none.
fn ommers(&self, block_id: BlockId) -> EthResult<Option<Vec<reth_primitives::Header>>> {
Ok(LoadBlock::provider(self).ommers_by_id(block_id)?)
fn ommers(
&self,
block_id: BlockId,
) -> Result<Option<Vec<reth_primitives::Header>>, Self::Error> {
LoadBlock::provider(self).ommers_by_id(block_id).map_err(Self::Error::from_eth_err)
}
/// Returns uncle block at given index in given block.
@ -162,13 +187,18 @@ pub trait EthBlocks: LoadBlock {
&self,
block_id: BlockId,
index: Index,
) -> impl Future<Output = EthResult<Option<RichBlock>>> + Send {
) -> impl Future<Output = Result<Option<RichBlock>, Self::Error>> + Send {
async move {
let uncles = if block_id.is_pending() {
// Pending block can be fetched directly without need for caching
LoadBlock::provider(self).pending_block()?.map(|block| block.ommers)
LoadBlock::provider(self)
.pending_block()
.map_err(Self::Error::from_eth_err)?
.map(|block| block.ommers)
} else {
LoadBlock::provider(self).ommers_by_id(block_id)?
LoadBlock::provider(self)
.ommers_by_id(block_id)
.map_err(Self::Error::from_eth_err)?
}
.unwrap_or_default();
@ -198,7 +228,7 @@ pub trait LoadBlock: LoadPendingBlock + SpawnBlocking {
fn block(
&self,
block_id: BlockId,
) -> impl Future<Output = EthResult<Option<SealedBlock>>> + Send {
) -> impl Future<Output = Result<Option<SealedBlock>, Self::Error>> + Send {
async move {
self.block_with_senders(block_id)
.await
@ -210,12 +240,13 @@ pub trait LoadBlock: LoadPendingBlock + SpawnBlocking {
fn block_with_senders(
&self,
block_id: BlockId,
) -> impl Future<Output = EthResult<Option<SealedBlockWithSenders>>> + Send {
) -> impl Future<Output = Result<Option<SealedBlockWithSenders>, Self::Error>> + Send {
async move {
if block_id.is_pending() {
// Pending block can be fetched directly without need for caching
let maybe_pending =
LoadPendingBlock::provider(self).pending_block_with_senders()?;
let maybe_pending = LoadPendingBlock::provider(self)
.pending_block_with_senders()
.map_err(Self::Error::from_eth_err)?;
return if maybe_pending.is_some() {
Ok(maybe_pending)
} else {
@ -223,12 +254,18 @@ pub trait LoadBlock: LoadPendingBlock + SpawnBlocking {
}
}
let block_hash = match LoadPendingBlock::provider(self).block_hash_for_id(block_id)? {
let block_hash = match LoadPendingBlock::provider(self)
.block_hash_for_id(block_id)
.map_err(Self::Error::from_eth_err)?
{
Some(block_hash) => block_hash,
None => return Ok(None),
};
Ok(self.cache().get_sealed_block_with_senders(block_hash).await?)
self.cache()
.get_sealed_block_with_senders(block_hash)
.await
.map_err(Self::Error::from_eth_err)
}
}
}

View File

@ -2,12 +2,14 @@
//! are executed on the `tokio` runtime.
use futures::Future;
use reth_rpc_eth_types::{EthApiError, EthResult};
use reth_rpc_eth_types::EthApiError;
use reth_tasks::{pool::BlockingTaskPool, TaskSpawner};
use tokio::sync::{oneshot, AcquireError, OwnedSemaphorePermit};
use crate::EthApiTypes;
/// Executes code on a blocking thread.
pub trait SpawnBlocking: Clone + Send + Sync + 'static {
pub trait SpawnBlocking: EthApiTypes + Clone + Send + Sync + 'static {
/// Returns a handle for spawning IO heavy blocking tasks.
///
/// Runtime access in default trait method implementations.
@ -33,9 +35,9 @@ pub trait SpawnBlocking: Clone + Send + Sync + 'static {
///
/// Note: This is expected for futures that are dominated by blocking IO operations, for tracing
/// or CPU bound operations in general use [`spawn_tracing`](Self::spawn_tracing).
fn spawn_blocking_io<F, R>(&self, f: F) -> impl Future<Output = EthResult<R>> + Send
fn spawn_blocking_io<F, R>(&self, f: F) -> impl Future<Output = Result<R, Self::Error>> + Send
where
F: FnOnce(Self) -> EthResult<R> + Send + 'static,
F: FnOnce(Self) -> Result<R, Self::Error> + Send + 'static,
R: Send + 'static,
{
let (tx, rx) = oneshot::channel();
@ -53,9 +55,9 @@ pub trait SpawnBlocking: Clone + Send + Sync + 'static {
/// Note: This is expected for futures that are predominantly CPU bound, as it uses `rayon`
/// under the hood, for blocking IO futures use [`spawn_blocking`](Self::spawn_blocking_io). See
/// <https://ryhl.io/blog/async-what-is-blocking/>.
fn spawn_tracing<F, R>(&self, f: F) -> impl Future<Output = EthResult<R>> + Send
fn spawn_tracing<F, R>(&self, f: F) -> impl Future<Output = Result<R, Self::Error>> + Send
where
F: FnOnce(Self) -> EthResult<R> + Send + 'static,
F: FnOnce(Self) -> Result<R, Self::Error> + Send + 'static,
R: Send + 'static,
{
let this = self.clone();

View File

@ -19,7 +19,7 @@ use reth_rpc_eth_types::{
apply_block_overrides, apply_state_overrides, caller_gas_allowance,
cap_tx_gas_limit_with_caller_allowance, get_precompiles, CallFees,
},
EthApiError, EthResult, RevertError, RpcInvalidTransactionError, StateCacheDb,
EthApiError, RevertError, RpcInvalidTransactionError, StateCacheDb,
};
use reth_rpc_server_types::constants::gas_oracle::{
CALL_STIPEND_GAS, ESTIMATE_GAS_ERROR_RATIO, MIN_TRANSACTION_GAS,
@ -33,6 +33,8 @@ use revm::{Database, DatabaseCommit};
use revm_inspectors::access_list::AccessListInspector;
use tracing::trace;
use crate::{AsEthApiError, FromEthApiError, FromEvmError, IntoEthApiError};
use super::{LoadBlock, LoadPendingBlock, LoadState, LoadTransaction, SpawnBlocking, Trace};
/// Execution related functions for the [`EthApiServer`](crate::EthApiServer) trait in
@ -44,7 +46,7 @@ pub trait EthCall: Call + LoadPendingBlock {
request: TransactionRequest,
at: BlockId,
state_override: Option<StateOverride>,
) -> impl Future<Output = EthResult<U256>> + Send {
) -> impl Future<Output = Result<U256, Self::Error>> + Send {
Call::estimate_gas_at(self, request, at, state_override)
}
@ -54,12 +56,12 @@ pub trait EthCall: Call + LoadPendingBlock {
request: TransactionRequest,
block_number: Option<BlockId>,
overrides: EvmOverrides,
) -> impl Future<Output = EthResult<Bytes>> + Send {
) -> impl Future<Output = Result<Bytes, Self::Error>> + Send {
async move {
let (res, _env) =
self.transact_call_at(request, block_number.unwrap_or_default(), overrides).await?;
ensure_success(res.result)
ensure_success(res.result).map_err(Self::Error::from_eth_err)
}
}
@ -70,14 +72,16 @@ pub trait EthCall: Call + LoadPendingBlock {
bundle: Bundle,
state_context: Option<StateContext>,
mut state_override: Option<StateOverride>,
) -> impl Future<Output = EthResult<Vec<EthCallResponse>>> + Send
) -> impl Future<Output = Result<Vec<EthCallResponse>, Self::Error>> + Send
where
Self: LoadBlock,
{
async move {
let Bundle { transactions, block_override } = bundle;
if transactions.is_empty() {
return Err(EthApiError::InvalidParams(String::from("transactions are empty.")))
return Err(
EthApiError::InvalidParams(String::from("transactions are empty.")).into()
)
}
let StateContext { transaction_index, block_number } =
@ -92,7 +96,7 @@ pub trait EthCall: Call + LoadPendingBlock {
self.block_with_senders(target_block)
)?;
let Some(block) = block else { return Err(EthApiError::UnknownBlockNumber) };
let Some(block) = block else { return Err(EthApiError::UnknownBlockNumber.into()) };
let gas_limit = self.call_gas_limit();
// we're essentially replaying the transactions in the block here, hence we need the
@ -138,14 +142,16 @@ pub trait EthCall: Call + LoadPendingBlock {
let state_overrides = state_override.take();
let overrides = EvmOverrides::new(state_overrides, block_overrides.clone());
let env = this.prepare_call_env(
cfg.clone(),
block_env.clone(),
tx,
gas_limit,
&mut db,
overrides,
)?;
let env = this
.prepare_call_env(
cfg.clone(),
block_env.clone(),
tx,
gas_limit,
&mut db,
overrides,
)
.map(Into::into)?;
let (res, _) = this.transact(&mut db, env)?;
match ensure_success(res.result) {
@ -179,7 +185,7 @@ pub trait EthCall: Call + LoadPendingBlock {
&self,
request: TransactionRequest,
block_number: Option<BlockId>,
) -> impl Future<Output = EthResult<AccessListWithGasUsed>> + Send
) -> impl Future<Output = Result<AccessListWithGasUsed, Self::Error>> + Send
where
Self: Trace,
{
@ -202,7 +208,7 @@ pub trait EthCall: Call + LoadPendingBlock {
block: BlockEnv,
at: BlockId,
mut request: TransactionRequest,
) -> EthResult<AccessListWithGasUsed>
) -> Result<AccessListWithGasUsed, Self::Error>
where
Self: Trace,
{
@ -230,7 +236,8 @@ pub trait EthCall: Call + LoadPendingBlock {
let to = if let Some(TxKind::Call(to)) = request.to {
to
} else {
let nonce = db.basic_ref(from)?.unwrap_or_default().nonce;
let nonce =
db.basic_ref(from).map_err(Self::Error::from_eth_err)?.unwrap_or_default().nonce;
from.create(nonce)
};
@ -250,7 +257,8 @@ pub trait EthCall: Call + LoadPendingBlock {
Err(RpcInvalidTransactionError::Revert(RevertError::new(output)))
}
ExecutionResult::Success { .. } => Ok(()),
}?;
}
.map_err(Self::Error::from_eth_err)?;
let access_list = inspector.into_access_list();
@ -279,9 +287,9 @@ pub trait Call: LoadState + SpawnBlocking {
fn evm_config(&self) -> &impl ConfigureEvm;
/// Executes the closure with the state that corresponds to the given [`BlockId`].
fn with_state_at_block<F, T>(&self, at: BlockId, f: F) -> EthResult<T>
fn with_state_at_block<F, R>(&self, at: BlockId, f: F) -> Result<R, Self::Error>
where
F: FnOnce(StateProviderTraitObjWrapper<'_>) -> EthResult<T>,
F: FnOnce(StateProviderTraitObjWrapper<'_>) -> Result<R, Self::Error>,
{
let state = self.state_at_block_id(at)?;
f(StateProviderTraitObjWrapper(&state))
@ -293,13 +301,13 @@ pub trait Call: LoadState + SpawnBlocking {
&self,
db: DB,
env: EnvWithHandlerCfg,
) -> EthResult<(ResultAndState, EnvWithHandlerCfg)>
) -> Result<(ResultAndState, EnvWithHandlerCfg), Self::Error>
where
DB: Database,
<DB as Database>::Error: Into<EthApiError>,
EthApiError: From<DB::Error>,
{
let mut evm = self.evm_config().evm_with_env(db, env);
let res = evm.transact()?;
let res = evm.transact().map_err(Self::Error::from_evm_err)?;
let (_, env) = evm.into_db_and_env_with_handler_cfg();
Ok((res, env))
}
@ -310,7 +318,7 @@ pub trait Call: LoadState + SpawnBlocking {
request: TransactionRequest,
at: BlockId,
overrides: EvmOverrides,
) -> impl Future<Output = EthResult<(ResultAndState, EnvWithHandlerCfg)>> + Send
) -> impl Future<Output = Result<(ResultAndState, EnvWithHandlerCfg), Self::Error>> + Send
where
Self: LoadPendingBlock,
{
@ -319,14 +327,14 @@ pub trait Call: LoadState + SpawnBlocking {
}
/// Executes the closure with the state that corresponds to the given [`BlockId`] on a new task
fn spawn_with_state_at_block<F, T>(
fn spawn_with_state_at_block<F, R>(
&self,
at: BlockId,
f: F,
) -> impl Future<Output = EthResult<T>> + Send
) -> impl Future<Output = Result<R, Self::Error>> + Send
where
F: FnOnce(StateProviderTraitObjWrapper<'_>) -> EthResult<T> + Send + 'static,
T: Send + 'static,
F: FnOnce(StateProviderTraitObjWrapper<'_>) -> Result<R, Self::Error> + Send + 'static,
R: Send + 'static,
{
self.spawn_tracing(move |this| {
let state = this.state_at_block_id(at)?;
@ -345,10 +353,10 @@ pub trait Call: LoadState + SpawnBlocking {
at: BlockId,
overrides: EvmOverrides,
f: F,
) -> impl Future<Output = EthResult<R>> + Send
) -> impl Future<Output = Result<R, Self::Error>> + Send
where
Self: LoadPendingBlock,
F: FnOnce(StateCacheDbRefMutWrapper<'_, '_>, EnvWithHandlerCfg) -> EthResult<R>
F: FnOnce(StateCacheDbRefMutWrapper<'_, '_>, EnvWithHandlerCfg) -> Result<R, Self::Error>
+ Send
+ 'static,
R: Send + 'static,
@ -373,7 +381,7 @@ pub trait Call: LoadState + SpawnBlocking {
f(StateCacheDbRefMutWrapper(&mut db), env)
})
.await
.map_err(|_| EthApiError::InternalBlockingTaskError)
.map_err(|_| EthApiError::InternalBlockingTaskError.into())
}
}
@ -390,10 +398,10 @@ pub trait Call: LoadState + SpawnBlocking {
&self,
hash: B256,
f: F,
) -> impl Future<Output = EthResult<Option<R>>> + Send
) -> impl Future<Output = Result<Option<R>, Self::Error>> + Send
where
Self: LoadBlock + LoadPendingBlock + LoadTransaction,
F: FnOnce(TransactionInfo, ResultAndState, StateCacheDb<'_>) -> EthResult<R>
F: FnOnce(TransactionInfo, ResultAndState, StateCacheDb<'_>) -> Result<R, Self::Error>
+ Send
+ 'static,
R: Send + 'static,
@ -453,10 +461,10 @@ pub trait Call: LoadState + SpawnBlocking {
block_env: BlockEnv,
transactions: impl IntoIterator<Item = TransactionSignedEcRecovered>,
target_tx_hash: B256,
) -> Result<usize, EthApiError>
) -> Result<usize, Self::Error>
where
DB: DatabaseRef,
EthApiError: From<<DB as DatabaseRef>::Error>,
EthApiError: From<DB::Error>,
{
let env = EnvWithHandlerCfg::new_with_cfg_env(cfg, block_env, Default::default());
@ -470,7 +478,7 @@ pub trait Call: LoadState + SpawnBlocking {
let sender = tx.signer();
self.evm_config().fill_tx_env(evm.tx_mut(), &tx.into_signed(), sender);
evm.transact_commit()?;
evm.transact_commit().map_err(Self::Error::from_evm_err)?;
index += 1;
}
Ok(index)
@ -482,7 +490,7 @@ pub trait Call: LoadState + SpawnBlocking {
request: TransactionRequest,
at: BlockId,
state_override: Option<StateOverride>,
) -> impl Future<Output = EthResult<U256>> + Send
) -> impl Future<Output = Result<U256, Self::Error>> + Send
where
Self: LoadPendingBlock,
{
@ -507,7 +515,7 @@ pub trait Call: LoadState + SpawnBlocking {
request: TransactionRequest,
state: S,
state_override: Option<StateOverride>,
) -> EthResult<U256>
) -> Result<U256, Self::Error>
where
S: StateProvider,
{
@ -537,7 +545,7 @@ pub trait Call: LoadState + SpawnBlocking {
// Apply any state overrides if specified.
if let Some(state_override) = state_override {
apply_state_overrides(state_override, &mut db)?;
apply_state_overrides(state_override, &mut db).map_err(Self::Error::from_eth_err)?;
}
// Optimize for simple transfer transactions, potentially reducing the gas estimate.
@ -568,7 +576,8 @@ pub trait Call: LoadState + SpawnBlocking {
// The caller allowance is check by doing `(account.balance - tx.value) / tx.gas_price`
if env.tx.gas_price > U256::ZERO {
// cap the highest gas limit by max gas caller can afford with given gas price
highest_gas_limit = highest_gas_limit.min(caller_gas_allowance(&mut db, &env.tx)?);
highest_gas_limit = highest_gas_limit
.min(caller_gas_allowance(&mut db, &env.tx).map_err(Self::Error::from_eth_err)?);
}
// We can now normalize the highest gas limit to a u64
@ -586,8 +595,9 @@ pub trait Call: LoadState + SpawnBlocking {
// If the gas price or gas limit was specified in the request, retry the transaction
// with the block's gas limit to determine if the failure was due to
// insufficient gas.
Err(EthApiError::InvalidTransaction(RpcInvalidTransactionError::GasTooHigh))
if tx_request_gas_limit.is_some() || tx_request_gas_price.is_some() =>
Err(err)
if err.is_gas_too_high() &&
(tx_request_gas_limit.is_some() || tx_request_gas_price.is_some()) =>
{
return Err(self.map_out_of_gas_err(block_env_gas_limit, env, &mut db))
}
@ -600,7 +610,7 @@ pub trait Call: LoadState + SpawnBlocking {
ExecutionResult::Halt { reason, gas_used } => {
// here we don't check for invalid opcode because already executed with highest gas
// limit
return Err(RpcInvalidTransactionError::halt(reason, gas_used).into())
return Err(RpcInvalidTransactionError::halt(reason, gas_used).into_eth_err())
}
ExecutionResult::Revert { output, .. } => {
// if price or limit was included in the request then we can execute the request
@ -609,7 +619,7 @@ pub trait Call: LoadState + SpawnBlocking {
Err(self.map_out_of_gas_err(block_env_gas_limit, env, &mut db))
} else {
// the transaction did revert
Err(RpcInvalidTransactionError::Revert(RevertError::new(output)).into())
Err(RpcInvalidTransactionError::Revert(RevertError::new(output)).into_eth_err())
}
}
};
@ -675,8 +685,7 @@ pub trait Call: LoadState + SpawnBlocking {
// Execute transaction and handle potential gas errors, adjusting limits accordingly.
match self.transact(&mut db, env.clone()) {
// Check if the error is due to gas being too high.
Err(EthApiError::InvalidTransaction(RpcInvalidTransactionError::GasTooHigh)) => {
Err(err) if err.is_gas_too_high() => {
// Increase the lowest gas limit if gas is too high
lowest_gas_limit = mid_gas_limit;
}
@ -713,7 +722,7 @@ pub trait Call: LoadState + SpawnBlocking {
tx_gas_limit: u64,
highest_gas_limit: &mut u64,
lowest_gas_limit: &mut u64,
) -> EthResult<()> {
) -> Result<(), Self::Error> {
match result {
ExecutionResult::Success { .. } => {
// Cap the highest gas limit with the succeeding gas limit.
@ -741,7 +750,7 @@ pub trait Call: LoadState + SpawnBlocking {
// These cases should be unreachable because we know the transaction
// succeeds, but if they occur, treat them as an
// error.
return Err(RpcInvalidTransactionError::EvmHalt(err).into())
return Err(RpcInvalidTransactionError::EvmHalt(err).into_eth_err())
}
}
}
@ -758,7 +767,7 @@ pub trait Call: LoadState + SpawnBlocking {
env_gas_limit: U256,
mut env: EnvWithHandlerCfg,
db: &mut CacheDB<StateProviderDatabase<S>>,
) -> EthApiError
) -> Self::Error
where
S: StateProvider,
{
@ -772,14 +781,14 @@ pub trait Call: LoadState + SpawnBlocking {
ExecutionResult::Success { .. } => {
// transaction succeeded by manually increasing the gas limit to
// highest, which means the caller lacks funds to pay for the tx
RpcInvalidTransactionError::BasicOutOfGas(req_gas_limit).into()
RpcInvalidTransactionError::BasicOutOfGas(req_gas_limit).into_eth_err()
}
ExecutionResult::Revert { output, .. } => {
// reverted again after bumping the limit
RpcInvalidTransactionError::Revert(RevertError::new(output)).into()
RpcInvalidTransactionError::Revert(RevertError::new(output)).into_eth_err()
}
ExecutionResult::Halt { reason, .. } => {
RpcInvalidTransactionError::EvmHalt(reason).into()
RpcInvalidTransactionError::EvmHalt(reason).into_eth_err()
}
}
}
@ -792,10 +801,10 @@ pub trait Call: LoadState + SpawnBlocking {
&self,
block_env: &BlockEnv,
request: TransactionRequest,
) -> EthResult<TxEnv> {
) -> Result<TxEnv, Self::Error> {
// Ensure that if versioned hashes are set, they're not empty
if request.blob_versioned_hashes.as_ref().map_or(false, |hashes| hashes.is_empty()) {
return Err(RpcInvalidTransactionError::BlobTransactionMissingBlobHashes.into())
return Err(RpcInvalidTransactionError::BlobTransactionMissingBlobHashes.into_eth_err())
}
let TransactionRequest {
@ -833,14 +842,18 @@ pub trait Call: LoadState + SpawnBlocking {
let env = TxEnv {
gas_limit: gas_limit
.try_into()
.map_err(|_| RpcInvalidTransactionError::GasUintOverflow)?,
.map_err(|_| RpcInvalidTransactionError::GasUintOverflow)
.map_err(Self::Error::from_eth_err)?,
nonce,
caller: from.unwrap_or_default(),
gas_price,
gas_priority_fee: max_priority_fee_per_gas,
transact_to: to.unwrap_or(TxKind::Create),
value: value.unwrap_or_default(),
data: input.try_into_unique_input()?.unwrap_or_default(),
data: input
.try_into_unique_input()
.map_err(Self::Error::from_eth_err)?
.unwrap_or_default(),
chain_id,
access_list: access_list.unwrap_or_default().into(),
// EIP-4844 fields
@ -863,7 +876,7 @@ pub trait Call: LoadState + SpawnBlocking {
cfg: CfgEnvWithHandlerCfg,
block: BlockEnv,
request: TransactionRequest,
) -> EthResult<EnvWithHandlerCfg> {
) -> Result<EnvWithHandlerCfg, Self::Error> {
let tx = self.create_txn_env(&block, request)?;
Ok(EnvWithHandlerCfg::new_with_cfg_env(cfg, block, tx))
}
@ -885,7 +898,7 @@ pub trait Call: LoadState + SpawnBlocking {
gas_limit: u64,
db: &mut CacheDB<DB>,
overrides: EvmOverrides,
) -> EthResult<EnvWithHandlerCfg>
) -> Result<EnvWithHandlerCfg, Self::Error>
where
DB: DatabaseRef,
EthApiError: From<<DB as DatabaseRef>::Error>,

View File

@ -0,0 +1,88 @@
//! Helper traits to wrap generic l1 errors, in network specific error type configured in
//! [`EthApiTypes`](crate::EthApiTypes).
use reth_rpc_eth_types::EthApiError;
use revm_primitives::EVMError;
/// Helper trait to wrap core [`EthApiError`].
pub trait FromEthApiError: From<EthApiError> {
/// Converts from error via [`EthApiError`].
fn from_eth_err<E>(err: E) -> Self
where
EthApiError: From<E>;
}
impl<T> FromEthApiError for T
where
T: From<EthApiError>,
{
fn from_eth_err<E>(err: E) -> Self
where
EthApiError: From<E>,
{
T::from(EthApiError::from(err))
}
}
/// Helper trait to wrap core [`EthApiError`].
pub trait IntoEthApiError: Into<EthApiError> {
/// Converts into error via [`EthApiError`].
fn into_eth_err<E>(self) -> E
where
E: FromEthApiError;
}
impl<T> IntoEthApiError for T
where
EthApiError: From<T>,
{
fn into_eth_err<E>(self) -> E
where
E: FromEthApiError,
{
E::from_eth_err(self)
}
}
/// Helper trait to access wrapped core error.
pub trait AsEthApiError {
/// Returns reference to [`EthApiError`], if this an error variant inherited from core
/// functionality.
fn as_err(&self) -> Option<&EthApiError>;
/// Returns `true` if error is
/// [`RpcInvalidTransactionError::GasTooHigh`](reth_rpc_eth_types::RpcInvalidTransactionError::GasTooHigh).
fn is_gas_too_high(&self) -> bool {
if let Some(err) = self.as_err() {
return err.is_gas_too_high()
}
false
}
}
impl AsEthApiError for EthApiError {
fn as_err(&self) -> Option<&EthApiError> {
Some(self)
}
}
/// Helper trait to convert from revm errors.
pub trait FromEvmError: From<EthApiError> {
/// Converts from a revm error.
fn from_evm_err<E>(err: EVMError<E>) -> Self
where
EthApiError: From<E>;
}
impl<T> FromEvmError for T
where
T: From<EthApiError>,
{
fn from_evm_err<E>(err: EVMError<E>) -> Self
where
EthApiError: From<E>,
{
err.into_eth_err()
}
}

View File

@ -4,12 +4,14 @@ use futures::Future;
use reth_primitives::U256;
use reth_provider::{BlockIdReader, BlockReaderIdExt, ChainSpecProvider, HeaderProvider};
use reth_rpc_eth_types::{
fee_history::calculate_reward_percentiles_for_block, EthApiError, EthResult, EthStateCache,
fee_history::calculate_reward_percentiles_for_block, EthApiError, EthStateCache,
FeeHistoryCache, FeeHistoryEntry, GasPriceOracle, RpcInvalidTransactionError,
};
use reth_rpc_types::{BlockNumberOrTag, FeeHistory};
use tracing::debug;
use crate::FromEthApiError;
use super::LoadBlock;
/// Fee related functions for the [`EthApiServer`](crate::EthApiServer) trait in the
@ -18,7 +20,7 @@ pub trait EthFees: LoadFee {
/// Returns a suggestion for a gas price for legacy transactions.
///
/// See also: <https://github.com/ethereum/pm/issues/328#issuecomment-853234014>
fn gas_price(&self) -> impl Future<Output = EthResult<U256>> + Send
fn gas_price(&self) -> impl Future<Output = Result<U256, Self::Error>> + Send
where
Self: LoadBlock,
{
@ -26,7 +28,7 @@ pub trait EthFees: LoadFee {
}
/// Returns a suggestion for a base fee for blob transactions.
fn blob_base_fee(&self) -> impl Future<Output = EthResult<U256>> + Send
fn blob_base_fee(&self) -> impl Future<Output = Result<U256, Self::Error>> + Send
where
Self: LoadBlock,
{
@ -34,7 +36,7 @@ pub trait EthFees: LoadFee {
}
/// Returns a suggestion for the priority fee (the tip)
fn suggested_priority_fee(&self) -> impl Future<Output = EthResult<U256>> + Send
fn suggested_priority_fee(&self) -> impl Future<Output = Result<U256, Self::Error>> + Send
where
Self: 'static,
{
@ -50,7 +52,7 @@ pub trait EthFees: LoadFee {
mut block_count: u64,
newest_block: BlockNumberOrTag,
reward_percentiles: Option<Vec<f64>>,
) -> impl Future<Output = EthResult<FeeHistory>> + Send {
) -> impl Future<Output = Result<FeeHistory, Self::Error>> + Send {
async move {
if block_count == 0 {
return Ok(FeeHistory::default())
@ -72,10 +74,11 @@ pub trait EthFees: LoadFee {
block_count = max_fee_history
}
let Some(end_block) =
LoadFee::provider(self).block_number_for_id(newest_block.into())?
let Some(end_block) = LoadFee::provider(self)
.block_number_for_id(newest_block.into())
.map_err(Self::Error::from_eth_err)?
else {
return Err(EthApiError::UnknownBlockNumber)
return Err(EthApiError::UnknownBlockNumber.into())
};
// need to add 1 to the end block to get the correct (inclusive) range
@ -91,7 +94,7 @@ pub trait EthFees: LoadFee {
// Note: The types used ensure that the percentiles are never < 0
if let Some(percentiles) = &reward_percentiles {
if percentiles.windows(2).any(|w| w[0] > w[1] || w[0] > 100.) {
return Err(EthApiError::InvalidRewardPercentiles)
return Err(EthApiError::InvalidRewardPercentiles.into())
}
}
@ -116,7 +119,7 @@ pub trait EthFees: LoadFee {
if let Some(fee_entries) = fee_entries {
if fee_entries.len() != block_count as usize {
return Err(EthApiError::InvalidBlockRange)
return Err(EthApiError::InvalidBlockRange.into())
}
for entry in &fee_entries {
@ -144,9 +147,9 @@ pub trait EthFees: LoadFee {
base_fee_per_blob_gas.push(last_entry.next_block_blob_fee().unwrap_or_default());
} else {
// read the requested header range
let headers = LoadFee::provider(self).sealed_headers_range(start_block..=end_block)?;
let headers = LoadFee::provider(self).sealed_headers_range(start_block..=end_block).map_err(Self::Error::from_eth_err)?;
if headers.len() != block_count as usize {
return Err(EthApiError::InvalidBlockRange)
return Err(EthApiError::InvalidBlockRange.into())
}
for header in &headers {
@ -162,7 +165,7 @@ pub trait EthFees: LoadFee {
if let Some(percentiles) = &reward_percentiles {
let (transactions, receipts) = LoadFee::cache(self)
.get_transactions_and_receipts(header.hash())
.await?
.await.map_err(Self::Error::from_eth_err)?
.ok_or(EthApiError::InvalidBlockRange)?;
rewards.push(
calculate_reward_percentiles_for_block(
@ -251,7 +254,7 @@ pub trait LoadFee: LoadBlock {
fn legacy_gas_price(
&self,
gas_price: Option<U256>,
) -> impl Future<Output = EthResult<U256>> + Send {
) -> impl Future<Output = Result<U256, Self::Error>> + Send {
async move {
match gas_price {
Some(gas_price) => Ok(gas_price),
@ -271,7 +274,7 @@ pub trait LoadFee: LoadBlock {
&self,
max_fee_per_gas: Option<U256>,
max_priority_fee_per_gas: Option<U256>,
) -> impl Future<Output = EthResult<(U256, U256)>> + Send {
) -> impl Future<Output = Result<(U256, U256), Self::Error>> + Send {
async move {
let max_fee_per_gas = match max_fee_per_gas {
Some(max_fee_per_gas) => max_fee_per_gas,
@ -303,7 +306,7 @@ pub trait LoadFee: LoadBlock {
fn eip4844_blob_fee(
&self,
blob_fee: Option<U256>,
) -> impl Future<Output = EthResult<U256>> + Send {
) -> impl Future<Output = Result<U256, Self::Error>> + Send {
async move {
match blob_fee {
Some(blob_fee) => Ok(blob_fee),
@ -315,7 +318,7 @@ pub trait LoadFee: LoadBlock {
/// Returns a suggestion for a gas price for legacy transactions.
///
/// See also: <https://github.com/ethereum/pm/issues/328#issuecomment-853234014>
fn gas_price(&self) -> impl Future<Output = EthResult<U256>> + Send {
fn gas_price(&self) -> impl Future<Output = Result<U256, Self::Error>> + Send {
let header = self.block(BlockNumberOrTag::Latest.into());
let suggested_tip = self.suggested_priority_fee();
async move {
@ -326,21 +329,21 @@ pub trait LoadFee: LoadBlock {
}
/// Returns a suggestion for a base fee for blob transactions.
fn blob_base_fee(&self) -> impl Future<Output = EthResult<U256>> + Send {
fn blob_base_fee(&self) -> impl Future<Output = Result<U256, Self::Error>> + Send {
async move {
self.block(BlockNumberOrTag::Latest.into())
.await?
.and_then(|h: reth_primitives::SealedBlock| h.next_block_blob_fee())
.ok_or(EthApiError::ExcessBlobGasNotSet)
.ok_or(EthApiError::ExcessBlobGasNotSet.into())
.map(U256::from)
}
}
/// Returns a suggestion for the priority fee (the tip)
fn suggested_priority_fee(&self) -> impl Future<Output = EthResult<U256>> + Send
fn suggested_priority_fee(&self) -> impl Future<Output = Result<U256, Self::Error>> + Send
where
Self: 'static,
{
self.gas_oracle().suggest_tip_cap()
async move { self.gas_oracle().suggest_tip_cap().await.map_err(Self::Error::from_eth_err) }
}
}

View File

@ -17,6 +17,7 @@
pub mod block;
pub mod blocking_task;
pub mod call;
pub mod error;
pub mod fee;
pub mod pending_block;
pub mod receipt;
@ -25,6 +26,7 @@ pub mod spec;
pub mod state;
pub mod trace;
pub mod transaction;
pub mod types;
pub use block::{EthBlocks, LoadBlock};
pub use blocking_task::SpawnBlocking;
@ -38,6 +40,8 @@ pub use state::{EthState, LoadState};
pub use trace::Trace;
pub use transaction::{EthTransactions, LoadTransaction, UpdateRawTxForwarder};
use crate::EthApiTypes;
/// Extension trait that bundles traits needed for tracing transactions.
pub trait TraceExt:
LoadTransaction + LoadBlock + LoadPendingBlock + SpawnBlocking + Trace + Call
@ -50,12 +54,21 @@ impl<T> TraceExt for T where T: LoadTransaction + LoadBlock + LoadPendingBlock +
///
/// This trait is automatically implemented for any type that implements all the `Eth` traits.
pub trait FullEthApi:
EthApiSpec + EthTransactions + EthBlocks + EthState + EthCall + EthFees + Trace + LoadReceipt
EthApiTypes
+ EthApiSpec
+ EthTransactions
+ EthBlocks
+ EthState
+ EthCall
+ EthFees
+ Trace
+ LoadReceipt
{
}
impl<T> FullEthApi for T where
T: EthApiSpec
T: EthApiTypes
+ EthApiSpec
+ EthTransactions
+ EthBlocks
+ EthState

View File

@ -19,27 +19,29 @@ use reth_primitives::{
EMPTY_OMMER_ROOT_HASH, U256,
};
use reth_provider::{
BlockReader, BlockReaderIdExt, ChainSpecProvider, EvmEnvProvider, StateProviderFactory,
BlockReader, BlockReaderIdExt, ChainSpecProvider, EvmEnvProvider, ProviderError,
StateProviderFactory,
};
use reth_revm::{
database::StateProviderDatabase, state_change::post_block_withdrawals_balance_increments,
};
use reth_rpc_eth_types::{
pending_block::pre_block_blockhashes_update, EthApiError, EthResult, PendingBlock,
PendingBlockEnv, PendingBlockEnvOrigin,
pending_block::pre_block_blockhashes_update, EthApiError, PendingBlock, PendingBlockEnv,
PendingBlockEnvOrigin,
};
use reth_transaction_pool::{BestTransactionsAttributes, TransactionPool};
use revm::{db::states::bundle_state::BundleRetention, DatabaseCommit, State};
use tokio::sync::Mutex;
use tracing::debug;
use crate::{EthApiTypes, FromEthApiError, FromEvmError};
use super::SpawnBlocking;
/// Loads a pending block from database.
///
/// Behaviour shared by several `eth_` RPC methods, not exclusive to `eth_` blocks RPC methods.
#[auto_impl::auto_impl(&, Arc)]
pub trait LoadPendingBlock {
pub trait LoadPendingBlock: EthApiTypes {
/// Returns a handle for reading data from disk.
///
/// Data access in default (L1) trait method implementations.
@ -65,16 +67,19 @@ pub trait LoadPendingBlock {
/// Configures the [`CfgEnvWithHandlerCfg`] and [`BlockEnv`] for the pending block
///
/// If no pending block is available, this will derive it from the `latest` block
fn pending_block_env_and_cfg(&self) -> EthResult<PendingBlockEnv> {
fn pending_block_env_and_cfg(&self) -> Result<PendingBlockEnv, Self::Error> {
let origin: PendingBlockEnvOrigin = if let Some(pending) =
self.provider().pending_block_with_senders()?
self.provider().pending_block_with_senders().map_err(Self::Error::from_eth_err)?
{
PendingBlockEnvOrigin::ActualPending(pending)
} else {
// no pending block from the CL yet, so we use the latest block and modify the env
// values that we can
let latest =
self.provider().latest_header()?.ok_or_else(|| EthApiError::UnknownBlockNumber)?;
let latest = self
.provider()
.latest_header()
.map_err(Self::Error::from_eth_err)?
.ok_or_else(|| EthApiError::UnknownBlockNumber)?;
let (mut latest_header, block_hash) = latest.split();
// child block
@ -102,12 +107,14 @@ pub trait LoadPendingBlock {
let mut block_env = BlockEnv::default();
// Note: for the PENDING block we assume it is past the known merge block and thus this will
// not fail when looking up the total difficulty value for the blockenv.
self.provider().fill_env_with_header(
&mut cfg,
&mut block_env,
origin.header(),
self.evm_config().clone(),
)?;
self.provider()
.fill_env_with_header(
&mut cfg,
&mut block_env,
origin.header(),
self.evm_config().clone(),
)
.map_err(Self::Error::from_eth_err)?;
Ok(PendingBlockEnv::new(cfg, block_env, origin))
}
@ -115,7 +122,7 @@ pub trait LoadPendingBlock {
/// Returns the locally built pending block
fn local_pending_block(
&self,
) -> impl Future<Output = EthResult<Option<SealedBlockWithSenders>>> + Send
) -> impl Future<Output = Result<Option<SealedBlockWithSenders>, Self::Error>> + Send
where
Self: SpawnBlocking,
{
@ -197,11 +204,17 @@ pub trait LoadPendingBlock {
///
/// After Cancun, if the origin is the actual pending block, the block includes the EIP-4788 pre
/// block contract call using the parent beacon block root received from the CL.
fn build_block(&self, env: PendingBlockEnv) -> EthResult<SealedBlockWithSenders> {
fn build_block(&self, env: PendingBlockEnv) -> Result<SealedBlockWithSenders, Self::Error>
where
EthApiError: From<ProviderError>,
{
let PendingBlockEnv { cfg, block_env, origin } = env;
let parent_hash = origin.build_target_hash();
let state_provider = self.provider().history_by_block_hash(parent_hash)?;
let state_provider = self
.provider()
.history_by_block_hash(parent_hash)
.map_err(Self::Error::from_eth_err)?;
let state = StateProviderDatabase::new(state_provider);
let mut db = State::builder().with_database(state).with_bundle_update().build();
@ -316,7 +329,7 @@ pub trait LoadPendingBlock {
}
err => {
// this is an error that we should treat as fatal for this attempt
return Err(err.into())
return Err(Self::Error::from_evm_err(err))
}
}
}
@ -359,7 +372,7 @@ pub trait LoadPendingBlock {
);
// increment account balances for withdrawals
db.increment_balances(balance_increments)?;
db.increment_balances(balance_increments).map_err(Self::Error::from_eth_err)?;
// merge all transitions into bundle state.
db.merge_transitions(BundleRetention::PlainState);
@ -378,7 +391,9 @@ pub trait LoadPendingBlock {
// calculate the state root
let state_provider = &db.database;
let state_root = state_provider.state_root(execution_outcome.state())?;
let state_root = state_provider
.state_root(execution_outcome.state())
.map_err(Self::Error::from_eth_err)?;
// create the block header
let transactions_root = calculate_transaction_root(&executed_txs);

View File

@ -3,14 +3,15 @@
use futures::Future;
use reth_primitives::{Receipt, TransactionMeta, TransactionSigned};
use reth_rpc_eth_types::{EthApiError, EthResult, EthStateCache, ReceiptBuilder};
use reth_rpc_eth_types::{EthApiError, EthStateCache, ReceiptBuilder};
use reth_rpc_types::AnyTransactionReceipt;
use crate::{EthApiTypes, FromEthApiError};
/// Assembles transaction receipt data w.r.t to network.
///
/// Behaviour shared by several `eth_` RPC methods, not exclusive to `eth_` receipts RPC methods.
#[auto_impl::auto_impl(&, Arc)]
pub trait LoadReceipt: Send + Sync {
pub trait LoadReceipt: EthApiTypes + Send + Sync {
/// Returns a handle for reading data from memory.
///
/// Data access in default (L1) trait method implementations.
@ -22,12 +23,17 @@ pub trait LoadReceipt: Send + Sync {
tx: TransactionSigned,
meta: TransactionMeta,
receipt: Receipt,
) -> impl Future<Output = EthResult<AnyTransactionReceipt>> + Send {
) -> impl Future<Output = Result<AnyTransactionReceipt, Self::Error>> + Send {
async move {
// get all receipts for the block
let all_receipts = match self.cache().get_receipts(meta.block_hash).await? {
let all_receipts = match self
.cache()
.get_receipts(meta.block_hash)
.await
.map_err(Self::Error::from_eth_err)?
{
Some(recpts) => recpts,
None => return Err(EthApiError::UnknownBlockNumber),
None => return Err(EthApiError::UnknownBlockNumber.into()),
};
Ok(ReceiptBuilder::new(&tx, meta, &receipt, &all_receipts)?.build())

View File

@ -8,15 +8,15 @@ use reth_primitives::{Address, BlockId, Bytes, Header, B256, U256};
use reth_provider::{
BlockIdReader, ChainSpecProvider, StateProvider, StateProviderBox, StateProviderFactory,
};
use reth_rpc_eth_types::{
EthApiError, EthResult, EthStateCache, PendingBlockEnv, RpcInvalidTransactionError,
};
use reth_rpc_eth_types::{EthApiError, EthStateCache, PendingBlockEnv, RpcInvalidTransactionError};
use reth_rpc_types::{serde_helpers::JsonStorageKey, EIP1186AccountProofResponse};
use reth_rpc_types_compat::proof::from_primitive_account_proof;
use reth_transaction_pool::{PoolTransaction, TransactionPool};
use revm::db::BundleState;
use revm_primitives::{BlockEnv, CfgEnvWithHandlerCfg, SpecId};
use crate::{EthApiTypes, FromEthApiError};
use super::{EthApiSpec, LoadPendingBlock, SpawnBlocking};
/// Helper methods for `eth_` methods relating to state (accounts).
@ -32,7 +32,7 @@ pub trait EthState: LoadState + SpawnBlocking {
&self,
address: Address,
block_id: Option<BlockId>,
) -> impl Future<Output = EthResult<U256>> + Send {
) -> impl Future<Output = Result<U256, Self::Error>> + Send {
LoadState::transaction_count(self, address, block_id)
}
@ -41,11 +41,12 @@ pub trait EthState: LoadState + SpawnBlocking {
&self,
address: Address,
block_id: Option<BlockId>,
) -> impl Future<Output = EthResult<Bytes>> + Send {
) -> 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)?
.account_code(address)
.map_err(Self::Error::from_eth_err)?
.unwrap_or_default()
.original_bytes())
})
@ -56,11 +57,12 @@ pub trait EthState: LoadState + SpawnBlocking {
&self,
address: Address,
block_id: Option<BlockId>,
) -> impl Future<Output = EthResult<U256>> + Send {
) -> impl Future<Output = Result<U256, Self::Error>> + Send {
self.spawn_blocking_io(move |this| {
Ok(this
.state_at_block_id_or_latest(block_id)?
.account_balance(address)?
.account_balance(address)
.map_err(Self::Error::from_eth_err)?
.unwrap_or_default())
})
}
@ -71,11 +73,12 @@ pub trait EthState: LoadState + SpawnBlocking {
address: Address,
index: JsonStorageKey,
block_id: Option<BlockId>,
) -> impl Future<Output = EthResult<B256>> + Send {
) -> impl Future<Output = Result<B256, Self::Error>> + Send {
self.spawn_blocking_io(move |this| {
Ok(B256::new(
this.state_at_block_id_or_latest(block_id)?
.storage(address, index.0)?
.storage(address, index.0)
.map_err(Self::Error::from_eth_err)?
.unwrap_or_default()
.to_be_bytes(),
))
@ -88,21 +91,25 @@ pub trait EthState: LoadState + SpawnBlocking {
address: Address,
keys: Vec<JsonStorageKey>,
block_id: Option<BlockId>,
) -> EthResult<impl Future<Output = EthResult<EIP1186AccountProofResponse>> + Send>
) -> Result<
impl Future<Output = Result<EIP1186AccountProofResponse, Self::Error>> + Send,
Self::Error,
>
where
Self: EthApiSpec,
{
let chain_info = self.chain_info()?;
let chain_info = self.chain_info().map_err(Self::Error::from_eth_err)?;
let block_id = block_id.unwrap_or_default();
// Check whether the distance to the block exceeds the maximum configured window.
let block_number = self
.provider()
.block_number_for_id(block_id)?
.block_number_for_id(block_id)
.map_err(Self::Error::from_eth_err)?
.ok_or(EthApiError::UnknownBlockNumber)?;
let max_window = self.max_proof_window();
if chain_info.best_number.saturating_sub(block_number) > max_window {
return Err(EthApiError::ExceedsMaxProofWindow)
return Err(EthApiError::ExceedsMaxProofWindow.into())
}
Ok(async move {
@ -113,7 +120,9 @@ pub trait EthState: LoadState + SpawnBlocking {
self.spawn_blocking_io(move |this| {
let state = this.state_at_block_id(block_id)?;
let storage_keys = keys.iter().map(|key| key.0).collect::<Vec<_>>();
let proof = state.proof(&BundleState::default(), address, &storage_keys)?;
let proof = state
.proof(&BundleState::default(), address, &storage_keys)
.map_err(Self::Error::from_eth_err)?;
Ok(from_primitive_account_proof(proof))
})
.await
@ -124,7 +133,7 @@ pub trait EthState: LoadState + SpawnBlocking {
/// Loads state from database.
///
/// Behaviour shared by several `eth_` RPC methods, not exclusive to `eth_` state RPC methods.
pub trait LoadState {
pub trait LoadState: EthApiTypes {
/// Returns a handle for reading state from database.
///
/// Data access in default trait method implementations.
@ -141,21 +150,21 @@ pub trait LoadState {
fn pool(&self) -> impl TransactionPool;
/// Returns the state at the given block number
fn state_at_hash(&self, block_hash: B256) -> EthResult<StateProviderBox> {
Ok(self.provider().history_by_block_hash(block_hash)?)
fn state_at_hash(&self, block_hash: B256) -> Result<StateProviderBox, Self::Error> {
self.provider().history_by_block_hash(block_hash).map_err(Self::Error::from_eth_err)
}
/// Returns the state at the given [`BlockId`] enum.
///
/// Note: if not [`BlockNumberOrTag::Pending`](reth_primitives::BlockNumberOrTag) then this
/// will only return canonical state. See also <https://github.com/paradigmxyz/reth/issues/4515>
fn state_at_block_id(&self, at: BlockId) -> EthResult<StateProviderBox> {
Ok(self.provider().state_by_block_id(at)?)
fn state_at_block_id(&self, at: BlockId) -> Result<StateProviderBox, Self::Error> {
self.provider().state_by_block_id(at).map_err(Self::Error::from_eth_err)
}
/// Returns the _latest_ state
fn latest_state(&self) -> EthResult<StateProviderBox> {
Ok(self.provider().latest()?)
fn latest_state(&self) -> Result<StateProviderBox, Self::Error> {
self.provider().latest().map_err(Self::Error::from_eth_err)
}
/// Returns the state at the given [`BlockId`] enum or the latest.
@ -164,7 +173,7 @@ pub trait LoadState {
fn state_at_block_id_or_latest(
&self,
block_id: Option<BlockId>,
) -> EthResult<StateProviderBox> {
) -> Result<StateProviderBox, Self::Error> {
if let Some(block_id) = block_id {
self.state_at_block_id(block_id)
} else {
@ -181,7 +190,7 @@ pub trait LoadState {
fn evm_env_at(
&self,
at: BlockId,
) -> impl Future<Output = EthResult<(CfgEnvWithHandlerCfg, BlockEnv, BlockId)>> + Send
) -> impl Future<Output = Result<(CfgEnvWithHandlerCfg, BlockEnv, BlockId), Self::Error>> + Send
where
Self: LoadPendingBlock + SpawnBlocking,
{
@ -193,9 +202,14 @@ pub trait LoadState {
} else {
// Use cached values if there is no pending block
let block_hash = LoadPendingBlock::provider(self)
.block_hash_for_id(at)?
.block_hash_for_id(at)
.map_err(Self::Error::from_eth_err)?
.ok_or_else(|| EthApiError::UnknownBlockNumber)?;
let (cfg, env) = self.cache().get_evm_env(block_hash).await?;
let (cfg, env) = self
.cache()
.get_evm_env(block_hash)
.await
.map_err(Self::Error::from_eth_err)?;
Ok((cfg, env, block_hash.into()))
}
}
@ -207,7 +221,7 @@ pub trait LoadState {
fn evm_env_for_raw_block(
&self,
header: &Header,
) -> impl Future<Output = EthResult<(CfgEnvWithHandlerCfg, BlockEnv)>> + Send
) -> impl Future<Output = Result<(CfgEnvWithHandlerCfg, BlockEnv), Self::Error>> + Send
where
Self: LoadPendingBlock + SpawnBlocking,
{
@ -230,7 +244,7 @@ pub trait LoadState {
&self,
address: Address,
block_id: Option<BlockId>,
) -> impl Future<Output = EthResult<U256>> + Send
) -> impl Future<Output = Result<U256, Self::Error>> + Send
where
Self: SpawnBlocking,
{
@ -240,15 +254,20 @@ pub trait LoadState {
if let Some(highest_nonce) =
address_txs.iter().map(|item| item.transaction.nonce()).max()
{
let tx_count = highest_nonce
.checked_add(1)
.ok_or(RpcInvalidTransactionError::NonceMaxValue)?;
let tx_count = highest_nonce.checked_add(1).ok_or(Self::Error::from(
EthApiError::InvalidTransaction(RpcInvalidTransactionError::NonceMaxValue),
))?;
return Ok(U256::from(tx_count))
}
}
let state = this.state_at_block_id_or_latest(block_id)?;
Ok(U256::from(state.account_nonce(address)?.unwrap_or_default()))
Ok(U256::from(
state
.account_nonce(address)
.map_err(Self::Error::from_eth_err)?
.unwrap_or_default(),
))
})
}
}

View File

@ -6,13 +6,15 @@ use reth_primitives::B256;
use reth_revm::database::StateProviderDatabase;
use reth_rpc_eth_types::{
cache::db::{StateCacheDb, StateCacheDbRefMutWrapper, StateProviderTraitObjWrapper},
EthApiError, EthResult,
EthApiError,
};
use reth_rpc_types::{BlockId, TransactionInfo};
use revm::{db::CacheDB, Database, DatabaseCommit, GetInspector, Inspector};
use revm_inspectors::tracing::{TracingInspector, TracingInspectorConfig};
use revm_primitives::{EnvWithHandlerCfg, EvmState, ExecutionResult, ResultAndState};
use crate::FromEvmError;
use super::{Call, LoadBlock, LoadPendingBlock, LoadState, LoadTransaction};
/// Executes CPU heavy tasks.
@ -29,10 +31,10 @@ pub trait Trace: LoadState {
db: DB,
env: EnvWithHandlerCfg,
inspector: I,
) -> EthResult<(ResultAndState, EnvWithHandlerCfg)>
) -> Result<(ResultAndState, EnvWithHandlerCfg), Self::Error>
where
DB: Database,
<DB as Database>::Error: Into<EthApiError>,
EthApiError: From<DB::Error>,
I: GetInspector<DB>,
{
self.inspect_and_return_db(db, env, inspector).map(|(res, env, _)| (res, env))
@ -48,14 +50,15 @@ pub trait Trace: LoadState {
db: DB,
env: EnvWithHandlerCfg,
inspector: I,
) -> EthResult<(ResultAndState, EnvWithHandlerCfg, DB)>
) -> Result<(ResultAndState, EnvWithHandlerCfg, DB), Self::Error>
where
DB: Database,
<DB as Database>::Error: Into<EthApiError>,
EthApiError: From<DB::Error>,
I: GetInspector<DB>,
{
let mut evm = self.evm_config().evm_with_env_and_inspector(db, env, inspector);
let res = evm.transact()?;
let res = evm.transact().map_err(Self::Error::from_evm_err)?;
let (db, env) = evm.into_db_and_env_with_handler_cfg();
Ok((res, env, db))
}
@ -73,10 +76,10 @@ pub trait Trace: LoadState {
config: TracingInspectorConfig,
at: BlockId,
f: F,
) -> EthResult<R>
) -> Result<R, Self::Error>
where
Self: Call,
F: FnOnce(TracingInspector, ResultAndState) -> EthResult<R>,
F: FnOnce(TracingInspector, ResultAndState) -> Result<R, Self::Error>,
{
self.with_state_at_block(at, |state| {
let mut db = CacheDB::new(StateProviderDatabase::new(state));
@ -99,10 +102,10 @@ pub trait Trace: LoadState {
config: TracingInspectorConfig,
at: BlockId,
f: F,
) -> impl Future<Output = EthResult<R>> + Send
) -> impl Future<Output = Result<R, Self::Error>> + Send
where
Self: LoadPendingBlock + Call,
F: FnOnce(TracingInspector, ResultAndState, StateCacheDb<'_>) -> EthResult<R>
F: FnOnce(TracingInspector, ResultAndState, StateCacheDb<'_>) -> Result<R, Self::Error>
+ Send
+ 'static,
R: Send + 'static,
@ -130,7 +133,7 @@ pub trait Trace: LoadState {
hash: B256,
config: TracingInspectorConfig,
f: F,
) -> impl Future<Output = EthResult<Option<R>>> + Send
) -> impl Future<Output = Result<Option<R>, Self::Error>> + Send
where
Self: LoadPendingBlock + LoadTransaction + Call,
F: FnOnce(
@ -138,7 +141,7 @@ pub trait Trace: LoadState {
TracingInspector,
ResultAndState,
StateCacheDb<'_>,
) -> EthResult<R>
) -> Result<R, Self::Error>
+ Send
+ 'static,
R: Send + 'static,
@ -160,10 +163,15 @@ pub trait Trace: LoadState {
hash: B256,
mut inspector: Insp,
f: F,
) -> impl Future<Output = EthResult<Option<R>>> + Send
) -> impl Future<Output = Result<Option<R>, Self::Error>> + Send
where
Self: LoadPendingBlock + LoadTransaction + Call,
F: FnOnce(TransactionInfo, Insp, ResultAndState, StateCacheDb<'_>) -> EthResult<R>
F: FnOnce(
TransactionInfo,
Insp,
ResultAndState,
StateCacheDb<'_>,
) -> Result<R, Self::Error>
+ Send
+ 'static,
Insp: for<'a, 'b> Inspector<StateCacheDbRefMutWrapper<'a, 'b>> + Send + 'static,
@ -222,7 +230,7 @@ pub trait Trace: LoadState {
highest_index: Option<u64>,
config: TracingInspectorConfig,
f: F,
) -> impl Future<Output = EthResult<Option<Vec<R>>>> + Send
) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
where
Self: LoadBlock,
F: Fn(
@ -231,7 +239,7 @@ pub trait Trace: LoadState {
ExecutionResult,
&EvmState,
&StateCacheDb<'_>,
) -> EthResult<R>
) -> Result<R, Self::Error>
+ Send
+ 'static,
R: Send + 'static,
@ -260,10 +268,16 @@ pub trait Trace: LoadState {
highest_index: Option<u64>,
mut inspector_setup: Setup,
f: F,
) -> impl Future<Output = EthResult<Option<Vec<R>>>> + Send
) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
where
Self: LoadBlock,
F: Fn(TransactionInfo, Insp, ExecutionResult, &EvmState, &StateCacheDb<'_>) -> EthResult<R>
F: Fn(
TransactionInfo,
Insp,
ExecutionResult,
&EvmState,
&StateCacheDb<'_>,
) -> Result<R, Self::Error>
+ Send
+ 'static,
Setup: FnMut() -> Insp + Send + 'static,
@ -360,7 +374,7 @@ pub trait Trace: LoadState {
block_id: BlockId,
config: TracingInspectorConfig,
f: F,
) -> impl Future<Output = EthResult<Option<Vec<R>>>> + Send
) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
where
Self: LoadBlock,
// This is the callback that's invoked for each transaction with the inspector, the result,
@ -371,7 +385,7 @@ pub trait Trace: LoadState {
ExecutionResult,
&EvmState,
&StateCacheDb<'_>,
) -> EthResult<R>
) -> Result<R, Self::Error>
+ Send
+ 'static,
R: Send + 'static,
@ -398,12 +412,18 @@ pub trait Trace: LoadState {
block_id: BlockId,
insp_setup: Setup,
f: F,
) -> impl Future<Output = EthResult<Option<Vec<R>>>> + Send
) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
where
Self: LoadBlock,
// This is the callback that's invoked for each transaction with the inspector, the result,
// state and db
F: Fn(TransactionInfo, Insp, ExecutionResult, &EvmState, &StateCacheDb<'_>) -> EthResult<R>
F: Fn(
TransactionInfo,
Insp,
ExecutionResult,
&EvmState,
&StateCacheDb<'_>,
) -> Result<R, Self::Error>
+ Send
+ 'static,
Setup: FnMut() -> Insp + Send + 'static,

View File

@ -24,9 +24,11 @@ use reth_rpc_types::{
use reth_rpc_types_compat::transaction::from_recovered_with_block_context;
use reth_transaction_pool::{TransactionOrigin, TransactionPool};
use super::EthSigner;
use crate::{FromEthApiError, IntoEthApiError};
use super::{Call, EthApiSpec, LoadBlock, LoadFee, LoadPendingBlock, LoadReceipt, SpawnBlocking};
use super::{
Call, EthApiSpec, EthSigner, LoadBlock, LoadFee, LoadPendingBlock, LoadReceipt, SpawnBlocking,
};
/// Transaction related functions for the [`EthApiServer`](crate::EthApiServer) trait in
/// the `eth_` namespace.
@ -75,7 +77,7 @@ pub trait EthTransactions: LoadTransaction {
fn transaction_by_hash(
&self,
hash: B256,
) -> impl Future<Output = EthResult<Option<TransactionSource>>> + Send {
) -> impl Future<Output = Result<Option<TransactionSource>, Self::Error>> + Send {
LoadTransaction::transaction_by_hash(self, hash)
}
@ -85,8 +87,10 @@ pub trait EthTransactions: LoadTransaction {
fn transactions_by_block(
&self,
block: B256,
) -> impl Future<Output = EthResult<Option<Vec<TransactionSigned>>>> + Send {
async move { Ok(self.cache().get_block_transactions(block).await?) }
) -> impl Future<Output = Result<Option<Vec<TransactionSigned>>, Self::Error>> + Send {
async move {
self.cache().get_block_transactions(block).await.map_err(Self::Error::from_eth_err)
}
}
/// Returns the EIP-2718 encoded transaction by hash.
@ -99,7 +103,7 @@ pub trait EthTransactions: LoadTransaction {
fn raw_transaction_by_hash(
&self,
hash: B256,
) -> impl Future<Output = EthResult<Option<Bytes>>> + Send {
) -> impl Future<Output = Result<Option<Bytes>, Self::Error>> + Send {
async move {
// Note: this is mostly used to fetch pooled transactions so we check the pool first
if let Some(tx) =
@ -110,7 +114,8 @@ pub trait EthTransactions: LoadTransaction {
self.spawn_blocking_io(move |ref this| {
Ok(LoadTransaction::provider(this)
.transaction_by_hash(hash)?
.transaction_by_hash(hash)
.map_err(Self::Error::from_eth_err)?
.map(|tx| tx.envelope_encoded()))
})
.await
@ -121,7 +126,7 @@ pub trait EthTransactions: LoadTransaction {
fn historical_transaction_by_hash_at(
&self,
hash: B256,
) -> impl Future<Output = EthResult<Option<(TransactionSource, B256)>>> + Send {
) -> impl Future<Output = Result<Option<(TransactionSource, B256)>, Self::Error>> + Send {
async move {
match self.transaction_by_hash_at(hash).await? {
None => Ok(None),
@ -137,7 +142,7 @@ pub trait EthTransactions: LoadTransaction {
fn transaction_receipt(
&self,
hash: B256,
) -> impl Future<Output = EthResult<Option<AnyTransactionReceipt>>> + Send
) -> impl Future<Output = Result<Option<AnyTransactionReceipt>, Self::Error>> + Send
where
Self: LoadReceipt + 'static,
{
@ -157,19 +162,26 @@ pub trait EthTransactions: LoadTransaction {
fn load_transaction_and_receipt(
&self,
hash: TxHash,
) -> impl Future<Output = EthResult<Option<(TransactionSigned, TransactionMeta, Receipt)>>> + Send
) -> impl Future<
Output = Result<Option<(TransactionSigned, TransactionMeta, Receipt)>, Self::Error>,
> + Send
where
Self: 'static,
{
let this = self.clone();
self.spawn_blocking_io(move |_| {
let (tx, meta) =
match LoadTransaction::provider(&this).transaction_by_hash_with_meta(hash)? {
Some((tx, meta)) => (tx, meta),
None => return Ok(None),
};
let (tx, meta) = match LoadTransaction::provider(&this)
.transaction_by_hash_with_meta(hash)
.map_err(Self::Error::from_eth_err)?
{
Some((tx, meta)) => (tx, meta),
None => return Ok(None),
};
let receipt = match EthTransactions::provider(&this).receipt_by_hash(hash)? {
let receipt = match EthTransactions::provider(&this)
.receipt_by_hash(hash)
.map_err(Self::Error::from_eth_err)?
{
Some(recpt) => recpt,
None => return Ok(None),
};
@ -185,7 +197,7 @@ pub trait EthTransactions: LoadTransaction {
&self,
block_id: BlockId,
index: usize,
) -> impl Future<Output = EthResult<Option<Transaction>>> + Send
) -> impl Future<Output = Result<Option<Transaction>, Self::Error>> + Send
where
Self: LoadBlock,
{
@ -216,7 +228,7 @@ pub trait EthTransactions: LoadTransaction {
&self,
block_id: BlockId,
index: usize,
) -> impl Future<Output = EthResult<Option<Bytes>>> + Send
) -> impl Future<Output = Result<Option<Bytes>, Self::Error>> + Send
where
Self: LoadBlock,
{
@ -234,7 +246,10 @@ pub trait EthTransactions: LoadTransaction {
/// Decodes and recovers the transaction and submits it to the pool.
///
/// Returns the hash of the transaction.
fn send_raw_transaction(&self, tx: Bytes) -> impl Future<Output = EthResult<B256>> + Send {
fn send_raw_transaction(
&self,
tx: Bytes,
) -> impl Future<Output = Result<B256, Self::Error>> + Send {
async move {
// On optimism, transactions are forwarded directly to the sequencer to be included in
// blocks that it builds.
@ -250,8 +265,11 @@ pub trait EthTransactions: LoadTransaction {
);
// submit the transaction to the pool with a `Local` origin
let hash =
self.pool().add_transaction(TransactionOrigin::Local, pool_transaction).await?;
let hash = self
.pool()
.add_transaction(TransactionOrigin::Local, pool_transaction)
.await
.map_err(Self::Error::from_eth_err)?;
Ok(hash)
}
@ -262,18 +280,18 @@ pub trait EthTransactions: LoadTransaction {
fn send_transaction(
&self,
mut request: TransactionRequest,
) -> impl Future<Output = EthResult<B256>> + Send
) -> impl Future<Output = Result<B256, Self::Error>> + Send
where
Self: EthApiSpec + LoadBlock + LoadPendingBlock + LoadFee + Call,
{
async move {
let from = match request.from {
Some(from) => from,
None => return Err(SignError::NoAccount.into()),
None => return Err(SignError::NoAccount.into_eth_err()),
};
if self.find_signer(&from).is_err() {
return Err(SignError::NoAccount.into());
return Err(SignError::NoAccount.into_eth_err());
}
// set nonce if not already set before
@ -447,7 +465,7 @@ pub trait EthTransactions: LoadTransaction {
TypedTransactionRequest::EIP4844(req)
}
None => return Err(EthApiError::ConflictingFeeFieldsInRequest),
None => return Err(EthApiError::ConflictingFeeFieldsInRequest.into()),
};
let signed_tx = self.sign_request(&from, transaction)?;
@ -457,13 +475,14 @@ pub trait EthTransactions: LoadTransaction {
let pool_transaction = match recovered.try_into() {
Ok(converted) => <<Self as LoadTransaction>::Pool as TransactionPool>::Transaction::from_recovered_pooled_transaction(converted),
Err(_) => return Err(EthApiError::TransactionConversionError),
Err(_) => return Err(EthApiError::TransactionConversionError.into()),
};
// submit the transaction to the pool with a `Local` origin
let hash = LoadTransaction::pool(self)
.add_transaction(TransactionOrigin::Local, pool_transaction)
.await?;
.await
.map_err(Self::Error::from_eth_err)?;
Ok(hash)
}
@ -474,16 +493,16 @@ pub trait EthTransactions: LoadTransaction {
&self,
from: &Address,
request: TypedTransactionRequest,
) -> EthResult<TransactionSigned> {
) -> Result<TransactionSigned, Self::Error> {
for signer in self.signers().read().iter() {
if signer.is_signer_for(from) {
return match signer.sign_transaction(request, from) {
Ok(tx) => Ok(tx),
Err(e) => Err(e.into()),
Err(e) => Err(e.into_eth_err()),
}
}
}
Err(EthApiError::InvalidTransactionSignature)
Err(EthApiError::InvalidTransactionSignature.into())
}
/// Signs given message. Returns the signature.
@ -491,23 +510,37 @@ pub trait EthTransactions: LoadTransaction {
&self,
account: Address,
message: Bytes,
) -> impl Future<Output = EthResult<Bytes>> + Send {
async move { Ok(self.find_signer(&account)?.sign(account, &message).await?.to_hex_bytes()) }
) -> impl Future<Output = Result<Bytes, Self::Error>> + Send {
async move {
Ok(self
.find_signer(&account)?
.sign(account, &message)
.await
.map_err(Self::Error::from_eth_err)?
.to_hex_bytes())
}
}
/// Encodes and signs the typed data according EIP-712. Payload must implement Eip712 trait.
fn sign_typed_data(&self, data: &TypedData, account: Address) -> EthResult<Bytes> {
Ok(self.find_signer(&account)?.sign_typed_data(account, data)?.to_hex_bytes())
fn sign_typed_data(&self, data: &TypedData, account: Address) -> Result<Bytes, Self::Error> {
Ok(self
.find_signer(&account)?
.sign_typed_data(account, data)
.map_err(Self::Error::from_eth_err)?
.to_hex_bytes())
}
/// Returns the signer for the given account, if found in configured signers.
fn find_signer(&self, account: &Address) -> Result<Box<(dyn EthSigner + 'static)>, SignError> {
fn find_signer(
&self,
account: &Address,
) -> Result<Box<(dyn EthSigner + 'static)>, Self::Error> {
self.signers()
.read()
.iter()
.find(|signer| signer.is_signer_for(account))
.map(|signer| dyn_clone::clone_box(&**signer))
.ok_or(SignError::NoAccount)
.ok_or_else(|| SignError::NoAccount.into_eth_err())
}
}
@ -543,12 +576,16 @@ pub trait LoadTransaction: SpawnBlocking {
fn transaction_by_hash(
&self,
hash: B256,
) -> impl Future<Output = EthResult<Option<TransactionSource>>> + Send {
) -> impl Future<Output = Result<Option<TransactionSource>, Self::Error>> + Send {
async move {
// Try to find the transaction on disk
let mut resp = self
.spawn_blocking_io(move |this| {
match this.provider().transaction_by_hash_with_meta(hash)? {
match 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
@ -590,7 +627,8 @@ pub trait LoadTransaction: SpawnBlocking {
fn transaction_by_hash_at(
&self,
transaction_hash: B256,
) -> impl Future<Output = EthResult<Option<(TransactionSource, BlockId)>>> + Send {
) -> impl Future<Output = Result<Option<(TransactionSource, BlockId)>, Self::Error>> + Send
{
async move {
match self.transaction_by_hash(transaction_hash).await? {
None => Ok(None),
@ -625,8 +663,8 @@ pub trait LoadTransaction: SpawnBlocking {
fn transaction_and_block(
&self,
hash: B256,
) -> impl Future<Output = EthResult<Option<(TransactionSource, SealedBlockWithSenders)>>> + Send
{
) -> impl Future<Output = Result<Option<(TransactionSource, SealedBlockWithSenders)>, Self::Error>>
+ Send {
async move {
let (transaction, at) = match self.transaction_by_hash_at(hash).await? {
None => return Ok(None),
@ -638,7 +676,11 @@ pub trait LoadTransaction: SpawnBlocking {
BlockId::Hash(hash) => hash.block_hash,
_ => return Ok(None),
};
let block = self.cache().get_block_with_senders(block_hash).await?;
let block = self
.cache()
.get_block_with_senders(block_hash)
.await
.map_err(Self::Error::from_eth_err)?;
Ok(block.map(|block| (transaction, block.seal(block_hash))))
}
}

View File

@ -0,0 +1,17 @@
//! Trait for specifying `eth` API types that may be network dependent.
use std::error::Error;
use crate::{AsEthApiError, FromEthApiError, FromEvmError};
/// Network specific `eth` API types.
pub trait EthApiTypes: Send + Sync {
/// Extension of [`EthApiError`](reth_rpc_eth_types::EthApiError), with network specific errors.
type Error: Into<jsonrpsee_types::error::ErrorObject<'static>>
+ FromEthApiError
+ AsEthApiError
+ FromEvmError
+ Error
+ Send
+ Sync;
}

View File

@ -21,6 +21,10 @@ pub mod pubsub;
pub use bundle::{EthBundleApiServer, EthCallBundleApiServer};
pub use core::{EthApiServer, FullEthApiServer};
pub use filter::EthFilterApiServer;
pub use helpers::{
error::{AsEthApiError, FromEthApiError, FromEvmError, IntoEthApiError},
types::EthApiTypes,
};
pub use pubsub::EthPubSubApiServer;
pub use helpers::transaction::RawTransactionForwarder;

View File

@ -55,14 +55,3 @@ tracing.workspace = true
[dev-dependencies]
serde_json.workspace = true
[features]
optimism = [
"reth-primitives/optimism",
"reth-provider/optimism",
"reth-revm/optimism",
"reth-chainspec/optimism",
"reth-execution-types/optimism",
"reth-revm/optimism",
"revm/optimism"
]

View File

@ -17,6 +17,7 @@ use reth_transaction_pool::error::{
};
use revm::primitives::{EVMError, ExecutionResult, HaltReason, OutOfGasError};
use revm_inspectors::tracing::{js::JsInspectorError, MuxError};
use tracing::error;
/// Result alias
pub type EthResult<T> = Result<T, EthApiError>;
@ -137,6 +138,11 @@ impl EthApiError {
pub fn other<E: ToRpcError>(err: E) -> Self {
Self::Other(Box::new(err))
}
/// Returns `true` if error is [`RpcInvalidTransactionError::GasTooHigh`]
pub const fn is_gas_too_high(&self) -> bool {
matches!(self, Self::InvalidTransaction(RpcInvalidTransactionError::GasTooHigh))
}
}
impl From<EthApiError> for jsonrpsee_types::error::ErrorObject<'static> {
@ -372,6 +378,11 @@ pub enum RpcInvalidTransactionError {
/// Any other error
#[error("{0}")]
Other(Box<dyn ToRpcError>),
/// Unexpected [`InvalidTransaction`](revm::primitives::InvalidTransaction) error, Optimism
/// errors should not be handled on this level.
// TODO: Remove when optimism feature removed in revm
#[error("unexpected transaction error")]
UnexpectedTransactionError,
}
impl RpcInvalidTransactionError {
@ -381,29 +392,6 @@ impl RpcInvalidTransactionError {
}
}
/// Optimism specific invalid transaction errors
#[cfg(feature = "optimism")]
#[derive(thiserror::Error, Debug)]
pub enum OptimismInvalidTransactionError {
/// A deposit transaction was submitted as a system transaction post-regolith.
#[error("no system transactions allowed after regolith")]
DepositSystemTxPostRegolith,
/// A deposit transaction halted post-regolith
#[error("deposit transaction halted after regolith")]
HaltedDepositPostRegolith,
}
#[cfg(feature = "optimism")]
impl ToRpcError for OptimismInvalidTransactionError {
fn to_rpc_error(&self) -> jsonrpsee_types::error::ErrorObject<'static> {
match self {
Self::DepositSystemTxPostRegolith | Self::HaltedDepositPostRegolith => {
rpc_err(EthRpcErrorCode::TransactionRejected.code(), self.to_string(), None)
}
}
}
}
impl RpcInvalidTransactionError {
/// Returns the rpc error code for this error.
const fn error_code(&self) -> i32 {
@ -462,7 +450,7 @@ impl From<revm::primitives::InvalidTransaction> for RpcInvalidTransactionError {
InvalidTransaction::InvalidChainId => Self::InvalidChainId,
InvalidTransaction::PriorityFeeGreaterThanMaxFee => Self::TipAboveFeeCap,
InvalidTransaction::GasPriceLessThanBasefee => Self::FeeCapTooLow,
InvalidTransaction::CallerGasLimitMoreThanBlock => Self::GasTooHigh,
InvalidTransaction::CallerGasLimitMoreThanBlock |
InvalidTransaction::CallGasCostMoreThanGasLimit => Self::GasTooHigh,
InvalidTransaction::RejectCallerWithCode => Self::SenderNoEOA,
InvalidTransaction::LackOfFundForMaxFee { .. } => Self::InsufficientFunds,
@ -488,17 +476,15 @@ impl From<revm::primitives::InvalidTransaction> for RpcInvalidTransactionError {
InvalidTransaction::AuthorizationListInvalidFields => {
Self::AuthorizationListInvalidFields
}
#[cfg(feature = "optimism")]
InvalidTransaction::OptimismError(err) => match err {
revm_primitives::OptimismInvalidTransaction::DepositSystemTxPostRegolith => {
Self::other(OptimismInvalidTransactionError::DepositSystemTxPostRegolith)
}
revm_primitives::OptimismInvalidTransaction::HaltedDepositPostRegolith => {
Self::Other(Box::new(
OptimismInvalidTransactionError::HaltedDepositPostRegolith,
))
}
},
#[allow(unreachable_patterns)]
_ => {
error!(target: "rpc",
?err,
"unexpected transaction error"
);
Self::UnexpectedTransactionError
}
}
}
}

View File

@ -90,5 +90,4 @@ optimism = [
"reth-provider/optimism",
"reth-rpc-eth-api/optimism",
"reth-revm/optimism",
"reth-rpc-eth-types/optimism",
]

View File

@ -14,8 +14,11 @@ use reth_provider::{
};
use reth_revm::database::StateProviderDatabase;
use reth_rpc_api::DebugApiServer;
use reth_rpc_eth_api::helpers::{Call, EthApiSpec, EthTransactions, TraceExt};
use reth_rpc_eth_types::{EthApiError, EthResult, StateCacheDb};
use reth_rpc_eth_api::{
helpers::{Call, EthApiSpec, EthTransactions, TraceExt},
EthApiTypes, FromEthApiError,
};
use reth_rpc_eth_types::{EthApiError, StateCacheDb};
use reth_rpc_server_types::{result::internal_rpc_err, ToRpcResult};
use reth_rpc_types::{
state::EvmOverrides,
@ -68,7 +71,7 @@ where
+ StateProviderFactory
+ EvmEnvProvider
+ 'static,
Eth: TraceExt + 'static,
Eth: EthApiTypes + TraceExt + 'static,
{
/// Acquires a permit to execute a tracing call.
async fn acquire_trace_permit(&self) -> Result<OwnedSemaphorePermit, AcquireError> {
@ -83,7 +86,7 @@ where
cfg: CfgEnvWithHandlerCfg,
block_env: BlockEnv,
opts: GethDebugTracingOptions,
) -> EthResult<Vec<TraceResult>> {
) -> Result<Vec<TraceResult>, Eth::Error> {
if transactions.is_empty() {
// nothing to trace
return Ok(Vec::new())
@ -141,9 +144,10 @@ where
&self,
rlp_block: Bytes,
opts: GethDebugTracingOptions,
) -> EthResult<Vec<TraceResult>> {
let block =
Block::decode(&mut rlp_block.as_ref()).map_err(BlockError::RlpDecodeRawBlock)?;
) -> Result<Vec<TraceResult>, Eth::Error> {
let block = Block::decode(&mut rlp_block.as_ref())
.map_err(BlockError::RlpDecodeRawBlock)
.map_err(Eth::Error::from_eth_err)?;
let (cfg, block_env) = self.eth_api().evm_env_for_raw_block(&block.header).await?;
// we trace on top the block's parent block
@ -158,8 +162,9 @@ where
.map(|tx| {
tx.into_ecrecovered()
.ok_or_else(|| EthApiError::InvalidTransactionSignature)
.map_err(Eth::Error::from_eth_err)
})
.collect::<EthResult<Vec<_>>>()?
.collect::<Result<Vec<_>, Eth::Error>>()?
} else {
block
.body
@ -167,8 +172,9 @@ where
.map(|tx| {
tx.into_ecrecovered_unchecked()
.ok_or_else(|| EthApiError::InvalidTransactionSignature)
.map_err(Eth::Error::from_eth_err)
})
.collect::<EthResult<Vec<_>>>()?
.collect::<Result<Vec<_>, Eth::Error>>()?
};
self.trace_block(parent.into(), transactions, cfg, block_env, opts).await
@ -179,11 +185,12 @@ where
&self,
block_id: BlockId,
opts: GethDebugTracingOptions,
) -> EthResult<Vec<TraceResult>> {
) -> Result<Vec<TraceResult>, Eth::Error> {
let block_hash = self
.inner
.provider
.block_hash_for_id(block_id)?
.block_hash_for_id(block_id)
.map_err(Eth::Error::from_eth_err)?
.ok_or_else(|| EthApiError::UnknownBlockNumber)?;
let ((cfg, block_env, _), block) = futures::try_join!(
@ -213,9 +220,9 @@ where
&self,
tx_hash: B256,
opts: GethDebugTracingOptions,
) -> EthResult<GethTrace> {
) -> Result<GethTrace, Eth::Error> {
let (transaction, block) = match self.inner.eth_api.transaction_and_block(tx_hash).await? {
None => return Err(EthApiError::TransactionNotFound),
None => return Err(EthApiError::TransactionNotFound.into()),
Some(res) => res,
};
let (cfg, block_env, _) = self.inner.eth_api.evm_env_at(block.hash().into()).await?;
@ -277,7 +284,7 @@ where
call: TransactionRequest,
block_id: Option<BlockId>,
opts: GethDebugTracingCallOptions,
) -> EthResult<GethTrace> {
) -> Result<GethTrace, Eth::Error> {
let at = block_id.unwrap_or_default();
let GethDebugTracingCallOptions { tracing_options, state_overrides, block_overrides } =
opts;
@ -330,22 +337,23 @@ where
TracingInspectorConfig::from_geth_prestate_config(&prestate_config),
);
let frame =
self.inner
.eth_api
.spawn_with_call_at(call, at, overrides, move |db, env| {
// wrapper is hack to get around 'higher-ranked lifetime error',
// see <https://github.com/rust-lang/rust/issues/100013>
let db = db.0;
let frame = self
.inner
.eth_api
.spawn_with_call_at(call, at, overrides, move |db, env| {
// wrapper is hack to get around 'higher-ranked lifetime error',
// see <https://github.com/rust-lang/rust/issues/100013>
let db = db.0;
let (res, _) =
this.eth_api().inspect(&mut *db, env, &mut inspector)?;
let frame = inspector
.into_geth_builder()
.geth_prestate_traces(&res, prestate_config, db)?;
Ok(frame)
})
.await?;
let (res, _) =
this.eth_api().inspect(&mut *db, env, &mut inspector)?;
let frame = inspector
.into_geth_builder()
.geth_prestate_traces(&res, prestate_config, db)
.map_err(Eth::Error::from_eth_err)?;
Ok(frame)
})
.await?;
return Ok(frame.into())
}
GethDebugBuiltInTracerType::NoopTracer => Ok(NoopFrame::default().into()),
@ -354,7 +362,8 @@ where
.into_mux_config()
.map_err(|_| EthApiError::InvalidTracerConfig)?;
let mut inspector = MuxInspector::try_from_config(mux_config)?;
let mut inspector = MuxInspector::try_from_config(mux_config)
.map_err(Eth::Error::from_eth_err)?;
let frame = self
.inner
@ -366,7 +375,9 @@ where
let (res, _) =
this.eth_api().inspect(&mut *db, env, &mut inspector)?;
let frame = inspector.try_into_mux_frame(&res, db)?;
let frame = inspector
.try_into_mux_frame(&res, db)
.map_err(Eth::Error::from_eth_err)?;
Ok(frame.into())
})
.await?;
@ -386,10 +397,11 @@ where
// <https://github.com/rust-lang/rust/issues/100013>
let db = db.0;
let mut inspector = JsInspector::new(code, config)?;
let mut inspector =
JsInspector::new(code, config).map_err(Eth::Error::from_eth_err)?;
let (res, _) =
this.eth_api().inspect(&mut *db, env.clone(), &mut inspector)?;
Ok(inspector.json_result(res, &env, db)?)
inspector.json_result(res, &env, db).map_err(Eth::Error::from_eth_err)
})
.await?;
@ -426,9 +438,9 @@ where
bundles: Vec<Bundle>,
state_context: Option<StateContext>,
opts: Option<GethDebugTracingCallOptions>,
) -> EthResult<Vec<Vec<GethTrace>>> {
) -> Result<Vec<Vec<GethTrace>>, Eth::Error> {
if bundles.is_empty() {
return Err(EthApiError::InvalidParams(String::from("bundles are empty.")))
return Err(EthApiError::InvalidParams(String::from("bundles are empty.")).into())
}
let StateContext { transaction_index, block_number } = state_context.unwrap_or_default();
@ -546,7 +558,7 @@ where
env: EnvWithHandlerCfg,
db: &mut StateCacheDb<'_>,
transaction_context: Option<TransactionContext>,
) -> EthResult<(GethTrace, revm_primitives::EvmState)> {
) -> Result<(GethTrace, revm_primitives::EvmState), Eth::Error> {
let GethDebugTracingOptions { config, tracer, tracer_config, .. } = opts;
if let Some(tracer) = tracer {
@ -584,11 +596,10 @@ where
);
let (res, _) = self.eth_api().inspect(&mut *db, env, &mut inspector)?;
let frame = inspector.into_geth_builder().geth_prestate_traces(
&res,
prestate_config,
db,
)?;
let frame = inspector
.into_geth_builder()
.geth_prestate_traces(&res, prestate_config, db)
.map_err(Eth::Error::from_eth_err)?;
return Ok((frame.into(), res.state))
}
@ -600,10 +611,13 @@ where
.into_mux_config()
.map_err(|_| EthApiError::InvalidTracerConfig)?;
let mut inspector = MuxInspector::try_from_config(mux_config)?;
let mut inspector = MuxInspector::try_from_config(mux_config)
.map_err(Eth::Error::from_eth_err)?;
let (res, _) = self.eth_api().inspect(&mut *db, env, &mut inspector)?;
let frame = inspector.try_into_mux_frame(&res, db)?;
let frame = inspector
.try_into_mux_frame(&res, db)
.map_err(Eth::Error::from_eth_err)?;
return Ok((frame.into(), res.state))
}
},
@ -613,11 +627,13 @@ where
code,
config,
transaction_context.unwrap_or_default(),
)?;
)
.map_err(Eth::Error::from_eth_err)?;
let (res, env) = self.eth_api().inspect(&mut *db, env, &mut inspector)?;
let state = res.state.clone();
let result = inspector.json_result(res, &env, db)?;
let result =
inspector.json_result(res, &env, db).map_err(Eth::Error::from_eth_err)?;
Ok((GethTrace::JS(result), state))
}
}
@ -690,7 +706,7 @@ where
///
/// Returns the bytes of the transaction for the given hash.
async fn raw_transaction(&self, hash: B256) -> RpcResult<Option<Bytes>> {
Ok(self.inner.eth_api.raw_transaction_by_hash(hash).await?)
self.inner.eth_api.raw_transaction_by_hash(hash).await.map_err(Into::into)
}
/// Handler for `debug_getRawTransactions`
@ -739,7 +755,9 @@ where
opts: Option<GethDebugTracingOptions>,
) -> RpcResult<Vec<TraceResult>> {
let _permit = self.acquire_trace_permit().await;
Ok(Self::debug_trace_raw_block(self, rlp_block, opts.unwrap_or_default()).await?)
Self::debug_trace_raw_block(self, rlp_block, opts.unwrap_or_default())
.await
.map_err(Into::into)
}
/// Handler for `debug_traceBlockByHash`
@ -749,7 +767,9 @@ where
opts: Option<GethDebugTracingOptions>,
) -> RpcResult<Vec<TraceResult>> {
let _permit = self.acquire_trace_permit().await;
Ok(Self::debug_trace_block(self, block.into(), opts.unwrap_or_default()).await?)
Self::debug_trace_block(self, block.into(), opts.unwrap_or_default())
.await
.map_err(Into::into)
}
/// Handler for `debug_traceBlockByNumber`
@ -759,7 +779,9 @@ where
opts: Option<GethDebugTracingOptions>,
) -> RpcResult<Vec<TraceResult>> {
let _permit = self.acquire_trace_permit().await;
Ok(Self::debug_trace_block(self, block.into(), opts.unwrap_or_default()).await?)
Self::debug_trace_block(self, block.into(), opts.unwrap_or_default())
.await
.map_err(Into::into)
}
/// Handler for `debug_traceTransaction`
@ -769,7 +791,9 @@ where
opts: Option<GethDebugTracingOptions>,
) -> RpcResult<GethTrace> {
let _permit = self.acquire_trace_permit().await;
Ok(Self::debug_trace_transaction(self, tx_hash, opts.unwrap_or_default()).await?)
Self::debug_trace_transaction(self, tx_hash, opts.unwrap_or_default())
.await
.map_err(Into::into)
}
/// Handler for `debug_traceCall`
@ -780,7 +804,9 @@ where
opts: Option<GethDebugTracingCallOptions>,
) -> RpcResult<GethTrace> {
let _permit = self.acquire_trace_permit().await;
Ok(Self::debug_trace_call(self, request, block_number, opts.unwrap_or_default()).await?)
Self::debug_trace_call(self, request, block_number, opts.unwrap_or_default())
.await
.map_err(Into::into)
}
async fn debug_trace_call_many(
@ -790,7 +816,7 @@ where
opts: Option<GethDebugTracingCallOptions>,
) -> RpcResult<Vec<Vec<GethTrace>>> {
let _permit = self.acquire_trace_permit().await;
Ok(Self::debug_trace_call_many(self, bundles, state_context, opts).await?)
Self::debug_trace_call_many(self, bundles, state_context, opts).await.map_err(Into::into)
}
async fn debug_backtrace_at(&self, _location: &str) -> RpcResult<()> {

View File

@ -10,6 +10,7 @@ use reth_primitives::{
PooledTransactionsElement, U256,
};
use reth_revm::database::StateProviderDatabase;
use reth_rpc_eth_api::{FromEthApiError, FromEvmError};
use reth_rpc_types::mev::{EthCallBundle, EthCallBundleResponse, EthCallBundleTransactionResult};
use reth_tasks::pool::BlockingTaskGuard;
use revm::{
@ -23,9 +24,7 @@ use reth_rpc_eth_api::{
helpers::{Call, EthTransactions, LoadPendingBlock},
EthCallBundleApiServer,
};
use reth_rpc_eth_types::{
utils::recover_raw_transaction, EthApiError, EthResult, RpcInvalidTransactionError,
};
use reth_rpc_eth_types::{utils::recover_raw_transaction, EthApiError, RpcInvalidTransactionError};
/// `Eth` bundle implementation.
pub struct EthBundle<Eth> {
@ -48,7 +47,10 @@ where
/// another (or the same) block. This can be used to simulate future blocks with the current
/// state, or it can be used to simulate a past block. The sender is responsible for signing the
/// transactions and using the correct nonce and ensuring validity
pub async fn call_bundle(&self, bundle: EthCallBundle) -> EthResult<EthCallBundleResponse> {
pub async fn call_bundle(
&self,
bundle: EthCallBundle,
) -> Result<EthCallBundleResponse, Eth::Error> {
let EthCallBundle {
txs,
block_number,
@ -61,12 +63,14 @@ where
if txs.is_empty() {
return Err(EthApiError::InvalidParams(
EthBundleError::EmptyBundleTransactions.to_string(),
))
)
.into())
}
if block_number == 0 {
return Err(EthApiError::InvalidParams(
EthBundleError::BundleMissingBlockNumber.to_string(),
))
)
.into())
}
let transactions = txs
@ -93,7 +97,8 @@ where
{
return Err(EthApiError::InvalidParams(
EthBundleError::Eip4844BlobGasExceeded.to_string(),
))
)
.into())
}
let block_id: reth_rpc_types::BlockId = state_block_number.into();
@ -121,7 +126,8 @@ where
let parent_block = block_env.number.saturating_to::<u64>();
// here we need to fetch the _next_ block's basefee based on the parent block <https://github.com/flashbots/mev-geth/blob/fddf97beec5877483f879a77b7dea2e58a58d653/internal/ethapi/api.go#L2130>
let parent = LoadPendingBlock::provider(&self.inner.eth_api)
.header_by_number(parent_block)?
.header_by_number(parent_block)
.map_err(Eth::Error::from_eth_err)?
.ok_or_else(|| EthApiError::UnknownBlockNumber)?;
if let Some(base_fee) = parent.next_block_base_fee(
LoadPendingBlock::provider(&self.inner.eth_api)
@ -146,7 +152,8 @@ where
let env = EnvWithHandlerCfg::new_with_cfg_env(cfg, block_env, TxEnv::default());
let db = CacheDB::new(StateProviderDatabase::new(state));
let initial_coinbase = DatabaseRef::basic_ref(&db, coinbase)?
let initial_coinbase = DatabaseRef::basic_ref(&db, coinbase)
.map_err(Eth::Error::from_eth_err)?
.map(|acc| acc.balance)
.unwrap_or_default();
let mut coinbase_balance_before_tx = initial_coinbase;
@ -164,8 +171,9 @@ where
// Verify that the given blob data, commitments, and proofs are all valid for
// this transaction.
if let PooledTransactionsElement::BlobTransaction(ref tx) = tx {
tx.validate(EnvKzgSettings::Default.get())
.map_err(|e| EthApiError::InvalidParams(e.to_string()))?;
tx.validate(EnvKzgSettings::Default.get()).map_err(|e| {
Eth::Error::from_eth_err(EthApiError::InvalidParams(e.to_string()))
})?;
}
let tx = tx.into_transaction();
@ -173,9 +181,11 @@ where
hash_bytes.extend_from_slice(tx.hash().as_slice());
let gas_price = tx
.effective_tip_per_gas(basefee)
.ok_or_else(|| RpcInvalidTransactionError::FeeCapTooLow)?;
.ok_or_else(|| RpcInvalidTransactionError::FeeCapTooLow)
.map_err(Eth::Error::from_eth_err)?;
Call::evm_config(&eth_api).fill_tx_env(evm.tx_mut(), &tx, signer);
let ResultAndState { result, state } = evm.transact()?;
let ResultAndState { result, state } =
evm.transact().map_err(Eth::Error::from_evm_err)?;
let gas_used = result.gas_used();
total_gas_used += gas_used;
@ -254,7 +264,7 @@ where
Eth: EthTransactions + LoadPendingBlock + Call + 'static,
{
async fn call_bundle(&self, request: EthCallBundle) -> RpcResult<EthCallBundleResponse> {
Ok(Self::call_bundle(self, request).await?)
Self::call_bundle(self, request).await.map_err(Into::into)
}
}

View File

@ -10,10 +10,11 @@ use reth_primitives::{BlockNumberOrTag, U256};
use reth_provider::{BlockReaderIdExt, CanonStateSubscriptions, ChainSpecProvider};
use reth_rpc_eth_api::{
helpers::{transaction::UpdateRawTxForwarder, EthSigner, SpawnBlocking},
RawTransactionForwarder,
EthApiTypes, RawTransactionForwarder,
};
use reth_rpc_eth_types::{
EthApiBuilderCtx, EthStateCache, FeeHistoryCache, GasCap, GasPriceOracle, PendingBlock,
EthApiBuilderCtx, EthApiError, EthStateCache, FeeHistoryCache, GasCap, GasPriceOracle,
PendingBlock,
};
use reth_tasks::{
pool::{BlockingTaskGuard, BlockingTaskPool},
@ -114,6 +115,13 @@ where
}
}
impl<Provider, Pool, Network, EvmConfig> EthApiTypes for EthApi<Provider, Pool, Network, EvmConfig>
where
Self: Send + Sync,
{
type Error = EthApiError;
}
impl<Provider, Pool, Network, EvmConfig> std::fmt::Debug
for EthApi<Provider, Pool, Network, EvmConfig>
{
@ -131,7 +139,7 @@ impl<Provider, Pool, Network, EvmConfig> Clone for EthApi<Provider, Pool, Networ
impl<Provider, Pool, Network, EvmConfig> SpawnBlocking
for EthApi<Provider, Pool, Network, EvmConfig>
where
Self: Clone + Send + Sync + 'static,
Self: EthApiTypes + Clone + Send + Sync + 'static,
{
#[inline]
fn io_task_spawner(&self) -> impl reth_tasks::TaskSpawner {

View File

@ -1,13 +1,13 @@
//! Builds an RPC receipt response w.r.t. data layout of network.
use reth_rpc_eth_api::helpers::LoadReceipt;
use reth_rpc_eth_api::{helpers::LoadReceipt, EthApiTypes};
use reth_rpc_eth_types::EthStateCache;
use crate::EthApi;
impl<Provider, Pool, Network, EvmConfig> LoadReceipt for EthApi<Provider, Pool, Network, EvmConfig>
where
Self: Send + Sync,
Self: EthApiTypes,
{
#[inline]
fn cache(&self) -> &EthStateCache {

View File

@ -3,7 +3,10 @@
use reth_provider::{ChainSpecProvider, StateProviderFactory};
use reth_transaction_pool::TransactionPool;
use reth_rpc_eth_api::helpers::{EthState, LoadState, SpawnBlocking};
use reth_rpc_eth_api::{
helpers::{EthState, LoadState, SpawnBlocking},
EthApiTypes,
};
use reth_rpc_eth_types::EthStateCache;
use crate::EthApi;
@ -19,6 +22,7 @@ where
impl<Provider, Pool, Network, EvmConfig> LoadState for EthApi<Provider, Pool, Network, EvmConfig>
where
Self: EthApiTypes,
Provider: StateProviderFactory + ChainSpecProvider,
Pool: TransactionPool,
{

View File

@ -1,5 +1,7 @@
//! Contains RPC handler implementations specific to transactions
use std::sync::Arc;
use reth_provider::{BlockReaderIdExt, TransactionsProvider};
use reth_rpc_eth_api::{
helpers::{EthSigner, EthTransactions, LoadTransaction, SpawnBlocking},
@ -23,7 +25,7 @@ where
}
#[inline]
fn raw_tx_forwarder(&self) -> Option<std::sync::Arc<dyn RawTransactionForwarder>> {
fn raw_tx_forwarder(&self) -> Option<Arc<dyn RawTransactionForwarder>> {
self.inner.raw_tx_forwarder()
}
@ -43,7 +45,7 @@ where
type Pool = Pool;
#[inline]
fn provider(&self) -> impl reth_provider::TransactionsProvider {
fn provider(&self) -> impl TransactionsProvider {
self.inner.provider()
}

View File

@ -85,7 +85,8 @@ where
TransferInspector::new(false),
|_tx_info, inspector, _, _| Ok(inspector.into_transfers()),
)
.await?
.await
.map_err(Into::into)?
.map(|transfer_operations| {
transfer_operations
.iter()
@ -115,7 +116,8 @@ where
_ => Ok(None),
})
.await
.map(Option::flatten)?;
.map(Option::flatten)
.map_err(Into::into)?;
Ok(maybe_revert)
}
@ -128,7 +130,8 @@ where
TracingInspectorConfig::default_parity(),
move |_tx_info, inspector, _, _| Ok(inspector.into_traces().into_nodes()),
)
.await?
.await
.map_err(Into::into)?
.map(|traces| {
traces
.into_iter()
@ -325,7 +328,8 @@ where
Ok(inspector.into_parity_builder().into_localized_transaction_traces(tx_info))
},
)
.await?
.await
.map_err(Into::into)?
.map(|traces| {
traces
.into_iter()

View File

@ -1,7 +1,7 @@
use std::{collections::HashSet, sync::Arc};
use async_trait::async_trait;
use jsonrpsee::core::RpcResult as Result;
use jsonrpsee::core::RpcResult;
use reth_chainspec::EthereumHardforks;
use reth_consensus_common::calc::{
base_block_reward, base_block_reward_pre_merge, block_reward, ommer_reward,
@ -11,11 +11,11 @@ use reth_primitives::{BlockId, Bytes, Header, B256, U256};
use reth_provider::{BlockReader, ChainSpecProvider, EvmEnvProvider, StateProviderFactory};
use reth_revm::database::StateProviderDatabase;
use reth_rpc_api::TraceApiServer;
use reth_rpc_eth_api::helpers::{Call, TraceExt};
use reth_rpc_eth_types::{
error::{EthApiError, EthResult},
utils::recover_raw_transaction,
use reth_rpc_eth_api::{
helpers::{Call, TraceExt},
FromEthApiError,
};
use reth_rpc_eth_types::{error::EthApiError, utils::recover_raw_transaction};
use reth_rpc_types::{
state::{EvmOverrides, StateOverride},
trace::{
@ -79,7 +79,10 @@ where
Eth: TraceExt + 'static,
{
/// Executes the given call and returns a number of possible traces for it.
pub async fn trace_call(&self, trace_request: TraceCallRequest) -> EthResult<TraceResults> {
pub async fn trace_call(
&self,
trace_request: TraceCallRequest,
) -> Result<TraceResults, Eth::Error> {
let at = trace_request.block_id.unwrap_or_default();
let config = TracingInspectorConfig::from_parity_config(&trace_request.trace_types);
let overrides =
@ -93,11 +96,10 @@ where
let db = db.0;
let (res, _) = this.eth_api().inspect(&mut *db, env, &mut inspector)?;
let trace_res = inspector.into_parity_builder().into_trace_results_with_state(
&res,
&trace_request.trace_types,
&db,
)?;
let trace_res = inspector
.into_parity_builder()
.into_trace_results_with_state(&res, &trace_request.trace_types, &db)
.map_err(Eth::Error::from_eth_err)?;
Ok(trace_res)
})
.await
@ -109,7 +111,7 @@ where
tx: Bytes,
trace_types: HashSet<TraceType>,
block_id: Option<BlockId>,
) -> EthResult<TraceResults> {
) -> Result<TraceResults, Eth::Error> {
let tx = recover_raw_transaction(tx)?;
let (cfg, block, at) = self.inner.eth_api.evm_env_at(block_id.unwrap_or_default()).await?;
@ -125,11 +127,10 @@ where
self.inner
.eth_api
.spawn_trace_at_with_state(env, config, at, move |inspector, res, db| {
Ok(inspector.into_parity_builder().into_trace_results_with_state(
&res,
&trace_types,
&db,
)?)
inspector
.into_parity_builder()
.into_trace_results_with_state(&res, &trace_types, &db)
.map_err(Eth::Error::from_eth_err)
})
.await
}
@ -142,7 +143,7 @@ where
&self,
calls: Vec<(TransactionRequest, HashSet<TraceType>)>,
block_id: Option<BlockId>,
) -> EthResult<Vec<TraceResults>> {
) -> Result<Vec<TraceResults>, Eth::Error> {
let at = block_id.unwrap_or(BlockId::pending());
let (cfg, block_env, at) = self.inner.eth_api.evm_env_at(at).await?;
@ -169,11 +170,10 @@ where
let mut inspector = TracingInspector::new(config);
let (res, _) = this.eth_api().inspect(&mut db, env, &mut inspector)?;
let trace_res = inspector.into_parity_builder().into_trace_results_with_state(
&res,
&trace_types,
&db,
)?;
let trace_res = inspector
.into_parity_builder()
.into_trace_results_with_state(&res, &trace_types, &db)
.map_err(Eth::Error::from_eth_err)?;
results.push(trace_res);
@ -196,16 +196,15 @@ where
&self,
hash: B256,
trace_types: HashSet<TraceType>,
) -> EthResult<TraceResults> {
) -> Result<TraceResults, Eth::Error> {
let config = TracingInspectorConfig::from_parity_config(&trace_types);
self.inner
.eth_api
.spawn_trace_transaction_in_block(hash, config, move |_, inspector, res, db| {
let trace_res = inspector.into_parity_builder().into_trace_results_with_state(
&res,
&trace_types,
&db,
)?;
let trace_res = inspector
.into_parity_builder()
.into_trace_results_with_state(&res, &trace_types, &db)
.map_err(Eth::Error::from_eth_err)?;
Ok(trace_res)
})
.await
@ -223,7 +222,7 @@ where
&self,
hash: B256,
indices: Vec<usize>,
) -> EthResult<Option<LocalizedTransactionTrace>> {
) -> Result<Option<LocalizedTransactionTrace>, Eth::Error> {
if indices.len() != 1 {
// The OG impl failed if it gets more than a single index
return Ok(None)
@ -238,7 +237,7 @@ where
&self,
hash: B256,
index: usize,
) -> EthResult<Option<LocalizedTransactionTrace>> {
) -> Result<Option<LocalizedTransactionTrace>, Eth::Error> {
Ok(self.trace_transaction(hash).await?.and_then(|traces| traces.into_iter().nth(index)))
}
@ -249,20 +248,21 @@ where
pub async fn trace_filter(
&self,
filter: TraceFilter,
) -> EthResult<Vec<LocalizedTransactionTrace>> {
) -> Result<Vec<LocalizedTransactionTrace>, Eth::Error> {
let matcher = filter.matcher();
let TraceFilter { from_block, to_block, after, count, .. } = filter;
let start = from_block.unwrap_or(0);
let end = if let Some(to_block) = to_block {
to_block
} else {
self.provider().best_block_number()?
self.provider().best_block_number().map_err(Eth::Error::from_eth_err)?
};
if start > end {
return Err(EthApiError::InvalidParams(
"invalid parameters: fromBlock cannot be greater than toBlock".to_string(),
))
)
.into())
}
// ensure that the range is not too large, since we need to fetch all blocks in the range
@ -270,11 +270,12 @@ where
if distance > 100 {
return Err(EthApiError::InvalidParams(
"Block range too large; currently limited to 100 blocks".to_string(),
))
)
.into())
}
// fetch all blocks in that range
let blocks = self.provider().block_range(start..=end)?;
let blocks = self.provider().block_range(start..=end).map_err(Eth::Error::from_eth_err)?;
// find relevant blocks to trace
let mut target_blocks = Vec::new();
@ -282,7 +283,10 @@ where
let mut transaction_indices = HashSet::new();
let mut highest_matching_index = 0;
for (tx_idx, tx) in block.body.iter().enumerate() {
let from = tx.recover_signer_unchecked().ok_or(BlockError::InvalidSignature)?;
let from = tx
.recover_signer_unchecked()
.ok_or(BlockError::InvalidSignature)
.map_err(Eth::Error::from_eth_err)?;
let to = tx.to();
if matcher.matches(from, to) {
let idx = tx_idx as u64;
@ -362,7 +366,7 @@ where
pub async fn trace_transaction(
&self,
hash: B256,
) -> EthResult<Option<Vec<LocalizedTransactionTrace>>> {
) -> Result<Option<Vec<LocalizedTransactionTrace>>, Eth::Error> {
self.inner
.eth_api
.spawn_trace_transaction_in_block(
@ -383,7 +387,7 @@ where
pub async fn trace_block(
&self,
block_id: BlockId,
) -> EthResult<Option<Vec<LocalizedTransactionTrace>>> {
) -> Result<Option<Vec<LocalizedTransactionTrace>>, Eth::Error> {
let traces = self.inner.eth_api.trace_block_with(
block_id,
TracingInspectorConfig::default_parity(),
@ -420,7 +424,7 @@ where
&self,
block_id: BlockId,
trace_types: HashSet<TraceType>,
) -> EthResult<Option<Vec<TraceResultsWithTransactionHash>>> {
) -> Result<Option<Vec<TraceResultsWithTransactionHash>>, Eth::Error> {
self.inner
.eth_api
.trace_block_with(
@ -433,7 +437,8 @@ where
// If statediffs were requested, populate them with the account balance and
// nonce from pre-state
if let Some(ref mut state_diff) = full_trace.state_diff {
populate_state_diff(state_diff, db, state.iter())?;
populate_state_diff(state_diff, db, state.iter())
.map_err(Eth::Error::from_eth_err)?;
}
let trace = TraceResultsWithTransactionHash {
@ -451,7 +456,7 @@ where
pub async fn trace_transaction_opcode_gas(
&self,
tx_hash: B256,
) -> EthResult<Option<TransactionOpcodeGas>> {
) -> Result<Option<TransactionOpcodeGas>, Eth::Error> {
self.inner
.eth_api
.spawn_trace_transaction_in_block_with_inspector(
@ -475,7 +480,7 @@ where
pub async fn trace_block_opcode_gas(
&self,
block_id: BlockId,
) -> EthResult<Option<BlockOpcodeGas>> {
) -> Result<Option<BlockOpcodeGas>, Eth::Error> {
let res = self
.inner
.eth_api
@ -508,7 +513,7 @@ where
/// - if Paris hardfork is activated, no block rewards are given
/// - if Paris hardfork is not activated, calculate block rewards with block number only
/// - if Paris hardfork is unknown, calculate block rewards with block number and ttd
fn calculate_base_block_reward(&self, header: &Header) -> EthResult<Option<u128>> {
fn calculate_base_block_reward(&self, header: &Header) -> Result<Option<u128>, Eth::Error> {
let chain_spec = self.provider().chain_spec();
let is_paris_activated = chain_spec.is_paris_active_at_block(header.number);
@ -518,7 +523,11 @@ where
None => {
// if Paris hardfork is unknown, we need to fetch the total difficulty at the
// block's height and check if it is pre-merge to calculate the base block reward
if let Some(header_td) = self.provider().header_td_by_number(header.number)? {
if let Some(header_td) = self
.provider()
.header_td_by_number(header.number)
.map_err(Eth::Error::from_eth_err)?
{
base_block_reward(
chain_spec.as_ref(),
header.number,
@ -584,11 +593,11 @@ where
block_id: Option<BlockId>,
state_overrides: Option<StateOverride>,
block_overrides: Option<Box<BlockOverrides>>,
) -> Result<TraceResults> {
) -> RpcResult<TraceResults> {
let _permit = self.acquire_trace_permit().await;
let request =
TraceCallRequest { call, trace_types, block_id, state_overrides, block_overrides };
Ok(Self::trace_call(self, request).await?)
Ok(Self::trace_call(self, request).await.map_err(Into::into)?)
}
/// Handler for `trace_callMany`
@ -596,9 +605,9 @@ where
&self,
calls: Vec<(TransactionRequest, HashSet<TraceType>)>,
block_id: Option<BlockId>,
) -> Result<Vec<TraceResults>> {
) -> RpcResult<Vec<TraceResults>> {
let _permit = self.acquire_trace_permit().await;
Ok(Self::trace_call_many(self, calls, block_id).await?)
Ok(Self::trace_call_many(self, calls, block_id).await.map_err(Into::into)?)
}
/// Handler for `trace_rawTransaction`
@ -607,9 +616,11 @@ where
data: Bytes,
trace_types: HashSet<TraceType>,
block_id: Option<BlockId>,
) -> Result<TraceResults> {
) -> RpcResult<TraceResults> {
let _permit = self.acquire_trace_permit().await;
Ok(Self::trace_raw_transaction(self, data, trace_types, block_id).await?)
Ok(Self::trace_raw_transaction(self, data, trace_types, block_id)
.await
.map_err(Into::into)?)
}
/// Handler for `trace_replayBlockTransactions`
@ -617,9 +628,11 @@ where
&self,
block_id: BlockId,
trace_types: HashSet<TraceType>,
) -> Result<Option<Vec<TraceResultsWithTransactionHash>>> {
) -> RpcResult<Option<Vec<TraceResultsWithTransactionHash>>> {
let _permit = self.acquire_trace_permit().await;
Ok(Self::replay_block_transactions(self, block_id, trace_types).await?)
Ok(Self::replay_block_transactions(self, block_id, trace_types)
.await
.map_err(Into::into)?)
}
/// Handler for `trace_replayTransaction`
@ -627,18 +640,18 @@ where
&self,
transaction: B256,
trace_types: HashSet<TraceType>,
) -> Result<TraceResults> {
) -> RpcResult<TraceResults> {
let _permit = self.acquire_trace_permit().await;
Ok(Self::replay_transaction(self, transaction, trace_types).await?)
Ok(Self::replay_transaction(self, transaction, trace_types).await.map_err(Into::into)?)
}
/// Handler for `trace_block`
async fn trace_block(
&self,
block_id: BlockId,
) -> Result<Option<Vec<LocalizedTransactionTrace>>> {
) -> RpcResult<Option<Vec<LocalizedTransactionTrace>>> {
let _permit = self.acquire_trace_permit().await;
Ok(Self::trace_block(self, block_id).await?)
Ok(Self::trace_block(self, block_id).await.map_err(Into::into)?)
}
/// Handler for `trace_filter`
@ -647,8 +660,8 @@ where
///
/// # Limitations
/// This currently requires block filter fields, since reth does not have address indices yet.
async fn trace_filter(&self, filter: TraceFilter) -> Result<Vec<LocalizedTransactionTrace>> {
Ok(Self::trace_filter(self, filter).await?)
async fn trace_filter(&self, filter: TraceFilter) -> RpcResult<Vec<LocalizedTransactionTrace>> {
Ok(Self::trace_filter(self, filter).await.map_err(Into::into)?)
}
/// Returns transaction trace at given index.
@ -657,33 +670,35 @@ where
&self,
hash: B256,
indices: Vec<Index>,
) -> Result<Option<LocalizedTransactionTrace>> {
) -> RpcResult<Option<LocalizedTransactionTrace>> {
let _permit = self.acquire_trace_permit().await;
Ok(Self::trace_get(self, hash, indices.into_iter().map(Into::into).collect()).await?)
Ok(Self::trace_get(self, hash, indices.into_iter().map(Into::into).collect())
.await
.map_err(Into::into)?)
}
/// Handler for `trace_transaction`
async fn trace_transaction(
&self,
hash: B256,
) -> Result<Option<Vec<LocalizedTransactionTrace>>> {
) -> RpcResult<Option<Vec<LocalizedTransactionTrace>>> {
let _permit = self.acquire_trace_permit().await;
Ok(Self::trace_transaction(self, hash).await?)
Ok(Self::trace_transaction(self, hash).await.map_err(Into::into)?)
}
/// Handler for `trace_transactionOpcodeGas`
async fn trace_transaction_opcode_gas(
&self,
tx_hash: B256,
) -> Result<Option<TransactionOpcodeGas>> {
) -> RpcResult<Option<TransactionOpcodeGas>> {
let _permit = self.acquire_trace_permit().await;
Ok(Self::trace_transaction_opcode_gas(self, tx_hash).await?)
Ok(Self::trace_transaction_opcode_gas(self, tx_hash).await.map_err(Into::into)?)
}
/// Handler for `trace_blockOpcodeGas`
async fn trace_block_opcode_gas(&self, block_id: BlockId) -> Result<Option<BlockOpcodeGas>> {
async fn trace_block_opcode_gas(&self, block_id: BlockId) -> RpcResult<Option<BlockOpcodeGas>> {
let _permit = self.acquire_trace_permit().await;
Ok(Self::trace_block_opcode_gas(self, block_id).await?)
Ok(Self::trace_block_opcode_gas(self, block_id).await.map_err(Into::into)?)
}
}

View File

@ -186,7 +186,7 @@ where
Ok(())
}
/// Shard and insert the indice list according to [`LoadMode`] and its length.
/// Shard and insert the indices list according to [`LoadMode`] and its length.
pub(crate) fn load_indices<H, C, P>(
cursor: &mut C,
partial_key: P,