From 789bb9c852429594eff3aa194b6333eca01bd30b Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 17 Dec 2018 20:40:25 +0100 Subject: [PATCH] Fill transaction hash on ethGetLog of light client. (#9938) * Fill transaction hash on ethGetLog of light client. This is enifficient but we keep align with spec. * Using better variables names. * Expose old fast light get log as `parity_getLogsLight`. * Update rpc/src/v1/helpers/light_fetch.rs Fix indent. Co-Authored-By: cheme * Factor common code between light_logs and logs. * Remove useless check * Rename parity light logs to 'parity_getLogsNoTransactionHash'. Fix indent and extra white lines. * Use vec instead of tree map to avoid inner function. * better loop --- rpc/src/v1/helpers/light_fetch.rs | 44 +++++++++++++++++++++++---- rpc/src/v1/impls/eth.rs | 50 +++++++++++++++++-------------- rpc/src/v1/impls/light/parity.rs | 14 +++++++-- rpc/src/v1/impls/parity.rs | 8 ++++- rpc/src/v1/traits/parity.rs | 8 ++++- 5 files changed, 92 insertions(+), 32 deletions(-) diff --git a/rpc/src/v1/helpers/light_fetch.rs b/rpc/src/v1/helpers/light_fetch.rs index cf854c77b..0473ff4c8 100644 --- a/rpc/src/v1/helpers/light_fetch.rs +++ b/rpc/src/v1/helpers/light_fetch.rs @@ -46,6 +46,7 @@ use ethereum_types::{U256, Address}; use hash::H256; use parking_lot::Mutex; use fastmap::H256FastMap; +use std::collections::BTreeMap; use transaction::{Action, Transaction as EthTransaction, PendingTransaction, SignedTransaction, LocalizedTransaction}; use v1::helpers::{CallRequest as CallRequestHelper, errors, dispatch}; @@ -310,9 +311,7 @@ impl LightFetch { })) } - /// Get transaction logs - pub fn logs(&self, filter: EthcoreFilter) -> impl Future, Error = Error> + Send { - use std::collections::BTreeMap; + pub fn logs_no_tx_hash(&self, filter: EthcoreFilter) -> impl Future, Error = Error> + Send { use jsonrpc_core::futures::stream::{self, Stream}; const MAX_BLOCK_RANGE: u64 = 1000; @@ -343,7 +342,7 @@ impl LightFetch { // insert them into a BTreeMap to maintain order by number and block index. stream::futures_unordered(receipts_futures) .fold(BTreeMap::new(), move |mut matches, (num, hash, receipts)| { - let mut block_index = 0; + let mut block_index: usize = 0; for (transaction_index, receipt) in receipts.into_iter().enumerate() { for (transaction_log_index, log) in receipt.logs.into_iter().enumerate() { if filter.matches(&log) { @@ -366,9 +365,9 @@ impl LightFetch { } } future::ok::<_,OnDemandError>(matches) - }) // and then collect them into a vector. - .map(|matches| matches.into_iter().map(|(_, v)| v).collect()) + }) .map_err(errors::on_demand_error) + .map(|matches| matches.into_iter().map(|(_, v)| v).collect()) }); match maybe_future { @@ -378,6 +377,39 @@ impl LightFetch { }) } + + /// Get transaction logs + pub fn logs(&self, filter: EthcoreFilter) -> impl Future, Error = Error> + Send { + use jsonrpc_core::futures::stream::{self, Stream}; + let fetcher_block = self.clone(); + self.logs_no_tx_hash(filter) + // retrieve transaction hash. + .and_then(move |mut result| { + let mut blocks = BTreeMap::new(); + for log in result.iter() { + let block_hash = log.block_hash.as_ref().expect("Previously initialized with value; qed"); + blocks.entry(block_hash.clone()).or_insert_with(|| { + fetcher_block.block(BlockId::Hash(block_hash.clone().into())) + }); + } + // future get blocks (unordered it) + stream::futures_unordered(blocks.into_iter().map(|(_, v)| v)).collect().map(move |blocks| { + let transactions_per_block: BTreeMap<_, _> = blocks.iter() + .map(|block| (block.hash(), block.transactions())).collect(); + for log in result.iter_mut() { + let log_index: U256 = log.transaction_index.expect("Previously initialized with value; qed").into(); + let block_hash = log.block_hash.clone().expect("Previously initialized with value; qed").into(); + let tx_hash = transactions_per_block.get(&block_hash) + // transaction index is from an enumerate call in log common so not need to check value + .and_then(|txs| txs.get(log_index.as_usize())) + .map(|tr| tr.hash().into()); + log.transaction_hash = tx_hash; + } + result + }) + }) + } + // Get a transaction by hash. also returns the index in the block. // Only returns transactions in the canonical chain. pub fn transaction_by_hash(&self, tx_hash: H256) diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 3a2e58040..8cf5104fd 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -146,6 +146,33 @@ enum PendingTransactionId { Location(PendingOrBlock, usize) } +pub fn base_logs (client: &C, miner: &M, filter: Filter) -> BoxFuture> where + C: miner::BlockChainClient + BlockChainClient + StateClient + Call, + M: MinerService { + let include_pending = filter.to_block == Some(BlockNumber::Pending); + let filter: EthcoreFilter = match filter.try_into() { + Ok(value) => value, + Err(err) => return Box::new(future::err(err)), + }; + let mut logs = match client.logs(filter.clone()) { + Ok(logs) => logs + .into_iter() + .map(From::from) + .collect::>(), + Err(id) => return Box::new(future::err(errors::filter_block_not_found(id))), + }; + + if include_pending { + let best_block = client.chain_info().best_block_number; + let pending = pending_logs(&*miner, best_block, &filter); + logs.extend(pending); + } + + let logs = limit_logs(logs, filter.limit); + + Box::new(future::ok(logs)) +} + impl EthClient where C: miner::BlockChainClient + BlockChainClient + StateClient + Call + EngineInfo, SN: SnapshotService, @@ -803,28 +830,7 @@ impl Eth for EthClient< } fn logs(&self, filter: Filter) -> BoxFuture> { - let include_pending = filter.to_block == Some(BlockNumber::Pending); - let filter: EthcoreFilter = match filter.try_into() { - Ok(value) => value, - Err(err) => return Box::new(future::err(err)), - }; - let mut logs = match self.client.logs(filter.clone()) { - Ok(logs) => logs - .into_iter() - .map(From::from) - .collect::>(), - Err(id) => return Box::new(future::err(errors::filter_block_not_found(id))), - }; - - if include_pending { - let best_block = self.client.chain_info().best_block_number; - let pending = pending_logs(&*self.miner, best_block, &filter); - logs.extend(pending); - } - - let logs = limit_logs(logs, filter.limit); - - Box::new(future::ok(logs)) + base_logs(&*self.client, &*self.miner, filter.into()) } fn work(&self, no_new_work_timeout: Trailing) -> Result { diff --git a/rpc/src/v1/impls/light/parity.rs b/rpc/src/v1/impls/light/parity.rs index c281760f3..397167ad3 100644 --- a/rpc/src/v1/impls/light/parity.rs +++ b/rpc/src/v1/impls/light/parity.rs @@ -28,7 +28,7 @@ use ethcore::account_provider::AccountProvider; use ethcore_logger::RotatingLogger; use jsonrpc_core::{Result, BoxFuture}; -use jsonrpc_core::futures::Future; +use jsonrpc_core::futures::{future, Future}; use jsonrpc_macros::Trailing; use v1::helpers::{self, errors, ipfs, SigningQueue, SignerService, NetworkSettings, verify_signature}; use v1::helpers::dispatch::LightDispatcher; @@ -41,7 +41,8 @@ use v1::types::{ TransactionStats, LocalTransactionStatus, LightBlockNumber, ChainStatus, Receipt, BlockNumber, ConsensusCapability, VersionInfo, - OperationsInfo, AccountInfo, HwAccountInfo, Header, RichHeader, RecoveredAccount + OperationsInfo, AccountInfo, HwAccountInfo, Header, RichHeader, RecoveredAccount, + Log, Filter, }; use Host; @@ -425,6 +426,15 @@ impl Parity for ParityClient { Err(errors::status_error(has_peers)) } } + + fn logs_no_tx_hash(&self, filter: Filter) -> BoxFuture> { + let filter = match filter.try_into() { + Ok(value) => value, + Err(err) => return Box::new(future::err(err)), + }; + Box::new(self.fetcher().logs_no_tx_hash(filter)) as BoxFuture<_> + } + fn verify_signature(&self, is_prefixed: bool, message: Bytes, r: H256, s: H256, v: U64) -> Result { verify_signature(is_prefixed, message, r, s, v, self.light_dispatch.client.signing_chain_id()) } diff --git a/rpc/src/v1/impls/parity.rs b/rpc/src/v1/impls/parity.rs index 9d73238a0..c2d57a345 100644 --- a/rpc/src/v1/impls/parity.rs +++ b/rpc/src/v1/impls/parity.rs @@ -47,7 +47,7 @@ use v1::types::{ Peers, Transaction, RpcSettings, Histogram, TransactionStats, LocalTransactionStatus, BlockNumber, ConsensusCapability, VersionInfo, - OperationsInfo, ChainStatus, + OperationsInfo, ChainStatus, Log, Filter, AccountInfo, HwAccountInfo, RichHeader, Receipt, RecoveredAccount, block_number_to_id }; @@ -505,6 +505,12 @@ impl Parity for ParityClient where } } + fn logs_no_tx_hash(&self, filter: Filter) -> BoxFuture> { + use v1::impls::eth::base_logs; + // only specific impl for lightclient + base_logs(&*self.client, &*self.miner, filter.into()) + } + fn verify_signature(&self, is_prefixed: bool, message: Bytes, r: H256, s: H256, v: U64) -> Result { verify_signature(is_prefixed, message, r, s, v, self.client.signing_chain_id()) } diff --git a/rpc/src/v1/traits/parity.rs b/rpc/src/v1/traits/parity.rs index 5ac467018..c4a697783 100644 --- a/rpc/src/v1/traits/parity.rs +++ b/rpc/src/v1/traits/parity.rs @@ -25,7 +25,7 @@ use v1::types::{ Peers, Transaction, RpcSettings, Histogram, RecoveredAccount, TransactionStats, LocalTransactionStatus, BlockNumber, ConsensusCapability, VersionInfo, - OperationsInfo, ChainStatus, + OperationsInfo, ChainStatus, Log, Filter, AccountInfo, HwAccountInfo, RichHeader, Receipt, }; @@ -241,5 +241,11 @@ build_rpc_trait! { /// as well as checks the signature for chain replay protection #[rpc(name = "parity_verifySignature")] fn verify_signature(&self, bool, Bytes, H256, H256, U64) -> Result; + + /// Returns logs matching given filter object. + /// Is allowed to skip filling transaction hash for faster query. + #[rpc(name = "parity_getLogsNoTransactionHash")] + fn logs_no_tx_hash(&self, Filter) -> BoxFuture>; + } }