log fetching for light client
This commit is contained in:
parent
a6b6c312b8
commit
f409168126
@ -537,23 +537,23 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
|
|||||||
Err(errors::deprecated("Compilation functionality is deprecated.".to_string()))
|
Err(errors::deprecated("Compilation functionality is deprecated.".to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn logs(&self, filter: Filter) -> Result<Vec<Log>, Error> {
|
fn logs(&self, filter: Filter) -> BoxFuture<Vec<Log>, Error> {
|
||||||
let include_pending = filter.to_block == Some(BlockNumber::Pending);
|
let include_pending = filter.to_block == Some(BlockNumber::Pending);
|
||||||
let filter: EthcoreFilter = filter.into();
|
let filter: EthcoreFilter = filter.into();
|
||||||
let mut logs = take_weak!(self.client).logs(filter.clone())
|
let mut logs = take_weakf!(self.client).logs(filter.clone())
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(From::from)
|
.map(From::from)
|
||||||
.collect::<Vec<Log>>();
|
.collect::<Vec<Log>>();
|
||||||
|
|
||||||
if include_pending {
|
if include_pending {
|
||||||
let best_block = take_weak!(self.client).chain_info().best_block_number;
|
let best_block = take_weakf!(self.client).chain_info().best_block_number;
|
||||||
let pending = pending_logs(&*take_weak!(self.miner), best_block, &filter);
|
let pending = pending_logs(&*take_weakf!(self.miner), best_block, &filter);
|
||||||
logs.extend(pending);
|
logs.extend(pending);
|
||||||
}
|
}
|
||||||
|
|
||||||
let logs = limit_logs(logs, filter.limit);
|
let logs = limit_logs(logs, filter.limit);
|
||||||
|
|
||||||
Ok(logs)
|
future::ok(logs).boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn work(&self, no_new_work_timeout: Trailing<u64>) -> Result<Work, Error> {
|
fn work(&self, no_new_work_timeout: Trailing<u64>) -> Result<Work, Error> {
|
||||||
|
@ -53,7 +53,7 @@ pub trait Filterable {
|
|||||||
fn polls(&self) -> &Mutex<PollManager<PollFilter>>;
|
fn polls(&self) -> &Mutex<PollManager<PollFilter>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Eth filter rpc implementation.
|
/// Eth filter rpc implementation for a full node.
|
||||||
pub struct EthFilterClient<C, M> where
|
pub struct EthFilterClient<C, M> where
|
||||||
C: BlockChainClient,
|
C: BlockChainClient,
|
||||||
M: MinerService {
|
M: MinerService {
|
||||||
@ -98,6 +98,8 @@ impl<C, M> Filterable for EthFilterClient<C, M> where C: BlockChainClient, M: Mi
|
|||||||
fn polls(&self) -> &Mutex<PollManager<PollFilter>> { &self.polls }
|
fn polls(&self) -> &Mutex<PollManager<PollFilter>> { &self.polls }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
impl<T: Filterable + Send + Sync + 'static> EthFilter for T {
|
impl<T: Filterable + Send + Sync + 'static> EthFilter for T {
|
||||||
fn new_filter(&self, filter: Filter) -> Result<RpcU256, Error> {
|
fn new_filter(&self, filter: Filter) -> Result<RpcU256, Error> {
|
||||||
let mut polls = self.polls().lock();
|
let mut polls = self.polls().lock();
|
||||||
|
@ -34,6 +34,7 @@ use ethcore::basic_account::BasicAccount;
|
|||||||
use ethcore::encoded;
|
use ethcore::encoded;
|
||||||
use ethcore::executed::{Executed, ExecutionError};
|
use ethcore::executed::{Executed, ExecutionError};
|
||||||
use ethcore::ids::BlockId;
|
use ethcore::ids::BlockId;
|
||||||
|
use ethcore::filter::Filter as EthcoreFilter;
|
||||||
use ethcore::transaction::{Action, SignedTransaction, Transaction as EthTransaction};
|
use ethcore::transaction::{Action, SignedTransaction, Transaction as EthTransaction};
|
||||||
use ethsync::LightSync;
|
use ethsync::LightSync;
|
||||||
use rlp::UntrustedRlp;
|
use rlp::UntrustedRlp;
|
||||||
@ -43,7 +44,9 @@ use util::{RwLock, Mutex, Uint, U256};
|
|||||||
use futures::{future, Future, BoxFuture, IntoFuture};
|
use futures::{future, Future, BoxFuture, IntoFuture};
|
||||||
use futures::sync::oneshot;
|
use futures::sync::oneshot;
|
||||||
|
|
||||||
|
use v1::impls::eth_filter::Filterable;
|
||||||
use v1::helpers::{CallRequest as CRequest, errors, limit_logs, dispatch};
|
use v1::helpers::{CallRequest as CRequest, errors, limit_logs, dispatch};
|
||||||
|
use v1::helpers::{PollFilter, PollManager};
|
||||||
use v1::helpers::block_import::is_major_importing;
|
use v1::helpers::block_import::is_major_importing;
|
||||||
use v1::traits::Eth;
|
use v1::traits::Eth;
|
||||||
use v1::types::{
|
use v1::types::{
|
||||||
@ -55,7 +58,7 @@ use v1::metadata::Metadata;
|
|||||||
|
|
||||||
use util::Address;
|
use util::Address;
|
||||||
|
|
||||||
/// Light client `ETH` RPC.
|
/// Light client `ETH` (and filter) RPC.
|
||||||
pub struct EthClient {
|
pub struct EthClient {
|
||||||
sync: Arc<LightSync>,
|
sync: Arc<LightSync>,
|
||||||
client: Arc<LightClient>,
|
client: Arc<LightClient>,
|
||||||
@ -63,6 +66,22 @@ pub struct EthClient {
|
|||||||
transaction_queue: Arc<RwLock<TransactionQueue>>,
|
transaction_queue: Arc<RwLock<TransactionQueue>>,
|
||||||
accounts: Arc<AccountProvider>,
|
accounts: Arc<AccountProvider>,
|
||||||
cache: Arc<Mutex<LightDataCache>>,
|
cache: Arc<Mutex<LightDataCache>>,
|
||||||
|
polls: Mutex<PollManager<PollFilter>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for EthClient {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
// each instance should have its own poll manager.
|
||||||
|
EthClient {
|
||||||
|
sync: self.sync.clone(),
|
||||||
|
client: self.client.clone(),
|
||||||
|
on_demand: self.on_demand.clone(),
|
||||||
|
transaction_queue: self.transaction_queue.clone(),
|
||||||
|
accounts: self.accounts.clone(),
|
||||||
|
cache: self.cache.clone(),
|
||||||
|
polls: Mutex::new(PollManager::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// helper for internal error: on demand sender cancelled.
|
// helper for internal error: on demand sender cancelled.
|
||||||
@ -90,6 +109,7 @@ impl EthClient {
|
|||||||
transaction_queue: transaction_queue,
|
transaction_queue: transaction_queue,
|
||||||
accounts: accounts,
|
accounts: accounts,
|
||||||
cache: cache,
|
cache: cache,
|
||||||
|
polls: Mutex::new(PollManager::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -484,19 +504,98 @@ impl Eth for EthClient {
|
|||||||
Err(errors::deprecated("Compilation of Solidity via RPC is deprecated".to_string()))
|
Err(errors::deprecated("Compilation of Solidity via RPC is deprecated".to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn logs(&self, _filter: Filter) -> Result<Vec<Log>, Error> {
|
fn logs(&self, filter: Filter) -> BoxFuture<Vec<Log>, Error> {
|
||||||
Err(errors::unimplemented(None))
|
let limit = filter.limit;
|
||||||
|
|
||||||
|
Filterable::logs(self, filter.into())
|
||||||
|
.map(move|logs| limit_logs(logs, limit))
|
||||||
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn work(&self, _timeout: Trailing<u64>) -> Result<Work, Error> {
|
fn work(&self, _timeout: Trailing<u64>) -> Result<Work, Error> {
|
||||||
Err(errors::unimplemented(None))
|
Err(errors::light_unimplemented(None))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn submit_work(&self, _nonce: RpcH64, _pow_hash: RpcH256, _mix_hash: RpcH256) -> Result<bool, Error> {
|
fn submit_work(&self, _nonce: RpcH64, _pow_hash: RpcH256, _mix_hash: RpcH256) -> Result<bool, Error> {
|
||||||
Err(errors::unimplemented(None))
|
Err(errors::light_unimplemented(None))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn submit_hashrate(&self, _rate: RpcU256, _id: RpcH256) -> Result<bool, Error> {
|
fn submit_hashrate(&self, _rate: RpcU256, _id: RpcH256) -> Result<bool, Error> {
|
||||||
Err(errors::unimplemented(None))
|
Err(errors::light_unimplemented(None))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This trait implementation triggers a blanked impl of `EthFilter`.
|
||||||
|
impl Filterable for EthClient {
|
||||||
|
fn best_block_number(&self) -> u64 { self.client.chain_info().best_block_number }
|
||||||
|
|
||||||
|
fn block_hash(&self, id: BlockId) -> Option<RpcH256> {
|
||||||
|
self.client.block_hash(id).map(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pending_transactions_hashes(&self, _block_number: u64) -> Vec<::util::H256> {
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn logs(&self, filter: EthcoreFilter) -> BoxFuture<Vec<Log>, Error> {
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use futures::stream::{self, Stream};
|
||||||
|
use util::H2048;
|
||||||
|
|
||||||
|
// early exit for "to" block before "from" block.
|
||||||
|
match filter.from_block {
|
||||||
|
BlockId::Latest | BlockId::Pending => {
|
||||||
|
let best = self.client.best_block_header();
|
||||||
|
let chain_info = self.client.chain_info();
|
||||||
|
if best.number() != chain_info.best_block_number || best.hash() != chain_info.best_block_hash {
|
||||||
|
return future::ok(Vec::new()).boxed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
let maybe_future = self.sync.with_context(move |ctx| {
|
||||||
|
// find all headers which match the filter, and fetch the receipts for each one.
|
||||||
|
// match them with their numbers for easy sorting later.
|
||||||
|
let bit_combos = filter.bloom_possibilities();
|
||||||
|
let receipts_futures: Vec<_> = self.client.ancestry_iter(filter.to_block)
|
||||||
|
.take_while(|ref hdr| BlockId::Number(hdr.number()) != filter.from_block)
|
||||||
|
.take_while(|ref hdr| BlockId::Hash(hdr.hash()) != filter.from_block)
|
||||||
|
.filter(|ref hdr| {
|
||||||
|
let hdr_bloom = hdr.log_bloom();
|
||||||
|
bit_combos.iter().find(|&bloom| hdr_bloom & *bloom == *bloom).is_some()
|
||||||
|
})
|
||||||
|
.map(|hdr| (hdr.number(), request::BlockReceipts(hdr)))
|
||||||
|
.map(|(num, req)| self.on_demand.block_receipts(ctx, req).map(move |x| (num, x)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// as the receipts come in, find logs within them which match the filter.
|
||||||
|
// 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, receipts)| {
|
||||||
|
for (block_index, log) in receipts.into_iter().flat_map(|r| r.logs).enumerate() {
|
||||||
|
if filter.matches(&log) {
|
||||||
|
matches.insert((num, block_index), log.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
future::ok(matches)
|
||||||
|
}) // and then collect them into a vector.
|
||||||
|
.map(|matches| matches.into_iter().map(|(_, v)| v).collect())
|
||||||
|
.map_err(err_premature_cancel)
|
||||||
|
});
|
||||||
|
|
||||||
|
match maybe_future {
|
||||||
|
Some(fut) => fut.boxed(),
|
||||||
|
None => future::err(errors::network_disabled()).boxed(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pending_logs(&self, _block_number: u64, _filter: &EthcoreFilter) -> Vec<Log> {
|
||||||
|
Vec::new() // light clients don't mine.
|
||||||
|
}
|
||||||
|
|
||||||
|
fn polls(&self) -> &Mutex<PollManager<PollFilter>> {
|
||||||
|
&self.polls
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,8 +162,8 @@ build_rpc_trait! {
|
|||||||
fn compile_serpent(&self, String) -> Result<Bytes, Error>;
|
fn compile_serpent(&self, String) -> Result<Bytes, Error>;
|
||||||
|
|
||||||
/// Returns logs matching given filter object.
|
/// Returns logs matching given filter object.
|
||||||
#[rpc(name = "eth_getLogs")]
|
#[rpc(async, name = "eth_getLogs")]
|
||||||
fn logs(&self, Filter) -> Result<Vec<Log>, Error>;
|
fn logs(&self, Filter) -> BoxFuture<Vec<Log>, Error>;
|
||||||
|
|
||||||
/// Returns the hash of the current block, the seedHash, and the boundary condition to be met.
|
/// Returns the hash of the current block, the seedHash, and the boundary condition to be met.
|
||||||
#[rpc(name = "eth_getWork")]
|
#[rpc(name = "eth_getWork")]
|
||||||
|
Loading…
Reference in New Issue
Block a user