2016-12-11 19:31:31 +01:00
|
|
|
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
|
2016-12-05 16:55:33 +01:00
|
|
|
// This file is part of Parity.
|
|
|
|
|
|
|
|
// Parity 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 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. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
//! A provider for the LES protocol. This is typically a full node, who can
|
|
|
|
//! give as much data as necessary to its peers.
|
|
|
|
|
|
|
|
use ethcore::blockchain_info::BlockChainInfo;
|
|
|
|
use ethcore::client::{BlockChainClient, ProvingBlockChainClient};
|
2016-12-15 18:19:19 +01:00
|
|
|
use ethcore::transaction::PendingTransaction;
|
2016-12-09 23:01:43 +01:00
|
|
|
use ethcore::ids::BlockId;
|
2016-12-28 13:44:51 +01:00
|
|
|
use ethcore::encoded;
|
2016-12-05 16:55:33 +01:00
|
|
|
|
|
|
|
use util::{Bytes, H256};
|
|
|
|
|
2016-12-05 17:09:05 +01:00
|
|
|
use request;
|
2016-12-05 16:55:33 +01:00
|
|
|
|
|
|
|
/// Defines the operations that a provider for `LES` must fulfill.
|
|
|
|
///
|
|
|
|
/// These are defined at [1], but may be subject to change.
|
|
|
|
/// Requests which can't be fulfilled should return either an empty RLP list
|
|
|
|
/// or empty vector where appropriate.
|
|
|
|
///
|
|
|
|
/// [1]: https://github.com/ethcore/parity/wiki/Light-Ethereum-Subprotocol-(LES)
|
2016-12-08 23:21:47 +01:00
|
|
|
#[cfg_attr(feature = "ipc", ipc(client_ident="LightProviderClient"))]
|
2016-12-05 16:55:33 +01:00
|
|
|
pub trait Provider: Send + Sync {
|
|
|
|
/// Provide current blockchain info.
|
|
|
|
fn chain_info(&self) -> BlockChainInfo;
|
|
|
|
|
|
|
|
/// Find the depth of a common ancestor between two blocks.
|
|
|
|
/// If either block is unknown or an ancestor can't be found
|
|
|
|
/// then return `None`.
|
|
|
|
fn reorg_depth(&self, a: &H256, b: &H256) -> Option<u64>;
|
|
|
|
|
|
|
|
/// Earliest block where state queries are available.
|
|
|
|
/// If `None`, no state queries are servable.
|
|
|
|
fn earliest_state(&self) -> Option<u64>;
|
|
|
|
|
|
|
|
/// Provide a list of headers starting at the requested block,
|
|
|
|
/// possibly in reverse and skipping `skip` at a time.
|
|
|
|
///
|
|
|
|
/// The returned vector may have any length in the range [0, `max`], but the
|
|
|
|
/// results within must adhere to the `skip` and `reverse` parameters.
|
2016-12-28 13:44:51 +01:00
|
|
|
fn block_headers(&self, req: request::Headers) -> Vec<encoded::Header> {
|
2016-12-11 15:40:31 +01:00
|
|
|
use request::HashOrNumber;
|
2016-12-05 16:55:33 +01:00
|
|
|
|
2016-12-19 14:54:10 +01:00
|
|
|
if req.max == 0 { return Vec::new() }
|
|
|
|
|
2016-12-11 15:40:31 +01:00
|
|
|
let best_num = self.chain_info().best_block_number;
|
|
|
|
let start_num = match req.start {
|
|
|
|
HashOrNumber::Number(start_num) => start_num,
|
|
|
|
HashOrNumber::Hash(hash) => match self.block_header(BlockId::Hash(hash)) {
|
|
|
|
None => {
|
|
|
|
trace!(target: "les_provider", "Unknown block hash {} requested", hash);
|
|
|
|
return Vec::new();
|
|
|
|
}
|
|
|
|
Some(header) => {
|
2016-12-28 13:44:51 +01:00
|
|
|
let num = header.number();
|
2016-12-19 14:54:10 +01:00
|
|
|
let canon_hash = self.block_header(BlockId::Number(num))
|
2016-12-28 13:44:51 +01:00
|
|
|
.map(|h| h.hash());
|
2016-12-19 14:54:10 +01:00
|
|
|
|
|
|
|
if req.max == 1 || canon_hash != Some(hash) {
|
2016-12-11 15:40:31 +01:00
|
|
|
// Non-canonical header or single header requested.
|
|
|
|
return vec![header];
|
|
|
|
}
|
|
|
|
|
|
|
|
num
|
|
|
|
}
|
2016-12-05 16:55:33 +01:00
|
|
|
}
|
2016-12-11 15:40:31 +01:00
|
|
|
};
|
2016-12-19 14:54:10 +01:00
|
|
|
|
2016-12-05 16:55:33 +01:00
|
|
|
(0u64..req.max as u64)
|
2016-12-08 12:20:18 +01:00
|
|
|
.map(|x: u64| x.saturating_mul(req.skip + 1))
|
2017-01-11 14:39:03 +01:00
|
|
|
.take_while(|x| if req.reverse { x < &start_num } else { best_num.saturating_sub(start_num) >= *x })
|
2016-12-05 16:55:33 +01:00
|
|
|
.map(|x| if req.reverse { start_num - x } else { start_num + x })
|
2016-12-09 23:01:43 +01:00
|
|
|
.map(|x| self.block_header(BlockId::Number(x)))
|
2016-12-05 16:55:33 +01:00
|
|
|
.take_while(|x| x.is_some())
|
|
|
|
.flat_map(|x| x)
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
2016-12-19 14:54:10 +01:00
|
|
|
/// Get a block header by id.
|
2016-12-28 13:44:51 +01:00
|
|
|
fn block_header(&self, id: BlockId) -> Option<encoded::Header>;
|
2016-12-19 14:54:10 +01:00
|
|
|
|
|
|
|
/// Provide as many as possible of the requested blocks (minus the headers) encoded
|
|
|
|
/// in RLP format.
|
2016-12-28 13:44:51 +01:00
|
|
|
fn block_bodies(&self, req: request::Bodies) -> Vec<Option<encoded::Body>> {
|
2016-12-05 16:55:33 +01:00
|
|
|
req.block_hashes.into_iter()
|
2016-12-09 23:01:43 +01:00
|
|
|
.map(|hash| self.block_body(BlockId::Hash(hash)))
|
2016-12-05 16:55:33 +01:00
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
2016-12-19 14:54:10 +01:00
|
|
|
/// Get a block body by id.
|
2016-12-28 13:44:51 +01:00
|
|
|
fn block_body(&self, id: BlockId) -> Option<encoded::Body>;
|
2016-12-19 14:54:10 +01:00
|
|
|
|
|
|
|
/// Provide the receipts as many as possible of the requested blocks.
|
|
|
|
/// Returns a vector of RLP-encoded lists of receipts.
|
2016-12-05 16:55:33 +01:00
|
|
|
fn receipts(&self, req: request::Receipts) -> Vec<Bytes> {
|
|
|
|
req.block_hashes.into_iter()
|
|
|
|
.map(|hash| self.block_receipts(&hash))
|
|
|
|
.map(|receipts| receipts.unwrap_or_else(|| ::rlp::EMPTY_LIST_RLP.to_vec()))
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
2016-12-19 14:54:10 +01:00
|
|
|
/// Get a block's receipts as an RLP-encoded list by block hash.
|
|
|
|
fn block_receipts(&self, hash: &H256) -> Option<Bytes>;
|
|
|
|
|
|
|
|
/// Provide a set of merkle proofs, as requested. Each request is a
|
|
|
|
/// block hash and request parameters.
|
|
|
|
///
|
|
|
|
/// Returns a vector of RLP-encoded lists satisfying the requests.
|
2016-12-05 16:55:33 +01:00
|
|
|
fn proofs(&self, req: request::StateProofs) -> Vec<Bytes> {
|
2016-12-05 17:09:05 +01:00
|
|
|
use rlp::{RlpStream, Stream};
|
2016-12-05 16:55:33 +01:00
|
|
|
|
|
|
|
let mut results = Vec::with_capacity(req.requests.len());
|
|
|
|
|
|
|
|
for request in req.requests {
|
2016-12-19 14:54:10 +01:00
|
|
|
let proof = self.state_proof(request);
|
2016-12-05 16:55:33 +01:00
|
|
|
|
2016-12-05 17:09:05 +01:00
|
|
|
let mut stream = RlpStream::new_list(proof.len());
|
|
|
|
for node in proof {
|
|
|
|
stream.append_raw(&node, 1);
|
2016-12-05 16:55:33 +01:00
|
|
|
}
|
2016-12-05 17:09:05 +01:00
|
|
|
|
|
|
|
results.push(stream.out());
|
2016-12-05 16:55:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
results
|
|
|
|
}
|
|
|
|
|
2016-12-19 14:54:10 +01:00
|
|
|
/// Get a state proof from a request. Each proof should be a vector
|
|
|
|
/// of rlp-encoded trie nodes, in ascending order by distance from the root.
|
|
|
|
fn state_proof(&self, req: request::StateProof) -> Vec<Bytes>;
|
|
|
|
|
|
|
|
/// Provide contract code for the specified (block_hash, account_hash) pairs.
|
|
|
|
/// Each item in the resulting vector is either the raw bytecode or empty.
|
|
|
|
fn contract_codes(&self, req: request::ContractCodes) -> Vec<Bytes> {
|
2016-12-05 16:55:33 +01:00
|
|
|
req.code_requests.into_iter()
|
2016-12-19 14:54:10 +01:00
|
|
|
.map(|req| self.contract_code(req))
|
2016-12-05 16:55:33 +01:00
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
2016-12-19 14:54:10 +01:00
|
|
|
/// Get contract code by request. Either the raw bytecode or empty.
|
|
|
|
fn contract_code(&self, req: request::ContractCode) -> Bytes;
|
|
|
|
|
|
|
|
/// Provide header proofs from the Canonical Hash Tries as well as the headers
|
|
|
|
/// they correspond to -- each element in the returned vector is a 2-tuple.
|
|
|
|
/// The first element is a block header and the second a merkle proof of
|
|
|
|
/// the header in a requested CHT.
|
2016-12-05 16:55:33 +01:00
|
|
|
fn header_proofs(&self, req: request::HeaderProofs) -> Vec<Bytes> {
|
2016-12-19 14:54:10 +01:00
|
|
|
use rlp::{self, RlpStream, Stream};
|
|
|
|
|
|
|
|
req.requests.into_iter()
|
|
|
|
.map(|req| self.header_proof(req))
|
|
|
|
.map(|maybe_proof| match maybe_proof {
|
|
|
|
None => rlp::EMPTY_LIST_RLP.to_vec(),
|
|
|
|
Some((header, proof)) => {
|
|
|
|
let mut stream = RlpStream::new_list(2);
|
2016-12-28 13:44:51 +01:00
|
|
|
stream.append_raw(&header.into_inner(), 1).begin_list(proof.len());
|
2016-12-19 14:54:10 +01:00
|
|
|
|
|
|
|
for node in proof {
|
|
|
|
stream.append_raw(&node, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
stream.out()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Provide a header proof from a given Canonical Hash Trie as well as the
|
|
|
|
/// corresponding header. The first element is the block header and the
|
|
|
|
/// second is a merkle proof of the CHT.
|
2016-12-28 13:44:51 +01:00
|
|
|
fn header_proof(&self, req: request::HeaderProof) -> Option<(encoded::Header, Vec<Bytes>)>;
|
2016-12-19 14:54:10 +01:00
|
|
|
|
|
|
|
/// Provide pending transactions.
|
|
|
|
fn ready_transactions(&self) -> Vec<PendingTransaction>;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Implementation of a light client data provider for a client.
|
|
|
|
impl<T: ProvingBlockChainClient + ?Sized> Provider for T {
|
|
|
|
fn chain_info(&self) -> BlockChainInfo {
|
|
|
|
BlockChainClient::chain_info(self)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn reorg_depth(&self, a: &H256, b: &H256) -> Option<u64> {
|
|
|
|
self.tree_route(a, b).map(|route| route.index as u64)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn earliest_state(&self) -> Option<u64> {
|
|
|
|
Some(self.pruning_info().earliest_state)
|
|
|
|
}
|
|
|
|
|
2016-12-28 13:44:51 +01:00
|
|
|
fn block_header(&self, id: BlockId) -> Option<encoded::Header> {
|
2016-12-19 14:54:10 +01:00
|
|
|
BlockChainClient::block_header(self, id)
|
|
|
|
}
|
|
|
|
|
2016-12-28 13:44:51 +01:00
|
|
|
fn block_body(&self, id: BlockId) -> Option<encoded::Body> {
|
2016-12-19 14:54:10 +01:00
|
|
|
BlockChainClient::block_body(self, id)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn block_receipts(&self, hash: &H256) -> Option<Bytes> {
|
|
|
|
BlockChainClient::block_receipts(self, hash)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn state_proof(&self, req: request::StateProof) -> Vec<Bytes> {
|
|
|
|
match req.key2 {
|
|
|
|
Some(key2) => self.prove_storage(req.key1, key2, req.from_level, BlockId::Hash(req.block)),
|
|
|
|
None => self.prove_account(req.key1, req.from_level, BlockId::Hash(req.block)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn contract_code(&self, req: request::ContractCode) -> Bytes {
|
|
|
|
self.code_by_hash(req.account_key, BlockId::Hash(req.block_hash))
|
|
|
|
}
|
|
|
|
|
2017-01-16 17:10:30 +01:00
|
|
|
fn header_proof(&self, req: request::HeaderProof) -> Option<(encoded::Header, Vec<Bytes>)> {
|
|
|
|
use util::MemoryDB;
|
|
|
|
use util::trie::{Trie, TrieMut, TrieDB, TrieDBMut, Recorder};
|
|
|
|
|
|
|
|
if req.cht_number != ::cht::block_to_cht_number(req.block_number) {
|
|
|
|
debug!(target: "les_provider", "Requested CHT number mismatch with block number.");
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut memdb = MemoryDB::new();
|
|
|
|
let mut root = H256::default();
|
|
|
|
let mut needed_hdr = None;
|
|
|
|
{
|
|
|
|
let mut t = TrieDBMut::new(&mut memdb, &mut root);
|
|
|
|
let start_num = ::cht::start_number(req.cht_number);
|
|
|
|
for i in (0..::cht::SIZE).map(|x| x + start_num) {
|
|
|
|
match self.block_header(BlockId::Number(i)) {
|
|
|
|
None => return None,
|
|
|
|
Some(hdr) => {
|
|
|
|
t.insert(
|
|
|
|
&*::rlp::encode(&i),
|
|
|
|
&*::rlp::encode(&hdr.hash()),
|
|
|
|
).expect("fresh in-memory database is infallible; qed");
|
|
|
|
|
|
|
|
if i == req.block_number { needed_hdr = Some(hdr) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let needed_hdr = needed_hdr.expect("`needed_hdr` always set in loop, number checked before; qed");
|
|
|
|
|
|
|
|
let mut recorder = Recorder::new();
|
|
|
|
let t = TrieDB::new(&memdb, &root)
|
|
|
|
.expect("Same DB and root as just produced by TrieDBMut; qed");
|
|
|
|
|
|
|
|
if let Err(e) = t.get_with(&*::rlp::encode(&req.block_number), &mut recorder) {
|
|
|
|
debug!(target: "les_provider", "Error looking up number in freshly-created CHT: {}", e);
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: cache calculated CHT if possible.
|
|
|
|
let proof = recorder.drain().into_iter().map(|x| x.data).collect();
|
|
|
|
Some((needed_hdr, proof))
|
2016-12-05 16:55:33 +01:00
|
|
|
}
|
|
|
|
|
2016-12-16 14:54:26 +01:00
|
|
|
fn ready_transactions(&self) -> Vec<PendingTransaction> {
|
|
|
|
BlockChainClient::ready_transactions(self)
|
2016-12-05 16:55:33 +01:00
|
|
|
}
|
2016-12-15 18:19:19 +01:00
|
|
|
}
|