new requests in provider.

This commit is contained in:
Robert Habermeier 2017-03-06 17:36:56 +01:00
parent 8e9faa416d
commit 87f3d53607
3 changed files with 191 additions and 167 deletions

View File

@ -315,50 +315,3 @@ impl LightChainClient for Client {
Client::cht_root(self, i)
}
}
// dummy implementation, should be removed when a `TestClient` is added.
impl ::provider::Provider for Client {
fn chain_info(&self) -> BlockChainInfo {
Client::chain_info(self)
}
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> {
Client::block_header(self, id)
}
fn block_body(&self, _id: BlockId) -> Option<encoded::Body> {
None
}
fn block_receipts(&self, _hash: &H256) -> Option<Bytes> {
None
}
fn state_proof(&self, _req: ::request::StateProof) -> Vec<Bytes> {
Vec::new()
}
fn contract_code(&self, _req: ::request::ContractCode) -> Bytes {
Vec::new()
}
fn header_proof(&self, _req: ::request::HeaderProof) -> Option<(encoded::Header, Vec<Bytes>)> {
None
}
fn transaction_proof(&self, _req: ::request::TransactionProof) -> Option<Vec<DBValue>> {
None
}
fn ready_transactions(&self) -> Vec<::ethcore::transaction::PendingTransaction> {
Vec::new()
}
}

View File

