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::error::Error;
use request::Requests;
use request::NetworkRequests as Requests;
/// An I/O context which allows sending and receiving packets as well as
/// 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 provider::Provider;
use request::{Request, Requests, Response};
use request::{Request, NetworkRequests as Requests, Response};
use self::request_credits::{Credits, FlowParams};
use self::context::{Ctx, TickCtx};

View File

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

View File

@ -39,14 +39,14 @@ use std::sync::Arc;
// helper for encoding a single request into a packet.
// panics on bad backreference.
fn encode_single(request: Request) -> Requests {
fn encode_single(request: Request) -> NetworkRequests {
let mut builder = RequestBuilder::default();
builder.push(request).unwrap();
builder.build()
}
// 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);
stream.append(&req_id).append_list(&requests.requests());
stream.out()

View File

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

View File

@ -20,22 +20,30 @@
use std::collections::HashMap;
use request::{
IncompleteRequest, CompleteRequest, Request,
OutputKind, Output, NoSuchOutput, Response, ResponseError,
IncompleteRequest, OutputKind, Output, NoSuchOutput, ResponseError, ResponseLike,
};
/// Build chained requests. Push them onto the series with `push`,
/// and produce a `Requests` object with `build`. Outputs are checked for consistency.
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct RequestBuilder {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RequestBuilder<T: IncompleteRequest> {
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
/// 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| {
match self.output_kinds.get(&(req, idx)) {
Some(k) if k == &kind => Ok(()),
@ -54,7 +62,7 @@ impl RequestBuilder {
}
/// Convert this into a "requests" object.
pub fn build(self) -> Requests {
pub fn build(self) -> Requests<T> {
Requests {
outputs: HashMap::new(),
requests: self.requests,
@ -65,18 +73,18 @@ impl RequestBuilder {
/// Requests pending responses.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Requests {
pub struct Requests<T: IncompleteRequest> {
outputs: HashMap<(usize, usize), Output>,
requests: Vec<Request>,
requests: Vec<T>,
answered: usize,
}
impl Requests {
/// For each request, produce responses for each.
impl<T: IncompleteRequest + Clone> Requests<T> {
/// For each request, produce a response.
/// 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.
pub fn respond_to_all<F>(mut self, responder: F) -> Vec<Response>
where F: Fn(CompleteRequest) -> Option<Response>
pub fn respond_to_all<F>(mut self, responder: F) -> Vec<T::Response>
where F: Fn(T::Complete) -> Option<T::Response>
{
let mut responses = Vec::new();
@ -95,13 +103,13 @@ impl Requests {
/// Get access to the underlying slice of requests.
// 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.
pub fn num_answered(&self) -> usize { self.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() {
None
} else {
@ -113,12 +121,12 @@ impl Requests {
/// Supply a response for the next request.
/// 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;
// check validity.
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;
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.
/// They may be incomplete, with back-references to outputs
/// of prior requests.
@ -296,6 +299,7 @@ impl Encodable for Request {
impl IncompleteRequest for Request {
type Complete = CompleteRequest;
type Response = Response;
fn check_outputs<F>(&self, f: F) -> 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> {
match self {
Request::Headers(req) => req.complete().map(CompleteRequest::Headers),
@ -421,9 +429,9 @@ pub enum Response {
Execution(ExecutionResponse),
}
impl Response {
impl ResponseLike for Response {
/// 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 {
Response::Headers(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),
}
}
}
impl Response {
/// Inspect the kind of this response.
pub fn kind(&self) -> Kind {
match *self {
@ -490,6 +500,8 @@ impl Encodable for Response {
pub trait IncompleteRequest: Sized {
/// The complete variant of this request.
type Complete;
/// The response to this request.
type Response: ResponseLike;
/// 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.
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.
/// Will succeed if all fields have been filled, will fail otherwise.
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.
pub mod header {
use super::{Field, HashOrNumber, NoSuchOutput, OutputKind, Output};
@ -555,6 +578,7 @@ pub mod header {
impl super::IncompleteRequest for Incomplete {
type Complete = Complete;
type Response = Response;
fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
@ -608,9 +632,9 @@ pub mod header {
pub headers: Vec<encoded::Header>,
}
impl Response {
impl super::ResponseLike for Response {
/// 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 {
@ -671,6 +695,7 @@ pub mod header_proof {
impl super::IncompleteRequest for Incomplete {
type Complete = Complete;
type Response = Response;
fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
@ -719,9 +744,9 @@ pub mod header_proof {
pub td: U256,
}
impl Response {
impl super::ResponseLike for Response {
/// 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));
}
}
@ -776,6 +801,7 @@ pub mod block_receipts {
impl super::IncompleteRequest for Incomplete {
type Complete = Complete;
type Response = Response;
fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
@ -818,9 +844,9 @@ pub mod block_receipts {
pub receipts: Vec<Receipt>
}
impl Response {
impl super::ResponseLike for Response {
/// 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 {
@ -868,6 +894,7 @@ pub mod block_body {
impl super::IncompleteRequest for Incomplete {
type Complete = Complete;
type Response = Response;
fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
@ -910,9 +937,9 @@ pub mod block_body {
pub body: encoded::Body,
}
impl Response {
impl super::ResponseLike for Response {
/// 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 {
@ -971,6 +998,7 @@ pub mod account {
impl super::IncompleteRequest for Incomplete {
type Complete = Complete;
type Response = Response;
fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
@ -1039,9 +1067,9 @@ pub mod account {
pub storage_root: H256,
}
impl Response {
impl super::ResponseLike for Response {
/// 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(1, Output::Hash(self.storage_root));
}
@ -1109,6 +1137,7 @@ pub mod storage {
impl super::IncompleteRequest for Incomplete {
type Complete = Complete;
type Response = Response;
fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
@ -1184,9 +1213,9 @@ pub mod storage {
pub value: H256,
}
impl Response {
impl super::ResponseLike for Response {
/// 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));
}
}
@ -1243,6 +1272,7 @@ pub mod contract_code {
impl super::IncompleteRequest for Incomplete {
type Complete = Complete;
type Response = Response;
fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
@ -1299,9 +1329,9 @@ pub mod contract_code {
pub code: Bytes,
}
impl Response {
impl super::ResponseLike for Response {
/// 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 {
@ -1380,6 +1410,7 @@ pub mod execution {
impl super::IncompleteRequest for Incomplete {
type Complete = Complete;
type Response = Response;
fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
@ -1440,9 +1471,9 @@ pub mod execution {
pub items: Vec<DBValue>,
}
impl Response {
impl super::ResponseLike for Response {
/// 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 {