typestrong API
This commit is contained in:
parent
cf75a19e8d
commit
5793bb8fac
@ -22,6 +22,7 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
|
||||
use ethcore::basic_account::BasicAccount;
|
||||
@ -111,6 +112,22 @@ fn guess_capabilities(requests: &[CheckedRequest]) -> Capabilities {
|
||||
caps
|
||||
}
|
||||
|
||||
/// A future extracting the concrete output type of the generic adapter
|
||||
/// from a vector of responses.
|
||||
pub struct OnResponses<T: request::RequestAdapter> {
|
||||
receiver: Receiver<Vec<Response>>,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: request::RequestAdapter> Future for OnResponses<T> {
|
||||
type Item = T::Out;
|
||||
type Error = Canceled;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
self.receiver.poll().map(|async| async.map(T::extract_from))
|
||||
}
|
||||
}
|
||||
|
||||
/// On demand request service. See module docs for more details.
|
||||
/// Accumulates info about all peers' capabilities and dispatches
|
||||
/// requests to them accordingly.
|
||||
@ -122,8 +139,6 @@ pub struct OnDemand {
|
||||
cache: Arc<Mutex<Cache>>,
|
||||
}
|
||||
|
||||
const RESPONSES_MATCH: &'static str = "N requests always leads to N responses; qed";
|
||||
|
||||
impl OnDemand {
|
||||
/// Create a new `OnDemand` service with the given cache.
|
||||
pub fn new(cache: Arc<Mutex<Cache>>) -> Self {
|
||||
@ -146,12 +161,9 @@ impl OnDemand {
|
||||
match cached {
|
||||
Some(hash) => future::ok(hash).boxed(),
|
||||
None => {
|
||||
self.make_requests(ctx, vec![Request::HeaderProof(req)])
|
||||
self.request(ctx, req)
|
||||
.expect("request given fully fleshed out; qed")
|
||||
.map(|responses| match responses[0] {
|
||||
Response::HeaderProof(ref hash, _) => *hash,
|
||||
_ => panic!("header proof request leads to header proof response; qed")
|
||||
})
|
||||
.map(|(h, _)| h)
|
||||
.boxed()
|
||||
},
|
||||
}
|
||||
@ -168,12 +180,9 @@ impl OnDemand {
|
||||
match cached {
|
||||
Some(score) => future::ok(score).boxed(),
|
||||
None => {
|
||||
self.make_requests(ctx, vec![Request::HeaderProof(req)])
|
||||
self.request(ctx, req)
|
||||
.expect("request given fully fleshed out; qed")
|
||||
.map(|responses| match responses[0] {
|
||||
Response::HeaderProof(_, ref score) => *score,
|
||||
_ => panic!("header proof request leads to header proof response; qed")
|
||||
})
|
||||
.map(|(_, s)| s)
|
||||
.boxed()
|
||||
},
|
||||
}
|
||||
@ -194,12 +203,8 @@ impl OnDemand {
|
||||
match cached {
|
||||
(Some(hash), Some(score)) => future::ok((hash, score)).boxed(),
|
||||
_ => {
|
||||
self.make_requests(ctx, vec![Request::HeaderProof(req)])
|
||||
self.request(ctx, req)
|
||||
.expect("request given fully fleshed out; qed")
|
||||
.map(|responses| match responses[0] {
|
||||
Response::HeaderProof(ref hash, ref score) => (*hash, *score),
|
||||
_ => panic!("header proof request leads to header proof response; qed")
|
||||
})
|
||||
.boxed()
|
||||
},
|
||||
}
|
||||
@ -212,12 +217,8 @@ impl OnDemand {
|
||||
match { self.cache.lock().block_header(&req.0) } {
|
||||
Some(hdr) => future::ok(hdr).boxed(),
|
||||
None => {
|
||||
self.make_requests(ctx, vec![Request::HeaderByHash(req)])
|
||||
self.request(ctx, req)
|
||||
.expect("request given fully fleshed out; qed")
|
||||
.map(|mut responses| match responses.pop().expect(RESPONSES_MATCH) {
|
||||
Response::HeaderByHash(header) => header,
|
||||
_ => panic!("header request leads to header response; qed")
|
||||
})
|
||||
.boxed()
|
||||
},
|
||||
}
|
||||
@ -247,12 +248,8 @@ impl OnDemand {
|
||||
future::ok(encoded::Block::new(stream.out())).boxed()
|
||||
}
|
||||
None => {
|
||||
self.make_requests(ctx, vec![Request::Body(req)])
|
||||
self.request(ctx, req)
|
||||
.expect("request given fully fleshed out; qed")
|
||||
.map(|mut responses| match responses.pop().expect(RESPONSES_MATCH) {
|
||||
Response::Body(body) => body,
|
||||
_ => panic!("body request leads to body response; qed")
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
@ -270,12 +267,8 @@ impl OnDemand {
|
||||
match { self.cache.lock().block_receipts(&req.0.hash()) } {
|
||||
Some(receipts) => future::ok(receipts).boxed(),
|
||||
None => {
|
||||
self.make_requests(ctx, vec![Request::Receipts(req)])
|
||||
self.request(ctx, req)
|
||||
.expect("request given fully fleshed out; qed")
|
||||
.map(|mut responses| match responses.pop().expect(RESPONSES_MATCH) {
|
||||
Response::Receipts(receipts) => receipts,
|
||||
_ => panic!("receipts request leads to receipts response; qed")
|
||||
})
|
||||
.boxed()
|
||||
},
|
||||
}
|
||||
@ -285,12 +278,8 @@ impl OnDemand {
|
||||
/// to verify against.
|
||||
/// `None` here means that no account by the queried key exists in the queried state.
|
||||
pub fn account(&self, ctx: &BasicContext, req: request::Account) -> BoxFuture<Option<BasicAccount>, Canceled> {
|
||||
self.make_requests(ctx, vec![Request::Account(req)])
|
||||
self.request(ctx, req)
|
||||
.expect("request given fully fleshed out; qed")
|
||||
.map(|mut responses| match responses.pop().expect(RESPONSES_MATCH) {
|
||||
Response::Account(account) => account,
|
||||
_ => panic!("account request leads to account response; qed")
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
|
||||
@ -300,32 +289,24 @@ impl OnDemand {
|
||||
if req.code_hash == SHA3_EMPTY {
|
||||
future::ok(Vec::new()).boxed()
|
||||
} else {
|
||||
self.make_requests(ctx, vec![Request::Code(req)])
|
||||
self.request(ctx, req)
|
||||
.expect("request given fully fleshed out; qed")
|
||||
.map(|mut responses| match responses.pop().expect(RESPONSES_MATCH) {
|
||||
Response::Code(code) => code,
|
||||
_ => panic!("code request leads to code response; qed")
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
/// Request proof-of-execution for a transaction.
|
||||
pub fn transaction_proof(&self, ctx: &BasicContext, req: request::TransactionProof) -> BoxFuture<ExecutionResult, Canceled> {
|
||||
self.make_requests(ctx, vec![Request::Execution(req)])
|
||||
self.request(ctx, req)
|
||||
.expect("request given fully fleshed out; qed")
|
||||
.map(|mut responses| match responses.pop().expect(RESPONSES_MATCH) {
|
||||
Response::Execution(execution) => execution,
|
||||
_ => panic!("execution request leads to execution response; qed")
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
|
||||
/// Submit a batch of requests.
|
||||
/// Submit a vector of requests to be processed together.
|
||||
///
|
||||
/// Fails if back-references are not coherent.
|
||||
/// The returned vector of responses will match the requests exactly.
|
||||
pub fn make_requests(&self, ctx: &BasicContext, requests: Vec<Request>)
|
||||
/// The returned vector of responses will correspond to the requests exactly.
|
||||
pub fn request_raw(&self, ctx: &BasicContext, requests: Vec<Request>)
|
||||
-> Result<Receiver<Vec<Response>>, basic_request::NoSuchOutput>
|
||||
{
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
@ -359,6 +340,18 @@ impl OnDemand {
|
||||
Ok(receiver)
|
||||
}
|
||||
|
||||
/// Submit a strongly-typed batch of requests.
|
||||
///
|
||||
/// Fails if back-reference are not coherent.
|
||||
pub fn request<T>(&self, ctx: &BasicContext, requests: T) -> Result<OnResponses<T>, basic_request::NoSuchOutput>
|
||||
where T: request::RequestAdapter
|
||||
{
|
||||
self.request_raw(ctx, requests.make_requests()).map(|recv| OnResponses {
|
||||
receiver: recv,
|
||||
_marker: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
// dispatch pending requests, and discard those for which the corresponding
|
||||
// receiver has been dropped.
|
||||
fn dispatch_pending(&self, ctx: &BasicContext) {
|
||||
|
@ -34,6 +34,8 @@ use util::memorydb::MemoryDB;
|
||||
use util::sha3::Hashable;
|
||||
use util::trie::{Trie, TrieDB, TrieError};
|
||||
|
||||
const SUPPLIED_MATCHES: &'static str = "supplied responses always match produced requests; qed";
|
||||
|
||||
/// Core unit of the API: submit batches of these to be answered with `Response`s.
|
||||
#[derive(Clone)]
|
||||
pub enum Request {
|
||||
@ -53,6 +55,124 @@ pub enum Request {
|
||||
Execution(TransactionProof),
|
||||
}
|
||||
|
||||
/// A request argument.
|
||||
pub trait RequestArg {
|
||||
/// the response type.
|
||||
type Out;
|
||||
|
||||
/// Create the request type.
|
||||
/// `extract` must not fail when presented with the corresponding
|
||||
/// `Response`.
|
||||
fn make(self) -> Request;
|
||||
|
||||
/// May not panic if the response corresponds with the request
|
||||
/// from `make`.
|
||||
/// Is free to panic otherwise.
|
||||
fn extract(r: Response) -> Self::Out;
|
||||
}
|
||||
|
||||
/// An adapter can be thought of as a grouping of request argument types.
|
||||
/// This is implemented for various tuples and convenient types.
|
||||
pub trait RequestAdapter {
|
||||
/// The output type.
|
||||
type Out;
|
||||
|
||||
/// Infallibly produce requests. When `extract_from` is presented
|
||||
/// with the corresponding response vector, it may not fail.
|
||||
fn make_requests(self) -> Vec<Request>;
|
||||
|
||||
/// Extract the output type from the given responses.
|
||||
/// If they are the corresponding responses to the requests
|
||||
/// made by `make_requests`, do not panic.
|
||||
fn extract_from(Vec<Response>) -> Self::Out;
|
||||
}
|
||||
|
||||
// helper to implement `RequestArg` and `From` for a single request kind.
|
||||
macro_rules! impl_single {
|
||||
($variant: ident, $me: ty, $out: ty) => {
|
||||
impl RequestArg for $me {
|
||||
type Out = $out;
|
||||
|
||||
fn make(self) -> Request {
|
||||
Request::$variant(self)
|
||||
}
|
||||
|
||||
fn extract(r: Response) -> $out {
|
||||
match r {
|
||||
Response::$variant(x) => x,
|
||||
_ => panic!(SUPPLIED_MATCHES),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$me> for Request {
|
||||
fn from(me: $me) -> Request {
|
||||
Request::$variant(me)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// implement traits for each kind of request.
|
||||
impl_single!(HeaderProof, HeaderProof, (H256, U256));
|
||||
impl_single!(HeaderByHash, HeaderByHash, encoded::Header);
|
||||
impl_single!(Receipts, BlockReceipts, Vec<Receipt>);
|
||||
impl_single!(Body, Body, encoded::Block);
|
||||
impl_single!(Account, Account, Option<BasicAccount>);
|
||||
impl_single!(Code, Code, Bytes);
|
||||
impl_single!(Execution, TransactionProof, super::ExecutionResult);
|
||||
|
||||
macro_rules! impl_args {
|
||||
() => {
|
||||
impl<T: RequestArg> RequestAdapter for T {
|
||||
type Out = T::Out;
|
||||
|
||||
fn make_requests(self) -> Vec<Request> {
|
||||
vec![self.make()]
|
||||
}
|
||||
|
||||
fn extract_from(mut responses: Vec<Response>) -> Self::Out {
|
||||
T::extract(responses.pop().expect(SUPPLIED_MATCHES))
|
||||
}
|
||||
}
|
||||
};
|
||||
($first: ident, $($next: ident,)*) => {
|
||||
impl<
|
||||
$first: RequestArg,
|
||||
$($next: RequestArg,)*
|
||||
>
|
||||
RequestAdapter for ($first, $($next,)*) {
|
||||
type Out = ($first::Out, $($next::Out,)*);
|
||||
|
||||
fn make_requests(self) -> Vec<Request> {
|
||||
let ($first, $($next,)*) = self;
|
||||
|
||||
vec![
|
||||
$first.make(),
|
||||
$($next.make(),)*
|
||||
]
|
||||
}
|
||||
|
||||
fn extract_from(responses: Vec<Response>) -> Self::Out {
|
||||
let mut iter = responses.into_iter();
|
||||
(
|
||||
$first::extract(iter.next().expect(SUPPLIED_MATCHES)),
|
||||
$($next::extract(iter.next().expect(SUPPLIED_MATCHES)),)*
|
||||
)
|
||||
}
|
||||
}
|
||||
impl_args!($($next,)*);
|
||||
}
|
||||
}
|
||||
|
||||
mod impls {
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use super::{RequestAdapter, RequestArg, Request, Response, SUPPLIED_MATCHES};
|
||||
|
||||
impl_args!(A, B, C, D, E, F, G, H, I, J, K, L,);
|
||||
}
|
||||
|
||||
/// Requests coupled with their required data for verification.
|
||||
/// This is used internally but not part of the public API.
|
||||
#[derive(Clone)]
|
||||
@ -237,7 +357,7 @@ impl net_request::CheckedRequest for CheckedRequest {
|
||||
// check response against contained prover.
|
||||
match (self, response) {
|
||||
(&CheckedRequest::HeaderProof(ref prover, _), &NetResponse::HeaderProof(ref res)) =>
|
||||
prover.check_response(cache, &res.proof).map(|(h, s)| Response::HeaderProof(h, s)),
|
||||
prover.check_response(cache, &res.proof).map(Response::HeaderProof),
|
||||
(&CheckedRequest::HeaderByHash(ref prover, _), &NetResponse::Headers(ref res)) =>
|
||||
prover.check_response(cache, &res.headers).map(Response::HeaderByHash),
|
||||
(&CheckedRequest::Receipts(ref prover, _), &NetResponse::Receipts(ref res)) =>
|
||||
@ -260,7 +380,7 @@ impl net_request::CheckedRequest for CheckedRequest {
|
||||
pub enum Response {
|
||||
/// Response to a header proof request.
|
||||
/// Returns the hash and chain score.
|
||||
HeaderProof(H256, U256),
|
||||
HeaderProof((H256, U256)),
|
||||
/// Response to a header-by-hash request.
|
||||
HeaderByHash(encoded::Header),
|
||||
/// Response to a receipts request.
|
||||
|
@ -115,7 +115,6 @@ impl EthClient {
|
||||
on_demand: self.on_demand.clone(),
|
||||
sync: self.sync.clone(),
|
||||
cache: self.cache.clone(),
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user