diff --git a/ethcore/light/src/net/context.rs b/ethcore/light/src/net/context.rs index 64ddd19a3..33009d7f6 100644 --- a/ethcore/light/src/net/context.rs +++ b/ethcore/light/src/net/context.rs @@ -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 diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index e32e92145..7d006662c 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -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}; diff --git a/ethcore/light/src/net/request_set.rs b/ethcore/light/src/net/request_set.rs index 094fa1894..c5608050f 100644 --- a/ethcore/light/src/net/request_set.rs +++ b/ethcore/light/src/net/request_set.rs @@ -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; diff --git a/ethcore/light/src/net/tests/mod.rs b/ethcore/light/src/net/tests/mod.rs index 6dc5fbe7e..94788a727 100644 --- a/ethcore/light/src/net/tests/mod.rs +++ b/ethcore/light/src/net/tests/mod.rs @@ -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 { +fn make_packet(req_id: usize, requests: &NetworkRequests) -> Vec { let mut stream = RlpStream::new_list(2); stream.append(&req_id).append_list(&requests.requests()); stream.out() diff --git a/ethcore/light/src/on_demand/mod.rs b/ethcore/light/src/on_demand/mod.rs index c756844c9..e3ea28887 100644 --- a/ethcore/light/src/on_demand/mod.rs +++ b/ethcore/light/src/on_demand/mod.rs @@ -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 { None } - fn request_from(&self, _: PeerId, _: Requests) -> Result { + fn request_from(&self, _: PeerId, _: NetworkRequests) -> Result { unimplemented!() } fn make_announcement(&self, _: Announcement) { } diff --git a/ethcore/light/src/types/request/builder.rs b/ethcore/light/src/types/request/builder.rs index 77f1389c2..015edf4c4 100644 --- a/ethcore/light/src/types/request/builder.rs +++ b/ethcore/light/src/types/request/builder.rs @@ -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 { output_kinds: HashMap<(usize, usize), OutputKind>, - requests: Vec, + requests: Vec, } -impl RequestBuilder { +impl Default for RequestBuilder { + fn default() -> Self { + RequestBuilder { + output_kinds: HashMap::new(), + requests: Vec::new(), + } + } +} + +impl RequestBuilder { /// 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 { 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 { outputs: HashMap<(usize, usize), Output>, - requests: Vec, + requests: Vec, answered: usize, } -impl Requests { - /// For each request, produce responses for each. +impl Requests { + /// 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(mut self, responder: F) -> Vec - where F: Fn(CompleteRequest) -> Option + pub fn respond_to_all(mut self, responder: F) -> Vec + where F: Fn(T::Complete) -> Option { let mut responses = Vec::new(); @@ -95,13 +103,13 @@ impl Requests { /// Get access to the underlying slice of requests. // TODO: unimplemented -> Vec, // 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 { + pub fn next_complete(&self) -> Option { 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| { diff --git a/ethcore/light/src/types/request/mod.rs b/ethcore/light/src/types/request/mod.rs index 3099f8fed..4d0049696 100644 --- a/ethcore/light/src/types/request/mod.rs +++ b/ethcore/light/src/types/request/mod.rs @@ -197,6 +197,9 @@ impl Encodable for HashOrNumber { } } +/// Type alias for "network requests". +pub type NetworkRequests = Requests; + /// 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(&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 { 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(&self, f: F) where F: FnMut(usize, Output) { + fn fill_outputs(&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(&mut self, oracle: F) where F: Fn(usize, usize) -> Result; + /// 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; } +/// A response-like object. +/// +/// These contain re-usable outputs. +pub trait ResponseLike { + /// Write all re-usable outputs into the provided function. + fn fill_outputs(&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(&self, mut f: F) -> Result<(), NoSuchOutput> where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> @@ -608,9 +632,9 @@ pub mod header { pub headers: Vec, } - impl Response { + impl super::ResponseLike for Response { /// Fill reusable outputs by writing them into the function. - pub fn fill_outputs(&self, _: F) where F: FnMut(usize, Output) { } + fn fill_outputs(&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(&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(&self, mut f: F) where F: FnMut(usize, Output) { + fn fill_outputs(&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(&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 } - impl Response { + impl super::ResponseLike for Response { /// Fill reusable outputs by providing them to the function. - pub fn fill_outputs(&self, _: F) where F: FnMut(usize, Output) {} + fn fill_outputs(&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(&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(&self, _: F) where F: FnMut(usize, Output) {} + fn fill_outputs(&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(&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(&self, mut f: F) where F: FnMut(usize, Output) { + fn fill_outputs(&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(&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(&self, mut f: F) where F: FnMut(usize, Output) { + fn fill_outputs(&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(&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(&self, _: F) where F: FnMut(usize, Output) {} + fn fill_outputs(&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(&self, mut f: F) -> Result<(), NoSuchOutput> where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> @@ -1440,9 +1471,9 @@ pub mod execution { pub items: Vec, } - impl Response { + impl super::ResponseLike for Response { /// Fill reusable outputs by providing them to the function. - pub fn fill_outputs(&self, _: F) where F: FnMut(usize, Output) {} + fn fill_outputs(&self, _: F) where F: FnMut(usize, Output) {} } impl Decodable for Response {