diff --git a/Cargo.lock b/Cargo.lock index 7dc6f7bcb..b00701f32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -456,6 +456,21 @@ dependencies = [ "semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ethcore-light" +version = "1.5.0" +dependencies = [ + "ethcore 1.5.0", + "ethcore-io 1.5.0", + "ethcore-ipc 1.4.0", + "ethcore-ipc-codegen 1.4.0", + "ethcore-network 1.5.0", + "ethcore-util 1.5.0", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rlp 0.1.0", + "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ethcore-logger" version = "1.5.0" @@ -664,6 +679,7 @@ dependencies = [ "ethcore-ipc 1.4.0", "ethcore-ipc-codegen 1.4.0", "ethcore-ipc-nano 1.4.0", + "ethcore-light 1.5.0", "ethcore-network 1.5.0", "ethcore-util 1.5.0", "heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 9d6848b6a..4730cef0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,7 +55,7 @@ winapi = "0.2" daemonize = "0.2" [features] -default = ["ui-precompiled"] +default = ["ui-precompiled", "light"] ui = [ "dapps", @@ -80,7 +80,7 @@ ethstore-cli = ["ethcore/ethstore-cli"] evm-debug = ["ethcore/evm-debug"] evm-debug-tests = ["ethcore/evm-debug-tests"] slow-blocks = ["ethcore/slow-blocks"] -light = ["ethcore/light"] +light = ["ethcore/light", "ethsync/light"] [[bin]] path = "parity/main.rs" diff --git a/ethcore/light/Cargo.toml b/ethcore/light/Cargo.toml index 74400d7ab..9440bbf88 100644 --- a/ethcore/light/Cargo.toml +++ b/ethcore/light/Cargo.toml @@ -12,7 +12,7 @@ build = "build.rs" [dependencies] log = "0.3" -ethcore = { path = ".." } +ethcore = { path = "..", features = ["light"] } ethcore-util = { path = "../../util" } ethcore-network = { path = "../../util/network" } ethcore-io = { path = "../../util/io" } diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index a1b3b30b0..e729b4361 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -20,11 +20,13 @@ //! See https://github.com/ethcore/parity/wiki/Light-Ethereum-Subprotocol-(LES) use ethcore::transaction::SignedTransaction; +use ethcore::receipt::Receipt; + use io::TimerToken; use network::{NetworkProtocolHandler, NetworkContext, NetworkError, PeerId}; use rlp::{RlpStream, Stream, UntrustedRlp, View}; use util::hash::H256; -use util::{Mutex, RwLock, U256}; +use util::{Bytes, Mutex, RwLock, U256}; use time::SteadyTime; use std::collections::{HashMap, HashSet}; @@ -134,16 +136,50 @@ impl Peer { } } +/// Context for a network event. +pub struct EventContext<'a> { + /// Protocol implementation. + pub proto: &'a LightProtocol, + /// Network context to enable immediate response to + /// events. + pub io: &'a NetworkContext<'a>, + /// Relevant peer for event. + pub peer: PeerId, +} + /// An LES event handler. +/// +/// Each handler function takes a context which describes the relevant peer +/// and gives references to the IO layer and protocol structure so new messages +/// can be dispatched immediately. +/// +/// Request responses are not guaranteed to be complete or valid, but passed IDs will be correct. +/// Response handlers are not given a copy of the original request; it is assumed +/// that relevant data will be stored by interested handlers. pub trait Handler: Send + Sync { /// Called when a peer connects. - fn on_connect(&self, _id: PeerId, _status: &Status, _capabilities: &Capabilities) { } - /// Called when a peer disconnects - fn on_disconnect(&self, _id: PeerId) { } + fn on_connect(&self, _ctx: EventContext, _status: &Status, _capabilities: &Capabilities) { } + /// Called when a peer disconnects, with a list of unfulfilled request IDs as + /// of yet. + fn on_disconnect(&self, _ctx: EventContext, _unfulfilled: &[ReqId]) { } /// Called when a peer makes an announcement. - fn on_announcement(&self, _id: PeerId, _announcement: &Announcement) { } + fn on_announcement(&self, _ctx: EventContext, _announcement: &Announcement) { } /// Called when a peer requests relay of some transactions. - fn on_transactions(&self, _id: PeerId, _relay: &[SignedTransaction]) { } + fn on_transactions(&self, _ctx: EventContext, _relay: &[SignedTransaction]) { } + /// 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 is 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 is a series of trie + /// nodes is ascending order by distance from the root. + fn on_header_proofs(&self, _ctx: EventContext, _req_id: ReqId, _proofs: &[Vec]) { } } // a request and the time it was made. @@ -290,7 +326,8 @@ impl LightProtocol { /// Add an event handler. /// Ownership will be transferred to the protocol structure, /// and the handler will be kept alive as long as it is. - /// These are intended to be added at the beginning of the + /// These are intended to be added when the protocol structure + /// is initialized as a means of customizing its behavior. pub fn add_handler(&mut self, handler: Box) { self.handlers.push(handler); } @@ -313,12 +350,23 @@ impl LightProtocol { } // called when a peer disconnects. - fn on_disconnect(&self, peer: PeerId) { - // TODO: reassign all requests assigned to this peer. + fn on_disconnect(&self, peer: PeerId, io: &NetworkContext) { self.pending_peers.write().remove(&peer); - if self.peers.write().remove(&peer).is_some() { + if let Some(peer_info) = self.peers.write().remove(&peer) { + let unfulfilled: Vec<_> = peer_info.into_inner().current_asking.into_iter().map(ReqId).collect(); + { + let mut pending = self.pending_requests.write(); + for &ReqId(ref inner) in &unfulfilled { + pending.remove(inner); + } + } + for handler in &self.handlers { - handler.on_disconnect(peer) + handler.on_disconnect(EventContext { + peer: peer, + io: io, + proto: self, + }, &unfulfilled) } } } @@ -350,7 +398,7 @@ impl LightProtocol { } // Handle status message from peer. - fn status(&self, peer: &PeerId, data: UntrustedRlp) -> Result<(), Error> { + fn status(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { let pending = match self.pending_peers.write().remove(peer) { Some(pending) => pending, None => { @@ -377,45 +425,56 @@ impl LightProtocol { })); for handler in &self.handlers { - handler.on_connect(*peer, &status, &capabilities) + handler.on_connect(EventContext { + peer: *peer, + io: io, + proto: self, + }, &status, &capabilities) } Ok(()) } // Handle an announcement. - fn announcement(&self, peer: &PeerId, data: UntrustedRlp) -> Result<(), Error> { + fn announcement(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { if !self.peers.read().contains_key(peer) { debug!(target: "les", "Ignoring announcement from unknown peer"); return Ok(()) } let announcement = try!(status::parse_announcement(data)); - let peers = self.peers.read(); - let peer_info = match peers.get(peer) { - Some(info) => info, - None => return Ok(()), - }; - - let mut peer_info = peer_info.lock(); - - // update status. + // scope to ensure locks are dropped before moving into handler-space. { - // TODO: punish peer if they've moved backwards. - let status = &mut peer_info.status; - let last_head = status.head_hash; - status.head_hash = announcement.head_hash; - status.head_td = announcement.head_td; - status.head_num = announcement.head_num; - status.last_head = Some((last_head, announcement.reorg_depth)); + let peers = self.peers.read(); + let peer_info = match peers.get(peer) { + Some(info) => info, + None => return Ok(()), + }; + + let mut peer_info = peer_info.lock(); + + // update status. + { + // TODO: punish peer if they've moved backwards. + let status = &mut peer_info.status; + let last_head = status.head_hash; + status.head_hash = announcement.head_hash; + status.head_td = announcement.head_td; + status.head_num = announcement.head_num; + status.last_head = Some((last_head, announcement.reorg_depth)); + } + + // update capabilities. + peer_info.capabilities.update_from(&announcement); } - // update capabilities. - peer_info.capabilities.update_from(&announcement); - for handler in &self.handlers { - handler.on_announcement(*peer, &announcement); + handler.on_announcement(EventContext { + peer: *peer, + io: io, + proto: self, + }, &announcement); } Ok(()) @@ -736,7 +795,7 @@ impl LightProtocol { } // Receive a set of transactions to relay. - fn relay_transactions(&self, peer: &PeerId, data: UntrustedRlp) -> Result<(), Error> { + fn relay_transactions(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { const MAX_TRANSACTIONS: usize = 256; let txs: Vec<_> = try!(data.iter().take(MAX_TRANSACTIONS).map(|x| x.as_val::()).collect()); @@ -744,7 +803,11 @@ impl LightProtocol { debug!(target: "les", "Received {} transactions to relay from peer {}", txs.len(), peer); for handler in &self.handlers { - handler.on_transactions(*peer, &txs); + handler.on_transactions(EventContext { + peer: *peer, + io: io, + proto: self, + }, &txs); } Ok(()) @@ -761,8 +824,8 @@ impl NetworkProtocolHandler for LightProtocol { // handle the packet let res = match packet_id { - packet::STATUS => self.status(peer, rlp), - packet::ANNOUNCE => self.announcement(peer, rlp), + 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), @@ -782,7 +845,7 @@ impl NetworkProtocolHandler for LightProtocol { packet::GET_HEADER_PROOFS => self.get_header_proofs(peer, io, rlp), packet::HEADER_PROOFS => self.header_proofs(peer, io, rlp), - packet::SEND_TRANSACTIONS => self.relay_transactions(peer, rlp), + packet::SEND_TRANSACTIONS => self.relay_transactions(peer, io, rlp), other => { Err(Error::UnrecognizedPacket(other)) @@ -809,8 +872,8 @@ impl NetworkProtocolHandler for LightProtocol { self.on_connect(peer, io); } - fn disconnected(&self, _io: &NetworkContext, peer: &PeerId) { - self.on_disconnect(*peer); + fn disconnected(&self, io: &NetworkContext, peer: &PeerId) { + self.on_disconnect(*peer, io); } fn timeout(&self, _io: &NetworkContext, timer: TimerToken) { diff --git a/sync/Cargo.toml b/sync/Cargo.toml index 738f5f55c..df0f4840d 100644 --- a/sync/Cargo.toml +++ b/sync/Cargo.toml @@ -15,6 +15,7 @@ ethcore-ipc-codegen = { path = "../ipc/codegen" } ethcore-util = { path = "../util" } ethcore-network = { path = "../util/network" } ethcore-io = { path = "../util/io" } +ethcore-light = { path = "../ethcore/light", optional = true } ethcore = { path = "../ethcore" } rlp = { path = "../util/rlp" } clippy = { version = "0.0.103", optional = true} @@ -31,4 +32,5 @@ parking_lot = "0.3" [features] default = ["ipc"] dev = ["clippy", "ethcore/dev", "ethcore-util/dev"] +light = ["ethcore-light"] ipc = [] diff --git a/sync/src/lib.rs b/sync/src/lib.rs index 2061e4e3a..7a4f22fd7 100644 --- a/sync/src/lib.rs +++ b/sync/src/lib.rs @@ -37,6 +37,10 @@ extern crate semver; extern crate parking_lot; extern crate rlp; +#[cfg(feature = "light")] +extern crate ethcore_light as light; + + #[macro_use] extern crate log; #[macro_use]