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)
|
}.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);
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
|
@ -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>>;
|
||||||
|
|
||||||
|
@ -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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()));
|
||||||
|
}
|
||||||
|
@ -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>>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user