// Copyright 2015-2019 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum 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. // Parity Ethereum 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 Parity Ethereum. If not, see . //! Eth RPC interface for the light client. use std::{collections::BTreeSet, sync::Arc}; use jsonrpc_core::{ futures::{future, future::Either, Future}, BoxFuture, Result, }; use light::{ cache::Cache as LightDataCache, cht, client::LightChainClient, on_demand::{request, OnDemandRequester}, TransactionQueue, }; use ethereum_types::{Address, H160, H256, H64, U256, U64}; use hash::{KECCAK_EMPTY_LIST_RLP, KECCAK_NULL_RLP}; use parking_lot::{Mutex, RwLock}; use rlp::Rlp; use types::{ encoded, filter::Filter as EthcoreFilter, ids::BlockId, transaction::SignedTransaction, }; use v1::{ helpers::{ deprecated::{self, DeprecationNotice}, errors, light_fetch::{self, LightFetch}, limit_logs, PollManager, SyncPollFilter, }, impls::eth_filter::Filterable, metadata::Metadata, traits::Eth, types::{ Block, BlockNumber, BlockTransactions, Bytes, CallRequest, EthAccount, Filter, Index, LightBlockNumber, Log, Receipt, RichBlock, SyncInfo as RpcSyncInfo, SyncStatus as RpcSyncStatus, Transaction, Work, }, }; use sync::{LightNetworkDispatcher, LightSyncInfo, LightSyncProvider, ManageNetwork}; const NO_INVALID_BACK_REFS: &str = "Fails only on invalid back-references; back-references here known to be valid; qed"; /// Light client `ETH` (and filter) RPC. pub struct EthClient< C, S: LightSyncProvider + LightNetworkDispatcher + 'static, OD: OnDemandRequester + 'static, > { sync: Arc, client: Arc, on_demand: Arc, transaction_queue: Arc>, accounts: Arc Vec
+ Send + Sync>, cache: Arc>, polls: Mutex>, poll_lifetime: u32, gas_price_percentile: usize, deprecation_notice: DeprecationNotice, } impl Clone for EthClient where S: LightSyncProvider + LightNetworkDispatcher + 'static, OD: OnDemandRequester + 'static, { 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(self.poll_lifetime)), poll_lifetime: self.poll_lifetime, gas_price_percentile: self.gas_price_percentile, deprecation_notice: Default::default(), } } } impl EthClient where C: LightChainClient + 'static, S: LightSyncProvider + LightNetworkDispatcher + ManageNetwork + 'static, OD: OnDemandRequester + 'static, { /// Create a new `EthClient` with a handle to the light sync instance, client, /// and on-demand request service, which is assumed to be attached as a handler. pub fn new( sync: Arc, client: Arc, on_demand: Arc, transaction_queue: Arc>, accounts: Arc Vec
+ Send + Sync>, cache: Arc>, gas_price_percentile: usize, poll_lifetime: u32, ) -> Self { EthClient { sync, client, on_demand, transaction_queue, accounts, cache, polls: Mutex::new(PollManager::new(poll_lifetime)), poll_lifetime, gas_price_percentile, deprecation_notice: Default::default(), } } /// Create a light data fetcher instance. fn fetcher(&self) -> LightFetch { LightFetch { client: self.client.clone(), on_demand: self.on_demand.clone(), sync: self.sync.clone(), cache: self.cache.clone(), gas_price_percentile: self.gas_price_percentile, } } // get a "rich" block structure. Fails on unknown block. fn rich_block(&self, id: BlockId, include_txs: bool) -> BoxFuture { let (on_demand, sync) = (self.on_demand.clone(), self.sync.clone()); let (client, engine) = (self.client.clone(), self.client.engine().clone()); // helper for filling out a rich block once we've got a block and a score. let fill_rich = move |block: encoded::Block, score: Option| { let header = block.decode_header(); let extra_info = engine.extra_info(&header); RichBlock { inner: Block { hash: Some(header.hash()), size: Some(block.rlp().as_raw().len().into()), parent_hash: *header.parent_hash(), uncles_hash: *header.uncles_hash(), author: *header.author(), miner: *header.author(), state_root: *header.state_root(), transactions_root: *header.transactions_root(), receipts_root: *header.receipts_root(), number: Some(header.number().into()), gas_used: *header.gas_used(), gas_limit: *header.gas_limit(), logs_bloom: Some(*header.log_bloom()), timestamp: header.timestamp().into(), difficulty: *header.difficulty(), total_difficulty: score.map(Into::into), seal_fields: header.seal().iter().cloned().map(Into::into).collect(), uncles: block.uncle_hashes().into_iter().map(Into::into).collect(), transactions: match include_txs { true => BlockTransactions::Full( block .view() .localized_transactions() .into_iter() .map(Transaction::from_localized) .collect(), ), _ => BlockTransactions::Hashes( block .transaction_hashes() .into_iter() .map(Into::into) .collect(), ), }, extra_data: Bytes::new(header.extra_data().clone()), }, extra_info, } }; // get the block itself. Box::new(self.fetcher().block(id).and_then(move |block| { // then fetch the total difficulty (this is much easier after getting the block). match client.score(id) { Some(score) => Either::A(future::ok(fill_rich(block, Some(score)))), None => { // make a CHT request to fetch the chain score. let req = cht::block_to_cht_number(block.number()) .and_then(|num| client.cht_root(num as usize)) .and_then(|root| request::HeaderProof::new(block.number(), root)); let req = match req { Some(req) => req, None => { // somehow the genesis block slipped past other checks. // return it now. let score = client .block_header(BlockId::Number(0)) .expect("genesis always stored; qed") .difficulty(); return Either::A(future::ok(fill_rich(block, Some(score)))); } }; // three possible outcomes: // - network is down. // - we get a score, but our hash is non-canonical. // - we get a score, and our hash is canonical. let maybe_fut = sync.with_context(move |ctx| { on_demand.request(ctx, req).expect(NO_INVALID_BACK_REFS) }); match maybe_fut { Some(fut) => Either::B( fut.map(move |(hash, score)| { let score = if hash == block.hash() { Some(score) } else { None }; fill_rich(block, score) }) .map_err(errors::on_demand_error), ), None => Either::A(future::err(errors::network_disabled())), } } } })) } } impl Eth for EthClient where C: LightChainClient + 'static, S: LightSyncInfo + LightSyncProvider + LightNetworkDispatcher + ManageNetwork + 'static, OD: OnDemandRequester + 'static, { type Metadata = Metadata; fn protocol_version(&self) -> Result { Ok(format!("{}", ::light::net::MAX_PROTOCOL_VERSION)) } fn syncing(&self) -> Result { if self.sync.is_major_importing() { let chain_info = self.client.chain_info(); let current_block = U256::from(chain_info.best_block_number); let highest_block = self .sync .highest_block() .map(U256::from) .unwrap_or_else(|| current_block); Ok(RpcSyncStatus::Info(RpcSyncInfo { starting_block: U256::from(self.sync.start_block()), current_block, highest_block, warp_chunks_amount: None, warp_chunks_processed: None, })) } else { Ok(RpcSyncStatus::None) } } fn author(&self) -> Result { (self.accounts)() .first() .cloned() .map(From::from) .ok_or_else(|| errors::account("No accounts were found", "")) } fn is_mining(&self) -> Result { Ok(false) } fn chain_id(&self) -> Result> { Ok(self.client.signing_chain_id().map(U64::from)) } fn hashrate(&self) -> Result { Ok(Default::default()) } fn gas_price(&self) -> BoxFuture { Box::new(self.fetcher().gas_price()) } fn accounts(&self) -> Result> { self.deprecation_notice .print("eth_accounts", deprecated::msgs::ACCOUNTS); Ok((self.accounts)().into_iter().map(Into::into).collect()) } fn block_number(&self) -> Result { Ok(self.client.chain_info().best_block_number.into()) } fn balance(&self, address: H160, num: Option) -> BoxFuture { Box::new( self.fetcher() .account( address, num.unwrap_or_default().to_block_id(), self.transaction_queue.clone(), ) .map(|acc| acc.map_or(0.into(), |a| a.balance)), ) } fn storage_at(&self, _address: H160, _key: U256, _num: Option) -> BoxFuture { Box::new(future::err(errors::unimplemented(None))) } fn block_by_hash(&self, hash: H256, include_txs: bool) -> BoxFuture> { Box::new(self.rich_block(BlockId::Hash(hash), include_txs).map(Some)) } fn block_by_number(&self, num: BlockNumber, include_txs: bool) -> BoxFuture> { Box::new(self.rich_block(num.to_block_id(), include_txs).map(Some)) } fn transaction_count(&self, address: H160, num: Option) -> BoxFuture { Box::new( self.fetcher() .account( address, num.unwrap_or_default().to_block_id(), self.transaction_queue.clone(), ) .map(|acc| acc.map_or(0.into(), |a| a.nonce)), ) } fn block_transaction_count_by_hash(&self, hash: H256) -> BoxFuture> { let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone()); Box::new( self.fetcher() .header(BlockId::Hash(hash)) .and_then(move |hdr| { if hdr.transactions_root() == KECCAK_NULL_RLP { Either::A(future::ok(Some(U256::from(0)))) } else { sync.with_context(|ctx| on_demand.request(ctx, request::Body(hdr.into()))) .map(|x| x.expect(NO_INVALID_BACK_REFS)) .map(|x| x.map(|b| Some(U256::from(b.transactions_count())))) .map(|x| Either::B(x.map_err(errors::on_demand_error))) .unwrap_or_else(|| Either::A(future::err(errors::network_disabled()))) } }), ) } fn block_transaction_count_by_number(&self, num: BlockNumber) -> BoxFuture> { let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone()); Box::new( self.fetcher() .header(num.to_block_id()) .and_then(move |hdr| { if hdr.transactions_root() == KECCAK_NULL_RLP { Either::A(future::ok(Some(U256::from(0)))) } else { sync.with_context(|ctx| on_demand.request(ctx, request::Body(hdr.into()))) .map(|x| x.expect(NO_INVALID_BACK_REFS)) .map(|x| x.map(|b| Some(U256::from(b.transactions_count())))) .map(|x| Either::B(x.map_err(errors::on_demand_error))) .unwrap_or_else(|| Either::A(future::err(errors::network_disabled()))) } }), ) } fn block_uncles_count_by_hash(&self, hash: H256) -> BoxFuture> { let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone()); Box::new( self.fetcher() .header(BlockId::Hash(hash)) .and_then(move |hdr| { if hdr.uncles_hash() == KECCAK_EMPTY_LIST_RLP { Either::A(future::ok(Some(U256::from(0)))) } else { sync.with_context(|ctx| on_demand.request(ctx, request::Body(hdr.into()))) .map(|x| x.expect(NO_INVALID_BACK_REFS)) .map(|x| x.map(|b| Some(U256::from(b.uncles_count())))) .map(|x| Either::B(x.map_err(errors::on_demand_error))) .unwrap_or_else(|| Either::A(future::err(errors::network_disabled()))) } }), ) } fn block_uncles_count_by_number(&self, num: BlockNumber) -> BoxFuture> { let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone()); Box::new( self.fetcher() .header(num.to_block_id()) .and_then(move |hdr| { if hdr.uncles_hash() == KECCAK_EMPTY_LIST_RLP { Either::B(future::ok(Some(U256::from(0)))) } else { sync.with_context(|ctx| on_demand.request(ctx, request::Body(hdr.into()))) .map(|x| x.expect(NO_INVALID_BACK_REFS)) .map(|x| x.map(|b| Some(U256::from(b.uncles_count())))) .map(|x| Either::A(x.map_err(errors::on_demand_error))) .unwrap_or_else(|| Either::B(future::err(errors::network_disabled()))) } }), ) } fn code_at(&self, address: H160, num: Option) -> BoxFuture { Box::new( self.fetcher() .code(address, num.unwrap_or_default().to_block_id()) .map(Into::into), ) } fn send_raw_transaction(&self, raw: Bytes) -> Result { let best_header = self .client .best_block_header() .decode() .map_err(errors::decode)?; Rlp::new(&raw.into_vec()) .as_val() .map_err(errors::rlp) .and_then(|tx| { self.client .engine() .verify_transaction_basic(&tx, &best_header) .map_err(errors::transaction)?; let signed = SignedTransaction::new(tx).map_err(errors::transaction)?; let hash = signed.hash(); self.transaction_queue .write() .import(signed.into()) .map(|_| hash) .map_err(errors::transaction) }) .map(Into::into) } fn submit_transaction(&self, raw: Bytes) -> Result { self.send_raw_transaction(raw) } fn call(&self, req: CallRequest, num: Option) -> BoxFuture { Box::new( self.fetcher() .proved_read_only_execution(req, num, self.transaction_queue.clone()) .and_then(|res| match res { Ok(exec) => Ok(exec.output.into()), Err(e) => Err(errors::execution(e)), }), ) } fn estimate_gas(&self, req: CallRequest, num: Option) -> BoxFuture { // TODO: binary chop for more accurate estimates. Box::new( self.fetcher() .proved_read_only_execution(req, num, self.transaction_queue.clone()) .and_then(|res| match res { Ok(exec) => Ok(exec.refunded + exec.gas_used), Err(e) => Err(errors::execution(e)), }), ) } fn transaction_by_hash(&self, hash: H256) -> BoxFuture> { let in_txqueue = self.transaction_queue.read().get(&hash).is_some(); // The transaction is in the `local txqueue` then fetch the latest state from the network and attempt // to cull the transaction queue. if in_txqueue { // Note, this will block (relies on HTTP timeout) to make sure `cull` will finish to avoid having to call // `eth_getTransactionByHash` more than once to ensure the `txqueue` is up to `date` when it is called if let Err(e) = self .fetcher() .light_cull(self.transaction_queue.clone()) .wait() { debug!(target: "cull", "failed because of: {:?}", e); } if let Some(tx) = self.transaction_queue.read().get(&hash) { return Box::new(future::ok(Some(Transaction::from_pending(tx.clone())))); } } Box::new( self.fetcher() .transaction_by_hash(hash) .map(|x| x.map(|(tx, _)| tx)), ) } fn transaction_by_block_hash_and_index( &self, hash: H256, idx: Index, ) -> BoxFuture> { Box::new( self.fetcher() .block(BlockId::Hash(hash)) .map(move |block| light_fetch::extract_transaction_at_index(block, idx.value())), ) } fn transaction_by_block_number_and_index( &self, num: BlockNumber, idx: Index, ) -> BoxFuture> { Box::new( self.fetcher() .block(num.to_block_id()) .map(move |block| light_fetch::extract_transaction_at_index(block, idx.value())), ) } fn transaction_receipt(&self, hash: H256) -> BoxFuture> { let fetcher = self.fetcher(); Box::new(fetcher.transaction_by_hash(hash).and_then(move |tx| { // the block hash included in the transaction object here has // already been checked for canonicality and whether it contains // the transaction. match tx { Some((tx, index)) => match tx.block_hash { Some(block_hash) => { let extract_receipt = fetcher .receipts(BlockId::Hash(block_hash)) .and_then(move |mut receipts| future::ok(receipts.swap_remove(index))) .map(Receipt::from) .map(move |mut receipt| { receipt.transaction_hash = Some(hash); receipt.transaction_index = Some(index.into()); receipt.block_hash = Some(block_hash); receipt.block_number = tx.block_number; receipt }) .map(Some); Either::B(extract_receipt) } None => Either::A(future::err(errors::unknown_block())), }, None => Either::A(future::ok(None)), } })) } fn uncle_by_block_hash_and_index( &self, hash: H256, idx: Index, ) -> BoxFuture> { let client = self.client.clone(); Box::new( self.fetcher() .block(BlockId::Hash(hash)) .map(move |block| extract_uncle_at_index(block, idx, client)), ) } fn uncle_by_block_number_and_index( &self, num: BlockNumber, idx: Index, ) -> BoxFuture> { let client = self.client.clone(); Box::new( self.fetcher() .block(num.to_block_id()) .map(move |block| extract_uncle_at_index(block, idx, client)), ) } fn proof( &self, _address: H160, _values: Vec, _num: Option, ) -> BoxFuture { Box::new(future::err(errors::unimplemented(None))) } fn compilers(&self) -> Result> { Err(errors::deprecated( "Compilation functionality is deprecated.".to_string(), )) } fn compile_lll(&self, _: String) -> Result { Err(errors::deprecated( "Compilation of LLL via RPC is deprecated".to_string(), )) } fn compile_serpent(&self, _: String) -> Result { Err(errors::deprecated( "Compilation of Serpent via RPC is deprecated".to_string(), )) } fn compile_solidity(&self, _: String) -> Result { Err(errors::deprecated( "Compilation of Solidity via RPC is deprecated".to_string(), )) } fn logs(&self, filter: Filter) -> BoxFuture> { let limit = filter.limit; Box::new( Filterable::logs( self, match filter.try_into() { Ok(value) => value, Err(err) => return Box::new(future::err(err)), }, ) .map(move |logs| limit_logs(logs, limit)), ) } fn work(&self, _timeout: Option) -> Result { Err(errors::light_unimplemented(None)) } fn submit_work(&self, _nonce: H64, _pow_hash: H256, _mix_hash: H256) -> Result { Err(errors::light_unimplemented(None)) } fn submit_hashrate(&self, _rate: U256, _id: H256) -> Result { Err(errors::light_unimplemented(None)) } } // This trait implementation triggers a blanked impl of `EthFilter`. impl Filterable for EthClient where C: LightChainClient + 'static, S: LightSyncProvider + LightNetworkDispatcher + ManageNetwork + 'static, OD: OnDemandRequester + 'static, { fn best_block_number(&self) -> u64 { self.client.chain_info().best_block_number } fn block_hash(&self, id: BlockId) -> Option { self.client.block_hash(id) } fn pending_transaction_hashes(&self) -> BTreeSet { BTreeSet::new() } fn logs(&self, filter: EthcoreFilter) -> BoxFuture> { Box::new(self.fetcher().logs(filter)) as BoxFuture<_> } fn pending_logs(&self, _block_number: u64, _filter: &EthcoreFilter) -> Vec { Vec::new() // light clients don't mine. } fn polls(&self) -> &Mutex> { &self.polls } fn removed_logs( &self, _block_hash: ::ethereum_types::H256, _filter: &EthcoreFilter, ) -> (Vec, u64) { (Default::default(), 0) } } fn extract_uncle_at_index( block: encoded::Block, index: Index, client: Arc, ) -> Option { let uncle = match block.uncles().into_iter().nth(index.value()) { Some(u) => u, None => return None, }; let extra_info = client.engine().extra_info(&uncle); Some(RichBlock { inner: Block { hash: Some(uncle.hash()), size: None, parent_hash: *uncle.parent_hash(), uncles_hash: *uncle.uncles_hash(), author: *uncle.author(), miner: *uncle.author(), state_root: *uncle.state_root(), transactions_root: *uncle.transactions_root(), number: Some(uncle.number().into()), gas_used: *uncle.gas_used(), gas_limit: *uncle.gas_limit(), logs_bloom: Some(*uncle.log_bloom()), timestamp: uncle.timestamp().into(), difficulty: *uncle.difficulty(), total_difficulty: None, receipts_root: *uncle.receipts_root(), extra_data: uncle.extra_data().clone().into(), seal_fields: uncle.seal().iter().cloned().map(Into::into).collect(), uncles: vec![], transactions: BlockTransactions::Hashes(vec![]), }, extra_info, }) }