From df5d27d5163e8d6f6d00f4f8996ce384fcf804cf Mon Sep 17 00:00:00 2001 From: tzapu Date: Wed, 10 Jan 2018 12:34:34 +0200 Subject: [PATCH] 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 --- ethcore/src/client/client.rs | 52 +++++++++++++++++++------------ ethcore/src/client/test_client.rs | 4 +++ ethcore/src/client/traits.rs | 3 ++ rpc/src/v1/impls/light/trace.rs | 4 +++ rpc/src/v1/impls/traces.rs | 6 ++++ rpc/src/v1/tests/mocked/traces.rs | 10 ++++++ rpc/src/v1/traits/traces.rs | 4 +++ 7 files changed, 63 insertions(+), 20 deletions(-) diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index c114a62ac..ff8df21e5 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -1133,7 +1133,13 @@ impl Client { }.fake_sign(from) } - fn do_virtual_call(&self, env_info: &EnvInfo, state: &mut State, t: &SignedTransaction, analytics: CallAnalytics) -> Result { + fn do_virtual_call( + machine: &::machine::EthereumMachine, + env_info: &EnvInfo, + state: &mut State, + t: &SignedTransaction, + analytics: CallAnalytics, + ) -> Result { fn call( state: &mut State, 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, 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 { 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>, 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); diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index cc2f08bab..6965e1bd3 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -431,6 +431,10 @@ impl BlockChainClient for TestBlockChainClient { self.execution_result.read().clone().unwrap() } + fn replay_block_transactions(&self, _block: BlockId, _analytics: CallAnalytics) -> Result>, CallError> { + Ok(Box::new(self.execution_result.read().clone().unwrap().into_iter())) + } + fn block_total_difficulty(&self, _id: BlockId) -> Option { Some(U256::zero()) } diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index 58a05e4e0..e0b3a3e74 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -196,6 +196,9 @@ pub trait BlockChainClient : Sync + Send { /// Replays a given transaction for inspection. fn replay(&self, t: TransactionId, analytics: CallAnalytics) -> Result; + /// Replays all the transactions in a given block for inspection. + fn replay_block_transactions(&self, block: BlockId, analytics: CallAnalytics) -> Result>, CallError>; + /// Returns traces matching given filter. fn filter_traces(&self, filter: TraceFilter) -> Option>; diff --git a/rpc/src/v1/impls/light/trace.rs b/rpc/src/v1/impls/light/trace.rs index ae2763545..1d2c7fcaa 100644 --- a/rpc/src/v1/impls/light/trace.rs +++ b/rpc/src/v1/impls/light/trace.rs @@ -61,4 +61,8 @@ impl Traces for TracesClient { fn replay_transaction(&self, _transaction_hash: H256, _flags: TraceOptions) -> Result { Err(errors::light_unimplemented(None)) } + + fn replay_block_transactions(&self, _block_number: BlockNumber, _flags: TraceOptions) -> Result> { + Err(errors::light_unimplemented(None)) + } } diff --git a/rpc/src/v1/impls/traces.rs b/rpc/src/v1/impls/traces.rs index 34fe36e67..258f6747f 100644 --- a/rpc/src/v1/impls/traces.rs +++ b/rpc/src/v1/impls/traces.rs @@ -122,4 +122,10 @@ impl Traces for TracesClient where C: MiningBlockChainClient + 'static { .map(TraceResults::from) .map_err(errors::call) } + + fn replay_block_transactions(&self, block_number: BlockNumber, flags: TraceOptions) -> Result> { + 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) + } } diff --git a/rpc/src/v1/tests/mocked/traces.rs b/rpc/src/v1/tests/mocked/traces.rs index 491e9dadb..0b2e7d5cc 100644 --- a/rpc/src/v1/tests/mocked/traces.rs +++ b/rpc/src/v1/tests/mocked/traces.rs @@ -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())); +} diff --git a/rpc/src/v1/traits/traces.rs b/rpc/src/v1/traits/traces.rs index 35b601e3f..1fe01f47e 100644 --- a/rpc/src/v1/traits/traces.rs +++ b/rpc/src/v1/traits/traces.rs @@ -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; + + /// 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>; } }