generalize RequestBuilder

This commit is contained in:
Robert Habermeier 2017-04-05 15:02:44 +02:00
parent 60ce0aee1a
commit 35740456a4
7 changed files with 82 additions and 43 deletions

View File

@ -20,7 +20,7 @@ use network::{NetworkContext, PeerId, NodeId};
use super::{Announcement, LightProtocol, ReqId}; use super::{Announcement, LightProtocol, ReqId};
use super::error::Error; use super::error::Error;
use request::Requests; use request::NetworkRequests as Requests;
/// An I/O context which allows sending and receiving packets as well as /// An I/O context which allows sending and receiving packets as well as
/// disconnecting peers. This is used as a generalization of the portions /// disconnecting peers. This is used as a generalization of the portions

View File

@ -33,7 +33,7 @@ use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use provider::Provider; use provider::Provider;
use request::{Request, Requests, Response}; use request::{Request, NetworkRequests as Requests, Response};
use self::request_credits::{Credits, FlowParams}; use self::request_credits::{Credits, FlowParams};
use self::context::{Ctx, TickCtx}; use self::context::{Ctx, TickCtx};

View File

@ -25,7 +25,7 @@ use std::collections::{BTreeMap, HashMap};
use std::iter::FromIterator; use std::iter::FromIterator;
use request::Request; use request::Request;
use request::Requests; use request::NetworkRequests as Requests;
use net::{timeout, ReqId}; use net::{timeout, ReqId};
use util::U256; use util::U256;

View File

