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:
tzapu 2018-01-10 12:34:34 +02:00 committed by Afri Schoedon
parent b685b7fae3
commit df5d27d516
7 changed files with 63 additions and 20 deletions

View File

@ -1133,7 +1133,13 @@ impl Client {
}.fake_sign(from) }.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>( fn call<V, T>(
state: &mut State<StateDB>, state: &mut State<StateDB>,
env_info: &EnvInfo, env_info: &EnvInfo,
@ -1159,7 +1165,6 @@ impl Client {
} }
let state_diff = analytics.state_diffing; let state_diff = analytics.state_diffing;
let machine = self.engine.machine();
match (analytics.transaction_tracing, analytics.vm_tracing) { match (analytics.transaction_tracing, analytics.vm_tracing) {
(true, true) => call(state, env_info, machine, state_diff, t, TransactOptions::with_tracing_and_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. // that's just a copy of the state.
let mut state = self.state_at(block).ok_or(CallError::StatePruned)?; 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> { 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. // that's just a copy of the state.
let mut state = self.state_at(block).ok_or(CallError::StatePruned)?; let mut state = self.state_at(block).ok_or(CallError::StatePruned)?;
let mut results = Vec::with_capacity(transactions.len()); let mut results = Vec::with_capacity(transactions.len());
let machine = self.engine.machine();
for &(ref t, analytics) in transactions { 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; env_info.gas_used = ret.cumulative_gas_used;
results.push(ret); results.push(ret);
} }
@ -1295,28 +1302,33 @@ impl BlockChainClient for Client {
fn replay(&self, id: TransactionId, analytics: CallAnalytics) -> Result<Executed, CallError> { fn replay(&self, id: TransactionId, analytics: CallAnalytics) -> Result<Executed, CallError> {
let address = self.transaction_address(id).ok_or(CallError::TransactionNotFound)?; 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 block = BlockId::Hash(address.block_hash);
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();
if address.index >= txs.len() { const PROOF: &'static str = "The transaction address contains a valid index within block; qed";
return Err(CallError::TransactionNotFound); 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"; const PROOF: &'static str = "Transactions fetched from blockchain; blockchain transactions are valid; qed";
let rest = txs.split_off(address.index); const EXECUTE_PROOF: &'static str = "Transaction replayed; qed";
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);
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 { fn mode(&self) -> IpcMode {
let r = self.mode.lock().clone().into(); let r = self.mode.lock().clone().into();
trace!(target: "mode", "Asked for mode = {:?}. returning {:?}", &*self.mode.lock(), r); trace!(target: "mode", "Asked for mode = {:?}. returning {:?}", &*self.mode.lock(), r);

View File

@ -431,6 +431,10 @@ impl BlockChainClient for TestBlockChainClient {
self.execution_result.read().clone().unwrap() 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> { fn block_total_difficulty(&self, _id: BlockId) -> Option<U256> {
Some(U256::zero()) Some(U256::zero())
} }

View File

@ -196,6 +196,9 @@ pub trait BlockChainClient : Sync + Send {
/// Replays a given transaction for inspection. /// Replays a given transaction for inspection.
fn replay(&self, t: TransactionId, analytics: CallAnalytics) -> Result<Executed, CallError>; 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. /// Returns traces matching given filter.
fn filter_traces(&self, filter: TraceFilter) -> Option<Vec<LocalizedTrace>>; fn filter_traces(&self, filter: TraceFilter) -> Option<Vec<LocalizedTrace>>;

View File

@ -61,4 +61,8 @@ impl Traces for TracesClient {
fn replay_transaction(&self, _transaction_hash: H256, _flags: TraceOptions) -> Result<TraceResults> { fn replay_transaction(&self, _transaction_hash: H256, _flags: TraceOptions) -> Result<TraceResults> {
Err(errors::light_unimplemented(None)) Err(errors::light_unimplemented(None))
} }
fn replay_block_transactions(&self, _block_number: BlockNumber, _flags: TraceOptions) -> Result<Vec<TraceResults>> {
Err(errors::light_unimplemented(None))
}
} }

View File

@ -122,4 +122,10 @@ impl<C> Traces for TracesClient<C> where C: MiningBlockChainClient + 'static {
.map(TraceResults::from) .map(TraceResults::from)
.map_err(errors::call) .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)
}
} }

View File

@ -232,3 +232,13 @@ fn rpc_trace_replay_transaction_state_pruned() {
assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); 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()));
}

View File

@ -56,5 +56,9 @@ build_rpc_trait! {
/// Executes the transaction with the given hash and returns a number of possible traces for it. /// Executes the transaction with the given hash and returns a number of possible traces for it.
#[rpc(name = "trace_replayTransaction")] #[rpc(name = "trace_replayTransaction")]
fn replay_transaction(&self, H256, TraceOptions) -> Result<TraceResults>; 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>>;
} }
} }