// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of OpenEthereum. // OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with OpenEthereum. If not, see . //! Traces api implementation. use std::sync::Arc; use ethcore::client::{ BlockChainClient, BlockId, Call, CallAnalytics, EngineInfo, StateClient, StateInfo, TraceId, TransactionId, }; use ethereum_types::H256; use types::transaction::{SignedTransaction, TypedTransaction}; use jsonrpc_core::Result; use v1::{ helpers::{errors, fake_sign}, traits::Traces, types::{ block_number_to_id, BlockNumber, Bytes, CallRequest, Index, LocalizedTrace, TraceFilter, TraceOptions, TraceResults, TraceResultsWithTransactionHash, }, Metadata, }; fn to_call_analytics(flags: TraceOptions) -> CallAnalytics { CallAnalytics { transaction_tracing: flags.contains(&("trace".to_owned())), vm_tracing: flags.contains(&("vmTrace".to_owned())), state_diffing: flags.contains(&("stateDiff".to_owned())), } } /// Traces api implementation. pub struct TracesClient { client: Arc, } impl TracesClient { /// Creates new Traces client. pub fn new(client: &Arc) -> Self { TracesClient { client: client.clone(), } } } impl Traces for TracesClient where S: StateInfo + 'static, C: BlockChainClient + StateClient + Call + EngineInfo + 'static, { type Metadata = Metadata; fn filter(&self, filter: TraceFilter) -> Result>> { Ok(self .client .filter_traces(filter.into()) .map(|traces| traces.into_iter().map(LocalizedTrace::from).collect())) } fn block_traces(&self, block_number: BlockNumber) -> Result>> { let id = match block_number { BlockNumber::Pending => return Ok(None), num => block_number_to_id(num), }; Ok(self .client .block_traces(id) .map(|traces| traces.into_iter().map(LocalizedTrace::from).collect())) } fn transaction_traces(&self, transaction_hash: H256) -> Result>> { Ok(self .client .transaction_traces(TransactionId::Hash(transaction_hash)) .map(|traces| traces.into_iter().map(LocalizedTrace::from).collect())) } fn trace(&self, transaction_hash: H256, address: Vec) -> Result> { let id = TraceId { transaction: TransactionId::Hash(transaction_hash), address: address.into_iter().map(|i| i.value()).collect(), }; Ok(self.client.trace(id).map(LocalizedTrace::from)) } fn call( &self, request: CallRequest, flags: TraceOptions, block: Option, ) -> Result { let block = block.unwrap_or_default(); let request = CallRequest::into(request); let signed = fake_sign::sign_call(request)?; let id = match block { BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), BlockNumber::Num(num) => BlockId::Number(num), BlockNumber::Earliest => BlockId::Earliest, BlockNumber::Latest => BlockId::Latest, BlockNumber::Pending => { return Err(errors::invalid_params( "`BlockNumber::Pending` is not supported", (), )) } }; let mut state = self.client.state_at(id).ok_or_else(errors::state_pruned)?; let header = self .client .block_header(id) .ok_or_else(errors::state_pruned)?; self.client .call( &signed, to_call_analytics(flags), &mut state, &header .decode(self.client.engine().params().eip1559_transition) .map_err(errors::decode)?, ) .map(TraceResults::from) .map_err(errors::call) } fn call_many( &self, requests: Vec<(CallRequest, TraceOptions)>, block: Option, ) -> Result> { let block = block.unwrap_or_default(); let requests = requests .into_iter() .map(|(request, flags)| { let request = CallRequest::into(request); let signed = fake_sign::sign_call(request)?; Ok((signed, to_call_analytics(flags))) }) .collect::>>()?; let id = match block { BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), BlockNumber::Num(num) => BlockId::Number(num), BlockNumber::Earliest => BlockId::Earliest, BlockNumber::Latest => BlockId::Latest, BlockNumber::Pending => { return Err(errors::invalid_params( "`BlockNumber::Pending` is not supported", (), )) } }; let mut state = self.client.state_at(id).ok_or_else(errors::state_pruned)?; let header = self .client .block_header(id) .ok_or_else(errors::state_pruned)?; self.client .call_many( &requests, &mut state, &header .decode(self.client.engine().params().eip1559_transition) .map_err(errors::decode)?, ) .map(|results| results.into_iter().map(TraceResults::from).collect()) .map_err(errors::call) } fn raw_transaction( &self, raw_transaction: Bytes, flags: TraceOptions, block: Option, ) -> Result { let block = block.unwrap_or_default(); let tx = TypedTransaction::decode(&raw_transaction.0) .map_err(|e| errors::invalid_params("Transaction is not in valid Format", e))?; let signed = SignedTransaction::new(tx).map_err(errors::transaction)?; let id = match block { BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), BlockNumber::Num(num) => BlockId::Number(num), BlockNumber::Earliest => BlockId::Earliest, BlockNumber::Latest => BlockId::Latest, BlockNumber::Pending => { return Err(errors::invalid_params( "`BlockNumber::Pending` is not supported", (), )) } }; let mut state = self.client.state_at(id).ok_or_else(errors::state_pruned)?; let header = self .client .block_header(id) .ok_or_else(errors::state_pruned)?; self.client .call( &signed, to_call_analytics(flags), &mut state, &header .decode(self.client.engine().params().eip1559_transition) .map_err(errors::decode)?, ) .map(TraceResults::from) .map_err(errors::call) } fn replay_transaction( &self, transaction_hash: H256, flags: TraceOptions, ) -> Result { self.client .replay( TransactionId::Hash(transaction_hash), to_call_analytics(flags), ) .map(TraceResults::from) .map_err(errors::call) } fn replay_block_transactions( &self, block_number: BlockNumber, flags: TraceOptions, ) -> Result> { let id = match block_number { BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), BlockNumber::Num(num) => BlockId::Number(num), BlockNumber::Earliest => BlockId::Earliest, BlockNumber::Latest => BlockId::Latest, BlockNumber::Pending => { return Err(errors::invalid_params( "`BlockNumber::Pending` is not supported", (), )) } }; self.client .replay_block_transactions(id, to_call_analytics(flags)) .map(|results| results.map(TraceResultsWithTransactionHash::from).collect()) .map_err(errors::call) } }