les: flesh out event handler
This commit is contained in:
parent
b38d95328d
commit
cdc758368a
16
Cargo.lock
generated
16
Cargo.lock
generated
@ -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)",
|
||||
|
@ -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"
|
||||
|
@ -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" }
|
||||
|
@ -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<Receipt>]) { }
|
||||
/// 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<Bytes>]) { }
|
||||
/// 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<Bytes>]) { }
|
||||
}
|
||||
|
||||
// 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<Handler>) {
|
||||
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::<SignedTransaction>()).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) {
|
||||
|
@ -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 = []
|
||||
|
@ -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]
|
||||
|
Loading…
Reference in New Issue
Block a user