diff --git a/ethcore/light/Cargo.toml b/ethcore/light/Cargo.toml index 9e10449fb..cab75a36a 100644 --- a/ethcore/light/Cargo.toml +++ b/ethcore/light/Cargo.toml @@ -1,5 +1,5 @@ [package] -description = "Parity LES primitives" +description = "Parity Light Client Implementation" homepage = "http://parity.io" license = "GPL-3.0" name = "ethcore-light" diff --git a/ethcore/light/src/lib.rs b/ethcore/light/src/lib.rs index ada58d8de..b15c85242 100644 --- a/ethcore/light/src/lib.rs +++ b/ethcore/light/src/lib.rs @@ -35,9 +35,10 @@ pub mod client; pub mod cht; pub mod net; -pub mod on_demand; +//pub mod on_demand; pub mod transaction_queue; pub mod cache; +pub mod request_builder; #[cfg(not(feature = "ipc"))] pub mod provider; diff --git a/ethcore/light/src/net/context.rs b/ethcore/light/src/net/context.rs index bd0c8a6bb..332d497a1 100644 --- a/ethcore/light/src/net/context.rs +++ b/ethcore/light/src/net/context.rs @@ -12,7 +12,7 @@ // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Parity. If not, see . +// along with Parity. If not, see . //! I/O and event context generalizations. @@ -89,10 +89,6 @@ pub trait BasicContext { // TODO: maybe just put this on a timer in LightProtocol? fn make_announcement(&self, announcement: Announcement); - /// Find the maximum number of requests of a specific type which can be made from - /// supplied peer. - fn max_requests(&self, peer: PeerId, kind: request::Kind) -> usize; - /// Disconnect a peer. fn disconnect_peer(&self, peer: PeerId); @@ -131,10 +127,6 @@ impl<'a> BasicContext for TickCtx<'a> { self.proto.make_announcement(self.io, announcement); } - fn max_requests(&self, peer: PeerId, kind: request::Kind) -> usize { - self.proto.max_requests(peer, kind) - } - fn disconnect_peer(&self, peer: PeerId) { self.io.disconnect_peer(peer); } @@ -168,10 +160,6 @@ impl<'a> BasicContext for Ctx<'a> { self.proto.make_announcement(self.io, announcement); } - fn max_requests(&self, peer: PeerId, kind: request::Kind) -> usize { - self.proto.max_requests(peer, kind) - } - fn disconnect_peer(&self, peer: PeerId) { self.io.disconnect_peer(peer); } diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index 181f95e95..1b2433fbe 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -14,10 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -//! LES Protocol Version 1 implementation. +//! PIP Protocol Version 1 implementation. //! //! This uses a "Provider" to answer requests. -//! See https://github.com/ethcore/parity/wiki/Light-Ethereum-Subprotocol-(LES) use ethcore::transaction::{Action, UnverifiedTransaction}; use ethcore::receipt::Receipt; @@ -35,7 +34,7 @@ use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; use provider::Provider; -use request::{self, HashOrNumber, Request}; +use request::{self, HashOrNumber, Request, Response}; use self::request_credits::{Credits, FlowParams}; use self::context::{Ctx, TickCtx}; @@ -83,43 +82,24 @@ mod packet { // announcement of new block hashes or capabilities. pub const ANNOUNCE: u8 = 0x01; - // request and response for block headers - pub const GET_BLOCK_HEADERS: u8 = 0x02; - pub const BLOCK_HEADERS: u8 = 0x03; - - // request and response for block bodies - pub const GET_BLOCK_BODIES: u8 = 0x04; - pub const BLOCK_BODIES: u8 = 0x05; - - // request and response for transaction receipts. - pub const GET_RECEIPTS: u8 = 0x06; - pub const RECEIPTS: u8 = 0x07; - - // request and response for merkle proofs. - pub const GET_PROOFS: u8 = 0x08; - pub const PROOFS: u8 = 0x09; - - // request and response for contract code. - pub const GET_CONTRACT_CODES: u8 = 0x0a; - pub const CONTRACT_CODES: u8 = 0x0b; + // request and response. + pub const REQUEST: u8 = 0x02; + pub const RESPONSE: u8 = 0x03; // relay transactions to peers. - pub const SEND_TRANSACTIONS: u8 = 0x0c; - - // request and response for header proofs in a CHT. - pub const GET_HEADER_PROOFS: u8 = 0x0d; - pub const HEADER_PROOFS: u8 = 0x0e; + pub const SEND_TRANSACTIONS: u8 = 0x04; // request and response for transaction proof. - pub const GET_TRANSACTION_PROOF: u8 = 0x0f; - pub const TRANSACTION_PROOF: u8 = 0x10; + // TODO: merge with request/response. + pub const GET_TRANSACTION_PROOF: u8 = 0x05; + pub const TRANSACTION_PROOF: u8 = 0x06; } // timeouts for different kinds of requests. all values are in milliseconds. // TODO: variable timeouts based on request count. mod timeout { pub const HANDSHAKE: i64 = 2500; - pub const HEADERS: i64 = 5000; + pub const HEADERS: i64 = 2500; pub const BODIES: i64 = 5000; pub const RECEIPTS: i64 = 3500; pub const PROOFS: i64 = 4000; @@ -159,17 +139,6 @@ pub struct Peer { } impl Peer { - // check the maximum cost of a request, returning an error if there's - // not enough credits left. - // returns the calculated maximum cost. - fn deduct_max(&mut self, flow_params: &FlowParams, kind: request::Kind, max: usize) -> Result { - flow_params.recharge(&mut self.local_credits); - - let max_cost = flow_params.compute_cost(kind, max); - self.local_credits.deduct_cost(max_cost)?; - Ok(max_cost) - } - // refund credits for a request. returns new amount of credits. fn refund(&mut self, flow_params: &FlowParams, amount: U256) -> U256 { flow_params.refund(&mut self.local_credits, amount); @@ -197,20 +166,8 @@ pub trait Handler: Send + Sync { fn on_announcement(&self, _ctx: &EventContext, _announcement: &Announcement) { } /// Called when a peer requests relay of some transactions. fn on_transactions(&self, _ctx: &EventContext, _relay: &[UnverifiedTransaction]) { } - /// Called when a peer responds with block bodies. - fn on_block_bodies(&self, _ctx: &EventContext, _req_id: ReqId, _bodies: &[Bytes]) { } - /// Called when a peer responds with block headers. - fn on_block_headers(&self, _ctx: &EventContext, _req_id: ReqId, _headers: &[Bytes]) { } - /// Called when a peer responds with block receipts. - fn on_receipts(&self, _ctx: &EventContext, _req_id: ReqId, _receipts: &[Vec]) { } - /// Called when a peer responds with state proofs. Each proof should be a series of trie - /// nodes in ascending order by distance from the root. - fn on_state_proofs(&self, _ctx: &EventContext, _req_id: ReqId, _proofs: &[Vec]) { } - /// Called when a peer responds with contract code. - fn on_code(&self, _ctx: &EventContext, _req_id: ReqId, _codes: &[Bytes]) { } - /// Called when a peer responds with header proofs. Each proof should be a block header coupled - /// with a series of trie nodes is ascending order by distance from the root. - fn on_header_proofs(&self, _ctx: &EventContext, _req_id: ReqId, _proofs: &[(Bytes, Vec)]) { } + /// Called when a peer responds to requests. + fn on_responses(&self, _ctx: &EventContext, _req_id: ReqId, _relay: &[Response]) { } /// Called when a peer responds with a transaction proof. Each proof is a vector of state items. fn on_transaction_proof(&self, _ctx: &EventContext, _req_id: ReqId, _state_items: &[DBValue]) { } /// Called to "tick" the handler periodically. @@ -307,7 +264,7 @@ pub struct LightProtocol { impl LightProtocol { /// Create a new instance of the protocol manager. pub fn new(provider: Arc, params: Params) -> Self { - debug!(target: "les", "Initializing LES handler"); + debug!(target: "pip", "Initializing light protocol handler"); let genesis_hash = provider.chain_info().genesis_hash; LightProtocol { @@ -339,62 +296,15 @@ impl LightProtocol { ) } - /// Check the maximum amount of requests of a specific type - /// which a peer would be able to serve. Returns zero if the - /// peer is unknown or has no credit parameters. - fn max_requests(&self, peer: PeerId, kind: request::Kind) -> usize { - self.peers.read().get(&peer).and_then(|peer| { - let mut peer = peer.lock(); - match peer.remote_flow { - Some((ref mut c, ref flow)) => { - flow.recharge(c); - Some(flow.max_amount(&*c, kind)) - } - None => None, - } - }).unwrap_or(0) - } - /// Make a request to a peer. /// /// Fails on: nonexistent peer, network error, peer not server, /// insufficient credits. Does not check capabilities before sending. /// On success, returns a request id which can later be coordinated /// with an event. + // TODO: pass `Requests`. pub fn request_from(&self, io: &IoContext, peer_id: &PeerId, request: Request) -> Result { - let peers = self.peers.read(); - let peer = peers.get(peer_id).ok_or_else(|| Error::UnknownPeer)?; - let mut peer = peer.lock(); - - match peer.remote_flow { - Some((ref mut c, ref flow)) => { - flow.recharge(c); - let max = flow.compute_cost(request.kind(), request.amount()); - c.deduct_cost(max)?; - } - None => return Err(Error::NotServer), - } - - let req_id = self.req_id.fetch_add(1, Ordering::SeqCst); - let packet_data = encode_request(&request, req_id); - - trace!(target: "les", "Dispatching request {} to peer {}", req_id, peer_id); - - let packet_id = match request.kind() { - request::Kind::Headers => packet::GET_BLOCK_HEADERS, - request::Kind::Bodies => packet::GET_BLOCK_BODIES, - request::Kind::Receipts => packet::GET_RECEIPTS, - request::Kind::StateProofs => packet::GET_PROOFS, - request::Kind::Codes => packet::GET_CONTRACT_CODES, - request::Kind::HeaderProofs => packet::GET_HEADER_PROOFS, - request::Kind::TransactionProof => packet::GET_TRANSACTION_PROOF, - }; - - io.send(*peer_id, packet_id, packet_data); - - peer.pending_requests.insert(ReqId(req_id), request, SteadyTime::now()); - - Ok(ReqId(req_id)) + unimplemented!() } /// Make an announcement of new chain head and capabilities to all peers. @@ -427,7 +337,7 @@ impl LightProtocol { None => { // both values will always originate locally -- this means something // has gone really wrong - debug!(target: "les", "couldn't compute reorganization depth between {:?} and {:?}", + debug!(target: "pip", "couldn't compute reorganization depth between {:?} and {:?}", &announcement.head_hash, &peer_info.sent_head); 0 } @@ -474,11 +384,10 @@ impl LightProtocol { let req_id = ReqId(raw.val_at(0)?); let cur_credits: U256 = raw.val_at(1)?; - trace!(target: "les", "pre-verifying response from peer {}, kind={:?}", peer, kind); + trace!(target: "pip", "pre-verifying response from peer {}, kind={:?}", peer, kind); - let mut had_req = false; let peers = self.peers.read(); - let maybe_err = match peers.get(peer) { + let res = match peers.get(peer) { Some(peer_info) => { let mut peer_info = peer_info.lock(); let req_info = peer_info.pending_requests.remove(&req_id, SteadyTime::now()); @@ -486,69 +395,37 @@ impl LightProtocol { match (req_info, flow_info) { (Some(request), Some(flow_info)) => { - had_req = true; - let &mut (ref mut c, ref mut flow) = flow_info; let actual_credits = ::std::cmp::min(cur_credits, *flow.limit()); c.update_to(actual_credits); - if request.kind() != kind { - Some(Error::UnsolicitedResponse) - } else { - None - } + Ok(()) } - (None, _) => Some(Error::UnsolicitedResponse), - (_, None) => Some(Error::NotServer), // really should be impossible. + (None, _) => Err(Error::UnsolicitedResponse), + (_, None) => Err(Error::NotServer), // really should be impossible. } } - None => Some(Error::UnknownPeer), // probably only occurs in a race of some kind. + None => Err(Error::UnknownPeer), // probably only occurs in a race of some kind. }; - if had_req { - let id_guard = IdGuard::new(peers, *peer, req_id); - match maybe_err { - Some(err) => Err(err), - None => Ok(id_guard) - } - } else { - Err(maybe_err.expect("every branch without a request leads to error; qed")) - } + res.map(|_| IdGuard::new(peers, *peer, req_id)) } - /// Handle an LES packet using the given io context. + /// Handle a packet using the given io context. /// Packet data is _untrusted_, which means that invalid data won't lead to /// issues. pub fn handle_packet(&self, io: &IoContext, peer: &PeerId, packet_id: u8, data: &[u8]) { let rlp = UntrustedRlp::new(data); - trace!(target: "les", "Incoming packet {} from peer {}", packet_id, peer); + trace!(target: "pip", "Incoming packet {} from peer {}", packet_id, peer); // handle the packet let res = match packet_id { packet::STATUS => self.status(peer, io, rlp), packet::ANNOUNCE => self.announcement(peer, io, rlp), - packet::GET_BLOCK_HEADERS => self.get_block_headers(peer, io, rlp), - packet::BLOCK_HEADERS => self.block_headers(peer, io, rlp), - - packet::GET_BLOCK_BODIES => self.get_block_bodies(peer, io, rlp), - packet::BLOCK_BODIES => self.block_bodies(peer, io, rlp), - - packet::GET_RECEIPTS => self.get_receipts(peer, io, rlp), - packet::RECEIPTS => self.receipts(peer, io, rlp), - - packet::GET_PROOFS => self.get_proofs(peer, io, rlp), - packet::PROOFS => self.proofs(peer, io, rlp), - - packet::GET_CONTRACT_CODES => self.get_contract_code(peer, io, rlp), - packet::CONTRACT_CODES => self.contract_code(peer, io, rlp), - - packet::GET_HEADER_PROOFS => self.get_header_proofs(peer, io, rlp), - packet::HEADER_PROOFS => self.header_proofs(peer, io, rlp), - - packet::GET_TRANSACTION_PROOF => self.get_transaction_proof(peer, io, rlp), - packet::TRANSACTION_PROOF => self.transaction_proof(peer, io, rlp), + packet::REQUEST => self.request(peer, io, rlp), + packet::RESPONSE => self.response(peer, io, rlp), packet::SEND_TRANSACTIONS => self.relay_transactions(peer, io, rlp), @@ -577,7 +454,7 @@ impl LightProtocol { .collect(); for slowpoke in slowpokes { - debug!(target: "les", "Peer {} handshake timed out", slowpoke); + debug!(target: "pip", "Peer {} handshake timed out", slowpoke); pending.remove(&slowpoke); io.disconnect_peer(slowpoke); } @@ -587,7 +464,7 @@ impl LightProtocol { { for (peer_id, peer) in self.peers.read().iter() { if peer.lock().pending_requests.check_timeout(now) { - debug!(target: "les", "Peer {} request timeout", peer_id); + debug!(target: "pip", "Peer {} request timeout", peer_id); io.disconnect_peer(*peer_id); } } @@ -631,7 +508,7 @@ impl LightProtocol { /// called when a peer disconnects. pub fn on_disconnect(&self, peer: PeerId, io: &IoContext) { - trace!(target: "les", "Peer {} disconnecting", peer); + trace!(target: "pip", "Peer {} disconnecting", peer); self.pending_peers.write().remove(&peer); let unfulfilled = match self.peers.write().remove(&peer) { @@ -686,7 +563,7 @@ impl LightProtocol { let (status, capabilities, flow_params) = status::parse_handshake(data)?; - trace!(target: "les", "Connected peer with chain head {:?}", (status.head_hash, status.head_num)); + trace!(target: "pip", "Connected peer with chain head {:?}", (status.head_hash, status.head_num)); if (status.network_id, status.genesis_hash) != (self.network_id, self.genesis_hash) { return Err(Error::WrongNetwork); @@ -723,7 +600,7 @@ impl LightProtocol { // Handle an announcement. fn announcement(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> { if !self.peers.read().contains_key(peer) { - debug!(target: "les", "Ignoring announcement from unknown peer"); + debug!(target: "pip", "Ignoring announcement from unknown peer"); return Ok(()) } @@ -765,447 +642,19 @@ impl LightProtocol { Ok(()) } - // Handle a request for block headers. - fn get_block_headers(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> { - const MAX_HEADERS: usize = 512; - - let peers = self.peers.read(); - let peer = match peers.get(peer) { - Some(peer) => peer, - None => { - debug!(target: "les", "Ignoring request from unknown peer"); - return Ok(()) - } - }; - - let mut peer = peer.lock(); - - let req_id: u64 = data.val_at(0)?; - let data = data.at(1)?; - - let start_block = { - if data.at(0)?.size() == 32 { - HashOrNumber::Hash(data.val_at(0)?) - } else { - HashOrNumber::Number(data.val_at(0)?) - } - }; - - let req = request::Headers { - start: start_block, - max: ::std::cmp::min(MAX_HEADERS, data.val_at(1)?), - skip: data.val_at(2)?, - reverse: data.val_at(3)?, - }; - - let max_cost = peer.deduct_max(&self.flow_params, request::Kind::Headers, req.max)?; - - let response = self.provider.block_headers(req); - let actual_cost = self.flow_params.compute_cost(request::Kind::Headers, response.len()); - assert!(max_cost >= actual_cost, "Actual cost exceeded maximum computed cost."); - - let cur_credits = peer.refund(&self.flow_params, max_cost - actual_cost); - io.respond(packet::BLOCK_HEADERS, { - let mut stream = RlpStream::new_list(3); - stream.append(&req_id).append(&cur_credits).begin_list(response.len()); - - for header in response { - stream.append_raw(&header.into_inner(), 1); - } - - stream.out() - }); - - Ok(()) - } - - // Receive a response for block headers. - fn block_headers(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> { - let id_guard = self.pre_verify_response(peer, request::Kind::Headers, &raw)?; - let raw_headers: Vec<_> = raw.at(2)?.iter().map(|x| x.as_raw().to_owned()).collect(); - - let req_id = id_guard.defuse(); - for handler in &self.handlers { - handler.on_block_headers(&Ctx { - peer: *peer, - io: io, - proto: self, - }, req_id, &raw_headers); - } - - Ok(()) - } - - // Handle a request for block bodies. - fn get_block_bodies(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> { - const MAX_BODIES: usize = 256; - - let peers = self.peers.read(); - let peer = match peers.get(peer) { - Some(peer) => peer, - None => { - debug!(target: "les", "Ignoring request from unknown peer"); - return Ok(()) - } - }; - let mut peer = peer.lock(); - - let req_id: u64 = data.val_at(0)?; - - let req = request::Bodies { - block_hashes: data.at(1)?.iter() - .take(MAX_BODIES) - .map(|x| x.as_val()) - .collect::>()? - }; - - let max_cost = peer.deduct_max(&self.flow_params, request::Kind::Bodies, req.block_hashes.len())?; - - let response = self.provider.block_bodies(req); - let response_len = response.iter().filter(|x| x.is_some()).count(); - let actual_cost = self.flow_params.compute_cost(request::Kind::Bodies, response_len); - assert!(max_cost >= actual_cost, "Actual cost exceeded maximum computed cost."); - - let cur_credits = peer.refund(&self.flow_params, max_cost - actual_cost); - - io.respond(packet::BLOCK_BODIES, { - let mut stream = RlpStream::new_list(3); - stream.append(&req_id).append(&cur_credits).begin_list(response.len()); - - for body in response { - match body { - Some(body) => stream.append_raw(&body.into_inner(), 1), - None => stream.append_empty_data(), - }; - } - - stream.out() - }); - - Ok(()) - } - - // Receive a response for block bodies. - fn block_bodies(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> { - let id_guard = self.pre_verify_response(peer, request::Kind::Bodies, &raw)?; - let raw_bodies: Vec = raw.at(2)?.iter().map(|x| x.as_raw().to_owned()).collect(); - - let req_id = id_guard.defuse(); - for handler in &self.handlers { - handler.on_block_bodies(&Ctx { - peer: *peer, - io: io, - proto: self, - }, req_id, &raw_bodies); - } - - Ok(()) - } - - // Handle a request for receipts. - fn get_receipts(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> { - const MAX_RECEIPTS: usize = 256; - - let peers = self.peers.read(); - let peer = match peers.get(peer) { - Some(peer) => peer, - None => { - debug!(target: "les", "Ignoring request from unknown peer"); - return Ok(()) - } - }; - let mut peer = peer.lock(); - - let req_id: u64 = data.val_at(0)?; - - let req = request::Receipts { - block_hashes: data.at(1)?.iter() - .take(MAX_RECEIPTS) - .map(|x| x.as_val()) - .collect::>()? - }; - - let max_cost = peer.deduct_max(&self.flow_params, request::Kind::Receipts, req.block_hashes.len())?; - - let response = self.provider.receipts(req); - let response_len = response.iter().filter(|x| &x[..] != &::rlp::EMPTY_LIST_RLP).count(); - let actual_cost = self.flow_params.compute_cost(request::Kind::Receipts, response_len); - assert!(max_cost >= actual_cost, "Actual cost exceeded maximum computed cost."); - - let cur_credits = peer.refund(&self.flow_params, max_cost - actual_cost); - - io.respond(packet::RECEIPTS, { - let mut stream = RlpStream::new_list(3); - stream.append(&req_id).append(&cur_credits).begin_list(response.len()); - - for receipts in response { - stream.append_raw(&receipts, 1); - } - - stream.out() - }); - - Ok(()) - } - - // Receive a response for receipts. - fn receipts(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> { - let id_guard = self.pre_verify_response(peer, request::Kind::Receipts, &raw)?; - let raw_receipts: Vec> = raw.at(2)? - .iter() - .map(|x| x.as_val()) - .collect::>()?; - - let req_id = id_guard.defuse(); - for handler in &self.handlers { - handler.on_receipts(&Ctx { - peer: *peer, - io: io, - proto: self, - }, req_id, &raw_receipts); - } - - Ok(()) - } - - // Handle a request for proofs. - fn get_proofs(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> { - const MAX_PROOFS: usize = 128; - - let peers = self.peers.read(); - let peer = match peers.get(peer) { - Some(peer) => peer, - None => { - debug!(target: "les", "Ignoring request from unknown peer"); - return Ok(()) - } - }; - let mut peer = peer.lock(); - - let req_id: u64 = data.val_at(0)?; - - let req = { - let requests: Result, Error> = data.at(1)?.iter().take(MAX_PROOFS).map(|x| { - Ok(request::StateProof { - block: x.val_at(0)?, - key1: x.val_at(1)?, - key2: if x.at(2)?.is_empty() { None } else { Some(x.val_at(2)?) }, - from_level: x.val_at(3)?, - }) - }).collect(); - - request::StateProofs { - requests: requests?, - } - }; - - let max_cost = peer.deduct_max(&self.flow_params, request::Kind::StateProofs, req.requests.len())?; - - let response = self.provider.proofs(req); - let response_len = response.iter().filter(|x| &x[..] != &::rlp::EMPTY_LIST_RLP).count(); - let actual_cost = self.flow_params.compute_cost(request::Kind::StateProofs, response_len); - assert!(max_cost >= actual_cost, "Actual cost exceeded maximum computed cost."); - - let cur_credits = peer.refund(&self.flow_params, max_cost - actual_cost); - - io.respond(packet::PROOFS, { - let mut stream = RlpStream::new_list(3); - stream.append(&req_id).append(&cur_credits).begin_list(response.len()); - - for proof in response { - stream.append_raw(&proof, 1); - } - - stream.out() - }); - - Ok(()) - } - - // Receive a response for proofs. - fn proofs(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> { - let id_guard = self.pre_verify_response(peer, request::Kind::StateProofs, &raw)?; - - let raw_proofs: Vec> = raw.at(2)?.iter() - .map(|x| x.iter().map(|node| node.as_raw().to_owned()).collect()) - .collect(); - - let req_id = id_guard.defuse(); - for handler in &self.handlers { - handler.on_state_proofs(&Ctx { - peer: *peer, - io: io, - proto: self, - }, req_id, &raw_proofs); - } - - Ok(()) - } - - // Handle a request for contract code. - fn get_contract_code(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> { - const MAX_CODES: usize = 256; - - let peers = self.peers.read(); - let peer = match peers.get(peer) { - Some(peer) => peer, - None => { - debug!(target: "les", "Ignoring request from unknown peer"); - return Ok(()) - } - }; - let mut peer = peer.lock(); - - let req_id: u64 = data.val_at(0)?; - - let req = { - let requests: Result, Error> = data.at(1)?.iter().take(MAX_CODES).map(|x| { - Ok(request::ContractCode { - block_hash: x.val_at(0)?, - account_key: x.val_at(1)?, - }) - }).collect(); - - request::ContractCodes { - code_requests: requests?, - } - }; - - let max_cost = peer.deduct_max(&self.flow_params, request::Kind::Codes, req.code_requests.len())?; - - let response = self.provider.contract_codes(req); - let response_len = response.iter().filter(|x| !x.is_empty()).count(); - let actual_cost = self.flow_params.compute_cost(request::Kind::Codes, response_len); - assert!(max_cost >= actual_cost, "Actual cost exceeded maximum computed cost."); - - let cur_credits = peer.refund(&self.flow_params, max_cost - actual_cost); - - io.respond(packet::CONTRACT_CODES, { - let mut stream = RlpStream::new_list(3); - stream.append(&req_id).append(&cur_credits).begin_list(response.len()); - - for code in response { - stream.append(&code); - } - - stream.out() - }); - - Ok(()) - } - - // Receive a response for contract code. - fn contract_code(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> { - let id_guard = self.pre_verify_response(peer, request::Kind::Codes, &raw)?; - - let raw_code: Vec = raw.at(2)?.iter() - .map(|x| x.as_val()) - .collect::>()?; - - let req_id = id_guard.defuse(); - for handler in &self.handlers { - handler.on_code(&Ctx { - peer: *peer, - io: io, - proto: self, - }, req_id, &raw_code); - } - - Ok(()) - } - - // Handle a request for header proofs - fn get_header_proofs(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> { - const MAX_PROOFS: usize = 256; - - let peers = self.peers.read(); - let peer = match peers.get(peer) { - Some(peer) => peer, - None => { - debug!(target: "les", "Ignoring request from unknown peer"); - return Ok(()) - } - }; - let mut peer = peer.lock(); - - let req_id: u64 = data.val_at(0)?; - - let req = { - let requests: Result, Error> = data.at(1)?.iter().take(MAX_PROOFS).map(|x| { - Ok(request::HeaderProof { - cht_number: x.val_at(0)?, - block_number: x.val_at(1)?, - from_level: x.val_at(2)?, - }) - }).collect(); - - request::HeaderProofs { - requests: requests?, - } - }; - - let max_cost = peer.deduct_max(&self.flow_params, request::Kind::HeaderProofs, req.requests.len())?; - - let response = self.provider.header_proofs(req); - let response_len = response.iter().filter(|x| &x[..] != ::rlp::EMPTY_LIST_RLP).count(); - let actual_cost = self.flow_params.compute_cost(request::Kind::HeaderProofs, response_len); - assert!(max_cost >= actual_cost, "Actual cost exceeded maximum computed cost."); - - let cur_credits = peer.refund(&self.flow_params, max_cost - actual_cost); - - io.respond(packet::HEADER_PROOFS, { - let mut stream = RlpStream::new_list(3); - stream.append(&req_id).append(&cur_credits).begin_list(response.len()); - - for proof in response { - stream.append_raw(&proof, 1); - } - - stream.out() - }); - - Ok(()) - } - - // Receive a response for header proofs - fn header_proofs(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> { - fn decode_res(raw: UntrustedRlp) -> Result<(Bytes, Vec), ::rlp::DecoderError> { - Ok(( - raw.val_at(0)?, - raw.at(1)?.iter().map(|x| x.as_raw().to_owned()).collect(), - )) - } - - let id_guard = self.pre_verify_response(peer, request::Kind::HeaderProofs, &raw)?; - let raw_proofs: Vec<_> = raw.at(2)?.iter() - .map(decode_res) - .collect::>()?; - - let req_id = id_guard.defuse(); - for handler in &self.handlers { - handler.on_header_proofs(&Ctx { - peer: *peer, - io: io, - proto: self, - }, req_id, &raw_proofs); - } - - Ok(()) - } - - // Receive a request for proof-of-execution. - fn get_transaction_proof(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> { - // refuse to execute more than this amount of gas at once. - // this is appx. the point at which the proof of execution would no longer fit in - // a single Devp2p packet. + fn request(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> { + // the maximum amount of requests we'll fill in a single packet. + const MAX_REQUESTS: usize = 512; + // the maximum amount of gas we'll prove execution of in a single packet. const MAX_GAS: usize = 50_000_000; - use util::Uint; + + use ::request_builder::RequestBuilder; let peers = self.peers.read(); let peer = match peers.get(peer) { Some(peer) => peer, None => { - debug!(target: "les", "Ignoring request from unknown peer"); + debug!(target: "pip", "Ignoring request from unknown peer"); return Ok(()) } }; @@ -1213,68 +662,11 @@ impl LightProtocol { let req_id: u64 = raw.val_at(0)?; - let req = { - let req_rlp = raw.at(1)?; - request::TransactionProof { - at: req_rlp.val_at(0)?, - from: req_rlp.val_at(1)?, - action: if req_rlp.at(2)?.is_empty() { - Action::Create - } else { - Action::Call(req_rlp.val_at(2)?) - }, - gas: ::std::cmp::min(req_rlp.val_at(3)?, MAX_GAS.into()), - gas_price: req_rlp.val_at(4)?, - value: req_rlp.val_at(5)?, - data: req_rlp.val_at(6)?, - } - }; - - // always charge the peer for all the gas. - peer.deduct_max(&self.flow_params, request::Kind::TransactionProof, req.gas.low_u64() as usize)?; - - let response = match self.provider.transaction_proof(req) { - Some(res) => res, - None => vec![], - }; - - let cur_credits = peer.local_credits.current(); - - io.respond(packet::TRANSACTION_PROOF, { - let mut stream = RlpStream::new_list(3); - stream.append(&req_id).append(&cur_credits).begin_list(response.len()); - - for state_item in response { - stream.append(&&state_item[..]); - } - - stream.out() - }); - - Ok(()) + unimplemented!() } - // Receive a response for proof-of-execution. - fn transaction_proof(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> { - let id_guard = self.pre_verify_response(peer, request::Kind::HeaderProofs, &raw)?; - let raw_proof: Vec = raw.at(2)?.iter() - .map(|rlp| { - let mut db_val = DBValue::new(); - db_val.append_slice(rlp.data()?); - Ok(db_val) - }) - .collect::, ::rlp::DecoderError>>()?; - - let req_id = id_guard.defuse(); - for handler in &self.handlers { - handler.on_transaction_proof(&Ctx { - peer: *peer, - io: io, - proto: self, - }, req_id, &raw_proof); - } - - Ok(()) + fn response(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> { + unimplemented!() } // Receive a set of transactions to relay. @@ -1286,7 +678,7 @@ impl LightProtocol { .map(|x| x.as_val::()) .collect::>()?; - debug!(target: "les", "Received {} transactions to relay from peer {}", txs.len(), peer); + debug!(target: "pip", "Received {} transactions to relay from peer {}", txs.len(), peer); for handler in &self.handlers { handler.on_transactions(&Ctx { @@ -1305,11 +697,11 @@ fn punish(peer: PeerId, io: &IoContext, e: Error) { match e.punishment() { Punishment::None => {} Punishment::Disconnect => { - debug!(target: "les", "Disconnecting peer {}: {}", peer, e); + debug!(target: "pip", "Disconnecting peer {}: {}", peer, e); io.disconnect_peer(peer) } Punishment::Disable => { - debug!(target: "les", "Disabling peer {}: {}", peer, e); + debug!(target: "pip", "Disabling peer {}: {}", peer, e); io.disable_peer(peer) } } @@ -1339,112 +731,7 @@ impl NetworkProtocolHandler for LightProtocol { match timer { TIMEOUT => self.timeout_check(io), TICK_TIMEOUT => self.tick_handlers(io), - _ => warn!(target: "les", "received timeout on unknown token {}", timer), - } - } -} - -// Helper for encoding the request to RLP with the given ID. -fn encode_request(req: &Request, req_id: usize) -> Vec { - match *req { - Request::Headers(ref headers) => { - let mut stream = RlpStream::new_list(2); - stream.append(&req_id).begin_list(4); - - match headers.start { - HashOrNumber::Hash(ref hash) => stream.append(hash), - HashOrNumber::Number(ref num) => stream.append(num), - }; - - stream - .append(&headers.max) - .append(&headers.skip) - .append(&headers.reverse); - - stream.out() - } - Request::Bodies(ref request) => { - let mut stream = RlpStream::new_list(2); - stream.append(&req_id).begin_list(request.block_hashes.len()); - - for hash in &request.block_hashes { - stream.append(hash); - } - - stream.out() - } - Request::Receipts(ref request) => { - let mut stream = RlpStream::new_list(2); - stream.append(&req_id).begin_list(request.block_hashes.len()); - - for hash in &request.block_hashes { - stream.append(hash); - } - - stream.out() - } - Request::StateProofs(ref request) => { - let mut stream = RlpStream::new_list(2); - stream.append(&req_id).begin_list(request.requests.len()); - - for proof_req in &request.requests { - stream.begin_list(4) - .append(&proof_req.block) - .append(&proof_req.key1); - - match proof_req.key2 { - Some(ref key2) => stream.append(key2), - None => stream.append_empty_data(), - }; - - stream.append(&proof_req.from_level); - } - - stream.out() - } - Request::Codes(ref request) => { - let mut stream = RlpStream::new_list(2); - stream.append(&req_id).begin_list(request.code_requests.len()); - - for code_req in &request.code_requests { - stream.begin_list(2) - .append(&code_req.block_hash) - .append(&code_req.account_key); - } - - stream.out() - } - Request::HeaderProofs(ref request) => { - let mut stream = RlpStream::new_list(2); - stream.append(&req_id).begin_list(request.requests.len()); - - for proof_req in &request.requests { - stream.begin_list(3) - .append(&proof_req.cht_number) - .append(&proof_req.block_number) - .append(&proof_req.from_level); - } - - stream.out() - } - Request::TransactionProof(ref request) => { - let mut stream = RlpStream::new_list(2); - stream.append(&req_id).begin_list(7) - .append(&request.at) - .append(&request.from); - - match request.action { - Action::Create => stream.append_empty_data(), - Action::Call(ref to) => stream.append(to), - }; - - stream - .append(&request.gas) - .append(&request.gas_price) - .append(&request.value) - .append(&request.data); - - stream.out() + _ => warn!(target: "pip", "received timeout on unknown token {}", timer), } } } diff --git a/ethcore/light/src/net/request_credits.rs b/ethcore/light/src/net/request_credits.rs index 97aa9b431..e3821e05a 100644 --- a/ethcore/light/src/net/request_credits.rs +++ b/ethcore/light/src/net/request_credits.rs @@ -26,7 +26,7 @@ //! Current default costs are picked completely arbitrarily, not based //! on any empirical timings or mathematical models. -use request; +use request::{self, Request}; use super::packet; use super::error::Error; @@ -34,10 +34,6 @@ use rlp::*; use util::U256; use time::{Duration, SteadyTime}; -/// A request cost specification. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Cost(pub U256, pub U256); - /// Credits value. /// /// Produced and recharged using `FlowParams`. @@ -81,93 +77,43 @@ impl Credits { /// A cost table, mapping requests to base and per-request costs. #[derive(Debug, Clone, PartialEq, Eq)] pub struct CostTable { - headers: Cost, // cost per header - bodies: Cost, - receipts: Cost, - state_proofs: Cost, - contract_codes: Cost, - header_proofs: Cost, - transaction_proof: Cost, // cost per gas. + base: U256, // cost per packet. + headers: U256, // cost per header + body: U256, + receipts: U256, + account: U256, + storage: U256, + code: U256, + header_proof: U256, + transaction_proof: U256, // cost per gas. } impl Default for CostTable { fn default() -> Self { // arbitrarily chosen constants. CostTable { - headers: Cost(100000.into(), 10000.into()), - bodies: Cost(150000.into(), 15000.into()), - receipts: Cost(50000.into(), 5000.into()), - state_proofs: Cost(250000.into(), 25000.into()), - contract_codes: Cost(200000.into(), 20000.into()), - header_proofs: Cost(150000.into(), 15000.into()), - transaction_proof: Cost(100000.into(), 2.into()), + base: 100000.into(), + headers: 10000.into(), + body: 15000.into(), + receipts: 5000.into(), + account: 25000.into(), + storage: 25000.into(), + code: 20000.into(), + header_proof: 15000.into(), + transaction_proof: 2.into(), } } } impl RlpEncodable for CostTable { fn rlp_append(&self, s: &mut RlpStream) { - fn append_cost(s: &mut RlpStream, msg_id: u8, cost: &Cost) { - s.begin_list(3) - .append(&msg_id) - .append(&cost.0) - .append(&cost.1); - } - - s.begin_list(7); - - append_cost(s, packet::GET_BLOCK_HEADERS, &self.headers); - append_cost(s, packet::GET_BLOCK_BODIES, &self.bodies); - append_cost(s, packet::GET_RECEIPTS, &self.receipts); - append_cost(s, packet::GET_PROOFS, &self.state_proofs); - append_cost(s, packet::GET_CONTRACT_CODES, &self.contract_codes); - append_cost(s, packet::GET_HEADER_PROOFS, &self.header_proofs); - append_cost(s, packet::GET_TRANSACTION_PROOF, &self.transaction_proof); + unimplemented!() } } impl RlpDecodable for CostTable { fn decode(decoder: &D) -> Result where D: Decoder { - let rlp = decoder.as_rlp(); - - let mut headers = None; - let mut bodies = None; - let mut receipts = None; - let mut state_proofs = None; - let mut contract_codes = None; - let mut header_proofs = None; - let mut transaction_proof = None; - - for row in rlp.iter() { - let msg_id: u8 = row.val_at(0)?; - let cost = { - let base = row.val_at(1)?; - let per = row.val_at(2)?; - - Cost(base, per) - }; - - match msg_id { - packet::GET_BLOCK_HEADERS => headers = Some(cost), - packet::GET_BLOCK_BODIES => bodies = Some(cost), - packet::GET_RECEIPTS => receipts = Some(cost), - packet::GET_PROOFS => state_proofs = Some(cost), - packet::GET_CONTRACT_CODES => contract_codes = Some(cost), - packet::GET_HEADER_PROOFS => header_proofs = Some(cost), - packet::GET_TRANSACTION_PROOF => transaction_proof = Some(cost), - _ => return Err(DecoderError::Custom("Unrecognized message in cost table")), - } - } - - Ok(CostTable { - headers: headers.ok_or(DecoderError::Custom("No headers cost specified"))?, - bodies: bodies.ok_or(DecoderError::Custom("No bodies cost specified"))?, - receipts: receipts.ok_or(DecoderError::Custom("No receipts cost specified"))?, - state_proofs: state_proofs.ok_or(DecoderError::Custom("No proofs cost specified"))?, - contract_codes: contract_codes.ok_or(DecoderError::Custom("No contract codes specified"))?, - header_proofs: header_proofs.ok_or(DecoderError::Custom("No header proofs cost specified"))?, - transaction_proof: transaction_proof.ok_or(DecoderError::Custom("No transaction proof gas cost specified"))?, - }) + unimplemented!() } } @@ -192,17 +138,19 @@ impl FlowParams { /// Create effectively infinite flow params. pub fn free() -> Self { - let free_cost = Cost(0.into(), 0.into()); + let free_cost: U256 = 0.into(); FlowParams { limit: (!0u64).into(), recharge: 1.into(), costs: CostTable { + base: free_cost.clone(), headers: free_cost.clone(), - bodies: free_cost.clone(), + body: free_cost.clone(), receipts: free_cost.clone(), - state_proofs: free_cost.clone(), - contract_codes: free_cost.clone(), - header_proofs: free_cost.clone(), + account: free_cost.clone(), + storage: free_cost.clone(), + code: free_cost.clone(), + header_proof: free_cost.clone(), transaction_proof: free_cost, } } @@ -219,56 +167,20 @@ impl FlowParams { /// Compute the actual cost of a request, given the kind of request /// and number of requests made. - pub fn compute_cost(&self, kind: request::Kind, amount: usize) -> U256 { - let cost = match kind { - request::Kind::Headers => &self.costs.headers, - request::Kind::Bodies => &self.costs.bodies, - request::Kind::Receipts => &self.costs.receipts, - request::Kind::StateProofs => &self.costs.state_proofs, - request::Kind::Codes => &self.costs.contract_codes, - request::Kind::HeaderProofs => &self.costs.header_proofs, - request::Kind::TransactionProof => &self.costs.transaction_proof, - }; - - let amount: U256 = amount.into(); - cost.0 + (amount * cost.1) - } - - /// Compute the maximum number of costs of a specific kind which can be made - /// with the given amount of credits - /// Saturates at `usize::max()`. This is not a problem in practice because - /// this amount of requests is already prohibitively large. - pub fn max_amount(&self, credits: &Credits, kind: request::Kind) -> usize { - use util::Uint; - use std::usize; - - let cost = match kind { - request::Kind::Headers => &self.costs.headers, - request::Kind::Bodies => &self.costs.bodies, - request::Kind::Receipts => &self.costs.receipts, - request::Kind::StateProofs => &self.costs.state_proofs, - request::Kind::Codes => &self.costs.contract_codes, - request::Kind::HeaderProofs => &self.costs.header_proofs, - request::Kind::TransactionProof => &self.costs.transaction_proof, - }; - - let start = credits.current(); - - if start <= cost.0 { - return 0; - } else if cost.1 == U256::zero() { - return usize::MAX; - } - - let max = (start - cost.0) / cost.1; - if max >= usize::MAX.into() { - usize::MAX - } else { - max.as_u64() as usize + pub fn compute_cost(&self, request: &Request) -> U256 { + match *request { + Request::Headers(ref req) => self.costs.headers * req.max.into(), + Request::HeaderProof(_) => self.costs.header_proof, + Request::Body(_) => self.costs.body, + Request::Receipts(_) => self.costs.receipts, + Request::Account(_) => self.costs.account, + Request::Storage(_) => self.costs.storage, + Request::Code(_) => self.costs.code, + Request::Execution(ref req) => self.costs.transaction_proof * req.gas, } } - /// Create initial credits.. + /// Create initial credits. pub fn create_credits(&self) -> Credits { Credits { estimate: self.limit, diff --git a/ethcore/light/src/net/request_set.rs b/ethcore/light/src/net/request_set.rs index e6d4068da..c329d780f 100644 --- a/ethcore/light/src/net/request_set.rs +++ b/ethcore/light/src/net/request_set.rs @@ -89,22 +89,7 @@ impl RequestSet { None => return false, }; - let kind = self.reqs.values() - .next() - .map(|r| r.kind()) - .expect("base time implies `reqs` non-empty; qed"); - - let kind_timeout = match kind { - request::Kind::Headers => timeout::HEADERS, - request::Kind::Bodies => timeout::BODIES, - request::Kind::Receipts => timeout::RECEIPTS, - request::Kind::StateProofs => timeout::PROOFS, - request::Kind::Codes => timeout::CONTRACT_CODES, - request::Kind::HeaderProofs => timeout::HEADER_PROOFS, - request::Kind::TransactionProof => timeout::TRANSACTION_PROOF, - }; - - base + Duration::milliseconds(kind_timeout) <= now + unimplemented!() } /// Collect all pending request ids. diff --git a/ethcore/light/src/provider.rs b/ethcore/light/src/provider.rs index 4a9a96999..be9239e4d 100644 --- a/ethcore/light/src/provider.rs +++ b/ethcore/light/src/provider.rs @@ -121,7 +121,7 @@ pub trait Provider: Send + Sync { /// Provide a proof-of-execution for the given transaction proof request. /// Returns a vector of all state items necessary to execute the transaction. - fn transaction_proof(&self, req: request::TransactionProof) -> Option>; + fn transaction_proof(&self, req: request::CompleteExecutionRequest) -> Option; } // Implementation of a light client data provider for a client. @@ -143,12 +143,12 @@ impl Provider for T { } fn block_body(&self, req: request::CompleteBodyRequest) -> Option { - BlockChainClient::block_body(self, id) + BlockChainClient::block_body(self, BlockId::Hash(req.hash)) .map(|body| ::request::BodyResponse { body: body }) } fn block_receipts(&self, req: request::CompleteReceiptsRequest) -> Option { - BlockChainClient::block_receipts(self, hash) + BlockChainClient::block_receipts(self, &req.hash) .map(|x| ::request::ReceiptsResponse { receipts: ::rlp::decode(&x) }) } @@ -165,7 +165,7 @@ impl Provider for T { } fn storage_proof(&self, req: request::CompleteStorageRequest) -> Option { - self.prove_account(req.address_hash, req.key_hash, BlockId::Hash(req.block_hash)).map(|(proof, item) | { + self.prove_storage(req.address_hash, req.key_hash, BlockId::Hash(req.block_hash)).map(|(proof, item) | { ::request::StorageResponse { proof: proof, value: item, @@ -173,7 +173,7 @@ impl Provider for T { }) } - fn contract_code(&self, req: request::ContractCode) -> Option { + fn contract_code(&self, req: request::CompleteCodeRequest) -> Option { self.state_data(&req.code_hash) .map(|code| ::request::CodeResponse { code: code }) } @@ -239,7 +239,7 @@ impl Provider for T { fn transaction_proof(&self, req: request::CompleteExecutionRequest) -> Option { use ethcore::transaction::Transaction; - let id = BlockId::Hash(req.at); + let id = BlockId::Hash(req.block_hash); let nonce = match self.nonce(&req.from, id.clone()) { Some(nonce) => nonce, None => return None, @@ -321,7 +321,7 @@ impl Provider for LightProvider { None } - fn transaction_proof(&self, _req: request::TransactionProof) -> Option> { + fn transaction_proof(&self, _req: request::CompleteExecutionRequest) -> Option { None } diff --git a/ethcore/light/src/request_builder.rs b/ethcore/light/src/request_builder.rs new file mode 100644 index 000000000..6233075bb --- /dev/null +++ b/ethcore/light/src/request_builder.rs @@ -0,0 +1,116 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Request chain builder utility. +//! Push requests with `push`. Back-references and data required to verify responses must be +//! supplied as well. + +use std::collections::{HashMap, VecDeque}; +use request::{ + IncompleteRequest, CompleteRequest, Request, + Field, OutputKind, Output, NoSuchOutput, Response, +}; + +/// 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 { + output_kinds: HashMap<(usize, usize), OutputKind>, + requests: Vec, +} + +impl RequestBuilder { + /// Attempt to push a request onto the request chain. Fails if the request + /// references a non-existant output of a prior request. + pub fn push(&mut self, request: Request) -> Result<(), NoSuchOutput> { + request.check_outputs(|req, idx, kind| { + match self.output_kinds.get(&(req, idx)) { + Some(k) if k == &kind => Ok(()), + _ => Err(NoSuchOutput), + } + })?; + let req_idx = self.requests.len(); + request.note_outputs(|idx, kind| { self.output_kinds.insert((req_idx, idx), kind); }); + self.requests.push(request); + Ok(()) + } + + /// Convert this into a "requests" object. + pub fn build(self) -> Requests { + Requests { + output_kinds: self.output_kinds, + outputs: HashMap::new(), + requests: self.requests, + offset: 0, + } + } +} + +/// Requests pending responses. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Requests { + output_kinds: HashMap<(usize, usize), OutputKind>, + outputs: HashMap<(usize, usize), Output>, + requests: Vec, + offset: usize, // offset for splitting. +} + +impl Requests { + /// For each request, produce responses for each. + /// 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 + { + let mut responses = Vec::new(); + let mut found_bad = false; + let offset = self.offset; + let output_kinds = self.output_kinds; + let mut outputs = self.outputs; + for (idx, req) in self.requests.into_iter().enumerate().map(|(idx, req)| (idx + offset, req)) { + let complete = req.fill(|req_idx, out_idx| outputs.get(&(req_idx, out_idx)).cloned().ok_or(NoSuchOutput)) + .expect("All outputs checked as invariant of `Requests` object; qed"); + + match responder(complete) { + Some(response) => { + response.fill_outputs(|out_idx, output| { + match output_kinds.get(&(idx, out_idx)) { + None => {}, + Some(out) => if out == &output.kind() { + outputs.insert((idx, out_idx), output); + } else { + // output kind doesn't match expected. + found_bad = true; + } + } + }); + + if found_bad { + return responses; + } + + responses.push(response); + } + None => return responses, + } + } + + responses + } + + /// Get access to the underlying slice of requests. + pub fn requests(&self) -> &[Request] { &self.requests } +} diff --git a/ethcore/light/src/types/request.rs b/ethcore/light/src/types/request.rs index 7ad16ea4d..a84a37435 100644 --- a/ethcore/light/src/types/request.rs +++ b/ethcore/light/src/types/request.rs @@ -19,7 +19,7 @@ use std::collections::HashMap; use ethcore::transaction::Action; -use rlp::{Encodable, Decodable, Decoder, DecoderError, RlpStream, Stream}; +use rlp::{Encodable, Decodable, Decoder, DecoderError, RlpStream, Stream, View}; use util::{Address, H256, U256, Uint}; // re-exports of request types. @@ -65,6 +65,7 @@ pub use self::execution::{ }; /// Error indicating a reference to a non-existent or wrongly-typed output. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct NoSuchOutput; /// An input to a request. @@ -77,7 +78,7 @@ pub enum Field { BackReference(usize, usize), } -impl From for Field { +impl From for Field { fn from(val: T) -> Self { Field::Scalar(val) } @@ -119,7 +120,8 @@ pub enum Output { } impl Output { - fn kind(&self) -> OutputKind { + /// Get the output kind. + pub fn kind(&self) -> OutputKind { match *self { Output::Hash(_) => OutputKind::Hash, Output::Number(_) => OutputKind::Number, @@ -158,6 +160,24 @@ impl From for HashOrNumber { } } +impl Decodable for HashOrNumber { + fn decode(decoder: &D) -> Result where D: Decoder { + let rlp = decoder.as_rlp(); + + rlp.val_at::(0).map(HashOrNumber::Hash) + .or_else(|_| rlp.val_at(0).map(HashOrNumber::Number)) + } +} + +impl Encodable for HashOrNumber { + fn rlp_append(&self, s: &mut RlpStream) { + match *self { + HashOrNumber::Hash(ref hash) => s.append(hash), + HashOrNumber::Number(ref num) => s.append(num), + }; + } +} + /// All request types, as they're sent over the network. #[derive(Debug, Clone, PartialEq, Eq)] pub enum Request { @@ -305,13 +325,20 @@ impl IncompleteRequest for Request { pub enum Kind { /// A request for headers. Headers = 0, + /// A request for a header proof. HeaderProof = 1, // TransactionIndex = 2, + /// A request for block receipts. Receipts = 3, + /// A request for a block body. Body = 4, + /// A request for an account + merkle proof. Account = 5, + /// A request for contract storage + merkle proof Storage = 6, + /// A request for contract. Code = 7, + /// A request for transaction execution + state proof. Execution = 8, } @@ -336,7 +363,7 @@ impl Decodable for Kind { impl Encodable for Kind { fn rlp_append(&self, s: &mut RlpStream) { - s.append(self as &u8); + s.append(&(*self as u8)); } } @@ -366,14 +393,14 @@ impl Response { /// Fill reusable outputs by writing them into the function. pub fn fill_outputs(&self, mut f: F) where F: FnMut(usize, Output) { match *self { - Response::Headers(res) => res.fill_outputs(f), - Response::HeaderProof(res) => res.fill_outputs(f), - Response::Receipts(res) => res.fill_outputs(f), - Response::Body(res) => res.fill_outputs(f), - Response::Account(res) => res.fill_outputs(f), - Response::Storage(res) => res.fill_outputs(f), - Response::Code(res) => res.fill_outputs(f), - Response::Execution(res) => res.fill_outputs(f), + Response::Headers(ref res) => res.fill_outputs(f), + Response::HeaderProof(ref res) => res.fill_outputs(f), + Response::Receipts(ref res) => res.fill_outputs(f), + Response::Body(ref res) => res.fill_outputs(f), + Response::Account(ref res) => res.fill_outputs(f), + Response::Storage(ref res) => res.fill_outputs(f), + Response::Code(ref res) => res.fill_outputs(f), + Response::Execution(ref res) => res.fill_outputs(f), } } @@ -386,7 +413,7 @@ impl Response { Response::Account(_) => Kind::Account, Response::Storage(_) => Kind::Storage, Response::Code(_) => Kind::Code, - Respnse::Execution(_) => Kind::Execution, + Response::Execution(_) => Kind::Execution, } } } @@ -403,7 +430,7 @@ impl Decodable for Response { Kind::Account => Ok(Response::Account(rlp.val_at(1)?)), Kind::Storage => Ok(Response::Storage(rlp.val_at(1)?)), Kind::Code => Ok(Response::Code(rlp.val_at(1)?)), - Kind::Execution=> Ok(Response::Execution(rlp.val_at(1)?)), + Kind::Execution => Ok(Response::Execution(rlp.val_at(1)?)), } } } @@ -427,6 +454,7 @@ impl Encodable for Response { /// A potentially incomplete request. pub trait IncompleteRequest: Sized { + /// The complete variant of this request. type Complete; /// Check prior outputs against the needed inputs. @@ -453,7 +481,6 @@ pub mod header { use super::{Field, HashOrNumber, NoSuchOutput, OutputKind, Output}; use ethcore::encoded; use rlp::{Encodable, Decodable, Decoder, DecoderError, RlpStream, Stream, View}; - use util::U256; /// Potentially incomplete headers request. #[derive(Debug, Clone, PartialEq, Eq)] @@ -461,9 +488,9 @@ pub mod header { /// Start block. pub start: Field, /// Skip between. - pub skip: U256, + pub skip: u64, /// Maximum to return. - pub max: U256, + pub max: u64, /// Whether to reverse from start. pub reverse: bool, } @@ -499,7 +526,7 @@ pub mod header { match self.start { Field::Scalar(_) => Ok(()), Field::BackReference(req, idx) => - f(req, idx, OutputKind::Hash).or_else(|| f(req, idx, OutputKind::Number)) + f(req, idx, OutputKind::Hash).or_else(|_| f(req, idx, OutputKind::Number)) } } @@ -532,9 +559,9 @@ pub mod header { /// Start block. pub start: HashOrNumber, /// Skip between. - pub skip: U256, + pub skip: u64, /// Maximum to return. - pub max: U256, + pub max: u64, /// Whether to reverse from start. pub reverse: bool, } @@ -695,7 +722,7 @@ pub mod block_receipts { use super::{Field, NoSuchOutput, OutputKind, Output}; use ethcore::receipt::Receipt; use rlp::{Encodable, Decodable, Decoder, DecoderError, RlpStream, Stream, View}; - use util::{Bytes, U256, H256}; + use util::H256; /// Potentially incomplete block receipts request. #[derive(Debug, Clone, PartialEq, Eq)] @@ -725,7 +752,7 @@ pub mod block_receipts { fn check_outputs(&self, mut f: F) -> Result<(), NoSuchOutput> where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> { - match self.num { + match self.hash { Field::Scalar(_) => Ok(()), Field::BackReference(req, idx) => f(req, idx, OutputKind::Hash), } @@ -791,7 +818,7 @@ pub mod block_body { use super::{Field, NoSuchOutput, OutputKind, Output}; use ethcore::encoded; use rlp::{Encodable, Decodable, Decoder, DecoderError, RlpStream, Stream, View}; - use util::{Bytes, U256, H256}; + use util::H256; /// Potentially incomplete block body request. #[derive(Debug, Clone, PartialEq, Eq)] @@ -821,7 +848,7 @@ pub mod block_body { fn check_outputs(&self, mut f: F) -> Result<(), NoSuchOutput> where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> { - match self.num { + match self.hash { Field::Scalar(_) => Ok(()), Field::BackReference(req, idx) => f(req, idx, OutputKind::Hash), } @@ -869,14 +896,14 @@ pub mod block_body { impl Decodable for Response { fn decode(decoder: &D) -> Result where D: Decoder { use ethcore::header::Header as FullHeader; - use ethcore::transaction::SignedTransaction; + use ethcore::transaction::UnverifiedTransaction; let rlp = decoder.as_rlp(); let body_rlp = rlp.at(0)?; // check body validity. let _: Vec = rlp.val_at(0)?; - let _: Vec = rlp.val_at(1)?; + let _: Vec = rlp.val_at(1)?; Ok(Response { body: encoded::Body::new(body_rlp.as_raw().to_owned()), @@ -895,7 +922,6 @@ pub mod block_body { /// A request for an account proof. pub mod account { use super::{Field, NoSuchOutput, OutputKind, Output}; - use ethcore::encoded; use rlp::{Encodable, Decodable, Decoder, DecoderError, RlpStream, Stream, View}; use util::{Bytes, U256, H256}; @@ -1028,7 +1054,7 @@ pub mod account { .append(&self.nonce) .append(&self.balance) .append(&self.code_hash) - .append(&self.storage_root) + .append(&self.storage_root); } } } @@ -1036,9 +1062,8 @@ pub mod account { /// A request for a storage proof. pub mod storage { use super::{Field, NoSuchOutput, OutputKind, Output}; - use ethcore::encoded; use rlp::{Encodable, Decodable, Decoder, DecoderError, RlpStream, Stream, View}; - use util::{Bytes, U256, H256}; + use util::{Bytes, H256}; /// Potentially incomplete request for an storage proof. #[derive(Debug, Clone, PartialEq, Eq)] @@ -1182,9 +1207,8 @@ pub mod storage { /// A request for contract code. pub mod contract_code { use super::{Field, NoSuchOutput, OutputKind, Output}; - use ethcore::encoded; use rlp::{Encodable, Decodable, Decoder, DecoderError, RlpStream, Stream, View}; - use util::{Bytes, U256, H256}; + use util::{Bytes, H256}; /// Potentially incomplete contract code request. #[derive(Debug, Clone, PartialEq, Eq)] @@ -1299,7 +1323,6 @@ pub mod contract_code { /// A request for proof of execution. pub mod execution { use super::{Field, NoSuchOutput, OutputKind, Output}; - use ethcore::encoded; use ethcore::transaction::Action; use rlp::{Encodable, Decodable, Decoder, DecoderError, RlpStream, Stream, View}; use util::{Bytes, Address, U256, H256, DBValue}; @@ -1328,7 +1351,7 @@ pub mod execution { let rlp = decoder.as_rlp(); Ok(Incomplete { block_hash: rlp.val_at(0)?, - address: rlp.val_at(1)?, + from: rlp.val_at(1)?, action: rlp.val_at(2)?, gas: rlp.val_at(3)?, gas_price: rlp.val_at(4)?, @@ -1344,7 +1367,7 @@ pub mod execution { .append(&self.block_hash) .append(&self.from); - match *self.action { + match self.action { Action::Create => s.append_empty_data(), Action::Call(ref addr) => s.append(addr), }; @@ -1432,7 +1455,7 @@ pub mod execution { let mut items = Vec::new(); for raw_item in rlp.at(0)?.iter() { let mut item = DBValue::new(); - item.append_slice(raw_item.data()); + item.append_slice(raw_item.data()?); items.push(item); } @@ -1444,7 +1467,7 @@ pub mod execution { impl Encodable for Response { fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(&self.items.len()); + s.begin_list(self.items.len()); for item in &self.items { s.append(&&**item);