new requests in provider.
This commit is contained in:
parent
8e9faa416d
commit
87f3d53607
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user