2019-01-07 11:33:07 +01:00
|
|
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
|
|
|
// This file is part of Parity Ethereum.
|
2016-12-05 16:55:33 +01:00
|
|
|
|
2019-01-07 11:33:07 +01:00
|
|
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
2016-12-05 16:55:33 +01:00
|
|
|
// 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.
|
|
|
|
|
2019-01-07 11:33:07 +01:00
|
|
|
// Parity Ethereum is distributed in the hope that it will be useful,
|
2016-12-05 16:55:33 +01:00
|
|
|
// 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
|
2019-01-07 11:33:07 +01:00
|
|
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
2016-12-05 16:55:33 +01:00
|
|
|
|
2017-10-08 18:19:27 +02:00
|
|
|
//! A provider for the PIP protocol. This is typically a full node, who can
|
2016-12-05 16:55:33 +01:00
|
|
|
//! give as much data as necessary to its peers.
|
|
|
|
|
2017-02-09 18:42:18 +01:00
|
|
|
use std::sync::Arc;
|
|
|
|
|
2020-08-05 06:08:03 +02:00
|
|
|
use common_types::{
|
|
|
|
blockchain_info::BlockChainInfo, encoded, ids::BlockId, transaction::PendingTransaction,
|
|
|
|
};
|
|
|
|
use ethcore::client::{
|
|
|
|
BlockChainClient, BlockInfo as ClientBlockInfo, ChainInfo, ProvingBlockChainClient,
|
|
|
|
};
|
2018-01-10 13:35:18 +01:00
|
|
|
use ethereum_types::H256;
|
2017-09-02 20:09:13 +02:00
|
|
|
use parking_lot::RwLock;
|
2016-12-05 16:55:33 +01:00
|
|
|
|
2017-02-03 18:47:03 +01:00
|
|
|
use cht::{self, BlockInfo};
|
2020-08-05 06:08:03 +02:00
|
|
|
use client::{AsLightClient, LightChainClient};
|
2017-02-09 18:42:18 +01:00
|
|
|
use transaction_queue::TransactionQueue;
|
2017-02-03 18:47:03 +01:00
|
|
|
|
2016-12-05 17:09:05 +01:00
|
|
|
use request;
|
2016-12-05 16:55:33 +01:00
|
|
|
|
2018-08-25 23:06:01 +02:00
|
|
|
/// Maximum allowed size of a headers request.
|
|
|
|
pub const MAX_HEADERS_PER_REQUEST: u64 = 512;
|
|
|
|
|
2017-03-23 13:17:05 +01:00
|
|
|
/// Defines the operations that a provider for the light subprotocol must fulfill.
|
2016-12-05 16:55:33 +01:00
|
|
|
pub trait Provider: Send + Sync {
|
2020-08-05 06:08:03 +02:00
|
|
|
/// 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.
|
|
|
|
fn block_headers(
|
|
|
|
&self,
|
|
|
|
req: request::CompleteHeadersRequest,
|
|
|
|
) -> Option<request::HeadersResponse> {
|
|
|
|
use request::HashOrNumber;
|
|
|
|
|
|
|
|
if req.max == 0 {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
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: "pip_provider", "Unknown block hash {} requested", hash);
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
Some(header) => {
|
|
|
|
let num = header.number();
|
|
|
|
let canon_hash = self.block_header(BlockId::Number(num)).map(|h| h.hash());
|
|
|
|
|
|
|
|
if req.max == 1 || canon_hash != Some(hash) {
|
|
|
|
// Non-canonical header or single header requested.
|
|
|
|
return Some(::request::HeadersResponse {
|
|
|
|
headers: vec![header],
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
num
|
|
|
|
}
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
let max = ::std::cmp::min(MAX_HEADERS_PER_REQUEST, req.max);
|
|
|
|
|
|
|
|
let headers: Vec<_> = (0_u64..max)
|
|
|
|
.map(|x: u64| x.saturating_mul(req.skip.saturating_add(1)))
|
|
|
|
.take_while(|&x| {
|
|
|
|
if req.reverse {
|
|
|
|
x < start_num
|
|
|
|
} else {
|
|
|
|
best_num.saturating_sub(start_num) >= x
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.map(|x| {
|
|
|
|
if req.reverse {
|
|
|
|
start_num.saturating_sub(x)
|
|
|
|
} else {
|
|
|
|
start_num.saturating_add(x)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.map(|x| self.block_header(BlockId::Number(x)))
|
|
|
|
.take_while(|x| x.is_some())
|
|
|
|
.flat_map(|x| x)
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
if headers.is_empty() {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(::request::HeadersResponse { headers })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get a block header by id.
|
|
|
|
fn block_header(&self, id: BlockId) -> Option<encoded::Header>;
|
|
|
|
|
|
|
|
/// Get a transaction index by hash.
|
|
|
|
fn transaction_index(
|
|
|
|
&self,
|
|
|
|
req: request::CompleteTransactionIndexRequest,
|
|
|
|
) -> Option<request::TransactionIndexResponse>;
|
|
|
|
|
|
|
|
/// Fulfill a block body request.
|
|
|
|
fn block_body(&self, req: request::CompleteBodyRequest) -> Option<request::BodyResponse>;
|
|
|
|
|
|
|
|
/// Fulfill a request for block receipts.
|
|
|
|
fn block_receipts(
|
|
|
|
&self,
|
|
|
|
req: request::CompleteReceiptsRequest,
|
|
|
|
) -> Option<request::ReceiptsResponse>;
|
|
|
|
|
|
|
|
/// Get an account proof.
|
|
|
|
fn account_proof(
|
|
|
|
&self,
|
|
|
|
req: request::CompleteAccountRequest,
|
|
|
|
) -> Option<request::AccountResponse>;
|
|
|
|
|
|
|
|
/// Get a storage proof.
|
|
|
|
fn storage_proof(
|
|
|
|
&self,
|
|
|
|
req: request::CompleteStorageRequest,
|
|
|
|
) -> Option<request::StorageResponse>;
|
|
|
|
|
|
|
|
/// Provide contract code for the specified (block_hash, code_hash) pair.
|
|
|
|
fn contract_code(&self, req: request::CompleteCodeRequest) -> Option<request::CodeResponse>;
|
|
|
|
|
|
|
|
/// Provide a header proof from a given Canonical Hash Trie as well as the
|
|
|
|
/// corresponding header.
|
|
|
|
fn header_proof(
|
|
|
|
&self,
|
|
|
|
req: request::CompleteHeaderProofRequest,
|
|
|
|
) -> Option<request::HeaderProofResponse>;
|
|
|
|
|
|
|
|
/// Provide pending transactions.
|
|
|
|
fn transactions_to_propagate(&self) -> Vec<PendingTransaction>;
|
|
|
|
|
|
|
|
/// Provide a proof-of-execution for the given transaction proof request.
|
|
|
|
/// Returns a vector of all state items necessary to execute the transaction.
|
|
|
|
fn transaction_proof(
|
|
|
|
&self,
|
|
|
|
req: request::CompleteExecutionRequest,
|
|
|
|
) -> Option<request::ExecutionResponse>;
|
|
|
|
|
|
|
|
/// Provide epoch signal data at given block hash. This should be just the
|
|
|
|
fn epoch_signal(&self, req: request::CompleteSignalRequest) -> Option<request::SignalResponse>;
|
2016-12-19 14:54:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Implementation of a light client data provider for a client.
|
|
|
|
impl<T: ProvingBlockChainClient + ?Sized> Provider for T {
|
2020-08-05 06:08:03 +02:00
|
|
|
fn chain_info(&self) -> BlockChainInfo {
|
|
|
|
ChainInfo::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)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn block_header(&self, id: BlockId) -> Option<encoded::Header> {
|
|
|
|
ClientBlockInfo::block_header(self, id)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn transaction_index(
|
|
|
|
&self,
|
|
|
|
req: request::CompleteTransactionIndexRequest,
|
|
|
|
) -> Option<request::TransactionIndexResponse> {
|
|
|
|
use common_types::ids::TransactionId;
|
|
|
|
|
|
|
|
self.transaction_receipt(TransactionId::Hash(req.hash))
|
|
|
|
.map(|receipt| request::TransactionIndexResponse {
|
|
|
|
num: receipt.block_number,
|
|
|
|
hash: receipt.block_hash,
|
|
|
|
index: receipt.transaction_index as u64,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn block_body(&self, req: request::CompleteBodyRequest) -> Option<request::BodyResponse> {
|
|
|
|
BlockChainClient::block_body(self, BlockId::Hash(req.hash))
|
|
|
|
.map(|body| ::request::BodyResponse { body })
|
|
|
|
}
|
|
|
|
|
|
|
|
fn block_receipts(
|
|
|
|
&self,
|
|
|
|
req: request::CompleteReceiptsRequest,
|
|
|
|
) -> Option<request::ReceiptsResponse> {
|
|
|
|
BlockChainClient::block_receipts(self, &req.hash).map(|x| ::request::ReceiptsResponse {
|
|
|
|
receipts: x.receipts,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn account_proof(
|
|
|
|
&self,
|
|
|
|
req: request::CompleteAccountRequest,
|
|
|
|
) -> Option<request::AccountResponse> {
|
|
|
|
self.prove_account(req.address_hash, BlockId::Hash(req.block_hash))
|
|
|
|
.map(|(proof, acc)| ::request::AccountResponse {
|
|
|
|
proof,
|
|
|
|
nonce: acc.nonce,
|
|
|
|
balance: acc.balance,
|
|
|
|
code_hash: acc.code_hash,
|
|
|
|
storage_root: acc.storage_root,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn storage_proof(
|
|
|
|
&self,
|
|
|
|
req: request::CompleteStorageRequest,
|
|
|
|
) -> Option<request::StorageResponse> {
|
|
|
|
self.prove_storage(
|
|
|
|
req.address_hash,
|
|
|
|
req.key_hash,
|
|
|
|
BlockId::Hash(req.block_hash),
|
|
|
|
)
|
|
|
|
.map(|(proof, item)| ::request::StorageResponse { proof, value: item })
|
|
|
|
}
|
|
|
|
|
|
|
|
fn contract_code(&self, req: request::CompleteCodeRequest) -> Option<request::CodeResponse> {
|
|
|
|
self.state_data(&req.code_hash)
|
|
|
|
.map(|code| ::request::CodeResponse { code })
|
|
|
|
}
|
|
|
|
|
|
|
|
fn header_proof(
|
|
|
|
&self,
|
|
|
|
req: request::CompleteHeaderProofRequest,
|
|
|
|
) -> Option<request::HeaderProofResponse> {
|
|
|
|
let cht_number = match cht::block_to_cht_number(req.num) {
|
|
|
|
Some(cht_num) => cht_num,
|
|
|
|
None => {
|
|
|
|
debug!(target: "pip_provider", "Requested CHT proof with invalid block number");
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut needed = None;
|
|
|
|
|
|
|
|
// build the CHT, caching the requested header as we pass through it.
|
|
|
|
let cht = {
|
|
|
|
let block_info = |id| {
|
|
|
|
let hdr = self.block_header(id);
|
|
|
|
let td = self.block_total_difficulty(id);
|
|
|
|
|
|
|
|
match (hdr, td) {
|
|
|
|
(Some(hdr), Some(td)) => {
|
|
|
|
let info = BlockInfo {
|
|
|
|
hash: hdr.hash(),
|
|
|
|
parent_hash: hdr.parent_hash(),
|
|
|
|
total_difficulty: td,
|
|
|
|
};
|
|
|
|
|
|
|
|
if hdr.number() == req.num {
|
|
|
|
needed = Some((hdr, td));
|
|
|
|
}
|
|
|
|
|
|
|
|
Some(info)
|
|
|
|
}
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
match cht::build(cht_number, block_info) {
|
|
|
|
Some(cht) => cht,
|
|
|
|
None => return None, // incomplete CHT.
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let (needed_hdr, needed_td) =
|
|
|
|
needed.expect("`needed` always set in loop, number checked before; qed");
|
|
|
|
|
|
|
|
// prove our result.
|
|
|
|
match cht.prove(req.num, 0) {
|
|
|
|
Ok(Some(proof)) => Some(::request::HeaderProofResponse {
|
|
|
|
proof,
|
|
|
|
hash: needed_hdr.hash(),
|
|
|
|
td: needed_td,
|
|
|
|
}),
|
|
|
|
Ok(None) => None,
|
|
|
|
Err(e) => {
|
|
|
|
debug!(target: "pip_provider", "Error looking up number in freshly-created CHT: {}", e);
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn transaction_proof(
|
|
|
|
&self,
|
|
|
|
req: request::CompleteExecutionRequest,
|
|
|
|
) -> Option<request::ExecutionResponse> {
|
|
|
|
use common_types::transaction::Transaction;
|
|
|
|
|
|
|
|
let id = BlockId::Hash(req.block_hash);
|
|
|
|
let nonce = match self.nonce(&req.from, id) {
|
|
|
|
Some(nonce) => nonce,
|
|
|
|
None => return None,
|
|
|
|
};
|
|
|
|
let transaction = Transaction {
|
|
|
|
nonce,
|
|
|
|
gas: req.gas,
|
|
|
|
gas_price: req.gas_price,
|
|
|
|
action: req.action,
|
|
|
|
value: req.value,
|
|
|
|
data: req.data,
|
|
|
|
}
|
|
|
|
.fake_sign(req.from);
|
|
|
|
|
|
|
|
self.prove_transaction(transaction, id)
|
|
|
|
.map(|(_, proof)| ::request::ExecutionResponse { items: proof })
|
|
|
|
}
|
|
|
|
|
|
|
|
fn transactions_to_propagate(&self) -> Vec<PendingTransaction> {
|
|
|
|
BlockChainClient::transactions_to_propagate(self)
|
|
|
|
.into_iter()
|
|
|
|
.map(|tx| tx.pending().clone())
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn epoch_signal(&self, req: request::CompleteSignalRequest) -> Option<request::SignalResponse> {
|
|
|
|
self.epoch_signal(req.block_hash)
|
|
|
|
.map(|signal| request::SignalResponse { signal })
|
|
|
|
}
|
2016-12-15 18:19:19 +01:00
|
|
|
}
|
2017-01-16 17:42:39 +01:00
|
|
|
|
2017-02-09 18:42:18 +01:00
|
|
|
/// The light client "provider" implementation. This wraps a `LightClient` and
|
|
|
|
/// a light transaction queue.
|
|
|
|
pub struct LightProvider<L> {
|
2020-08-05 06:08:03 +02:00
|
|
|
client: Arc<L>,
|
|
|
|
txqueue: Arc<RwLock<TransactionQueue>>,
|
2017-02-09 18:42:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<L> LightProvider<L> {
|
2020-08-05 06:08:03 +02:00
|
|
|
/// Create a new `LightProvider` from the given client and transaction queue.
|
|
|
|
pub fn new(client: Arc<L>, txqueue: Arc<RwLock<TransactionQueue>>) -> Self {
|
|
|
|
LightProvider { client, txqueue }
|
|
|
|
}
|
2017-02-09 18:42:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: draw from cache (shared between this and the RPC layer)
|
|
|
|
impl<L: AsLightClient + Send + Sync> Provider for LightProvider<L> {
|
2020-08-05 06:08:03 +02:00
|
|
|
fn chain_info(&self) -> BlockChainInfo {
|
|
|
|
self.client.as_light_client().chain_info()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn reorg_depth(&self, _a: &H256, _b: &H256) -> Option<u64> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
fn earliest_state(&self) -> Option<u64> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
fn block_header(&self, id: BlockId) -> Option<encoded::Header> {
|
|
|
|
self.client.as_light_client().block_header(id)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn transaction_index(
|
|
|
|
&self,
|
|
|
|
_req: request::CompleteTransactionIndexRequest,
|
|
|
|
) -> Option<request::TransactionIndexResponse> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
fn block_body(&self, _req: request::CompleteBodyRequest) -> Option<request::BodyResponse> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
fn block_receipts(
|
|
|
|
&self,
|
|
|
|
_req: request::CompleteReceiptsRequest,
|
|
|
|
) -> Option<request::ReceiptsResponse> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
fn account_proof(
|
|
|
|
&self,
|
|
|
|
_req: request::CompleteAccountRequest,
|
|
|
|
) -> Option<request::AccountResponse> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
fn storage_proof(
|
|
|
|
&self,
|
|
|
|
_req: request::CompleteStorageRequest,
|
|
|
|
) -> Option<request::StorageResponse> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
fn contract_code(&self, _req: request::CompleteCodeRequest) -> Option<request::CodeResponse> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
fn header_proof(
|
|
|
|
&self,
|
|
|
|
_req: request::CompleteHeaderProofRequest,
|
|
|
|
) -> Option<request::HeaderProofResponse> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
fn transaction_proof(
|
|
|
|
&self,
|
|
|
|
_req: request::CompleteExecutionRequest,
|
|
|
|
) -> Option<request::ExecutionResponse> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
fn epoch_signal(
|
|
|
|
&self,
|
|
|
|
_req: request::CompleteSignalRequest,
|
|
|
|
) -> Option<request::SignalResponse> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
fn transactions_to_propagate(&self) -> Vec<PendingTransaction> {
|
|
|
|
let chain_info = self.chain_info();
|
|
|
|
self.txqueue.read().ready_transactions(
|
|
|
|
chain_info.best_block_number,
|
|
|
|
chain_info.best_block_timestamp,
|
|
|
|
)
|
|
|
|
}
|
2017-02-09 18:42:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<L: AsLightClient> AsLightClient for LightProvider<L> {
|
2020-08-05 06:08:03 +02:00
|
|
|
type Client = L::Client;
|
2017-02-09 18:42:18 +01:00
|
|
|
|
2020-08-05 06:08:03 +02:00
|
|
|
fn as_light_client(&self) -> &L::Client {
|
|
|
|
self.client.as_light_client()
|
|
|
|
}
|
2017-02-09 18:42:18 +01:00
|
|
|
}
|
|
|
|
|
2017-01-16 17:42:39 +01:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2020-08-05 06:08:03 +02:00
|
|
|
use super::Provider;
|
|
|
|
use ethcore::client::{EachBlockWith, TestBlockChainClient};
|
2017-01-16 17:42:39 +01:00
|
|
|
|
2020-08-05 06:08:03 +02:00
|
|
|
#[test]
|
|
|
|
fn cht_proof() {
|
|
|
|
let client = TestBlockChainClient::new();
|
|
|
|
client.add_blocks(2000, EachBlockWith::Nothing);
|
2017-01-16 17:42:39 +01:00
|
|
|
|
2020-08-05 06:08:03 +02:00
|
|
|
let req = ::request::CompleteHeaderProofRequest { num: 1500 };
|
2017-01-16 17:42:39 +01:00
|
|
|
|
2020-08-05 06:08:03 +02:00
|
|
|
assert!(client.header_proof(req.clone()).is_none());
|
2017-01-16 17:42:39 +01:00
|
|
|
|
2020-08-05 06:08:03 +02:00
|
|
|
client.add_blocks(48, EachBlockWith::Nothing);
|
2017-01-16 17:42:39 +01:00
|
|
|
|
2020-08-05 06:08:03 +02:00
|
|
|
assert!(client.header_proof(req.clone()).is_some());
|
|
|
|
}
|
2017-01-16 17:42:39 +01:00
|
|
|
}
|