diff --git a/ethcore/light/src/on_demand/mod.rs b/ethcore/light/src/on_demand/mod.rs index 145e66703..f5a1fe02e 100644 --- a/ethcore/light/src/on_demand/mod.rs +++ b/ethcore/light/src/on_demand/mod.rs @@ -27,11 +27,10 @@ use std::sync::Arc; use ethcore::basic_account::BasicAccount; use ethcore::encoded; use ethcore::receipt::Receipt; -use ethcore::state::ProvedExecution; use ethcore::executed::{Executed, ExecutionError}; -use futures::{Async, Poll, Future}; -use futures::sync::oneshot::{self, Sender, Receiver}; +use futures::{future, Async, Poll, Future, BoxFuture}; +use futures::sync::oneshot::{self, Sender, Receiver, Canceled}; use network::PeerId; use rlp::RlpStream; use util::{Bytes, RwLock, Mutex, U256, H256}; @@ -39,11 +38,14 @@ use util::sha3::{SHA3_NULL_RLP, SHA3_EMPTY, SHA3_EMPTY_LIST_RLP}; use net::{self, Handler, Status, Capabilities, Announcement, EventContext, BasicContext, ReqId}; use cache::Cache; -use request::{self as basic_request, Request as NetworkRequest, Response as NetworkResponse}; +use request::{self as basic_request, Request as NetworkRequest}; pub mod request; -pub use self::request::{CheckedRequest ,Request, Response}; +pub use self::request::{CheckedRequest, Request, Response}; + +/// The result of execution +pub type ExecutionResult = Result; // relevant peer info. struct Peer { @@ -62,13 +64,6 @@ impl Peer { } } -// Which portions of a CHT proof should be sent. -enum ChtProofSender { - Both(Sender<(H256, U256)>), - Hash(Sender), - ChainScore(Sender), -} - // Attempted request info and sender to put received value. struct Pending { requests: basic_request::Requests, @@ -127,7 +122,7 @@ pub struct OnDemand { cache: Arc>, } -const RECEIVER_IN_SCOPE: &'static str = "Receiver is still in scope, so it's not dropped; qed"; +const RESPONSES_MATCH: &'static str = "N requests always leads to N responses; qed"; impl OnDemand { /// Create a new `OnDemand` service with the given cache. @@ -140,151 +135,191 @@ impl OnDemand { } } - // /// Request a header's hash by block number and CHT root hash. - // /// Returns the hash. - // pub fn hash_by_number(&self, ctx: &BasicContext, req: request::HeaderProof) -> Receiver { - // let (sender, receiver) = oneshot::channel(); - // let cached = { - // let mut cache = self.cache.lock(); - // cache.block_hash(&req.num()) - // }; + /// Request a header's hash by block number and CHT root hash. + /// Returns the hash. + pub fn hash_by_number(&self, ctx: &BasicContext, req: request::HeaderProof) -> BoxFuture { + let cached = { + let mut cache = self.cache.lock(); + cache.block_hash(&req.num()) + }; - // match cached { - // Some(hash) => sender.send(hash).expect(RECEIVER_IN_SCOPE), - // None => self.dispatch(ctx, Pending::HeaderProof(req, ChtProofSender::Hash(sender))), - // } - // receiver - // } + match cached { + Some(hash) => future::ok(hash).boxed(), + None => { + self.make_requests(ctx, vec![Request::HeaderProof(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") + }) + .boxed() + }, + } + } - // /// Request a canonical block's chain score. - // /// Returns the chain score. - // pub fn chain_score_by_number(&self, ctx: &BasicContext, req: request::HeaderProof) -> Receiver { - // let (sender, receiver) = oneshot::channel(); - // let cached = { - // let mut cache = self.cache.lock(); - // cache.block_hash(&req.num()).and_then(|hash| cache.chain_score(&hash)) - // }; + /// Request a canonical block's chain score. + /// Returns the chain score. + pub fn chain_score_by_number(&self, ctx: &BasicContext, req: request::HeaderProof) -> BoxFuture { + let cached = { + let mut cache = self.cache.lock(); + cache.block_hash(&req.num()).and_then(|hash| cache.chain_score(&hash)) + }; - // match cached { - // Some(score) => sender.send(score).expect(RECEIVER_IN_SCOPE), - // None => self.dispatch(ctx, Pending::HeaderProof(req, ChtProofSender::ChainScore(sender))), - // } + match cached { + Some(score) => future::ok(score).boxed(), + None => { + self.make_requests(ctx, vec![Request::HeaderProof(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") + }) + .boxed() + }, + } + } - // receiver - // } + /// Request a canonical block's hash and chain score by number. + /// Returns the hash and chain score. + pub fn hash_and_score_by_number(&self, ctx: &BasicContext, req: request::HeaderProof) -> BoxFuture<(H256, U256), Canceled> { + let cached = { + let mut cache = self.cache.lock(); + let hash = cache.block_hash(&req.num()); + ( + hash.clone(), + hash.and_then(|hash| cache.chain_score(&hash)), + ) + }; - // /// Request a canonical block's hash and chain score by number. - // /// Returns the hash and chain score. - // pub fn hash_and_score_by_number(&self, ctx: &BasicContext, req: request::HeaderProof) -> Receiver<(H256, U256)> { - // let (sender, receiver) = oneshot::channel(); - // let cached = { - // let mut cache = self.cache.lock(); - // let hash = cache.block_hash(&req.num()); - // ( - // hash.clone(), - // hash.and_then(|hash| cache.chain_score(&hash)), - // ) - // }; + match cached { + (Some(hash), Some(score)) => future::ok((hash, score)).boxed(), + _ => { + self.make_requests(ctx, vec![Request::HeaderProof(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() + }, + } + } - // match cached { - // (Some(hash), Some(score)) => sender.send((hash, score)).expect(RECEIVER_IN_SCOPE), - // _ => self.dispatch(ctx, Pending::HeaderProof(req, ChtProofSender::Both(sender))), - // } + /// Request a header by hash. This is less accurate than by-number because we don't know + /// where in the chain this header lies, and therefore can't find a peer who is supposed to have + /// it as easily. + pub fn header_by_hash(&self, ctx: &BasicContext, req: request::HeaderByHash) -> BoxFuture { + match { self.cache.lock().block_header(&req.0) } { + Some(hdr) => future::ok(hdr).boxed(), + None => { + self.make_requests(ctx, vec![Request::HeaderByHash(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() + }, + } + } - // receiver - // } + /// Request a block, given its header. Block bodies are requestable by hash only, + /// and the header is required anyway to verify and complete the block body + /// -- this just doesn't obscure the network query. + pub fn block(&self, ctx: &BasicContext, req: request::Body) -> BoxFuture { + // fast path for empty body. + if req.header.transactions_root() == SHA3_NULL_RLP && req.header.uncles_hash() == SHA3_EMPTY_LIST_RLP { + let mut stream = RlpStream::new_list(3); + stream.append_raw(&req.header.into_inner(), 1); + stream.begin_list(0); + stream.begin_list(0); - // /// Request a header by hash. This is less accurate than by-number because we don't know - // /// where in the chain this header lies, and therefore can't find a peer who is supposed to have - // /// it as easily. - // pub fn header_by_hash(&self, ctx: &BasicContext, req: request::HeaderByHash) -> Receiver { - // let (sender, receiver) = oneshot::channel(); - // match { self.cache.lock().block_header(&req.0) } { - // Some(hdr) => sender.send(hdr).expect(RECEIVER_IN_SCOPE), - // None => self.dispatch(ctx, Pending::HeaderByHash(req, sender)), - // } - // receiver - // } + future::ok(encoded::Block::new(stream.out())).boxed() + } else { + match { self.cache.lock().block_body(&req.hash) } { + Some(body) => { + let mut stream = RlpStream::new_list(3); + let body = body.rlp(); + stream.append_raw(&req.header.into_inner(), 1); + stream.append_raw(&body.at(0).as_raw(), 1); + stream.append_raw(&body.at(1).as_raw(), 1); - // /// Request a block, given its header. Block bodies are requestable by hash only, - // /// and the header is required anyway to verify and complete the block body - // /// -- this just doesn't obscure the network query. - // pub fn block(&self, ctx: &BasicContext, req: request::Body) -> Receiver { - // let (sender, receiver) = oneshot::channel(); + future::ok(encoded::Block::new(stream.out())).boxed() + } + None => { + self.make_requests(ctx, vec![Request::Body(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() + } + } + } + } - // // fast path for empty body. - // if req.header.transactions_root() == SHA3_NULL_RLP && req.header.uncles_hash() == SHA3_EMPTY_LIST_RLP { - // let mut stream = RlpStream::new_list(3); - // stream.append_raw(&req.header.into_inner(), 1); - // stream.begin_list(0); - // stream.begin_list(0); + /// Request the receipts for a block. The header serves two purposes: + /// provide the block hash to fetch receipts for, and for verification of the receipts root. + pub fn block_receipts(&self, ctx: &BasicContext, req: request::BlockReceipts) -> BoxFuture, Canceled> { + // fast path for empty receipts. + if req.0.receipts_root() == SHA3_NULL_RLP { + return future::ok(Vec::new()).boxed() + } - // sender.send(encoded::Block::new(stream.out())).expect(RECEIVER_IN_SCOPE); - // } else { - // match { self.cache.lock().block_body(&req.hash) } { - // Some(body) => { - // let mut stream = RlpStream::new_list(3); - // let body = body.rlp(); - // stream.append_raw(&req.header.into_inner(), 1); - // stream.append_raw(&body.at(0).as_raw(), 1); - // stream.append_raw(&body.at(1).as_raw(), 1); + match { self.cache.lock().block_receipts(&req.0.hash()) } { + Some(receipts) => future::ok(receipts).boxed(), + None => { + self.make_requests(ctx, vec![Request::Receipts(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() + }, + } + } - // sender.send(encoded::Block::new(stream.out())).expect(RECEIVER_IN_SCOPE); - // } - // None => self.dispatch(ctx, Pending::Block(req, sender)), - // } - // } - // receiver - // } + /// Request an account by address and block header -- which gives a hash to query and a state root + /// 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, Canceled> { + self.make_requests(ctx, vec![Request::Account(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() + } - // /// Request the receipts for a block. The header serves two purposes: - // /// provide the block hash to fetch receipts for, and for verification of the receipts root. - // pub fn block_receipts(&self, ctx: &BasicContext, req: request::BlockReceipts) -> Receiver> { - // let (sender, receiver) = oneshot::channel(); + /// Request code by address, known code hash, and block header. + pub fn code(&self, ctx: &BasicContext, req: request::Code) -> BoxFuture { + // fast path for no code. + if req.code_hash == SHA3_EMPTY { + future::ok(Vec::new()).boxed() + } else { + self.make_requests(ctx, vec![Request::Code(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() + } + } - // // fast path for empty receipts. - // if req.0.receipts_root() == SHA3_NULL_RLP { - // sender.send(Vec::new()).expect(RECEIVER_IN_SCOPE); - // } else { - // match { self.cache.lock().block_receipts(&req.0.hash()) } { - // Some(receipts) => sender.send(receipts).expect(RECEIVER_IN_SCOPE), - // None => self.dispatch(ctx, Pending::BlockReceipts(req, sender)), - // } - // } - - // receiver - // } - - // /// Request an account by address and block header -- which gives a hash to query and a state root - // /// to verify against. - // pub fn account(&self, ctx: &BasicContext, req: request::Account) -> Receiver { - // let (sender, receiver) = oneshot::channel(); - // self.dispatch(ctx, Pending::Account(req, sender)); - // receiver - // } - - // /// Request code by address, known code hash, and block header. - // pub fn code(&self, ctx: &BasicContext, req: request::Code) -> Receiver { - // let (sender, receiver) = oneshot::channel(); - - // // fast path for no code. - // if req.code_hash == SHA3_EMPTY { - // sender.send(Vec::new()).expect(RECEIVER_IN_SCOPE) - // } else { - // self.dispatch(ctx, Pending::Code(req, sender)); - // } - - // receiver - // } - - // /// Request proof-of-execution for a transaction. - // pub fn transaction_proof(&self, ctx: &BasicContext, req: request::TransactionProof) -> Receiver> { - // let (sender, receiver) = oneshot::channel(); - - // self.dispatch(ctx, Pending::TxProof(req, sender)); - - // receiver - // } + /// Request proof-of-execution for a transaction. + pub fn transaction_proof(&self, ctx: &BasicContext, req: request::TransactionProof) -> BoxFuture { + self.make_requests(ctx, vec![Request::Execution(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. /// @@ -295,6 +330,11 @@ impl OnDemand { { let (sender, receiver) = oneshot::channel(); + if requests.is_empty() { + assert!(sender.send(Vec::new()).is_ok(), "receiver still in scope; qed"); + return Ok(receiver); + } + let mut builder = basic_request::RequestBuilder::default(); let responses = Vec::with_capacity(requests.len()); @@ -314,6 +354,8 @@ impl OnDemand { sender: sender, }); + self.dispatch_pending(ctx); + Ok(receiver) } @@ -350,11 +392,13 @@ impl OnDemand { let peers = self.peers.read(); *pending = ::std::mem::replace(&mut *pending, Vec::new()).into_iter() .filter_map(|mut pending| match check_hangup(&mut pending.sender) { - true => Some(pending), - false => None, + false => Some(pending), + true => None, }) .filter_map(|pending| { for (peer_id, peer) in peers.iter() { // .shuffle? + // TODO: see which requests can be answered by the cache? + if !peer.can_fulfill(&pending.required_capabilities) { continue } @@ -412,19 +456,20 @@ impl Handler for OnDemand { fn on_responses(&self, ctx: &EventContext, req_id: ReqId, responses: &[basic_request::Response]) { use request::IncompleteRequest; - let peer = ctx.peer(); let mut pending = match self.in_transit.write().remove(&req_id) { Some(req) => req, None => return, }; // for each incoming response - // 1. ensure verification data filled. + // 1. ensure verification data filled. (still TODO since on_demand doesn't use back-references yet) // 2. pending.requests.supply_response // 3. if extracted on-demand response for response in responses { - match pending.requests.supply_response(response) { - Ok(response) => pending.responses.push(response), + match pending.requests.supply_response(&*self.cache, response) { + Ok(response) => { + pending.responses.push(response) + } Err(e) => { let peer = ctx.peer(); debug!(target: "on_demand", "Peer {} gave bad response: {:?}", peer, e); @@ -500,13 +545,13 @@ mod tests { #[test] fn detects_hangup() { let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::hours(6)))); - let on_demand = OnDemand::new(cache, 0.into()); + let on_demand = OnDemand::new(cache); let result = on_demand.header_by_hash(&FakeContext, request::HeaderByHash(H256::default())); - assert!(on_demand.orphaned_requests.read().len() == 1); + assert!(on_demand.pending.read().len() == 1); drop(result); on_demand.dispatch_pending(&FakeContext); - assert!(on_demand.orphaned_requests.read().is_empty()); + assert!(on_demand.pending.read().is_empty()); } } diff --git a/ethcore/light/src/on_demand/request.rs b/ethcore/light/src/on_demand/request.rs index ad348445c..d6751bc86 100644 --- a/ethcore/light/src/on_demand/request.rs +++ b/ethcore/light/src/on_demand/request.rs @@ -29,12 +29,13 @@ use ethcore::transaction::SignedTransaction; use request::{self as net_request, IncompleteRequest, Output, OutputKind}; use rlp::{RlpStream, UntrustedRlp}; -use util::{Address, Bytes, DBValue, HashDB, H256, U256}; +use util::{Address, Bytes, DBValue, HashDB, Mutex, H256, U256}; use util::memorydb::MemoryDB; use util::sha3::Hashable; use util::trie::{Trie, TrieDB, TrieError}; /// Core unit of the API: submit batches of these to be answered with `Response`s. +#[derive(Clone)] pub enum Request { /// A request for a header proof. HeaderProof(HeaderProof), @@ -227,27 +228,28 @@ impl IncompleteRequest for CheckedRequest { impl net_request::CheckedRequest for CheckedRequest { type Extract = Response; type Error = Error; + type Environment = Mutex<::cache::Cache>; /// Check whether the response matches (beyond the type). - fn check_response(&self, response: &Self::Response) -> Result { + fn check_response(&self, cache: &Mutex<::cache::Cache>, response: &Self::Response) -> Result { use ::request::Response as NetResponse; // check response against contained prover. match (self, response) { (&CheckedRequest::HeaderProof(ref prover, _), &NetResponse::HeaderProof(ref res)) => - prover.check_response(&res.proof).map(|(h, s)| Response::HeaderProof(h, s)), + prover.check_response(cache, &res.proof).map(|(h, s)| Response::HeaderProof(h, s)), (&CheckedRequest::HeaderByHash(ref prover, _), &NetResponse::Headers(ref res)) => - prover.check_response(&res.headers).map(Response::HeaderByHash), + prover.check_response(cache, &res.headers).map(Response::HeaderByHash), (&CheckedRequest::Receipts(ref prover, _), &NetResponse::Receipts(ref res)) => - prover.check_response(&res.receipts).map(Response::Receipts), + prover.check_response(cache, &res.receipts).map(Response::Receipts), (&CheckedRequest::Body(ref prover, _), &NetResponse::Body(ref res)) => - prover.check_response(&res.body).map(Response::Body), + prover.check_response(cache, &res.body).map(Response::Body), (&CheckedRequest::Account(ref prover, _), &NetResponse::Account(ref res)) => - prover.check_response(&res.proof).map(Response::Account), + prover.check_response(cache, &res.proof).map(Response::Account), (&CheckedRequest::Code(ref prover, _), &NetResponse::Code(ref res)) => - prover.check_response(&res.code).map(Response::Code), + prover.check_response(cache, &res.code).map(Response::Code), (&CheckedRequest::Execution(ref prover, _), &NetResponse::Execution(ref res)) => - Ok(Response::Execution(prover.check_response(&res.items))), + prover.check_response(cache, &res.items).map(Response::Execution), _ => Err(Error::WrongKind), } } @@ -271,7 +273,7 @@ pub enum Response { /// Response to a request for code. Code(Vec), /// Response to a request for proved execution. - Execution(ProvedExecution), // TODO: make into `Result` + Execution(super::ExecutionResult), } /// Errors in verification. @@ -339,9 +341,15 @@ impl HeaderProof { pub fn cht_root(&self) -> H256 { self.cht_root } /// Check a response with a CHT proof, get a hash and total difficulty back. - pub fn check_response(&self, proof: &[Bytes]) -> Result<(H256, U256), Error> { + pub fn check_response(&self, cache: &Mutex<::cache::Cache>, proof: &[Bytes]) -> Result<(H256, U256), Error> { match ::cht::check_proof(proof, self.num, self.cht_root) { - Some((expected_hash, td)) => Ok((expected_hash, td)), + Some((expected_hash, td)) => { + let mut cache = cache.lock(); + cache.insert_block_hash(self.num, expected_hash); + cache.insert_chain_score(expected_hash, td); + + Ok((expected_hash, td)) + } None => Err(Error::BadProof), } } @@ -353,11 +361,14 @@ pub struct HeaderByHash(pub H256); impl HeaderByHash { /// Check a response for the header. - pub fn check_response(&self, headers: &[encoded::Header]) -> Result { + pub fn check_response(&self, cache: &Mutex<::cache::Cache>, headers: &[encoded::Header]) -> Result { let header = headers.get(0).ok_or(Error::Empty)?; let hash = header.sha3(); match hash == self.0 { - true => Ok(header.clone()), + true => { + cache.lock().insert_block_header(hash, header.clone()); + Ok(header.clone()) + } false => Err(Error::WrongHash(self.0, hash)), } } @@ -383,7 +394,7 @@ impl Body { } /// Check a response for this block body. - pub fn check_response(&self, body: &encoded::Body) -> Result { + pub fn check_response(&self, cache: &Mutex<::cache::Cache>, body: &encoded::Body) -> Result { // check the integrity of the the body against the header let tx_root = ::util::triehash::ordered_trie_root(body.rlp().at(0).iter().map(|r| r.as_raw().to_vec())); if tx_root != self.header.transactions_root() { @@ -401,6 +412,8 @@ impl Body { stream.append_raw(body.rlp().at(0).as_raw(), 1); stream.append_raw(body.rlp().at(1).as_raw(), 1); + cache.lock().insert_block_body(self.hash, body.clone()); + Ok(encoded::Block::new(stream.out())) } } @@ -411,12 +424,15 @@ pub struct BlockReceipts(pub encoded::Header); impl BlockReceipts { /// Check a response with receipts against the stored header. - pub fn check_response(&self, receipts: &[Receipt]) -> Result, Error> { + pub fn check_response(&self, cache: &Mutex<::cache::Cache>, receipts: &[Receipt]) -> Result, Error> { let receipts_root = self.0.receipts_root(); let found_root = ::util::triehash::ordered_trie_root(receipts.iter().map(|r| ::rlp::encode(r).to_vec())); match receipts_root == found_root { - true => Ok(receipts.to_vec()), + true => { + cache.lock().insert_block_receipts(receipts_root, receipts.to_vec()); + Ok(receipts.to_vec()) + } false => Err(Error::WrongTrieRoot(receipts_root, found_root)), } } @@ -433,7 +449,7 @@ pub struct Account { impl Account { /// Check a response with an account against the stored header. - pub fn check_response(&self, proof: &[Bytes]) -> Result, Error> { + pub fn check_response(&self, _: &Mutex<::cache::Cache>, proof: &[Bytes]) -> Result, Error> { let state_root = self.header.state_root(); let mut db = MemoryDB::new(); @@ -465,7 +481,7 @@ pub struct Code { impl Code { /// Check a response with code against the code hash. - pub fn check_response(&self, code: &[u8]) -> Result, Error> { + pub fn check_response(&self, _: &Mutex<::cache::Cache>, code: &[u8]) -> Result, Error> { let found_hash = code.sha3(); if found_hash == self.code_hash { Ok(code.to_vec()) @@ -490,23 +506,29 @@ pub struct TransactionProof { impl TransactionProof { /// Check the proof, returning the proved execution or indicate that the proof was bad. - pub fn check_response(&self, state_items: &[DBValue]) -> ProvedExecution { + pub fn check_response(&self, _: &Mutex<::cache::Cache>, state_items: &[DBValue]) -> Result { let root = self.header.state_root(); - state::check_proof( + let proved_execution = state::check_proof( state_items, root, &self.tx, &*self.engine, &self.env_info, - ) + ); + + match proved_execution { + ProvedExecution::BadProof => Err(Error::BadProof), + ProvedExecution::Failed(e) => Ok(Err(e)), + ProvedExecution::Complete(e) => Ok(Ok(e)), + } } } #[cfg(test)] mod tests { use super::*; - use util::{MemoryDB, Address, H256}; + use util::{MemoryDB, Address, Mutex, H256}; use util::trie::{Trie, TrieMut, SecTrieDB, SecTrieDBMut}; use util::trie::recorder::Recorder; @@ -515,6 +537,10 @@ mod tests { use ethcore::encoded; use ethcore::receipt::Receipt; + fn make_cache() -> ::cache::Cache { + ::cache::Cache::new(Default::default(), ::time::Duration::seconds(1)) + } + #[test] fn no_invalid_header_by_number() { assert!(HeaderProof::new(0, Default::default()).is_none()) @@ -544,7 +570,8 @@ mod tests { let proof = cht.prove(10_000, 0).unwrap().unwrap(); let req = HeaderProof::new(10_000, cht.root()).unwrap(); - assert!(req.check_response(&proof[..]).is_ok()); + let cache = Mutex::new(make_cache()); + assert!(req.check_response(&cache, &proof[..]).is_ok()); } #[test] @@ -555,7 +582,8 @@ mod tests { let hash = header.hash(); let raw_header = encoded::Header::new(::rlp::encode(&header).to_vec()); - assert!(HeaderByHash(hash).check_response(&[raw_header]).is_ok()) + let cache = Mutex::new(make_cache()); + assert!(HeaderByHash(hash).check_response(&cache, &[raw_header]).is_ok()) } #[test] @@ -571,8 +599,9 @@ mod tests { hash: header.hash(), }; + let cache = Mutex::new(make_cache()); let response = encoded::Body::new(body_stream.drain().to_vec()); - assert!(req.check_response(&response).is_ok()) + assert!(req.check_response(&cache, &response).is_ok()) } #[test] @@ -593,7 +622,8 @@ mod tests { let req = BlockReceipts(encoded::Header::new(::rlp::encode(&header).to_vec())); - assert!(req.check_response(&receipts).is_ok()) + let cache = Mutex::new(make_cache()); + assert!(req.check_response(&cache, &receipts).is_ok()) } #[test] @@ -642,7 +672,8 @@ mod tests { address: addr, }; - assert!(req.check_response(&proof[..]).is_ok()); + let cache = Mutex::new(make_cache()); + assert!(req.check_response(&cache, &proof[..]).is_ok()); } #[test] @@ -653,7 +684,8 @@ mod tests { code_hash: ::util::Hashable::sha3(&code), }; - assert!(req.check_response(&code).is_ok()); - assert!(req.check_response(&[]).is_err()); + let cache = Mutex::new(make_cache()); + assert!(req.check_response(&cache, &code).is_ok()); + assert!(req.check_response(&cache, &[]).is_err()); } } diff --git a/ethcore/light/src/types/request/builder.rs b/ethcore/light/src/types/request/builder.rs index 6a40d288e..3dfb9ff40 100644 --- a/ethcore/light/src/types/request/builder.rs +++ b/ethcore/light/src/types/request/builder.rs @@ -118,7 +118,7 @@ impl Requests { 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: &T::Response) + pub fn supply_response(&mut self, env: &T::Environment, response: &T::Response) -> Result> { let idx = self.answered; @@ -126,7 +126,7 @@ impl Requests { // check validity. if idx == self.requests.len() { return Err(ResponseError::Unexpected) } let extracted = self.requests[idx] - .check_response(&response).map_err(ResponseError::Validity)?; + .check_response(env, response).map_err(ResponseError::Validity)?; let outputs = &mut self.outputs; response.fill_outputs(|out_idx, output| { @@ -157,7 +157,7 @@ impl Requests { let mut responses = Vec::new(); while let Some(response) = self.next_complete().and_then(&responder) { - match self.supply_response(&response) { + match self.supply_response(&(), &response) { Ok(()) => responses.push(response), Err(e) => { debug!(target: "pip", "produced bad response to request: {:?}", e); diff --git a/ethcore/light/src/types/request/mod.rs b/ethcore/light/src/types/request/mod.rs index f26908acd..3dd2db629 100644 --- a/ethcore/light/src/types/request/mod.rs +++ b/ethcore/light/src/types/request/mod.rs @@ -382,8 +382,9 @@ impl IncompleteRequest for Request { impl CheckedRequest for Request { type Extract = (); type Error = WrongKind; + type Environment = (); - fn check_response(&self, response: &Response) -> Result<(), WrongKind> { + fn check_response(&self, _: &(), response: &Response) -> Result<(), WrongKind> { if self.kind() == response.kind() { Ok(()) } else { @@ -566,9 +567,11 @@ pub trait CheckedRequest: IncompleteRequest { type Extract; /// Error encountered during the check. type Error; + /// Environment passed to response check. + type Environment; /// Check whether the response matches (beyond the type). - fn check_response(&self, _response: &Self::Response) -> Result; + fn check_response(&self, &Self::Environment, &Self::Response) -> Result; } /// A response-like object.