added a new RPC call trace_replayBlockTransactions (#7366)
* intial add trace_replayBlockTransactions * cleanup timing calls add execution proof * WIP implementing changes * fix for trace_replayBlockTransactions rpc call * cleanup comments * cleanup, proof, can't workout lifetime issues yet * Fix lifetimes issue. * naive rpc test * updated docs
This commit is contained in:
parent
b685b7fae3
commit
df5d27d516
@ -1133,7 +1133,13 @@ impl Client {
|
||||
}.fake_sign(from)
|
||||
}
|
||||
|
||||
fn do_virtual_call(&self, env_info: &EnvInfo, state: &mut State<StateDB>, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, CallError> {
|
||||
fn do_virtual_call(
|
||||
machine: &::machine::EthereumMachine,
|
||||
env_info: &EnvInfo,
|
||||
state: &mut State<StateDB>,
|
||||
t: &SignedTransaction,
|
||||
analytics: CallAnalytics,
|
||||
) -> Result<Executed, CallError> {
|
||||
fn call<V, T>(
|
||||
state: &mut State<StateDB>,
|
||||
env_info: &EnvInfo,
|
||||
@ -1159,7 +1165,6 @@ impl Client {
|
||||
}
|
||||
|
||||
let state_diff = analytics.state_diffing;
|
||||
let machine = self.engine.machine();
|
||||
|
||||
match (analytics.transaction_tracing, analytics.vm_tracing) {
|
||||
(true, true) => call(state, env_info, machine, state_diff, t, TransactOptions::with_tracing_and_vm_tracing()),
|
||||
@ -1208,8 +1213,9 @@ impl BlockChainClient for Client {
|
||||
|
||||
// that's just a copy of the state.
|
||||
let mut state = self.state_at(block).ok_or(CallError::StatePruned)?;
|
||||
let machine = self.engine.machine();
|
||||
|
||||
self.do_virtual_call(&env_info, &mut state, transaction, analytics)
|
||||
Self::do_virtual_call(machine, &env_info, &mut state, transaction, analytics)
|
||||
}
|
||||
|
||||
fn call_many(&self, transactions: &[(SignedTransaction, CallAnalytics)], block: BlockId) -> Result<Vec<Executed>, CallError> {
|
||||
@ -1219,9 +1225,10 @@ impl BlockChainClient for Client {
|
||||
// that's just a copy of the state.
|
||||
let mut state = self.state_at(block).ok_or(CallError::StatePruned)?;
|
||||
let mut results = Vec::with_capacity(transactions.len());
|
||||
let machine = self.engine.machine();
|
||||
|
||||
for &(ref t, analytics) in transactions {
|
||||
let ret = self.do_virtual_call(&env_info, &mut state, t, analytics)?;
|
||||
let ret = Self::do_virtual_call(machine, &env_info, &mut state, t, analytics)?;
|
||||
env_info.gas_used = ret.cumulative_gas_used;
|
||||
results.push(ret);
|
||||
}
|
||||
@ -1295,28 +1302,33 @@ impl BlockChainClient for Client {
|
||||
|
||||
fn replay(&self, id: TransactionId, analytics: CallAnalytics) -> Result<Executed, CallError> {
|
||||
let address = self.transaction_address(id).ok_or(CallError::TransactionNotFound)?;
|
||||
let mut env_info = self.env_info(BlockId::Hash(address.block_hash)).ok_or(CallError::StatePruned)?;
|
||||
let body = self.block_body(BlockId::Hash(address.block_hash)).ok_or(CallError::StatePruned)?;
|
||||
let mut state = self.state_at_beginning(BlockId::Hash(address.block_hash)).ok_or(CallError::StatePruned)?;
|
||||
let mut txs = body.transactions();
|
||||
let block = BlockId::Hash(address.block_hash);
|
||||
|
||||
if address.index >= txs.len() {
|
||||
return Err(CallError::TransactionNotFound);
|
||||
const PROOF: &'static str = "The transaction address contains a valid index within block; qed";
|
||||
Ok(self.replay_block_transactions(block, analytics)?.nth(address.index).expect(PROOF))
|
||||
}
|
||||
|
||||
fn replay_block_transactions(&self, block: BlockId, analytics: CallAnalytics) -> Result<Box<Iterator<Item = Executed>>, CallError> {
|
||||
let mut env_info = self.env_info(block).ok_or(CallError::StatePruned)?;
|
||||
let body = self.block_body(block).ok_or(CallError::StatePruned)?;
|
||||
let mut state = self.state_at_beginning(block).ok_or(CallError::StatePruned)?;
|
||||
let txs = body.transactions();
|
||||
let engine = self.engine.clone();
|
||||
|
||||
const PROOF: &'static str = "Transactions fetched from blockchain; blockchain transactions are valid; qed";
|
||||
let rest = txs.split_off(address.index);
|
||||
for t in txs {
|
||||
let t = SignedTransaction::new(t).expect(PROOF);
|
||||
let x = Executive::new(&mut state, &env_info, self.engine.machine()).transact(&t, TransactOptions::with_no_tracing())?;
|
||||
env_info.gas_used = env_info.gas_used + x.gas_used;
|
||||
}
|
||||
let first = rest.into_iter().next().expect("We split off < `address.index`; Length is checked earlier; qed");
|
||||
let t = SignedTransaction::new(first).expect(PROOF);
|
||||
const EXECUTE_PROOF: &'static str = "Transaction replayed; qed";
|
||||
|
||||
self.do_virtual_call(&env_info, &mut state, &t, analytics)
|
||||
Ok(Box::new(txs.into_iter()
|
||||
.map(move |t| {
|
||||
let t = SignedTransaction::new(t).expect(PROOF);
|
||||
let machine = engine.machine();
|
||||
let x = Self::do_virtual_call(machine, &env_info, &mut state, &t, analytics).expect(EXECUTE_PROOF);
|
||||
env_info.gas_used = env_info.gas_used + x.gas_used;
|
||||
x
|
||||
})))
|
||||
}
|
||||
|
||||
|
||||
fn mode(&self) -> IpcMode {
|
||||
let r = self.mode.lock().clone().into();
|
||||
trace!(target: "mode", "Asked for mode = {:?}. returning {:?}", &*self.mode.lock(), r);
|
||||
|
@ -431,6 +431,10 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
self.execution_result.read().clone().unwrap()
|
||||
}
|
||||
|
||||
fn replay_block_transactions(&self, _block: BlockId, _analytics: CallAnalytics) -> Result<Box<Iterator<Item = Executed>>, CallError> {
|
||||
Ok(Box::new(self.execution_result.read().clone().unwrap().into_iter()))
|
||||
}
|
||||
|
||||
fn block_total_difficulty(&self, _id: BlockId) -> Option<U256> {
|
||||
Some(U256::zero())
|
||||
}
|
||||
|
@ -196,6 +196,9 @@ pub trait BlockChainClient : Sync + Send {
|
||||
/// Replays a given transaction for inspection.
|
||||
fn replay(&self, t: TransactionId, analytics: CallAnalytics) -> Result<Executed, CallError>;
|
||||
|
||||
/// Replays all the transactions in a given block for inspection.
|
||||
fn replay_block_transactions(&self, block: BlockId, analytics: CallAnalytics) -> Result<Box<Iterator<Item = Executed>>, CallError>;
|
||||
|
||||
/// Returns traces matching given filter.
|
||||
fn filter_traces(&self, filter: TraceFilter) -> Option<Vec<LocalizedTrace>>;
|
||||
|
||||
|
@ -61,4 +61,8 @@ impl Traces for TracesClient {
|
||||
fn replay_transaction(&self, _transaction_hash: H256, _flags: TraceOptions) -> Result<TraceResults> {
|
||||
Err(errors::light_unimplemented(None))
|
||||
}
|
||||
|
||||
fn replay_block_transactions(&self, _block_number: BlockNumber, _flags: TraceOptions) -> Result<Vec<TraceResults>> {
|
||||
Err(errors::light_unimplemented(None))
|
||||
}
|
||||
}
|
||||
|
@ -122,4 +122,10 @@ impl<C> Traces for TracesClient<C> where C: MiningBlockChainClient + 'static {
|
||||
.map(TraceResults::from)
|
||||
.map_err(errors::call)
|
||||
}
|
||||
|
||||
fn replay_block_transactions(&self, block_number: BlockNumber, flags: TraceOptions) -> Result<Vec<TraceResults>> {
|
||||
self.client.replay_block_transactions(block_number.into(), to_call_analytics(flags))
|
||||
.map(|results| results.into_iter().map(TraceResults::from).collect())
|
||||
.map_err(errors::call)
|
||||
}
|
||||
}
|
||||
|
@ -232,3 +232,13 @@ fn rpc_trace_replay_transaction_state_pruned() {
|
||||
|
||||
assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rpc_trace_replay_block_transactions() {
|
||||
let tester = io();
|
||||
|
||||
let request = r#"{"jsonrpc":"2.0","method":"trace_replayBlockTransactions","params":["0x10", ["trace", "stateDiff", "vmTrace"]],"id":1}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","result":[{"output":"0x010203","stateDiff":null,"trace":[],"vmTrace":null}],"id":1}"#;
|
||||
|
||||
assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned()));
|
||||
}
|
||||
|
@ -56,5 +56,9 @@ build_rpc_trait! {
|
||||
/// Executes the transaction with the given hash and returns a number of possible traces for it.
|
||||
#[rpc(name = "trace_replayTransaction")]
|
||||
fn replay_transaction(&self, H256, TraceOptions) -> Result<TraceResults>;
|
||||
|
||||
/// Executes all the transactions at the given block and returns a number of possible traces for each transaction.
|
||||
#[rpc(name = "trace_replayBlockTransactions")]
|
||||
fn replay_block_transactions(&self, BlockNumber, TraceOptions) -> Result<Vec<TraceResults>>;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user