@ -39,14 +39,14 @@ use std::sync::Arc;
// helper for encoding a single request into a packet. // helper for encoding a single request into a packet.
// panics on bad backreference. // panics on bad backreference.
fn encode_single(request: Request) -> Requests { fn encode_single(request: Request) -> NetworkRequests {
let mut builder = RequestBuilder::default(); let mut builder = RequestBuilder::default();
builder.push(request).unwrap(); builder.push(request).unwrap();
builder.build() builder.build()
} }
// helper for making a packet out of `Requests`. // helper for making a packet out of `Requests`.
fn make_packet(req_id: usize, requests: &Requests) -> Vec<u8> { fn make_packet(req_id: usize, requests: &NetworkRequests) -> Vec<u8> {
let mut stream = RlpStream::new_list(2); let mut stream = RlpStream::new_list(2);
stream.append(&req_id).append_list(&requests.requests()); stream.append(&req_id).append_list(&requests.requests());
stream.out() stream.out()

View File

@ -562,7 +562,7 @@ mod tests {
use cache::Cache; use cache::Cache;
use net::{Announcement, BasicContext, ReqId, Error as LesError}; use net::{Announcement, BasicContext, ReqId, Error as LesError};
use request::Requests; use request::NetworkRequests;
use network::{PeerId, NodeId}; use network::{PeerId, NodeId};
use time::Duration; use time::Duration;
@ -572,7 +572,7 @@ mod tests {
impl BasicContext for FakeContext { impl BasicContext for FakeContext {
fn persistent_peer_id(&self, _: PeerId) -> Option<NodeId> { None } fn persistent_peer_id(&self, _: PeerId) -> Option<NodeId> { None }
fn request_from(&self, _: PeerId, _: Requests) -> Result<ReqId, LesError> { fn request_from(&self, _: PeerId, _: NetworkRequests) -> Result<ReqId, LesError> {
unimplemented!() unimplemented!()
} }
fn make_announcement(&self, _: Announcement) { } fn make_announcement(&self, _: Announcement) { }

View File

@ -20,22 +20,30 @@
use std::collections::HashMap; use std::collections::HashMap;
use request::{ use request::{
IncompleteRequest, CompleteRequest, Request, IncompleteRequest, OutputKind, Output, NoSuchOutput, ResponseError, ResponseLike,
OutputKind, Output, NoSuchOutput, Response, ResponseError,
}; };
/// Build chained requests. Push them onto the series with `push`, /// Build chained requests. Push them onto the series with `push`,
/// and produce a `Requests` object with `build`. Outputs are checked for consistency. /// and produce a `Requests` object with `build`. Outputs are checked for consistency.
#[derive(Debug, Default, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct RequestBuilder { pub struct RequestBuilder<T: IncompleteRequest> {
output_kinds: HashMap<(usize, usize), OutputKind>, output_kinds: HashMap<(usize, usize), OutputKind>,
requests: Vec<Request>, requests: Vec<T>,
} }
impl RequestBuilder { impl<T: IncompleteRequest> Default for RequestBuilder<T> {
fn default() -> Self {
RequestBuilder {
output_kinds: HashMap::new(),
requests: Vec::new(),
}
}
}
impl<T: IncompleteRequest> RequestBuilder<T> {
/// Attempt to push a request onto the request chain. Fails if the request /// Attempt to push a request onto the request chain. Fails if the request
/// references a non-existent output of a prior request. /// references a non-existent output of a prior request.
pub fn push(&mut self, request: Request) -> Result<(), NoSuchOutput> { pub fn push(&mut self, request: T) -> Result<(), NoSuchOutput> {
request.check_outputs(|req, idx, kind| { request.check_outputs(|req, idx, kind| {
match self.output_kinds.get(&(req, idx)) { match self.output_kinds.get(&(req, idx)) {
Some(k) if k == &kind => Ok(()), Some(k) if k == &kind => Ok(()),
@ -54,7 +62,7 @@ impl RequestBuilder {
} }
/// Convert this into a "requests" object. /// Convert this into a "requests" object.
pub fn build(self) -> Requests { pub fn build(self) -> Requests<T> {
Requests { Requests {
outputs: HashMap::new(), outputs: HashMap::new(),
requests: self.requests, requests: self.requests,
@ -65,18 +73,18 @@ impl RequestBuilder {
/// Requests pending responses. /// Requests pending responses.
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct Requests { pub struct Requests<T: IncompleteRequest> {
outputs: HashMap<(usize, usize), Output>, outputs: HashMap<(usize, usize), Output>,
requests: Vec<Request>, requests: Vec<T>,
answered: usize, answered: usize,
} }
impl Requests { impl<T: IncompleteRequest + Clone> Requests<T> {
/// For each request, produce responses for each. /// For each request, produce a response.
/// The responses vector produced goes up to the point where the responder /// The responses vector produced goes up to the point where the responder
/// first returns `None`, an invalid response, or until all requests have been responded to. /// first returns `None`, an invalid response, or until all requests have been responded to.
pub fn respond_to_all<F>(mut self, responder: F) -> Vec<Response> pub fn respond_to_all<F>(mut self, responder: F) -> Vec<T::Response>
where F: Fn(CompleteRequest) -> Option<Response> where F: Fn(T::Complete) -> Option<T::Response>
{ {
let mut responses = Vec::new(); let mut responses = Vec::new();
@ -95,13 +103,13 @@ impl Requests {
/// Get access to the underlying slice of requests. /// Get access to the underlying slice of requests.
// TODO: unimplemented -> Vec<Request>, // do we _have to_ allocate? // TODO: unimplemented -> Vec<Request>, // do we _have to_ allocate?
pub fn requests(&self) -> &[Request] { &self.requests } pub fn requests(&self) -> &[T] { &self.requests }
/// Get the number of answered requests. /// Get the number of answered requests.
pub fn num_answered(&self) -> usize { self.answered } pub fn num_answered(&self) -> usize { self.answered }
/// Get the next request as a filled request. Returns `None` when all requests answered. /// Get the next request as a filled request. Returns `None` when all requests answered.
pub fn next_complete(&self) -> Option<CompleteRequest> { pub fn next_complete(&self) -> Option<T::Complete> {
if self.answered == self.requests.len() { if self.answered == self.requests.len() {
None None
} else { } else {
@ -113,12 +121,12 @@ impl Requests {
/// Supply a response for the next request. /// Supply a response for the next request.
/// Fails on: wrong request kind, all requests answered already. /// Fails on: wrong request kind, all requests answered already.
pub fn supply_response(&mut self, response: &Response) -> Result<(), ResponseError> { pub fn supply_response(&mut self, response: &T::Response) -> Result<(), ResponseError> {
let idx = self.answered; let idx = self.answered;
// check validity. // check validity.
if idx == self.requests.len() { return Err(ResponseError::Unexpected) } if idx == self.requests.len() { return Err(ResponseError::Unexpected) }
if self.requests[idx].kind() != response.kind() { return Err(ResponseError::WrongKind) } if !self.requests[idx].check_response(&response) { return Err(ResponseError::WrongKind) }
let outputs = &mut self.outputs; let outputs = &mut self.outputs;
response.fill_outputs(|out_idx, output| { response.fill_outputs(|out_idx, output| {

View File

@ -197,6 +197,9 @@ impl Encodable for HashOrNumber {
} }
} }
/// Type alias for "network requests".
pub type NetworkRequests = Requests<Request>;
/// All request types, as they're sent over the network. /// All request types, as they're sent over the network.
/// They may be incomplete, with back-references to outputs /// They may be incomplete, with back-references to outputs
/// of prior requests. /// of prior requests.
@ -296,6 +299,7 @@ impl Encodable for Request {
impl IncompleteRequest for Request { impl IncompleteRequest for Request {
type Complete = CompleteRequest; type Complete = CompleteRequest;
type Response = Response;
fn check_outputs<F>(&self, f: F) -> Result<(), NoSuchOutput> fn check_outputs<F>(&self, f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
@ -338,6 +342,10 @@ impl IncompleteRequest for Request {
} }
} }
fn check_response(&self, response: &Response) -> bool {
self.kind() == response.kind()
}
fn complete(self) -> Result<Self::Complete, NoSuchOutput> { fn complete(self) -> Result<Self::Complete, NoSuchOutput> {
match self { match self {
Request::Headers(req) => req.complete().map(CompleteRequest::Headers), Request::Headers(req) => req.complete().map(CompleteRequest::Headers),
@ -421,9 +429,9 @@ pub enum Response {
Execution(ExecutionResponse), Execution(ExecutionResponse),
} }
impl Response { impl ResponseLike for Response {
/// Fill reusable outputs by writing them into the function. /// Fill reusable outputs by writing them into the function.
pub fn fill_outputs<F>(&self, f: F) where F: FnMut(usize, Output) { fn fill_outputs<F>(&self, f: F) where F: FnMut(usize, Output) {
match *self { match *self {
Response::Headers(ref res) => res.fill_outputs(f), Response::Headers(ref res) => res.fill_outputs(f),
Response::HeaderProof(ref res) => res.fill_outputs(f), Response::HeaderProof(ref res) => res.fill_outputs(f),
@ -435,7 +443,9 @@ impl Response {
Response::Execution(ref res) => res.fill_outputs(f), Response::Execution(ref res) => res.fill_outputs(f),
} }
} }
}
impl Response {
/// Inspect the kind of this response. /// Inspect the kind of this response.
pub fn kind(&self) -> Kind { pub fn kind(&self) -> Kind {
match *self { match *self {
@ -490,6 +500,8 @@ impl Encodable for Response {
pub trait IncompleteRequest: Sized { pub trait IncompleteRequest: Sized {
/// The complete variant of this request. /// The complete variant of this request.
type Complete; type Complete;
/// The response to this request.
type Response: ResponseLike;
/// Check prior outputs against the needed inputs. /// Check prior outputs against the needed inputs.
/// ///
@ -508,11 +520,22 @@ pub trait IncompleteRequest: Sized {
/// Only outputs previously checked with `check_outputs` may be available. /// Only outputs previously checked with `check_outputs` may be available.
fn fill<F>(&mut self, oracle: F) where F: Fn(usize, usize) -> Result<Output, NoSuchOutput>; fn fill<F>(&mut self, oracle: F) where F: Fn(usize, usize) -> Result<Output, NoSuchOutput>;
/// Check whether the response matches (beyond the type).
fn check_response(&self, _response: &Self::Response) -> bool { true }
/// Attempt to convert this request into its complete variant. /// Attempt to convert this request into its complete variant.
/// Will succeed if all fields have been filled, will fail otherwise. /// Will succeed if all fields have been filled, will fail otherwise.
fn complete(self) -> Result<Self::Complete, NoSuchOutput>; fn complete(self) -> Result<Self::Complete, NoSuchOutput>;
} }
/// A response-like object.
///
/// These contain re-usable outputs.
pub trait ResponseLike {
/// Write all re-usable outputs into the provided function.
fn fill_outputs<F>(&self, output_store: F) where F: FnMut(usize, Output);
}
/// Header request. /// Header request.
pub mod header { pub mod header {
use super::{Field, HashOrNumber, NoSuchOutput, OutputKind, Output}; use super::{Field, HashOrNumber, NoSuchOutput, OutputKind, Output};
@ -555,6 +578,7 @@ pub mod header {
impl super::IncompleteRequest for Incomplete { impl super::IncompleteRequest for Incomplete {
type Complete = Complete; type Complete = Complete;
type Response = Response;
fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput> fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
@ -608,9 +632,9 @@ pub mod header {
pub headers: Vec<encoded::Header>, pub headers: Vec<encoded::Header>,
} }
impl Response { impl super::ResponseLike for Response {
/// Fill reusable outputs by writing them into the function. /// Fill reusable outputs by writing them into the function.
pub fn fill_outputs<F>(&self, _: F) where F: FnMut(usize, Output) { } fn fill_outputs<F>(&self, _: F) where F: FnMut(usize, Output) { }
} }
impl Decodable for Response { impl Decodable for Response {
@ -671,6 +695,7 @@ pub mod header_proof {
impl super::IncompleteRequest for Incomplete { impl super::IncompleteRequest for Incomplete {
type Complete = Complete; type Complete = Complete;
type Response = Response;
fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput> fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
@ -719,9 +744,9 @@ pub mod header_proof {
pub td: U256, pub td: U256,
} }
impl Response { impl super::ResponseLike for Response {
/// Fill reusable outputs by providing them to the function. /// Fill reusable outputs by providing them to the function.
pub fn fill_outputs<F>(&self, mut f: F) where F: FnMut(usize, Output) { fn fill_outputs<F>(&self, mut f: F) where F: FnMut(usize, Output) {
f(0, Output::Hash(self.hash)); f(0, Output::Hash(self.hash));
} }
} }
@ -776,6 +801,7 @@ pub mod block_receipts {
impl super::IncompleteRequest for Incomplete { impl super::IncompleteRequest for Incomplete {
type Complete = Complete; type Complete = Complete;
type Response = Response;
fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput> fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
@ -818,9 +844,9 @@ pub mod block_receipts {
pub receipts: Vec<Receipt> pub receipts: Vec<Receipt>
} }
impl Response { impl super::ResponseLike for Response {
/// Fill reusable outputs by providing them to the function. /// Fill reusable outputs by providing them to the function.
pub fn fill_outputs<F>(&self, _: F) where F: FnMut(usize, Output) {} fn fill_outputs<F>(&self, _: F) where F: FnMut(usize, Output) {}
} }
impl Decodable for Response { impl Decodable for Response {
@ -868,6 +894,7 @@ pub mod block_body {
impl super::IncompleteRequest for Incomplete { impl super::IncompleteRequest for Incomplete {
type Complete = Complete; type Complete = Complete;
type Response = Response;
fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput> fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
@ -910,9 +937,9 @@ pub mod block_body {
pub body: encoded::Body, pub body: encoded::Body,
} }
impl Response { impl super::ResponseLike for Response {
/// Fill reusable outputs by providing them to the function. /// Fill reusable outputs by providing them to the function.
pub fn fill_outputs<F>(&self, _: F) where F: FnMut(usize, Output) {} fn fill_outputs<F>(&self, _: F) where F: FnMut(usize, Output) {}
} }
impl Decodable for Response { impl Decodable for Response {
@ -971,6 +998,7 @@ pub mod account {
impl super::IncompleteRequest for Incomplete { impl super::IncompleteRequest for Incomplete {
type Complete = Complete; type Complete = Complete;
type Response = Response;
fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput> fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
@ -1039,9 +1067,9 @@ pub mod account {
pub storage_root: H256, pub storage_root: H256,
} }
impl Response { impl super::ResponseLike for Response {
/// Fill reusable outputs by providing them to the function. /// Fill reusable outputs by providing them to the function.
pub fn fill_outputs<F>(&self, mut f: F) where F: FnMut(usize, Output) { fn fill_outputs<F>(&self, mut f: F) where F: FnMut(usize, Output) {
f(0, Output::Hash(self.code_hash)); f(0, Output::Hash(self.code_hash));
f(1, Output::Hash(self.storage_root)); f(1, Output::Hash(self.storage_root));
} }
@ -1109,6 +1137,7 @@ pub mod storage {
impl super::IncompleteRequest for Incomplete { impl super::IncompleteRequest for Incomplete {
type Complete = Complete; type Complete = Complete;
type Response = Response;
fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput> fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
@ -1184,9 +1213,9 @@ pub mod storage {
pub value: H256, pub value: H256,
} }
impl Response { impl super::ResponseLike for Response {
/// Fill reusable outputs by providing them to the function. /// Fill reusable outputs by providing them to the function.
pub fn fill_outputs<F>(&self, mut f: F) where F: FnMut(usize, Output) { fn fill_outputs<F>(&self, mut f: F) where F: FnMut(usize, Output) {
f(0, Output::Hash(self.value)); f(0, Output::Hash(self.value));
} }
} }
@ -1243,6 +1272,7 @@ pub mod contract_code {
impl super::IncompleteRequest for Incomplete { impl super::IncompleteRequest for Incomplete {
type Complete = Complete; type Complete = Complete;
type Response = Response;
fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput> fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
@ -1299,9 +1329,9 @@ pub mod contract_code {
pub code: Bytes, pub code: Bytes,
} }
impl Response { impl super::ResponseLike for Response {
/// Fill reusable outputs by providing them to the function. /// Fill reusable outputs by providing them to the function.
pub fn fill_outputs<F>(&self, _: F) where F: FnMut(usize, Output) {} fn fill_outputs<F>(&self, _: F) where F: FnMut(usize, Output) {}
} }
impl Decodable for Response { impl Decodable for Response {
@ -1380,6 +1410,7 @@ pub mod execution {
impl super::IncompleteRequest for Incomplete { impl super::IncompleteRequest for Incomplete {
type Complete = Complete; type Complete = Complete;
type Response = Response;
fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput> fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
@ -1440,9 +1471,9 @@ pub mod execution {
pub items: Vec<DBValue>, pub items: Vec<DBValue>,
} }
impl Response { impl super::ResponseLike for Response {
/// Fill reusable outputs by providing them to the function. /// Fill reusable outputs by providing them to the function.
pub fn fill_outputs<F>(&self, _: F) where F: FnMut(usize, Output) {} fn fill_outputs<F>(&self, _: F) where F: FnMut(usize, Output) {}
} }
impl Decodable for Response { impl Decodable for Response {