mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-01-30 01:28:21 -05:00
fix(rpc): use parent block state when tracing blocks (#2574)
This commit is contained in:
@@ -41,15 +41,28 @@ pub fn fill_cfg_env(
|
||||
cfg_env.perf_all_precompiles_have_balance = false;
|
||||
cfg_env.perf_analyse_created_bytecodes = AnalysisKind::Analyse;
|
||||
}
|
||||
|
||||
/// Fill block environment from Block.
|
||||
pub fn fill_block_env(
|
||||
block_env: &mut BlockEnv,
|
||||
chain_spec: &ChainSpec,
|
||||
header: &Header,
|
||||
after_merge: bool,
|
||||
) {
|
||||
let coinbase = block_coinbase(chain_spec, header, after_merge);
|
||||
fill_block_env_with_coinbase(block_env, header, after_merge, coinbase);
|
||||
}
|
||||
|
||||
/// Fill block environment with coinbase.
|
||||
#[inline]
|
||||
pub fn fill_block_env_with_coinbase(
|
||||
block_env: &mut BlockEnv,
|
||||
header: &Header,
|
||||
after_merge: bool,
|
||||
coinbase: Address,
|
||||
) {
|
||||
block_env.number = U256::from(header.number);
|
||||
block_env.coinbase = block_coinbase(chain_spec, header, after_merge);
|
||||
block_env.coinbase = coinbase;
|
||||
block_env.timestamp = U256::from(header.timestamp);
|
||||
if after_merge {
|
||||
block_env.prevrandao = Some(header.mix_hash);
|
||||
|
||||
@@ -162,11 +162,8 @@ where
|
||||
.await
|
||||
.err()
|
||||
.unwrap();
|
||||
TraceApiClient::trace_block(client, block_id).await.err().unwrap();
|
||||
TraceApiClient::replay_block_transactions(client, block_id, HashSet::default())
|
||||
.await
|
||||
.err()
|
||||
.unwrap();
|
||||
TraceApiClient::trace_block(client, block_id).await.unwrap();
|
||||
TraceApiClient::replay_block_transactions(client, block_id, HashSet::default()).await.unwrap();
|
||||
|
||||
assert!(is_unimplemented(
|
||||
TraceApiClient::trace_filter(client, trace_filter).await.err().unwrap()
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::{
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use jsonrpsee::core::RpcResult;
|
||||
use reth_primitives::{Block, BlockId, BlockNumberOrTag, Bytes, H256, U256};
|
||||
use reth_primitives::{Block, BlockId, BlockNumberOrTag, Bytes, TransactionSigned, H256, U256};
|
||||
use reth_provider::{BlockProvider, HeaderProvider, StateProviderBox};
|
||||
use reth_revm::{
|
||||
database::{State, SubState},
|
||||
@@ -27,6 +27,7 @@ use reth_rpc_types::{
|
||||
BlockError, CallRequest, RichBlock,
|
||||
};
|
||||
use revm::primitives::Env;
|
||||
use revm_primitives::{BlockEnv, CfgEnv};
|
||||
|
||||
/// `debug` API implementation.
|
||||
///
|
||||
@@ -37,7 +38,6 @@ pub struct DebugApi<Client, Eth> {
|
||||
client: Client,
|
||||
/// The implementation of `eth` API
|
||||
eth_api: Eth,
|
||||
|
||||
// restrict the number of concurrent calls to `debug_traceTransaction`
|
||||
tracing_call_guard: TracingCallGuard,
|
||||
}
|
||||
@@ -58,42 +58,15 @@ where
|
||||
Client: BlockProvider + HeaderProvider + 'static,
|
||||
Eth: EthTransactions + 'static,
|
||||
{
|
||||
/// Replays the given block and returns the trace of each transaction.
|
||||
///
|
||||
/// This expects a rlp encoded block
|
||||
///
|
||||
/// Note, the parent of this block must be present, or it will fail.
|
||||
pub async fn debug_trace_raw_block(
|
||||
/// Trace the entire block
|
||||
fn trace_block_with(
|
||||
&self,
|
||||
rlp_block: Bytes,
|
||||
_opts: GethDebugTracingOptions,
|
||||
) -> EthResult<Vec<TraceResult>> {
|
||||
let block =
|
||||
Block::decode(&mut rlp_block.as_ref()).map_err(BlockError::RlpDecodeRawBlock)?;
|
||||
let _parent = block.parent_hash;
|
||||
|
||||
// TODO we need the state after the parent block
|
||||
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Replays a block and returns the trace of each transaction.
|
||||
pub async fn debug_trace_block(
|
||||
&self,
|
||||
block_id: BlockId,
|
||||
at: BlockId,
|
||||
transactions: Vec<TransactionSigned>,
|
||||
cfg: CfgEnv,
|
||||
block_env: BlockEnv,
|
||||
opts: GethDebugTracingOptions,
|
||||
) -> EthResult<Vec<TraceResult>> {
|
||||
let block_hash = self
|
||||
.client
|
||||
.block_hash_for_id(block_id)?
|
||||
.ok_or_else(|| EthApiError::UnknownBlockNumber)?;
|
||||
|
||||
let ((cfg, block_env, at), transactions) = futures::try_join!(
|
||||
self.eth_api.evm_env_at(block_hash.into()),
|
||||
self.eth_api.transactions_by_block(block_hash),
|
||||
)?;
|
||||
let transactions = transactions.ok_or_else(|| EthApiError::UnknownBlockNumber)?;
|
||||
|
||||
// replay all transactions of the block
|
||||
self.eth_api.with_state_at_block(at, move |state| {
|
||||
let mut results = Vec::with_capacity(transactions.len());
|
||||
@@ -112,6 +85,50 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
/// Replays the given block and returns the trace of each transaction.
|
||||
///
|
||||
/// This expects a rlp encoded block
|
||||
///
|
||||
/// Note, the parent of this block must be present, or it will fail.
|
||||
pub async fn debug_trace_raw_block(
|
||||
&self,
|
||||
rlp_block: Bytes,
|
||||
opts: GethDebugTracingOptions,
|
||||
) -> EthResult<Vec<TraceResult>> {
|
||||
let block =
|
||||
Block::decode(&mut rlp_block.as_ref()).map_err(BlockError::RlpDecodeRawBlock)?;
|
||||
|
||||
let (cfg, block_env) = self.eth_api.evm_env_for_raw_block(&block.header).await?;
|
||||
|
||||
// we trace on top the block's parent block
|
||||
let parent = block.parent_hash;
|
||||
self.trace_block_with(parent.into(), block.body, cfg, block_env, opts)
|
||||
}
|
||||
|
||||
/// Replays a block and returns the trace of each transaction.
|
||||
pub async fn debug_trace_block(
|
||||
&self,
|
||||
block_id: BlockId,
|
||||
opts: GethDebugTracingOptions,
|
||||
) -> EthResult<Vec<TraceResult>> {
|
||||
let block_hash = self
|
||||
.client
|
||||
.block_hash_for_id(block_id)?
|
||||
.ok_or_else(|| EthApiError::UnknownBlockNumber)?;
|
||||
|
||||
let ((cfg, block_env, _), block) = futures::try_join!(
|
||||
self.eth_api.evm_env_at(block_hash.into()),
|
||||
self.eth_api.block_by_id(block_id),
|
||||
)?;
|
||||
|
||||
let block = block.ok_or_else(|| EthApiError::UnknownBlockNumber)?;
|
||||
// we need to get the state of the parent block because we're replaying this block on top of
|
||||
// its parent block's state
|
||||
let state_at = block.parent_hash;
|
||||
|
||||
self.trace_block_with(state_at.into(), block.body, cfg, block_env, opts)
|
||||
}
|
||||
|
||||
/// Trace the transaction according to the provided options.
|
||||
///
|
||||
/// Ref: <https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers>
|
||||
@@ -128,10 +145,10 @@ where
|
||||
|
||||
// we need to get the state of the parent block because we're essentially replaying the
|
||||
// block the transaction is included in
|
||||
let parent_block = block.parent_hash;
|
||||
let state_at = block.parent_hash;
|
||||
let block_txs = block.body;
|
||||
|
||||
self.eth_api.with_state_at_block(parent_block.into(), |state| {
|
||||
self.eth_api.with_state_at_block(state_at.into(), |state| {
|
||||
// configure env for the target transaction
|
||||
let tx = transaction.into_recovered();
|
||||
|
||||
|
||||
@@ -11,15 +11,15 @@ use async_trait::async_trait;
|
||||
|
||||
use reth_network_api::NetworkInfo;
|
||||
use reth_primitives::{
|
||||
Address, BlockId, BlockNumberOrTag, Bytes, FromRecoveredTransaction, IntoRecoveredTransaction,
|
||||
Receipt, SealedBlock,
|
||||
Address, BlockId, BlockNumberOrTag, Bytes, FromRecoveredTransaction, Header,
|
||||
IntoRecoveredTransaction, Receipt, SealedBlock,
|
||||
TransactionKind::{Call, Create},
|
||||
TransactionMeta, TransactionSigned, TransactionSignedEcRecovered, H256, U128, U256, U64,
|
||||
};
|
||||
use reth_provider::{BlockProvider, EvmEnvProvider, StateProviderBox, StateProviderFactory};
|
||||
use reth_revm::{
|
||||
database::{State, SubState},
|
||||
env::tx_env_with_recovered,
|
||||
env::{fill_block_env_with_coinbase, tx_env_with_recovered},
|
||||
tracing::{TracingInspector, TracingInspectorConfig},
|
||||
};
|
||||
use reth_rpc_types::{
|
||||
@@ -32,7 +32,7 @@ use revm::{
|
||||
primitives::{BlockEnv, CfgEnv},
|
||||
Inspector,
|
||||
};
|
||||
use revm_primitives::{utilities::create_address, Env, ResultAndState};
|
||||
use revm_primitives::{utilities::create_address, Env, ResultAndState, SpecId};
|
||||
|
||||
/// Commonly used transaction related functions for the [EthApi] type in the `eth_` namespace
|
||||
#[async_trait::async_trait]
|
||||
@@ -51,12 +51,22 @@ pub trait EthTransactions: Send + Sync {
|
||||
/// for.
|
||||
async fn evm_env_at(&self, at: BlockId) -> EthResult<(CfgEnv, BlockEnv, BlockId)>;
|
||||
|
||||
/// Returns the revm evm env for the raw block header
|
||||
///
|
||||
/// This is used for tracing raw blocks
|
||||
async fn evm_env_for_raw_block(&self, at: &Header) -> EthResult<(CfgEnv, BlockEnv)>;
|
||||
|
||||
/// Get all transactions in the block with the given hash.
|
||||
///
|
||||
/// Returns `None` if block does not exist.
|
||||
async fn transactions_by_block(&self, block: H256)
|
||||
-> EthResult<Option<Vec<TransactionSigned>>>;
|
||||
|
||||
/// Get the entire block for the given id.
|
||||
///
|
||||
/// Returns `None` if block does not exist.
|
||||
async fn block_by_id(&self, id: BlockId) -> EthResult<Option<SealedBlock>>;
|
||||
|
||||
/// Get all transactions in the block with the given hash.
|
||||
///
|
||||
/// Returns `None` if block does not exist.
|
||||
@@ -205,6 +215,16 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
async fn evm_env_for_raw_block(&self, header: &Header) -> EthResult<(CfgEnv, BlockEnv)> {
|
||||
// get the parent config first
|
||||
let (cfg, mut block_env, _) = self.evm_env_at(header.parent_hash.into()).await?;
|
||||
|
||||
let after_merge = cfg.spec_id >= SpecId::MERGE;
|
||||
fill_block_env_with_coinbase(&mut block_env, header, after_merge, header.beneficiary);
|
||||
|
||||
Ok((cfg, block_env))
|
||||
}
|
||||
|
||||
async fn transactions_by_block(
|
||||
&self,
|
||||
block: H256,
|
||||
@@ -212,14 +232,15 @@ where
|
||||
Ok(self.cache().get_block_transactions(block).await?)
|
||||
}
|
||||
|
||||
async fn block_by_id(&self, id: BlockId) -> EthResult<Option<SealedBlock>> {
|
||||
self.block(id).await
|
||||
}
|
||||
|
||||
async fn transactions_by_block_id(
|
||||
&self,
|
||||
block: BlockId,
|
||||
) -> EthResult<Option<Vec<TransactionSigned>>> {
|
||||
match self.client().block_hash_for_id(block)? {
|
||||
None => Ok(None),
|
||||
Some(hash) => self.transactions_by_block(hash).await,
|
||||
}
|
||||
self.block_by_id(block).await.map(|block| block.map(|block| block.body))
|
||||
}
|
||||
|
||||
async fn transaction_by_hash(&self, hash: H256) -> EthResult<Option<TransactionSource>> {
|
||||
@@ -248,9 +269,9 @@ where
|
||||
|
||||
async fn transaction_by_hash_at(
|
||||
&self,
|
||||
hash: H256,
|
||||
transaction_hash: H256,
|
||||
) -> EthResult<Option<(TransactionSource, BlockId)>> {
|
||||
match self.transaction_by_hash(hash).await? {
|
||||
match self.transaction_by_hash(transaction_hash).await? {
|
||||
None => return Ok(None),
|
||||
Some(tx) => {
|
||||
let res = match tx {
|
||||
|
||||
@@ -213,20 +213,26 @@ where
|
||||
where
|
||||
F: Fn(TransactionInfo, TracingInspector, ResultAndState) -> EthResult<R> + Send,
|
||||
{
|
||||
let block_hash = match self.client.block_hash_for_id(block_id)? {
|
||||
Some(hash) => hash,
|
||||
let ((cfg, block_env, _), block) = futures::try_join!(
|
||||
self.eth_api.evm_env_at(block_id),
|
||||
self.eth_api.block_by_id(block_id),
|
||||
)?;
|
||||
|
||||
let block = match block {
|
||||
Some(block) => block,
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
let ((cfg, block_env, at), transactions) = futures::try_join!(
|
||||
self.eth_api.evm_env_at(block_hash.into()),
|
||||
self.eth_api.transactions_by_block(block_hash),
|
||||
)?;
|
||||
let transactions = transactions.ok_or_else(|| EthApiError::UnknownBlockNumber)?;
|
||||
// we need to get the state of the parent block because we're replaying this block on top of
|
||||
// its parent block's state
|
||||
let state_at = block.parent_hash;
|
||||
|
||||
let block_hash = block.hash;
|
||||
let transactions = block.body;
|
||||
|
||||
// replay all transactions of the block
|
||||
self.eth_api
|
||||
.with_state_at_block(at, move |state| {
|
||||
.with_state_at_block(state_at.into(), move |state| {
|
||||
let mut results = Vec::with_capacity(transactions.len());
|
||||
let mut db = SubState::new(State::new(state));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user