chore(rpc): more trace prep (#1752)

This commit is contained in:
Matthias Seitz
2023-03-14 17:07:08 +01:00
committed by GitHub
parent 8e5644dac0
commit c78effcb3f
6 changed files with 107 additions and 41 deletions

View File

@@ -576,7 +576,9 @@ where
NetApi::new(self.network.clone(), eth_api.clone()).into_rpc().into()
}
RethRpcModule::Trace => {
TraceApi::new(self.client.clone(), eth_cache.clone()).into_rpc().into()
TraceApi::new(self.client.clone(), eth_api.clone(), eth_cache.clone())
.into_rpc()
.into()
}
RethRpcModule::Web3 => Web3Api::new(self.network.clone()).into_rpc().into(),
})

View File

@@ -198,9 +198,6 @@ where
assert!(is_unimplemented(
TraceApiClient::trace_get(client, H256::default(), vec![]).await.err().unwrap()
));
assert!(is_unimplemented(
TraceApiClient::trace_transaction(client, H256::default()).await.err().unwrap()
));
}
async fn test_basic_web3_calls<C>(client: &C)

View File

@@ -4,6 +4,7 @@ use crate::{
eth::{
error::{EthApiError, EthResult, InvalidTransactionError, RevertError},
revm_utils::{build_call_evm_env, get_precompiles, inspect, transact},
EthTransactions,
},
EthApi,
};
@@ -18,6 +19,7 @@ use reth_rpc_types::{
state::{AccountOverride, StateOverride},
CallRequest,
};
use reth_transaction_pool::TransactionPool;
use revm::{
db::{CacheDB, DatabaseRef},
primitives::{
@@ -33,31 +35,10 @@ const MIN_CREATE_GAS: u64 = 53_000u64;
impl<Client, Pool, Network> EthApi<Client, Pool, Network>
where
Pool: TransactionPool + Clone + 'static,
Client: BlockProvider + StateProviderFactory + EvmEnvProvider + 'static,
Network: Send + Sync + 'static,
{
/// Returns the revm evm env for the requested [BlockId]
///
/// If the [BlockId] this will return the [BlockId::Hash] of the block the env was configured
/// for.
async fn evm_env_at(&self, at: BlockId) -> EthResult<(CfgEnv, BlockEnv, BlockId)> {
// TODO handle Pending state's env
match at {
BlockId::Number(BlockNumberOrTag::Pending) => {
// This should perhaps use the latest env settings and update block specific
// settings like basefee/number
unimplemented!("support pending state env")
}
hash_or_num => {
let block_hash = self
.client()
.block_hash_for_id(hash_or_num)?
.ok_or_else(|| EthApiError::UnknownBlockNumber)?;
let (cfg, env) = self.cache().get_evm_env(block_hash).await?;
Ok((cfg, env, block_hash.into()))
}
}
}
/// Executes the call request at the given [BlockId]
pub(crate) async fn call_at(
&self,

View File

@@ -27,10 +27,10 @@ use std::collections::BTreeMap;
#[async_trait::async_trait]
impl<Client, Pool, Network> EthApiServer for EthApi<Client, Pool, Network>
where
Self: EthApiSpec,
Self: EthApiSpec + EthTransactions,
Pool: TransactionPool + 'static,
Client: BlockProvider + HeaderProvider + StateProviderFactory + EvmEnvProvider + 'static,
Network: 'static,
Network: Send + Sync + 'static,
{
/// Handler for: `eth_protocolVersion`
async fn protocol_version(&self) -> Result<U64> {

View File

@@ -5,32 +5,64 @@ use crate::{
};
use async_trait::async_trait;
use reth_primitives::{
BlockId, Bytes, FromRecoveredTransaction, IntoRecoveredTransaction, TransactionSigned,
TransactionSignedEcRecovered, H256, U256,
BlockId, BlockNumberOrTag, Bytes, FromRecoveredTransaction, IntoRecoveredTransaction,
TransactionSigned, TransactionSignedEcRecovered, H256, U256,
};
use reth_provider::{BlockProvider, EvmEnvProvider, StateProviderFactory, TransactionsProvider};
use reth_provider::{BlockProvider, EvmEnvProvider, StateProviderFactory};
use reth_rlp::Decodable;
use reth_rpc_types::{Index, Transaction, TransactionRequest};
use reth_transaction_pool::{TransactionOrigin, TransactionPool};
use revm::primitives::{BlockEnv, CfgEnv};
/// Commonly used transaction related functions for the [EthApi] type in the `eth_` namespace
#[async_trait::async_trait]
pub trait EthTransactions: Send + Sync {
/// Returns the revm evm env for the requested [BlockId]
///
/// If the [BlockId] this will return the [BlockId::Hash] of the block the env was configured
/// for.
async fn evm_env_at(&self, at: BlockId) -> EthResult<(CfgEnv, BlockEnv, BlockId)>;
/// Returns the transaction by hash.
///
/// Checks the pool and state.
///
/// Returns `Ok(None)` if no matching transaction was found.
async fn transaction_by_hash(&self, hash: H256) -> EthResult<Option<TransactionSource>>;
/// Returns the transaction by including its corresponding [BlockId]
async fn transaction_by_hash_at(
&self,
hash: H256,
) -> EthResult<Option<(TransactionSource, BlockId)>>;
}
#[async_trait]
impl<Client, Pool, Network> EthTransactions for EthApi<Client, Pool, Network>
where
Pool: TransactionPool + Clone + 'static,
Client: TransactionsProvider + 'static,
Client: BlockProvider + StateProviderFactory + EvmEnvProvider + 'static,
Network: Send + Sync + 'static,
{
async fn evm_env_at(&self, at: BlockId) -> EthResult<(CfgEnv, BlockEnv, BlockId)> {
// TODO handle Pending state's env
match at {
BlockId::Number(BlockNumberOrTag::Pending) => {
// This should perhaps use the latest env settings and update block specific
// settings like basefee/number
unimplemented!("support pending state env")
}
hash_or_num => {
let block_hash = self
.client()
.block_hash_for_id(hash_or_num)?
.ok_or_else(|| EthApiError::UnknownBlockNumber)?;
let (cfg, env) = self.cache().get_evm_env(block_hash).await?;
Ok((cfg, env, block_hash.into()))
}
}
}
async fn transaction_by_hash(&self, hash: H256) -> EthResult<Option<TransactionSource>> {
if let Some(tx) = self.pool().get(&hash).map(|tx| tx.transaction.to_recovered_transaction())
{
@@ -55,6 +87,38 @@ where
}
}
}
async fn transaction_by_hash_at(
&self,
hash: H256,
) -> EthResult<Option<(TransactionSource, BlockId)>> {
match self.transaction_by_hash(hash).await? {
None => return Ok(None),
Some(tx) => {
let res = match tx {
tx @ TransactionSource::Pool(_) => {
(tx, BlockId::Number(BlockNumberOrTag::Pending))
}
TransactionSource::Database {
transaction,
index,
block_hash,
block_number,
} => {
let at = BlockId::Hash(block_hash.into());
let tx = TransactionSource::Database {
transaction,
index,
block_hash,
block_number,
};
(tx, at)
}
};
Ok(Some(res))
}
}
}
}
// === impl EthApi ===
@@ -140,6 +204,15 @@ pub enum TransactionSource {
},
}
impl From<TransactionSource> for TransactionSignedEcRecovered {
fn from(value: TransactionSource) -> Self {
match value {
TransactionSource::Pool(tx) => tx,
TransactionSource::Database { transaction, .. } => transaction,
}
}
}
impl From<TransactionSource> for Transaction {
fn from(value: TransactionSource) -> Self {
match value {

View File

@@ -1,4 +1,7 @@
use crate::{eth::cache::EthStateCache, result::internal_rpc_err};
use crate::{
eth::{cache::EthStateCache, EthTransactions},
result::internal_rpc_err,
};
use async_trait::async_trait;
use jsonrpsee::core::RpcResult as Result;
use reth_primitives::{BlockId, Bytes, H256};
@@ -14,26 +17,29 @@ use std::collections::HashSet;
///
/// This type provides the functionality for handling `trace` related requests.
#[derive(Clone)]
pub struct TraceApi<Client> {
pub struct TraceApi<Client, Eth> {
/// The client that can interact with the chain.
client: Client,
/// Access to commonly used code of the `eth` namespace
eth_api: Eth,
/// The async cache frontend for eth related data
eth_cache: EthStateCache,
}
// === impl TraceApi ===
impl<Client> TraceApi<Client> {
impl<Client, Eth> TraceApi<Client, Eth> {
/// Create a new instance of the [TraceApi]
pub fn new(client: Client, eth_cache: EthStateCache) -> Self {
Self { client, eth_cache }
pub fn new(client: Client, eth_api: Eth, eth_cache: EthStateCache) -> Self {
Self { client, eth_api, eth_cache }
}
}
#[async_trait]
impl<Client> TraceApiServer for TraceApi<Client>
impl<Client, Eth> TraceApiServer for TraceApi<Client, Eth>
where
Client: BlockProvider + StateProviderFactory + EvmEnvProvider + 'static,
Eth: EthTransactions + 'static,
{
/// Handler for `trace_call`
async fn trace_call(
@@ -107,13 +113,20 @@ where
/// Handler for `trace_transaction`
async fn trace_transaction(
&self,
_hash: H256,
hash: H256,
) -> Result<Option<Vec<LocalizedTransactionTrace>>> {
let (_transaction, at) = match self.eth_api.transaction_by_hash_at(hash).await? {
None => return Ok(None),
Some(res) => res,
};
let (_cfg, _block_env, _at) = self.eth_api.evm_env_at(at).await?;
Err(internal_rpc_err("unimplemented"))
}
}
impl<Client> std::fmt::Debug for TraceApi<Client> {
impl<Client, Eth> std::fmt::Debug for TraceApi<Client, Eth> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TraceApi").finish_non_exhaustive()
}