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) 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 /// The returned vector may have any length in the range [0, `max`], but the
/// results within must adhere to the `skip` and `reverse` parameters. /// 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; 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 best_num = self.chain_info().best_block_number;
let start_num = match req.start { 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)) { HashOrNumber::Hash(hash) => match self.block_header(BlockId::Hash(hash)) {
None => { None => {
trace!(target: "les_provider", "Unknown block hash {} requested", hash); trace!(target: "les_provider", "Unknown block hash {} requested", hash);
return Vec::new(); return None;
} }
Some(header) => { Some(header) => {
let num = header.number(); let num = header.number();
@ -79,7 +79,9 @@ pub trait Provider: Send + Sync {
if req.max == 1 || canon_hash != Some(hash) { if req.max == 1 || canon_hash != Some(hash) {
// Non-canonical header or single header requested. // Non-canonical header or single header requested.
return vec![header]; return Some(::request::HeadersResponse {
headers: vec![header],
})
} }
num 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)) .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 }) .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| if req.reverse { start_num - x } else { start_num + x })
.map(|x| self.block_header(BlockId::Number(x))) .map(|x| self.block_header(BlockId::Number(x)))
.take_while(|x| x.is_some()) .take_while(|x| x.is_some())
.flat_map(|x| x) .flat_map(|x| x)
.collect() .collect();
Some(::request::HeadersResponse { headers: headers })
} }
/// Get a block header by id. /// Get a block header by id.
fn block_header(&self, id: BlockId) -> Option<encoded::Header>; fn block_header(&self, id: BlockId) -> Option<encoded::Header>;
/// Provide as many as possible of the requested blocks (minus the headers) encoded /// Fulfill a block body request.
/// in RLP format. fn block_body(&self, req: request::CompleteBodyRequest) -> Option<request::BodyResponse>;
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()
}
/// Get a block body by id. /// Fulfill a request for block receipts.
fn block_body(&self, id: BlockId) -> Option<encoded::Body>; fn block_receipts(&self, req: request::CompleteReceiptsRequest) -> Option<request::ReceiptsResponse>;
/// Provide the receipts as many as possible of the requested blocks. /// Get an account proof.
/// Returns a vector of RLP-encoded lists of receipts. fn account_proof(&self, req: request::CompleteAccountRequest) -> Option<request::AccountResponse>;
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 a block's receipts as an RLP-encoded list by block hash. /// Get a storage proof.
fn block_receipts(&self, hash: &H256) -> Option<Bytes>; fn storage_proof(&self, req: request::CompleteStorageRequest) -> Option<request::StorageResponse>;
/// Provide a set of merkle proofs, as requested. Each request is a /// Provide contract code for the specified (block_hash, code_hash) pair.
/// block hash and request parameters. fn contract_code(&self, req: request::CompleteCodeRequest) -> Option<request::CodeResponse>;
///
/// 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 a header proof from a given Canonical Hash Trie as well as the /// 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 /// corresponding header.
/// second is a merkle proof of the CHT. fn header_proof(&self, req: request::CompleteHeaderProofRequest) -> Option<request::HeaderProofResponse>;
fn header_proof(&self, req: request::HeaderProof) -> Option<(encoded::Header, Vec<Bytes>)>;
/// Provide pending transactions. /// Provide pending transactions.
fn ready_transactions(&self) -> Vec<PendingTransaction>; fn ready_transactions(&self) -> Vec<PendingTransaction>;
@ -217,32 +149,52 @@ impl<T: ProvingBlockChainClient + ?Sized> Provider for T {
BlockChainClient::block_header(self, id) 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) 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) BlockChainClient::block_receipts(self, hash)
.map(|x| ::request::ReceiptsResponse { receipts: ::rlp::decode(&x) })
} }
fn state_proof(&self, req: request::StateProof) -> Vec<Bytes> { fn account_proof(&self, req: request::CompleteAccountRequest) -> Option<request::AccountResponse> {
match req.key2 { self.prove_account(req.address_hash, BlockId::Hash(req.block_hash)).map(|(proof, acc)| {
Some(key2) => self.prove_storage(req.key1, key2, req.from_level, BlockId::Hash(req.block)), ::request::AccountResponse {
None => self.prove_account(req.key1, req.from_level, BlockId::Hash(req.block)), 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 { fn storage_proof(&self, req: request::CompleteStorageRequest) -> Option<request::StorageResponse> {
self.code_by_hash(req.account_key, BlockId::Hash(req.block_hash)) 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>)> { fn contract_code(&self, req: request::ContractCode) -> Option<request::CodeResponse> {
if Some(req.cht_number) != cht::block_to_cht_number(req.block_number) { self.state_data(&req.code_hash)
debug!(target: "les_provider", "Requested CHT number mismatch with block number."); .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; return None;
} }
};
let mut needed_hdr = None; let mut needed = None;
// build the CHT, caching the requested header as we pass through it. // build the CHT, caching the requested header as we pass through it.
let cht = { let cht = {
@ -258,8 +210,8 @@ impl<T: ProvingBlockChainClient + ?Sized> Provider for T {
total_difficulty: td, total_difficulty: td,
}; };
if hdr.number() == req.block_number { if hdr.number() == req.num {
needed_hdr = Some(hdr); needed = Some((hdr, td));
} }
Some(info) 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, Some(cht) => cht,
None => return None, // incomplete 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. // prove our result.
match cht.prove(req.block_number, req.from_level) { match cht.prove(req.num, 0) {
Ok(Some(proof)) => Some((needed_hdr, proof)), Ok(Some(proof)) => Some(::request::HeaderProofResponse {
proof: proof,
hash: needed_hdr.hash(),
td: needed_td,
}),
Ok(None) => None, Ok(None) => None,
Err(e) => { Err(e) => {
debug!(target: "les_provider", "Error looking up number in freshly-created CHT: {}", 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) 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 None
} }
fn block_receipts(&self, _hash: &H256) -> Option<Bytes> { fn block_receipts(&self, req: request::CompleteReceiptsRequest) -> Option<request::ReceiptsResponse> {
None None
} }
fn state_proof(&self, _req: request::StateProof) -> Vec<Bytes> { fn account_proof(&self, req: request::CompleteAccountRequest) -> Option<request::AccountResponse> {
Vec::new() None
} }
fn contract_code(&self, _req: request::ContractCode) -> Bytes { fn storage_proof(&self, req: request::CompleteStorageRequest) -> Option<request::StorageResponse> {
Vec::new() 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 None
} }

View File

@ -113,6 +113,15 @@ pub enum Output {
Number(u64), 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. /// Response output kinds which can be used as back-references.
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum OutputKind { pub enum OutputKind {
@ -145,6 +154,7 @@ impl From<u64> for HashOrNumber {
} }
/// All request types, as they're sent over the network. /// All request types, as they're sent over the network.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Request { pub enum Request {
/// A request for block headers. /// A request for block headers.
Headers(IncompleteHeadersRequest), Headers(IncompleteHeadersRequest),
@ -164,6 +174,27 @@ pub enum Request {
// Transaction proof. // 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 { impl Request {
fn kind(&self) -> RequestKind { fn kind(&self) -> RequestKind {
match *self { 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. /// Kinds of requests.
/// Doubles as the "ID" field of the request. /// Doubles as the "ID" field of the request.
#[repr(u8)] #[repr(u8)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RequestKind { pub enum RequestKind {
/// A request for headers. /// A request for headers.
Headers = 0, 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. /// A potentially incomplete request.
pub trait IncompleteRequest: Sized { pub trait IncompleteRequest: Sized {
type Complete; type Complete;
@ -369,7 +480,8 @@ pub mod header {
/// The output of a request for headers. /// The output of a request for headers.
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct Response { pub struct Response {
header: Vec<encoded::Header>, /// The headers requested.
pub headers: Vec<encoded::Header>,
} }
impl Response { impl Response {
@ -523,7 +635,6 @@ pub mod block_receipts {
hash: hash, hash: hash,
}) })
} }
} }
/// A complete block receipts request. /// A complete block receipts request.