fix(op): ensure EthApiServer helper trait method default impls, call OpEthApi overrides (#9879)

This commit is contained in:
Emilia Hane
2024-07-30 17:35:56 +02:00
committed by GitHub
parent cf10543d6d
commit e4ae2a7ac1
22 changed files with 400 additions and 239 deletions

View File

@ -13,8 +13,6 @@ workspace = true
[dependencies]
# reth
reth-chainspec.workspace = true
reth-errors.workspace = true
reth-evm-optimism.workspace = true
reth-evm.workspace = true
reth-primitives.workspace = true
@ -27,6 +25,8 @@ reth-tasks = { workspace = true, features = ["rayon"] }
reth-transaction-pool.workspace = true
reth-rpc.workspace = true
reth-node-api.workspace = true
reth-network-api.workspace = true
reth-network.workspace = true
# ethereum
alloy-primitives.workspace = true
@ -43,6 +43,7 @@ jsonrpsee-types.workspace = true
# misc
thiserror.workspace = true
serde = { workspace = true, features = ["derive"] }
derive_more.workspace = true
[dev-dependencies]
serde_json.workspace = true
@ -55,7 +56,6 @@ client = [
]
optimism = [
"reth-chainspec/optimism",
"reth-evm-optimism/optimism",
"reth-primitives/optimism",
"reth-provider/optimism",

View File

@ -1,22 +1,29 @@
//! Loads and formats OP block RPC response.
use reth_node_api::FullNodeComponents;
use reth_primitives::TransactionMeta;
use reth_provider::{BlockReaderIdExt, HeaderProvider};
use reth_rpc_eth_api::{
helpers::{EthApiSpec, EthBlocks, LoadBlock, LoadReceipt, LoadTransaction},
helpers::{
EthApiSpec, EthBlocks, LoadBlock, LoadPendingBlock, LoadReceipt, LoadTransaction,
SpawnBlocking,
},
FromEthApiError,
};
use reth_rpc_eth_types::{EthStateCache, ReceiptBuilder};
use reth_rpc_types::{AnyTransactionReceipt, BlockId};
use crate::{op_receipt_fields, OpEthApi};
use crate::{op_receipt_fields, OpEthApi, OpEthApiError};
impl<Eth> EthBlocks for OpEthApi<Eth>
impl<N> EthBlocks for OpEthApi<N>
where
Eth: EthBlocks + EthApiSpec + LoadTransaction,
Self: LoadBlock + EthApiSpec + LoadTransaction,
Self::Error: From<OpEthApiError>,
N: FullNodeComponents,
{
#[inline]
fn provider(&self) -> impl HeaderProvider {
EthBlocks::provider(&self.inner)
self.inner.provider()
}
async fn block_receipts(
@ -69,11 +76,17 @@ where
}
}
impl<Eth: LoadBlock> LoadBlock for OpEthApi<Eth> {
impl<N> LoadBlock for OpEthApi<N>
where
Self: LoadPendingBlock + SpawnBlocking,
N: FullNodeComponents,
{
#[inline]
fn provider(&self) -> impl BlockReaderIdExt {
LoadBlock::provider(&self.inner)
self.inner.provider()
}
#[inline]
fn cache(&self) -> &EthStateCache {
self.inner.cache()
}

View File

@ -1,28 +1,37 @@
use reth_evm::ConfigureEvm;
use reth_node_api::FullNodeComponents;
use reth_primitives::{
revm_primitives::{BlockEnv, OptimismFields, TxEnv},
Bytes,
Bytes, TxKind, U256,
};
use reth_rpc_eth_api::{
helpers::{Call, EthCall},
EthApiTypes, FromEthApiError,
helpers::{Call, EthCall, LoadState, SpawnBlocking},
FromEthApiError, IntoEthApiError,
};
use reth_rpc_eth_types::EthApiError;
use reth_rpc_eth_types::{revm_utils::CallFees, RpcInvalidTransactionError};
use reth_rpc_types::TransactionRequest;
use crate::OpEthApi;
use crate::{OpEthApi, OpEthApiError};
impl<Eth: EthCall> EthCall for OpEthApi<Eth> where EthApiError: From<Eth::Error> {}
impl<Eth> Call for OpEthApi<Eth>
impl<N> EthCall for OpEthApi<N>
where
Eth: Call + EthApiTypes,
EthApiError: From<Eth::Error>,
Self: Call,
N: FullNodeComponents,
{
}
impl<N> Call for OpEthApi<N>
where
Self: LoadState + SpawnBlocking,
Self::Error: From<OpEthApiError>,
N: FullNodeComponents,
{
#[inline]
fn call_gas_limit(&self) -> u64 {
self.inner.call_gas_limit()
self.inner.gas_cap()
}
#[inline]
fn evm_config(&self) -> &impl ConfigureEvm {
self.inner.evm_config()
}
@ -32,10 +41,68 @@ where
block_env: &BlockEnv,
request: TransactionRequest,
) -> Result<TxEnv, Self::Error> {
let mut env =
self.inner.create_txn_env(block_env, request).map_err(Self::Error::from_eth_err)?;
// 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_eth_err())
}
env.optimism = OptimismFields { enveloped_tx: Some(Bytes::new()), ..Default::default() };
let TransactionRequest {
from,
to,
gas_price,
max_fee_per_gas,
max_priority_fee_per_gas,
gas,
value,
input,
nonce,
access_list,
chain_id,
blob_versioned_hashes,
max_fee_per_blob_gas,
// authorization_list,
..
} = request;
let CallFees { max_priority_fee_per_gas, gas_price, max_fee_per_blob_gas } =
CallFees::ensure_fees(
gas_price.map(U256::from),
max_fee_per_gas.map(U256::from),
max_priority_fee_per_gas.map(U256::from),
block_env.basefee,
blob_versioned_hashes.as_deref(),
max_fee_per_blob_gas.map(U256::from),
block_env.get_blob_gasprice().map(U256::from),
)?;
let gas_limit = gas.unwrap_or_else(|| block_env.gas_limit.min(U256::from(u64::MAX)).to());
#[allow(clippy::needless_update)]
let env = TxEnv {
gas_limit: gas_limit
.try_into()
.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()
.map_err(Self::Error::from_eth_err)?
.unwrap_or_default(),
chain_id,
access_list: access_list.unwrap_or_default().into(),
// EIP-4844 fields
blob_hashes: blob_versioned_hashes.unwrap_or_default(),
max_fee_per_blob_gas,
// EIP-7702 fields
// authorization_list: TODO
authorization_list: Default::default(),
optimism: OptimismFields { enveloped_tx: Some(Bytes::new()), ..Default::default() },
};
Ok(env)
}

View File

@ -7,30 +7,53 @@ mod block;
mod call;
mod pending_block;
use std::{future::Future, sync::Arc};
use std::{fmt, sync::Arc};
use alloy_primitives::{Address, U64};
use reth_chainspec::{ChainInfo, ChainSpec};
use reth_errors::RethResult;
use alloy_primitives::U256;
use derive_more::Deref;
use reth_evm::ConfigureEvm;
use reth_node_api::{BuilderProvider, FullNodeComponents};
use reth_provider::{BlockReaderIdExt, ChainSpecProvider, HeaderProvider, StateProviderFactory};
use reth_rpc::eth::DevSigner;
use reth_network::NetworkHandle;
use reth_network_api::NetworkInfo;
use reth_node_api::{BuilderProvider, FullNodeComponents, FullNodeTypes};
use reth_provider::{
BlockIdReader, BlockNumReader, BlockReaderIdExt, ChainSpecProvider, HeaderProvider,
StateProviderFactory,
};
use reth_rpc::eth::{core::EthApiInner, DevSigner};
use reth_rpc_eth_api::{
helpers::{
AddDevSigners, EthApiSpec, EthFees, EthSigner, EthState, LoadFee, LoadState, SpawnBlocking,
Trace, UpdateRawTxForwarder,
AddDevSigners, EthApiSpec, EthFees, EthState, LoadBlock, LoadFee, LoadState, SpawnBlocking,
Trace,
},
EthApiTypes, RawTransactionForwarder,
EthApiTypes,
};
use reth_rpc_eth_types::{EthStateCache, FeeHistoryCache, GasPriceOracle};
use reth_tasks::{
pool::{BlockingTaskGuard, BlockingTaskPool},
TaskExecutor, TaskSpawner,
};
use reth_rpc_eth_types::EthStateCache;
use reth_rpc_types::SyncStatus;
use reth_tasks::{pool::BlockingTaskPool, TaskSpawner};
use reth_transaction_pool::TransactionPool;
use tokio::sync::{AcquireError, OwnedSemaphorePermit};
use crate::OpEthApiError;
/// Adapter for [`EthApiInner`], which holds all the data required to serve core `eth_` API.
pub type EthApiNodeBackend<N> = EthApiInner<
<N as FullNodeTypes>::Provider,
<N as FullNodeComponents>::Pool,
NetworkHandle,
<N as FullNodeComponents>::Evm,
>;
/// Adapter for [`EthApiBuilderCtx`].
pub type EthApiBuilderCtx<N> = reth_rpc_eth_types::EthApiBuilderCtx<
<N as FullNodeTypes>::Provider,
<N as FullNodeComponents>::Pool,
<N as FullNodeComponents>::Evm,
NetworkHandle,
TaskExecutor,
<N as FullNodeTypes>::Provider,
>;
/// OP-Reth `Eth` API implementation.
///
/// This type provides the functionality for handling `eth_` related requests.
@ -41,148 +64,187 @@ use crate::OpEthApiError;
///
/// This type implements the [`FullEthApi`](reth_rpc_eth_api::helpers::FullEthApi) by implemented
/// all the `Eth` helper traits and prerequisite traits.
#[derive(Debug, Clone)]
pub struct OpEthApi<Eth> {
inner: Eth,
#[derive(Clone, Deref)]
pub struct OpEthApi<N: FullNodeComponents> {
inner: Arc<EthApiNodeBackend<N>>,
}
impl<Eth> OpEthApi<Eth> {
/// Creates a new `OpEthApi` from the provided `Eth` implementation.
pub const fn new(inner: Eth) -> Self {
Self { inner }
impl<N: FullNodeComponents> OpEthApi<N> {
/// Creates a new instance for given context.
#[allow(clippy::type_complexity)]
pub fn with_spawner(ctx: &EthApiBuilderCtx<N>) -> Self {
let blocking_task_pool =
BlockingTaskPool::build().expect("failed to build blocking task pool");
let inner = EthApiInner::new(
ctx.provider.clone(),
ctx.pool.clone(),
ctx.network.clone(),
ctx.cache.clone(),
ctx.new_gas_price_oracle(),
ctx.config.rpc_gas_cap,
ctx.config.eth_proof_window,
blocking_task_pool,
ctx.new_fee_history_cache(),
ctx.evm_config.clone(),
ctx.executor.clone(),
None,
ctx.config.proof_permits,
);
Self { inner: Arc::new(inner) }
}
}
impl<Eth> EthApiTypes for OpEthApi<Eth>
impl<N> EthApiTypes for OpEthApi<N>
where
Eth: Send + Sync,
Self: Send + Sync,
N: FullNodeComponents,
{
type Error = OpEthApiError;
}
impl<Eth: EthApiSpec> EthApiSpec for OpEthApi<Eth> {
fn protocol_version(&self) -> impl Future<Output = RethResult<U64>> + Send {
self.inner.protocol_version()
impl<N> EthApiSpec for OpEthApi<N>
where
N: FullNodeComponents,
{
#[inline]
fn provider(&self) -> impl ChainSpecProvider + BlockNumReader {
self.inner.provider()
}
fn chain_id(&self) -> U64 {
self.inner.chain_id()
#[inline]
fn network(&self) -> impl NetworkInfo {
self.inner.network()
}
fn chain_info(&self) -> RethResult<ChainInfo> {
self.inner.chain_info()
#[inline]
fn starting_block(&self) -> U256 {
self.inner.starting_block()
}
fn accounts(&self) -> Vec<Address> {
self.inner.accounts()
}
fn is_syncing(&self) -> bool {
self.inner.is_syncing()
}
fn sync_status(&self) -> RethResult<SyncStatus> {
self.inner.sync_status()
}
fn chain_spec(&self) -> Arc<ChainSpec> {
self.inner.chain_spec()
#[inline]
fn signers(&self) -> &parking_lot::RwLock<Vec<Box<dyn reth_rpc_eth_api::helpers::EthSigner>>> {
self.inner.signers()
}
}
impl<Eth: SpawnBlocking> SpawnBlocking for OpEthApi<Eth> {
impl<N> SpawnBlocking for OpEthApi<N>
where
Self: Send + Sync + Clone + 'static,
N: FullNodeComponents,
{
#[inline]
fn io_task_spawner(&self) -> impl TaskSpawner {
self.inner.io_task_spawner()
self.inner.task_spawner()
}
#[inline]
fn tracing_task_pool(&self) -> &BlockingTaskPool {
self.inner.tracing_task_pool()
self.inner.blocking_task_pool()
}
fn acquire_owned(
&self,
) -> impl Future<Output = Result<OwnedSemaphorePermit, AcquireError>> + Send {
self.inner.acquire_owned()
}
fn acquire_many_owned(
&self,
n: u32,
) -> impl Future<Output = Result<OwnedSemaphorePermit, AcquireError>> + Send {
self.inner.acquire_many_owned(n)
#[inline]
fn tracing_task_guard(&self) -> &BlockingTaskGuard {
self.inner.blocking_task_guard()
}
}
impl<Eth: LoadFee> LoadFee for OpEthApi<Eth> {
fn provider(&self) -> impl reth_provider::BlockIdReader + HeaderProvider + ChainSpecProvider {
LoadFee::provider(&self.inner)
impl<N> LoadFee for OpEthApi<N>
where
Self: LoadBlock,
N: FullNodeComponents,
{
#[inline]
fn provider(&self) -> impl BlockIdReader + HeaderProvider + ChainSpecProvider {
self.inner.provider()
}
#[inline]
fn cache(&self) -> &EthStateCache {
LoadFee::cache(&self.inner)
self.inner.cache()
}
fn gas_oracle(&self) -> &reth_rpc_eth_types::GasPriceOracle<impl BlockReaderIdExt> {
#[inline]
fn gas_oracle(&self) -> &GasPriceOracle<impl BlockReaderIdExt> {
self.inner.gas_oracle()
}
fn fee_history_cache(&self) -> &reth_rpc_eth_types::FeeHistoryCache {
#[inline]
fn fee_history_cache(&self) -> &FeeHistoryCache {
self.inner.fee_history_cache()
}
}
impl<Eth: LoadState> LoadState for OpEthApi<Eth> {
impl<N> LoadState for OpEthApi<N>
where
Self: Send + Sync,
N: FullNodeComponents,
{
#[inline]
fn provider(&self) -> impl StateProviderFactory + ChainSpecProvider {
LoadState::provider(&self.inner)
self.inner.provider()
}
#[inline]
fn cache(&self) -> &EthStateCache {
LoadState::cache(&self.inner)
self.inner.cache()
}
#[inline]
fn pool(&self) -> impl TransactionPool {
LoadState::pool(&self.inner)
self.inner.pool()
}
}
impl<Eth: EthState> EthState for OpEthApi<Eth> {
impl<N> EthState for OpEthApi<N>
where
Self: LoadState + SpawnBlocking,
N: FullNodeComponents,
{
#[inline]
fn max_proof_window(&self) -> u64 {
self.inner.max_proof_window()
self.inner.eth_proof_window()
}
}
impl<Eth: EthFees> EthFees for OpEthApi<Eth> {}
impl<N> EthFees for OpEthApi<N>
where
Self: LoadFee,
N: FullNodeComponents,
{
}
impl<Eth: Trace> Trace for OpEthApi<Eth> {
impl<N> Trace for OpEthApi<N>
where
Self: LoadState,
N: FullNodeComponents,
{
#[inline]
fn evm_config(&self) -> &impl ConfigureEvm {
self.inner.evm_config()
}
}
impl<Eth: AddDevSigners> AddDevSigners for OpEthApi<Eth> {
fn signers(&self) -> &parking_lot::RwLock<Vec<Box<dyn EthSigner>>> {
self.inner.signers()
}
impl<N: FullNodeComponents> AddDevSigners for OpEthApi<N> {
fn with_dev_accounts(&self) {
*self.signers().write() = DevSigner::random_signers(20)
}
}
impl<Eth: UpdateRawTxForwarder> UpdateRawTxForwarder for OpEthApi<Eth> {
fn set_eth_raw_transaction_forwarder(&self, forwarder: Arc<dyn RawTransactionForwarder>) {
self.inner.set_eth_raw_transaction_forwarder(forwarder);
}
}
impl<N, Eth> BuilderProvider<N> for OpEthApi<Eth>
impl<N> BuilderProvider<N> for OpEthApi<N>
where
Eth: BuilderProvider<N>,
N: FullNodeComponents,
{
type Ctx<'a> = <Eth as BuilderProvider<N>>::Ctx<'a>;
type Ctx<'a> = &'a EthApiBuilderCtx<N>;
fn builder() -> Box<dyn for<'a> Fn(Self::Ctx<'a>) -> Self + Send> {
Box::new(|ctx| Self { inner: Eth::builder()(ctx) })
Box::new(|ctx| Self::with_spawner(ctx))
}
}
impl<N: FullNodeComponents> fmt::Debug for OpEthApi<N> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("OpEthApi").finish_non_exhaustive()
}
}

View File

@ -1,19 +1,21 @@
//! Loads OP pending block for a RPC response.
use reth_evm::ConfigureEvm;
use reth_node_api::FullNodeComponents;
use reth_primitives::{revm_primitives::BlockEnv, BlockNumber, B256};
use reth_provider::{
BlockReaderIdExt, ChainSpecProvider, EvmEnvProvider, ExecutionOutcome, StateProviderFactory,
};
use reth_rpc_eth_api::helpers::LoadPendingBlock;
use reth_rpc_eth_api::helpers::{LoadPendingBlock, SpawnBlocking};
use reth_rpc_eth_types::PendingBlock;
use reth_transaction_pool::TransactionPool;
use crate::OpEthApi;
impl<Eth> LoadPendingBlock for OpEthApi<Eth>
impl<N> LoadPendingBlock for OpEthApi<N>
where
Eth: LoadPendingBlock,
Self: SpawnBlocking,
N: FullNodeComponents,
{
#[inline]
fn provider(

View File

@ -1,5 +1,6 @@
//! Loads and formats OP receipt RPC response.
use reth_node_api::FullNodeComponents;
use reth_primitives::{Receipt, TransactionMeta, TransactionSigned};
use reth_rpc_eth_api::{
helpers::{EthApiSpec, LoadReceipt, LoadTransaction},
@ -8,15 +9,17 @@ use reth_rpc_eth_api::{
use reth_rpc_eth_types::{EthApiError, EthStateCache, ReceiptBuilder};
use reth_rpc_types::{AnyTransactionReceipt, OptimismTransactionReceiptFields};
use crate::{OpEthApi, OptimismTxMeta};
use crate::{OpEthApi, OpEthApiError, OptimismTxMeta};
impl<Eth> LoadReceipt for OpEthApi<Eth>
impl<N> LoadReceipt for OpEthApi<N>
where
Eth: LoadReceipt + EthApiSpec + LoadTransaction,
Self: EthApiSpec + LoadTransaction,
Self::Error: From<OpEthApiError>,
N: FullNodeComponents,
{
#[inline]
fn cache(&self) -> &EthStateCache {
LoadReceipt::cache(&self.inner)
self.inner.cache()
}
async fn build_transaction_receipt(

View File

@ -3,10 +3,11 @@
use std::sync::Arc;
use reth_evm_optimism::RethL1BlockInfo;
use reth_node_api::FullNodeComponents;
use reth_primitives::TransactionSigned;
use reth_provider::{BlockReaderIdExt, TransactionsProvider};
use reth_rpc_eth_api::{
helpers::{EthApiSpec, EthSigner, EthTransactions, LoadTransaction},
helpers::{EthApiSpec, EthSigner, EthTransactions, LoadTransaction, SpawnBlocking},
EthApiTypes, RawTransactionForwarder,
};
use reth_rpc_eth_types::EthStateCache;
@ -14,9 +15,13 @@ use revm::L1BlockInfo;
use crate::{OpEthApi, OpEthApiError};
impl<Eth: EthTransactions> EthTransactions for OpEthApi<Eth> {
impl<N> EthTransactions for OpEthApi<N>
where
Self: LoadTransaction,
N: FullNodeComponents,
{
fn provider(&self) -> impl BlockReaderIdExt {
EthTransactions::provider(&self.inner)
self.inner.provider()
}
fn raw_tx_forwarder(&self) -> Option<Arc<dyn RawTransactionForwarder>> {
@ -28,19 +33,23 @@ impl<Eth: EthTransactions> EthTransactions for OpEthApi<Eth> {
}
}
impl<Eth: LoadTransaction> LoadTransaction for OpEthApi<Eth> {
type Pool = Eth::Pool;
impl<N> LoadTransaction for OpEthApi<N>
where
Self: SpawnBlocking,
N: FullNodeComponents,
{
type Pool = N::Pool;
fn provider(&self) -> impl TransactionsProvider {
LoadTransaction::provider(&self.inner)
self.inner.provider()
}
fn cache(&self) -> &EthStateCache {
LoadTransaction::cache(&self.inner)
self.inner.cache()
}
fn pool(&self) -> &Self::Pool {
LoadTransaction::pool(&self.inner)
self.inner.pool()
}
}
@ -66,9 +75,11 @@ impl OptimismTxMeta {
}
}
impl<Eth> OpEthApi<Eth>
impl<N> OpEthApi<N>
where
Eth: EthApiSpec + LoadTransaction,
Self: EthApiSpec + LoadTransaction,
<Self as EthApiTypes>::Error: From<OpEthApiError>,
N: FullNodeComponents,
{
/// Builds [`OptimismTxMeta`] object using the provided [`TransactionSigned`], L1 block
/// info and block timestamp. The [`L1BlockInfo`] is used to calculate the l1 fee and l1 data
@ -86,15 +97,10 @@ where
let envelope_buf = tx.envelope_encoded();
let inner_l1_fee = l1_block_info
.l1_tx_data_fee(
&self.inner.chain_spec(),
block_timestamp,
&envelope_buf,
tx.is_deposit(),
)
.l1_tx_data_fee(&self.chain_spec(), block_timestamp, &envelope_buf, tx.is_deposit())
.map_err(|_| OpEthApiError::L1BlockFeeError)?;
let inner_l1_data_gas = l1_block_info
.l1_data_gas(&self.inner.chain_spec(), block_timestamp, &envelope_buf)
.l1_data_gas(&self.chain_spec(), block_timestamp, &envelope_buf)
.map_err(|_| OpEthApiError::L1BlockGasError)?;
(
Some(inner_l1_fee.saturating_to::<u128>()),