Skip to content

Commit

Permalink
finish debug trace call many
Browse files Browse the repository at this point in the history
  • Loading branch information
mattsse committed Aug 3, 2023
1 parent 1eb2abb commit 7c45ace
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 35 deletions.
72 changes: 70 additions & 2 deletions crates/rpc/rpc-types/src/eth/call.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use reth_primitives::{AccessList, Address, BlockId, Bytes, U256, U64, U8};
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize, Serializer};

use crate::BlockOverrides;

Expand All @@ -20,7 +20,60 @@ pub struct StateContext {
/// Block Number
pub block_number: Option<BlockId>,
/// Inclusive number of tx to replay in block. -1 means replay all
pub transaction_index: Option<isize>,
pub transaction_index: Option<TransactionIndex>,
}

/// Represents a transaction index where -1 means all transactions
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
pub enum TransactionIndex {
/// -1 means all transactions
#[default]
All,
/// Transaction index
Index(usize),
}

impl TransactionIndex {
/// Returns true if this is the all variant
pub fn is_all(&self) -> bool {
matches!(self, TransactionIndex::All)
}

/// Returns the index if this is the index variant
pub fn index(&self) -> Option<usize> {
match self {
TransactionIndex::All => None,
TransactionIndex::Index(idx) => Some(*idx),
}
}
}

impl Serialize for TransactionIndex {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
TransactionIndex::All => serializer.serialize_i8(-1),
TransactionIndex::Index(idx) => idx.serialize(serializer),
}
}
}

impl<'de> Deserialize<'de> for TransactionIndex {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
match isize::deserialize(deserializer)? {
-1 => Ok(TransactionIndex::All),
idx if idx < -1 => Err(serde::de::Error::custom(format!(
"Invalid transaction index, expected -1 or positive integer, got {}",
idx
))),
idx => Ok(TransactionIndex::Index(idx as usize)),
}
}
}

/// Call request
Expand Down Expand Up @@ -133,6 +186,21 @@ pub struct CallInputError;
mod tests {
use super::*;

#[test]
fn transaction_index() {
let s = "-1";
let idx = serde_json::from_str::<TransactionIndex>(s).unwrap();
assert_eq!(idx, TransactionIndex::All);

let s = "5";
let idx = serde_json::from_str::<TransactionIndex>(s).unwrap();
assert_eq!(idx, TransactionIndex::Index(5));

let s = "-2";
let res = serde_json::from_str::<TransactionIndex>(s);
assert!(res.is_err());
}

#[test]
fn serde_call_request() {
let s = r#"{"accessList":[],"data":"0x0902f1ac","to":"0xa478c2975ab1ea89e8196811f51a7b7ade33eb11","type":"0x02"}"#;
Expand Down
69 changes: 36 additions & 33 deletions crates/rpc/rpc/src/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,46 +348,50 @@ where
}

let StateContext { transaction_index, block_number } = state_context.unwrap_or_default();
let transaction_index = transaction_index.unwrap_or_default();

let transaction_index = transaction_index.unwrap_or(-1);
let state_at = block_number.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest));

let block_hash = self
.inner
.provider
.block_hash_for_id(state_at)?
.ok_or_else(|| EthApiError::UnknownBlockNumber)?;

let target_block = block_number.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest));
let ((cfg, block_env, _), block) = futures::try_join!(
self.inner.eth_api.evm_env_at(block_hash.into()),
self.inner.eth_api.block_by_id(state_at),
self.inner.eth_api.evm_env_at(target_block),
self.inner.eth_api.block_by_id(target_block),
)?;

let block = block.ok_or_else(|| EthApiError::UnknownBlockNumber)?;
let tracing_options = opts.unwrap_or_default();
let gas_limit = self.inner.eth_api.call_gas_limit();

// we're essentially replaying the transactions in the block here, hence we need the state
// that points to the beginning of the block, which is the state at the parent block
let mut at = block.parent_hash;
let mut replay_block_txs = true;

// but if all transactions are to be replayed, we can use the state at the block itself
let num_txs = transaction_index.index().unwrap_or(block.body.len());
if num_txs == block.body.len() {
at = block.hash;
replay_block_txs = false;
}

self.on_blocking_task(|this| async move {
this.inner.eth_api.with_state_at_block(state_at, |state| {
let gas_limit = this.inner.eth_api.call_gas_limit();
let this = self.clone();
self.inner
.eth_api
.spawn_with_state_at_block(at.into(), move |state| {
let mut results = Vec::with_capacity(bundles.len());
let mut db = SubState::new(State::new(state));

let mut transactions = if transaction_index == -1 {
block.body.into_iter()
} else {
block.body
[0..usize::try_from(transaction_index).expect("Tx index outside bounds.")]
.to_vec()
.into_iter()
};

// Execute all transactions until index
while let Some(tx) = transactions.next() {
let tx = tx.into_ecrecovered().ok_or(BlockError::InvalidSignature)?;
let tx = tx_env_with_recovered(&tx);
let env = Env { cfg: cfg.clone(), block: block_env.clone(), tx };
let (res, _) = transact(&mut db, env)?;
db.commit(res.state);
if replay_block_txs {
// only need to replay the transactions in the block if not all transactions are
// to be replayed
let transactions = block.body.into_iter().take(num_txs);

// Execute all transactions until index
for tx in transactions {
let tx = tx.into_ecrecovered().ok_or(BlockError::InvalidSignature)?;
let tx = tx_env_with_recovered(&tx);
let env = Env { cfg: cfg.clone(), block: block_env.clone(), tx };
let (res, _) = transact(&mut db, env)?;
db.commit(res.state);
}
}

// Trace all bundles
Expand All @@ -412,7 +416,7 @@ where
let (trace, state) = this.trace_transaction(
tracing_options.clone(),
env,
state_at,
target_block,
&mut db,
)?;

Expand All @@ -424,8 +428,7 @@ where
}
Ok(results)
})
})
.await
.await
}

/// Executes the configured transaction with the environment on the given database.
Expand Down

0 comments on commit 7c45ace

Please sign in to comment.