@ -59,10 +59,10 @@ pub trait Provider: Send + Sync {
///
/// 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::Headers) -> Vec<encoded::Header> {
fn block_headers(&self, req: request::CompleteHeadersRequest) -> Option<request::HeadersResponse> {
use request::HashOrNumber;
if req.max == 0 { return Vec::new() }
if req.max == 0 { return None }
let best_num = self.chain_info().best_block_number;
let start_num = match req.start {
@ -70,7 +70,7 @@ pub trait Provider: Send + Sync {
HashOrNumber::Hash(hash) => match self.block_header(BlockId::Hash(hash)) {
None => {
trace!(target: "les_provider", "Unknown block hash {} requested", hash);
return Vec::new();
return None;
}
Some(header) => {
let num = header.number();
@ -79,7 +79,9 @@ pub trait Provider: Send + Sync {
if req.max == 1 || canon_hash != Some(hash) {
// Non-canonical header or single header requested.
return vec![header];
return Some(::request::HeadersResponse {
headers: vec![header],
})
}
num
@ -87,109 +89,39 @@ pub trait Provider: Send + Sync {
}
};
(0u64..req.max as u64)
let headers = (0u64..req.max as u64)
.map(|x: u64| x.saturating_mul(req.skip + 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 - x } else { start_num + x })
.map(|x| self.block_header(BlockId::Number(x)))
.take_while(|x| x.is_some())
.flat_map(|x| x)
.collect()
.collect();
Some(::request::HeadersResponse { headers: headers })
}
/// Get a block header by id.
fn block_header(&self, id: BlockId) -> Option<encoded::Header>;
/// Provide as many as possible of the requested blocks (minus the headers) encoded
/// in RLP format.
fn block_bodies(&self, req: request::Bodies) -> Vec<Option<encoded::Body>> {
req.block_hashes.into_iter()
.map(|hash| self.block_body(BlockId::Hash(hash)))
.collect()
}
/// Fulfill a block body request.
fn block_body(&self, req: request::CompleteBodyRequest) -> Option<request::BodyResponse>;
/// Get a block body by id.
fn block_body(&self, id: BlockId) -> Option<encoded::Body>;
/// Fulfill a request for block receipts.
fn block_receipts(&self, req: request::CompleteReceiptsRequest) -> Option<request::ReceiptsResponse>;
/// Provide the receipts as many as possible of the requested blocks.
/// Returns a vector of RLP-encoded lists of receipts.
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()
}
/// Get an account proof.
fn account_proof(&self, req: request::CompleteAccountRequest) -> Option<request::AccountResponse>;
/// Get a block's receipts as an RLP-encoded list by block hash.
fn block_receipts(&self, hash: &H256) -> Option<Bytes>;
/// Get a storage proof.
fn storage_proof(&self, req: request::CompleteStorageRequest) -> Option<request::StorageResponse>;
/// 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.
fn proofs(&self, req: request::StateProofs) -> Vec<Bytes> {
use rlp::{RlpStream, Stream};
let mut results = Vec::with_capacity(req.requests.len());
for request in req.requests {
let proof = self.state_proof(request);
let mut stream = RlpStream::new_list(proof.len());
for node in proof {
stream.append_raw(&node, 1);
}
results.push(stream.out());
}
results
}
/// 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> {
req.code_requests.into_iter()
.map(|req| self.contract_code(req))
.collect()
}
/// 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.
fn header_proofs(&self, req: request::HeaderProofs) -> Vec<Bytes> {
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);
stream.append_raw(&header.into_inner(), 1).begin_list(proof.len());
for node in proof {
stream.append_raw(&node, 1);
}
stream.out()
}
})
.collect()
}
/// 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. The first element is the block header and the
/// second is a merkle proof of the CHT.
fn header_proof(&self, req: request::HeaderProof) -> Option<(encoded::Header, Vec<Bytes>)>;
/// corresponding header.
fn header_proof(&self, req: request::CompleteHeaderProofRequest) -> Option<request::HeaderProofResponse>;
/// Provide pending transactions.
fn ready_transactions(&self) -> Vec<PendingTransaction>;
@ -217,32 +149,52 @@ impl<T: ProvingBlockChainClient + ?Sized> Provider for T {
BlockChainClient::block_header(self, id)
}
fn block_body(&self, id: BlockId) -> Option<encoded::Body> {
fn block_body(&self, req: request::CompleteBodyRequest) -> Option<request::BodyResponse>;
BlockChainClient::block_body(self, id)
.map(|body| ::request::BodyResponse { body: body })
}
fn block_receipts(&self, hash: &H256) -> Option<Bytes> {
fn block_receipts(&self, req: request::CompleteReceiptsRequest) -> Option<request::ReceiptsResponse>;
BlockChainClient::block_receipts(self, hash)
.map(|x| ::request::ReceiptsResponse { receipts: ::rlp::decode(&x) })
}
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 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: proof,
nonce: acc.nonce,
balance: acc.balance,
code_hash: acc.code_hash,
storage_root: acc.storage_root,
}
}))
}
fn contract_code(&self, req: request::ContractCode) -> Bytes {
self.code_by_hash(req.account_key, BlockId::Hash(req.block_hash))
fn storage_proof(&self, req: request::CompleteStorageRequest) -> Option<request::StorageResponse> {
self.prove_account(req.address_hash, req.key_hash, BlockId::Hash(req.block_hash)).map(|(proof, item) | {
::request::StorageResponse {
proof: proof,
value: item,
}
}))
}
fn header_proof(&self, req: request::HeaderProof) -> Option<(encoded::Header, Vec<Bytes>)> {
if Some(req.cht_number) != cht::block_to_cht_number(req.block_number) {
debug!(target: "les_provider", "Requested CHT number mismatch with block number.");
fn contract_code(&self, req: request::ContractCode) -> Option<request::CodeResponse> {
self.state_data(&req.code_hash)
.map(|code| ::request::CodeResponse { code: 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: "les_provider", "Requested CHT proof with invalid block number");
return None;
}
};
let mut needed_hdr = None;
let mut needed = None;
// build the CHT, caching the requested header as we pass through it.
let cht = {
@ -258,8 +210,8 @@ impl<T: ProvingBlockChainClient + ?Sized> Provider for T {
total_difficulty: td,
};
if hdr.number() == req.block_number {
needed_hdr = Some(hdr);
if hdr.number() == req.num {
needed = Some((hdr, td));
}
Some(info)
@ -268,17 +220,21 @@ impl<T: ProvingBlockChainClient + ?Sized> Provider for T {
}
};
match cht::build(req.cht_number, block_info) {
match cht::build(cht_number, block_info) {
Some(cht) => cht,
None => return None, // incomplete CHT.
}
};
let needed_hdr = needed_hdr.expect("`needed_hdr` always set in loop, number checked before; qed");
let (needed_hdr, needed_td) = needed.expect("`needed` always set in loop, number checked before; qed");
// prove our result.
match cht.prove(req.block_number, req.from_level) {
Ok(Some(proof)) => Some((needed_hdr, proof)),
match cht.prove(req.num, 0) {
Ok(Some(proof)) => Some(::request::HeaderProofResponse {
proof: proof,
hash: needed_hdr.hash(),
td: needed_td,
}),
Ok(None) => None,
Err(e) => {
debug!(target: "les_provider", "Error looking up number in freshly-created CHT: {}", e);
@ -347,23 +303,27 @@ impl<L: AsLightClient + Send + Sync> Provider for LightProvider<L> {
self.client.as_light_client().block_header(id)
}
fn block_body(&self, _id: BlockId) -> Option<encoded::Body> {
fn block_body(&self, req: request::CompleteBodyRequest) -> Option<request::BodyResponse> {
None
}
fn block_receipts(&self, _hash: &H256) -> Option<Bytes> {
fn block_receipts(&self, req: request::CompleteReceiptsRequest) -> Option<request::ReceiptsResponse> {
None
}
fn state_proof(&self, _req: request::StateProof) -> Vec<Bytes> {
Vec::new()
fn account_proof(&self, req: request::CompleteAccountRequest) -> Option<request::AccountResponse> {
None
}
fn contract_code(&self, _req: request::ContractCode) -> Bytes {
Vec::new()
fn storage_proof(&self, req: request::CompleteStorageRequest) -> Option<request::StorageResponse> {
None
}
fn header_proof(&self, _req: request::HeaderProof) -> Option<(encoded::Header, Vec<Bytes>)> {
fn contract_code(&self, req: request::CompleteCodeRequest) -> Option<request::CodeResponse> {
None
}
fn header_proof(&self, req: request::CompleteHeaderProofRequest) -> Option<request::HeaderProofResponse> {
None
}

View File

@ -113,6 +113,15 @@ pub enum Output {
Number(u64),
}
impl Output {
fn kind(&self) -> OutputKind {
match *self {
Output::Hash(_) => OutputKind::Hash,
Output::Number(_) => OutputKind::Number,
}
}
}
/// Response output kinds which can be used as back-references.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum OutputKind {
@ -145,6 +154,7 @@ impl From<u64> for HashOrNumber {
}
/// All request types, as they're sent over the network.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Request {
/// A request for block headers.
Headers(IncompleteHeadersRequest),
@ -164,6 +174,27 @@ pub enum Request {
// Transaction proof.
}
/// All request types, as they're sent over the network.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CompleteRequest {
/// A request for block headers.
Headers(CompleteHeadersRequest),
/// A request for a header proof (from a CHT)
HeaderProof(CompleteHeaderProofRequest),
// TransactionIndex,
/// A request for a block's receipts.
Receipts(CompleteReceiptsRequest),
/// A request for a block body.
Body(CompleteBodyRequest),
/// A request for a merkle proof of an account.
Account(CompleteAccountRequest),
/// A request for a merkle proof of contract storage.
Storage(CompleteStorageRequest),
/// A request for contract code.
Code(CompleteCodeRequest),
// Transaction proof.
}
impl Request {
fn kind(&self) -> RequestKind {
match *self {
@ -210,10 +241,54 @@ impl Encodable for Request {
}
}
impl IncompleteRequest for Request {
type Complete = CompleteRequest;
fn check_outputs<F>(&self, f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
{
match *self {
Request::Headers(ref req) => req.check_outputs(f),
Request::HeaderProof(ref req) => req.check_outputs(f),
Request::Receipts(ref req) => req.check_outputs(f),
Request::Body(ref req) => req.check_outputs(f),
Request::Account(ref req) => req.check_outputs(f),
Request::Storage(ref req) => req.check_outputs(f),
Request::Code(ref req) => req.check_outputs(f),
}
}
fn note_outputs<F>(&self, f: F) where F: FnMut(usize, OutputKind) {
match *self {
Request::Headers(ref req) => req.note_outputs(f),
Request::HeaderProof(ref req) => req.note_outputs(f),
Request::Receipts(ref req) => req.note_outputs(f),
Request::Body(ref req) => req.note_outputs(f),
Request::Account(ref req) => req.note_outputs(f),
Request::Storage(ref req) => req.note_outputs(f),
Request::Code(ref req) => req.note_outputs(f),
}
}
fn fill<F>(self, oracle: F) -> Result<Self::Complete, NoSuchOutput>
where F: Fn(usize, usize) -> Result<Output, NoSuchOutput>
{
match self {
Request::Headers(req) => CompleteRequest::Headers(req.fill(oracle)),
Request::HeaderProof(req) => CompleteRequest::HeaderProof(req.fill(oracle)),
Request::Receipts(req) => CompleteRequest::Receipts(req.fill(oracle)),
Request::Body(req) => CompleteRequest::Body(req.fill(oracle)),
Request::Account(req) => CompleteRequest::Account(req.fill(oracle)),
Request::Storage(req) => CompleteRequest::Storage(req.fill(oracle)),
Request::Code(req) => CompleteRequest::Code(req.fill(oracle)),
}
}
}
/// Kinds of requests.
/// Doubles as the "ID" field of the request.
#[repr(u8)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RequestKind {
/// A request for headers.
Headers = 0,
@ -252,6 +327,42 @@ impl Encodable for RequestKind {
}
}
/// All response types.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Response {
/// A response for block headers.
Headers(HeadersResponse),
/// A response for a header proof (from a CHT)
HeaderProof(HeaderProofResponse),
// TransactionIndex,
/// A response for a block's receipts.
Receipts(ReceiptsResponse),
/// A response for a block body.
Body(BodyResponse),
/// A response for a merkle proof of an account.
Account(AccountResponse),
/// A response for a merkle proof of contract storage.
Storage(StorageResponse),
/// A response for contract code.
Code(CodeResponse),
// Transaction proof.
}
impl Response {
/// Fill reusable outputs by writing them into the function.
pub fn fill_outputs<F>(&self, mut f: F) where F: FnMut(usize, Output) {
match *self {
Response::Headers(res) => res.fill_outputs(f)
Response::HeaderProof(res) => res.fill_outputs(f)
Response::Receipts(res) => res.fill_outputs(f)
Response::Body(res) => res.fill_outputs(f)
Response::Account(res) => res.fill_outputs(f)
Response::Storage(res) => res.fill_outputs(f)
Response::Code(res) => res.fill_outputs(f)
}
}
}
/// A potentially incomplete request.
pub trait IncompleteRequest: Sized {
type Complete;
@ -369,7 +480,8 @@ pub mod header {
/// The output of a request for headers.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Response {
header: Vec<encoded::Header>,
/// The headers requested.
pub headers: Vec<encoded::Header>,
}
impl Response {
@ -523,7 +635,6 @@ pub mod block_receipts {
hash: hash,
})
}
}
/// A complete block receipts request.