From 8c64400654d6657696ec9ea28510ceec21a140d9 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 13 Dec 2016 21:09:43 +0100 Subject: [PATCH] light: change sync module name --- sync/src/light/mod.rs | 111 ------------------- sync/src/light_sync/mod.rs | 213 +++++++++++++++++++++++++++++++++++++ 2 files changed, 213 insertions(+), 111 deletions(-) delete mode 100644 sync/src/light/mod.rs create mode 100644 sync/src/light_sync/mod.rs diff --git a/sync/src/light/mod.rs b/sync/src/light/mod.rs deleted file mode 100644 index 5806dd6d2..000000000 --- a/sync/src/light/mod.rs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2015, 2016 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 . - -//! Light client synchronization. -//! -//! This will synchronize the header chain using LES messages. -//! Dataflow is largely one-directional as headers are pushed into -//! the light client queue for import. Where possible, they are batched -//! in groups. -//! -//! This is written assuming that the client and sync service are running -//! in the same binary; unlike a full node - -use std::collections::{BinaryHeap, HashMap}; -use std::sync::Arc; - -use light::Client; -use light::net::{Handler, EventContext, Capabilities}; -use light::request; -use network: PeerId; -use util::{U256, H256}; - -struct Peer { - head_td: U256, - head_hash: H256, - head_num: u64, -} - -// The block downloader. -// This is instantiated with a starting and a target block -// and produces a priority queue of requests for headers which should be -// fulfilled. -struct Downloader { - start: u64, - target: (H256, u64), - requests: BinaryHeap, -} - -impl Downloader { - // create a new downloader. - fn new(start: u64, target: (H256, u64)) -> Self { - Downloader { - start: start, - target: target, - requests: BinaryHeap::new(), - } - } -} - -/// Light client synchronization manager. See module docs for more details. -pub struct LightSync { - best_seen: Mutex>, // best seen block on the network. - peers: RwLock>, // peers which are relevant to synchronization. - client: Arc, - downloader: Downloader, - assigned_requests: HashMap, -} - -impl Handler for LightSync { - fn on_connect(&self, ctx: &EventContext, status: &Status, capabilities: &Capabilities) { - if !capabilities.serve_headers { - trace!(target: "sync", "Ignoring irrelevant peer: {}", ctx.peer()); - return; - } - - { - let mut best = self.best_seen.lock(); - if best_seen.as_ref().map_or(true, |ref best| status.head_td > best.1) { - *best_seen = Some(status.head_hash, status.head_td) - } - } - - self.peers.write().insert(ctx.peer(), Peer { - head_td: status.head_td, - head_hash: status.head_hash, - head_num: status.head_num, - }); - } -} - -impl LightSync { - fn assign_request(&self, p-eer: PeerId); -} - -// public API -impl LightSync { - /// Create a new instance of `LightSync`. - /// - /// This won't do anything until registered as a handler - /// so it can receive - pub fn new(client: Arc) -> Self { - LightSync { - best_seen: Mutex::new(None), - peers: HashMap::new(), - client: client, - } - } -} diff --git a/sync/src/light_sync/mod.rs b/sync/src/light_sync/mod.rs new file mode 100644 index 000000000..d49b0dc17 --- /dev/null +++ b/sync/src/light_sync/mod.rs @@ -0,0 +1,213 @@ +// Copyright 2015, 2016 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 . + +//! Light client synchronization. +//! +//! This will synchronize the header chain using LES messages. +//! Dataflow is largely one-directional as headers are pushed into +//! the light client queue for import. Where possible, they are batched +//! in groups. +//! +//! This is written assuming that the client and sync service are running +//! in the same binary; unlike a full node + +use std::collections::{BinaryHeap, HashMap}; +use std::fmt; +use std::sync::Arc; + +use light::client::{Client, BlockDescriptor}; +use light::net::{Error as NetError, Handler, EventContext, Capabilities, ReqId}; +use light::request; +use network::PeerId; +use rlp::{UntrustedRlp, View}; +use util::{U256, H256}; + +// How many headers we request at a time when searching for best +// common ancestor with peer. +const UNCONFIRMED_SEARCH_SIZE: u64 = 128; + +#[derive(Debug)] +enum Error { + // Peer is useless for now. + UselessPeer, + // Peer returned a malformed response. + MalformedResponse, + // Peer returned known bad block. + BadBlock, + // Peer had a prehistoric common ancestor. + PrehistoricAncestor, + // Protocol-level error. + ProtocolLevel(NetError), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Error::UselessPeer => write!(f, "Peer is useless"), + Error::MalformedResponse => write!(f, "Response malformed"), + Error::BadBlock => write!(f, "Block known to be bad"), + Error::PrehistoricAncestor => write!(f, "Common ancestor is prehistoric"), + Error::ProtocolLevel(ref err) => write!(f, "Protocol level error: {}", err), + } + } +} + +/// Peer chain info. +#[derive(Clone)] +struct ChainInfo { + head_td: U256, + head_hash: H256, + head_num: u64, +} + +/// A peer we haven't found a common ancestor for yet. +struct UnconfirmedPeer { + chain_info: ChainInfo, + last_batched: u64, + req_id: ReqId, +} + +impl UnconfirmedPeer { + /// Create an unconfirmed peer. Returns `None` if we cannot make a + /// common ancestors request for some reason. The event context provided + /// should be associated with this peer. + fn create(ctx: &EventContext, chain_info: ChainInfo, best_num: u64) -> Result { + let this = ctx.peer(); + + if ctx.max_requests(this, request::Kind::Headers) < UNCONFIRMED_SEARCH_SIZE { + return Err(Error::UselessPeer); // a peer which allows this few header reqs isn't useful anyway. + } + + let req_id = try!(ctx.request_from(this, request::Request::Headers(request::Headers { + start: best_num.into(), + max: ::std::cmp::min(best_num, UNCONFIRMED_SEARCH_SIZE), + skip: 0, + reverse: true, + }))); + + Ok(UnconfirmedPeer { + chain_info: chain_info, + last_batched: best_num, + req_id: ReqId, + }) + } + + /// Feed in the result of the headers query. If an error occurs, the request + /// is malformed. If a common (hash, number) pair is returned then this is + /// the common ancestor. If not, then another request for headers has been + /// dispatched. + fn check_batch(&mut self, ctx: &EventContext, client: &Client, headers: &[Bytes]) -> Result, Error> { + use ethcore::block_status::BlockStatus; + + let mut cur_num = self.last_batched; + let chain_info = client.chain_info(); + for raw_header in headers { + let header: Header = try!(UntrustedRlp::new(&raw_header).as_val().map_err(|_| Error::MalformedResponse)); + if header.number() != cur_num { return Err(Error::MalformedResponse) } + + if chain_info.first_block_number.map_or(false, |f| header.number() < f) { + return Err(Error::PrehistoricAncestor); + } + + let hash = header.hash(); + + match client.status(&hash) { + BlockStatus::InChain => return Ok(Some(hash)), + BlockStatus::Bad => return Err(Error::BadBlock), + BlockStatus::Unknown | BlockStatus::Queued => {}, + } + + cur_num -= 1; + } + let this = ctx.peer(); + + if cur_num == 0 { + trace!(target: "sync", "Peer {}: genesis as common ancestor", this); + return Ok(Some(chain_info.genesis_hash)); + } + + // nothing found, nothing prehistoric. + // send the next request. + let req_id = try!(ctx.request_from(this, request::Request::Headers(request::Headers { + start: cur_num, + max: ::std::cmp::min(cur_num, UNCONFIRMED_SEARCH_SIZE), + skip: 0, + reverse: true, + }))); + + self.req_id = req_id; + } +} + +/// Connected peers as state machines. +/// +/// On connection, we'll search for a common ancestor to their chain. +/// Once that's found, we can sync to this peer. +enum Peer { + // Searching for a common ancestor. + SearchCommon(Mutex), + // A peer we can sync to. + SyncTo(ChainInfo), +} + +/// Light client synchronization manager. See module docs for more details. +pub struct LightSync { + best_seen: Mutex>, // best seen block on the network. + peers: RwLock>, // peers which are relevant to synchronization. + client: Arc, + downloader: Downloader, + assigned_requests: HashMap, +} + +impl Handler for LightSync { + fn on_connect(&self, ctx: &EventContext, status: &Status, capabilities: &Capabilities) { + if !capabilities.serve_headers || status.head_num <= self.client.best_block().number { + trace!(target: "sync", "Ignoring irrelevant peer: {}", ctx.peer()); + return; + } + + let chain_info = ChainInfo { + head_td: status.head_td, + head_hash: status.head_hash, + head_num: status.head_num, + }; + let our_best = self.client.chain_info().best_block_number; + let unconfirmed = match UnconfirmedPeer::create(ctx, chain_info, our_best) { + Ok(unconfirmed) => unconfirmed, + Err(e) => { + trace!(target: "sync", "Failed to create unconfirmed peer: {}", e); + return; + } + }; + + self.peers.write().insert(ctx.peer(), Mutex::new(unconfirmed)); + } +} + +// public API +impl LightSync { + /// Create a new instance of `LightSync`. + /// + /// This won't do anything until registered as a handler + /// so it can receive + pub fn new(client: Arc) -> Self { + LightSync { + best_seen: Mutex::new(None), + peers: HashMap::new(), + client: client, + } + } +}