2016-12-14 22:57:30 +01:00
|
|
|
// 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 <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
//! Header download state machine.
|
|
|
|
|
2016-12-15 13:05:38 +01:00
|
|
|
use std::cmp::Ordering;
|
2016-12-15 15:50:36 +01:00
|
|
|
use std::collections::{BinaryHeap, HashMap, HashSet, VecDeque};
|
2017-01-11 14:39:03 +01:00
|
|
|
use std::fmt;
|
2016-12-14 22:57:30 +01:00
|
|
|
|
|
|
|
use ethcore::header::Header;
|
|
|
|
|
2016-12-15 21:51:08 +01:00
|
|
|
use light::net::ReqId;
|
2016-12-14 22:57:30 +01:00
|
|
|
use light::request::Headers as HeadersRequest;
|
|
|
|
|
|
|
|
use network::PeerId;
|
2016-12-15 21:51:08 +01:00
|
|
|
use util::{Bytes, H256};
|
2016-12-14 22:57:30 +01:00
|
|
|
|
2016-12-15 13:05:38 +01:00
|
|
|
use super::response;
|
2016-12-14 22:57:30 +01:00
|
|
|
|
|
|
|
// number of attempts to make to get a full scaffold for a sync round.
|
|
|
|
const SCAFFOLD_ATTEMPTS: usize = 3;
|
|
|
|
|
2016-12-15 15:50:36 +01:00
|
|
|
/// Context for a headers response.
|
|
|
|
pub trait ResponseContext {
|
|
|
|
/// Get the peer who sent this response.
|
|
|
|
fn responder(&self) -> PeerId;
|
|
|
|
/// Get the request ID this response corresponds to.
|
2016-12-15 21:51:08 +01:00
|
|
|
fn req_id(&self) -> &ReqId;
|
2016-12-15 15:50:36 +01:00
|
|
|
/// Get the (unverified) response data.
|
|
|
|
fn data(&self) -> &[Bytes];
|
|
|
|
/// Punish the responder.
|
|
|
|
fn punish_responder(&self);
|
|
|
|
}
|
|
|
|
|
2016-12-14 23:25:51 +01:00
|
|
|
/// Reasons for sync round abort.
|
2016-12-15 15:50:36 +01:00
|
|
|
#[derive(Debug, Clone)]
|
2016-12-14 23:25:51 +01:00
|
|
|
pub enum AbortReason {
|
2016-12-15 15:50:36 +01:00
|
|
|
/// Bad sparse header chain along with a list of peers who contributed to it.
|
|
|
|
BadScaffold(Vec<PeerId>),
|
2016-12-14 23:25:51 +01:00
|
|
|
/// No incoming data.
|
|
|
|
NoResponses,
|
2017-01-20 12:41:59 +01:00
|
|
|
/// Sync rounds completed.
|
|
|
|
TargetReached,
|
2016-12-14 23:25:51 +01:00
|
|
|
}
|
|
|
|
|
2016-12-15 13:05:38 +01:00
|
|
|
// A request for headers with a known starting header hash.
|
2016-12-14 22:57:30 +01:00
|
|
|
// and a known parent hash for the last block.
|
2016-12-15 13:05:38 +01:00
|
|
|
#[derive(PartialEq, Eq)]
|
|
|
|
struct SubchainRequest {
|
|
|
|
subchain_parent: (u64, H256),
|
|
|
|
headers_request: HeadersRequest,
|
|
|
|
subchain_end: (u64, H256),
|
|
|
|
downloaded: VecDeque<Header>,
|
2016-12-14 22:57:30 +01:00
|
|
|
}
|
|
|
|
|
2016-12-15 13:05:38 +01:00
|
|
|
// ordered by subchain parent number so pending requests towards the
|
|
|
|
// front of the round are dispatched first.
|
|
|
|
impl PartialOrd for SubchainRequest {
|
|
|
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
2016-12-16 23:21:51 +01:00
|
|
|
self.subchain_parent.0
|
|
|
|
.partial_cmp(&other.subchain_parent.0)
|
|
|
|
.map(Ordering::reverse)
|
2016-12-15 13:05:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Ord for SubchainRequest {
|
|
|
|
fn cmp(&self, other: &Self) -> Ordering {
|
2016-12-16 23:21:51 +01:00
|
|
|
self.subchain_parent.0.cmp(&other.subchain_parent.0).reverse()
|
2016-12-15 13:05:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Manages downloading of interior blocks of a sparse header chain.
|
2016-12-14 22:57:30 +01:00
|
|
|
pub struct Fetcher {
|
2016-12-15 13:05:38 +01:00
|
|
|
sparse: VecDeque<Header>, // sparse header chain.
|
|
|
|
requests: BinaryHeap<SubchainRequest>,
|
|
|
|
complete_requests: HashMap<H256, SubchainRequest>,
|
|
|
|
pending: HashMap<ReqId, SubchainRequest>,
|
2016-12-15 15:50:36 +01:00
|
|
|
scaffold_contributors: Vec<PeerId>,
|
2016-12-15 17:33:25 +01:00
|
|
|
ready: VecDeque<Header>,
|
|
|
|
end: (u64, H256),
|
2017-01-20 12:41:59 +01:00
|
|
|
target: (u64, H256),
|
2016-12-14 22:57:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Fetcher {
|
2016-12-15 15:50:36 +01:00
|
|
|
// Produce a new fetcher given a sparse headerchain, in ascending order along
|
|
|
|
// with a list of peers who helped produce the chain.
|
2016-12-15 17:33:25 +01:00
|
|
|
// The headers must be valid RLP at this point and must have a consistent
|
|
|
|
// non-zero gap between them. Will abort the round if found wrong.
|
2017-01-20 12:41:59 +01:00
|
|
|
fn new(sparse_headers: Vec<Header>, contributors: Vec<PeerId>, target: (u64, H256)) -> SyncRound {
|
2016-12-15 13:05:38 +01:00
|
|
|
let mut requests = BinaryHeap::with_capacity(sparse_headers.len() - 1);
|
|
|
|
|
2016-12-14 22:57:30 +01:00
|
|
|
for pair in sparse_headers.windows(2) {
|
|
|
|
let low_rung = &pair[0];
|
|
|
|
let high_rung = &pair[1];
|
|
|
|
|
|
|
|
let diff = high_rung.number() - low_rung.number();
|
2016-12-15 13:05:38 +01:00
|
|
|
|
|
|
|
// should never happen as long as we verify the gaps
|
|
|
|
// gotten from SyncRound::Start
|
|
|
|
if diff < 2 { continue }
|
2016-12-14 22:57:30 +01:00
|
|
|
|
|
|
|
let needed_headers = HeadersRequest {
|
|
|
|
start: high_rung.parent_hash().clone().into(),
|
|
|
|
max: diff as usize - 1,
|
|
|
|
skip: 0,
|
|
|
|
reverse: true,
|
|
|
|
};
|
|
|
|
|
2016-12-15 13:05:38 +01:00
|
|
|
requests.push(SubchainRequest {
|
|
|
|
headers_request: needed_headers,
|
|
|
|
subchain_end: (high_rung.number() - 1, *high_rung.parent_hash()),
|
|
|
|
downloaded: VecDeque::new(),
|
|
|
|
subchain_parent: (low_rung.number(), low_rung.hash()),
|
2016-12-14 22:57:30 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-12-15 17:33:25 +01:00
|
|
|
let end = match sparse_headers.last().map(|h| (h.number(), h.hash())) {
|
|
|
|
Some(end) => end,
|
2017-01-11 14:39:03 +01:00
|
|
|
None => return SyncRound::abort(AbortReason::BadScaffold(contributors), VecDeque::new()),
|
2016-12-15 17:33:25 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
SyncRound::Fetch(Fetcher {
|
2016-12-15 13:05:38 +01:00
|
|
|
sparse: sparse_headers.into(),
|
2016-12-14 22:57:30 +01:00
|
|
|
requests: requests,
|
2016-12-15 13:05:38 +01:00
|
|
|
complete_requests: HashMap::new(),
|
2016-12-14 22:57:30 +01:00
|
|
|
pending: HashMap::new(),
|
2016-12-15 15:50:36 +01:00
|
|
|
scaffold_contributors: contributors,
|
2016-12-15 17:33:25 +01:00
|
|
|
ready: VecDeque::new(),
|
|
|
|
end: end,
|
2017-01-20 12:41:59 +01:00
|
|
|
target: target,
|
2016-12-15 17:33:25 +01:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// collect complete requests and their subchain from the sparse header chain
|
|
|
|
// into the ready set in order.
|
|
|
|
fn collect_ready(&mut self) {
|
|
|
|
loop {
|
|
|
|
let start_hash = match self.sparse.front() {
|
|
|
|
Some(first) => first.hash(),
|
|
|
|
None => break,
|
|
|
|
};
|
|
|
|
|
|
|
|
match self.complete_requests.remove(&start_hash) {
|
|
|
|
None => break,
|
|
|
|
Some(complete_req) => {
|
|
|
|
self.ready.push_back(self.sparse.pop_front().expect("first known to exist; qed"));
|
|
|
|
self.ready.extend(complete_req.downloaded);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// frames are between two sparse headers and keyed by subchain parent, so the last
|
|
|
|
// remaining will be the last header.
|
|
|
|
if self.sparse.len() == 1 {
|
|
|
|
self.ready.push_back(self.sparse.pop_back().expect("sparse known to have one entry; qed"))
|
2016-12-14 22:57:30 +01:00
|
|
|
}
|
2017-01-20 12:41:59 +01:00
|
|
|
|
|
|
|
trace!(target: "sync", "{} headers ready to drain", self.ready.len());
|
2016-12-14 22:57:30 +01:00
|
|
|
}
|
|
|
|
|
2016-12-15 15:50:36 +01:00
|
|
|
fn process_response<R: ResponseContext>(mut self, ctx: &R) -> SyncRound {
|
2016-12-15 21:51:08 +01:00
|
|
|
let mut request = match self.pending.remove(ctx.req_id()) {
|
2016-12-15 13:05:38 +01:00
|
|
|
Some(request) => request,
|
2016-12-15 15:50:36 +01:00
|
|
|
None => return SyncRound::Fetch(self),
|
2016-12-15 13:05:38 +01:00
|
|
|
};
|
|
|
|
|
2017-01-20 12:41:59 +01:00
|
|
|
trace!(target: "sync", "Received response for subchain ({} -> {})",
|
|
|
|
request.subchain_parent.0 + 1, request.subchain_end.0);
|
|
|
|
|
2016-12-15 15:50:36 +01:00
|
|
|
let headers = ctx.data();
|
|
|
|
|
2016-12-15 13:05:38 +01:00
|
|
|
if headers.len() == 0 {
|
2016-12-15 15:50:36 +01:00
|
|
|
trace!(target: "sync", "Punishing peer {} for empty response", ctx.responder());
|
|
|
|
ctx.punish_responder();
|
2017-01-20 12:41:59 +01:00
|
|
|
|
|
|
|
self.requests.push(request);
|
2016-12-15 15:50:36 +01:00
|
|
|
return SyncRound::Fetch(self);
|
2016-12-15 13:05:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
match response::decode_and_verify(headers, &request.headers_request) {
|
|
|
|
Err(e) => {
|
2016-12-15 15:50:36 +01:00
|
|
|
trace!(target: "sync", "Punishing peer {} for invalid response ({})", ctx.responder(), e);
|
|
|
|
ctx.punish_responder();
|
|
|
|
|
|
|
|
// TODO: track number of attempts per request,
|
|
|
|
// abort if failure rate too high.
|
2016-12-15 13:05:38 +01:00
|
|
|
self.requests.push(request);
|
2016-12-15 15:50:36 +01:00
|
|
|
SyncRound::Fetch(self)
|
2016-12-15 13:05:38 +01:00
|
|
|
}
|
|
|
|
Ok(headers) => {
|
|
|
|
let mut parent_hash = None;
|
|
|
|
for header in headers {
|
|
|
|
if parent_hash.as_ref().map_or(false, |h| h != &header.hash()) {
|
2016-12-15 15:50:36 +01:00
|
|
|
trace!(target: "sync", "Punishing peer {} for parent mismatch", ctx.responder());
|
|
|
|
ctx.punish_responder();
|
|
|
|
|
2016-12-15 13:05:38 +01:00
|
|
|
self.requests.push(request);
|
2016-12-15 15:50:36 +01:00
|
|
|
return SyncRound::Fetch(self);
|
2016-12-15 13:05:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// incrementally update the frame request as we go so we can
|
|
|
|
// return at any time in the loop.
|
|
|
|
parent_hash = Some(header.parent_hash().clone());
|
|
|
|
request.headers_request.start = header.parent_hash().clone().into();
|
|
|
|
request.headers_request.max -= 1;
|
|
|
|
|
|
|
|
request.downloaded.push_front(header);
|
|
|
|
}
|
|
|
|
|
|
|
|
let subchain_parent = request.subchain_parent.1;
|
|
|
|
|
2017-01-11 14:39:03 +01:00
|
|
|
// check if the subchain portion has been completely filled.
|
2016-12-15 13:05:38 +01:00
|
|
|
if request.headers_request.max == 0 {
|
2016-12-15 15:50:36 +01:00
|
|
|
if parent_hash.map_or(true, |hash| hash != subchain_parent) {
|
|
|
|
let abort = AbortReason::BadScaffold(self.scaffold_contributors);
|
2017-01-11 14:39:03 +01:00
|
|
|
return SyncRound::abort(abort, self.ready);
|
2016-12-15 15:50:36 +01:00
|
|
|
}
|
|
|
|
|
2016-12-15 13:05:38 +01:00
|
|
|
self.complete_requests.insert(subchain_parent, request);
|
2016-12-15 18:45:11 +01:00
|
|
|
self.collect_ready();
|
2016-12-15 13:05:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// state transition not triggered until drain is finished.
|
2016-12-15 15:50:36 +01:00
|
|
|
(SyncRound::Fetch(self))
|
2016-12-15 13:05:38 +01:00
|
|
|
}
|
|
|
|
}
|
2016-12-14 22:57:30 +01:00
|
|
|
}
|
2016-12-15 16:19:28 +01:00
|
|
|
|
|
|
|
fn requests_abandoned(mut self, abandoned: &[ReqId]) -> SyncRound {
|
|
|
|
for abandoned in abandoned {
|
|
|
|
match self.pending.remove(abandoned) {
|
|
|
|
None => {},
|
|
|
|
Some(req) => self.requests.push(req),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: track failure rate and potentially abort.
|
|
|
|
SyncRound::Fetch(self)
|
|
|
|
}
|
2016-12-15 17:33:25 +01:00
|
|
|
|
2016-12-16 15:26:39 +01:00
|
|
|
fn dispatch_requests<D>(mut self, mut dispatcher: D) -> SyncRound
|
|
|
|
where D: FnMut(HeadersRequest) -> Option<ReqId>
|
2016-12-15 17:33:25 +01:00
|
|
|
{
|
|
|
|
while let Some(pending_req) = self.requests.pop() {
|
|
|
|
match dispatcher(pending_req.headers_request.clone()) {
|
|
|
|
Some(req_id) => {
|
|
|
|
trace!(target: "sync", "Assigned request for subchain ({} -> {})",
|
|
|
|
pending_req.subchain_parent.0 + 1, pending_req.subchain_end.0);
|
|
|
|
|
|
|
|
self.pending.insert(req_id, pending_req);
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
self.requests.push(pending_req);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SyncRound::Fetch(self)
|
|
|
|
}
|
|
|
|
|
2016-12-15 21:51:08 +01:00
|
|
|
fn drain(mut self, headers: &mut Vec<Header>, max: Option<usize>) -> SyncRound {
|
|
|
|
let max = ::std::cmp::min(max.unwrap_or(usize::max_value()), self.ready.len());
|
2016-12-15 17:33:25 +01:00
|
|
|
headers.extend(self.ready.drain(0..max));
|
|
|
|
|
|
|
|
if self.sparse.is_empty() && self.ready.is_empty() {
|
2017-01-11 14:39:03 +01:00
|
|
|
trace!(target: "sync", "sync round complete. Starting anew from {:?}", self.end);
|
2017-01-20 12:41:59 +01:00
|
|
|
SyncRound::begin(self.end, self.target)
|
2016-12-15 17:33:25 +01:00
|
|
|
} else {
|
|
|
|
SyncRound::Fetch(self)
|
|
|
|
}
|
|
|
|
}
|
2016-12-14 22:57:30 +01:00
|
|
|
}
|
|
|
|
|
2017-01-20 12:41:59 +01:00
|
|
|
// Compute scaffold parameters from non-zero distance between start and target block: (skip, pivots).
|
|
|
|
fn scaffold_params(diff: u64) -> (u64, usize) {
|
|
|
|
// default parameters.
|
|
|
|
// amount of blocks between each scaffold pivot.
|
|
|
|
const ROUND_SKIP: u64 = 255;
|
|
|
|
// amount of scaffold pivots: these are the Xs in "X___X___X"
|
|
|
|
const ROUND_PIVOTS: usize = 256;
|
|
|
|
|
|
|
|
let rem = diff % (ROUND_SKIP + 1);
|
|
|
|
if diff <= ROUND_SKIP {
|
|
|
|
// just request headers from the start to the target.
|
|
|
|
(0, rem as usize)
|
|
|
|
} else {
|
|
|
|
// the number of pivots necessary to exactly hit or overshoot the target.
|
|
|
|
let pivots_to_target = (diff / (ROUND_SKIP + 1)) + if rem == 0 { 0 } else { 1 };
|
|
|
|
let num_pivots = ::std::cmp::min(pivots_to_target, ROUND_PIVOTS as u64) as usize;
|
|
|
|
(ROUND_SKIP, num_pivots)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-15 15:50:36 +01:00
|
|
|
/// Round started: get stepped header chain.
|
2017-01-20 12:41:59 +01:00
|
|
|
/// from a start block with number X we request ROUND_PIVOTS headers stepped by ROUND_SKIP from
|
|
|
|
/// block X + 1 to a target >= X + 1.
|
|
|
|
/// If the sync target is within ROUND_SKIP of the start, we request
|
|
|
|
/// only those blocks. If the sync target is within (ROUND_SKIP + 1) * (ROUND_PIVOTS - 1) of
|
|
|
|
/// the start, we reduce the number of pivots so the target is outside it.
|
2016-12-15 15:50:36 +01:00
|
|
|
pub struct RoundStart {
|
2016-12-14 22:57:30 +01:00
|
|
|
start_block: (u64, H256),
|
2017-01-20 12:41:59 +01:00
|
|
|
target: (u64, H256),
|
2016-12-14 22:57:30 +01:00
|
|
|
pending_req: Option<(ReqId, HeadersRequest)>,
|
|
|
|
sparse_headers: Vec<Header>,
|
2016-12-15 15:50:36 +01:00
|
|
|
contributors: HashSet<PeerId>,
|
2016-12-14 23:25:51 +01:00
|
|
|
attempt: usize,
|
2017-01-20 12:41:59 +01:00
|
|
|
skip: u64,
|
|
|
|
pivots: usize,
|
2016-12-14 22:57:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl RoundStart {
|
2017-01-20 12:41:59 +01:00
|
|
|
fn new(start: (u64, H256), target: (u64, H256)) -> Self {
|
|
|
|
let (skip, pivots) = scaffold_params(target.0 - start.0);
|
|
|
|
|
|
|
|
trace!(target: "sync", "Beginning sync round: {} pivots and {} skip from block {}",
|
|
|
|
pivots, skip, start.0);
|
|
|
|
|
2016-12-14 22:57:30 +01:00
|
|
|
RoundStart {
|
2017-01-20 12:41:59 +01:00
|
|
|
start_block: start,
|
|
|
|
target: target,
|
2016-12-14 22:57:30 +01:00
|
|
|
pending_req: None,
|
|
|
|
sparse_headers: Vec::new(),
|
2016-12-15 15:50:36 +01:00
|
|
|
contributors: HashSet::new(),
|
2016-12-14 23:25:51 +01:00
|
|
|
attempt: 0,
|
2017-01-20 12:41:59 +01:00
|
|
|
skip: skip,
|
|
|
|
pivots: pivots,
|
2016-12-14 22:57:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-15 17:33:25 +01:00
|
|
|
// called on failed attempt. may trigger a transition after a number of attempts.
|
2016-12-16 14:53:36 +01:00
|
|
|
// a failed attempt is defined as any time a peer returns invalid or incomplete response
|
2016-12-15 16:19:28 +01:00
|
|
|
fn failed_attempt(mut self) -> SyncRound {
|
|
|
|
self.attempt += 1;
|
|
|
|
|
|
|
|
if self.attempt >= SCAFFOLD_ATTEMPTS {
|
2017-01-20 12:41:59 +01:00
|
|
|
return if self.sparse_headers.len() > 1 {
|
|
|
|
Fetcher::new(self.sparse_headers, self.contributors.into_iter().collect(), self.target)
|
2016-12-15 16:19:28 +01:00
|
|
|
} else {
|
2017-01-20 12:41:59 +01:00
|
|
|
let fetched_headers = if self.skip == 0 {
|
|
|
|
self.sparse_headers.into()
|
|
|
|
} else {
|
|
|
|
VecDeque::new()
|
|
|
|
};
|
|
|
|
|
|
|
|
SyncRound::abort(AbortReason::NoResponses, fetched_headers)
|
2016-12-15 16:19:28 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
SyncRound::Start(self)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-15 15:50:36 +01:00
|
|
|
fn process_response<R: ResponseContext>(mut self, ctx: &R) -> SyncRound {
|
2016-12-14 22:57:30 +01:00
|
|
|
let req = match self.pending_req.take() {
|
2016-12-15 21:51:08 +01:00
|
|
|
Some((id, ref req)) if ctx.req_id() == &id => { req.clone() }
|
2016-12-14 22:57:30 +01:00
|
|
|
other => {
|
|
|
|
self.pending_req = other;
|
2016-12-15 15:50:36 +01:00
|
|
|
return SyncRound::Start(self);
|
2016-12-14 22:57:30 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-12-15 15:50:36 +01:00
|
|
|
match response::decode_and_verify(ctx.data(), &req) {
|
2016-12-14 22:57:30 +01:00
|
|
|
Ok(headers) => {
|
2016-12-15 17:33:25 +01:00
|
|
|
if self.sparse_headers.len() == 0
|
|
|
|
&& headers.get(0).map_or(false, |x| x.parent_hash() != &self.start_block.1) {
|
|
|
|
trace!(target: "sync", "Wrong parent for first header in round");
|
|
|
|
ctx.punish_responder(); // or should we reset?
|
|
|
|
}
|
|
|
|
|
2016-12-15 15:50:36 +01:00
|
|
|
self.contributors.insert(ctx.responder());
|
2016-12-14 22:57:30 +01:00
|
|
|
self.sparse_headers.extend(headers);
|
|
|
|
|
2017-01-20 12:41:59 +01:00
|
|
|
if self.sparse_headers.len() == self.pivots {
|
|
|
|
return if self.skip == 0 {
|
|
|
|
SyncRound::abort(AbortReason::TargetReached, self.sparse_headers.into())
|
|
|
|
} else {
|
|
|
|
trace!(target: "sync", "Beginning fetch of blocks between {} sparse headers",
|
|
|
|
self.sparse_headers.len());
|
|
|
|
Fetcher::new(
|
|
|
|
self.sparse_headers,
|
|
|
|
self.contributors.into_iter().collect(),
|
|
|
|
self.target
|
|
|
|
)
|
|
|
|
}
|
2016-12-14 22:57:30 +01:00
|
|
|
}
|
|
|
|
}
|
2016-12-15 15:50:36 +01:00
|
|
|
Err(e) => {
|
|
|
|
trace!(target: "sync", "Punishing peer {} for malformed response ({})", ctx.responder(), e);
|
|
|
|
ctx.punish_responder();
|
|
|
|
}
|
2016-12-14 23:25:51 +01:00
|
|
|
};
|
|
|
|
|
2016-12-15 16:19:28 +01:00
|
|
|
self.failed_attempt()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn requests_abandoned(mut self, abandoned: &[ReqId]) -> SyncRound {
|
|
|
|
match self.pending_req.take() {
|
|
|
|
Some((id, req)) => {
|
|
|
|
if abandoned.iter().any(|r| r == &id) {
|
|
|
|
self.pending_req = None;
|
|
|
|
self.failed_attempt()
|
|
|
|
} else {
|
|
|
|
self.pending_req = Some((id, req));
|
|
|
|
SyncRound::Start(self)
|
|
|
|
}
|
2016-12-15 13:05:38 +01:00
|
|
|
}
|
2016-12-15 16:19:28 +01:00
|
|
|
None => SyncRound::Start(self),
|
2016-12-14 22:57:30 +01:00
|
|
|
}
|
|
|
|
}
|
2016-12-15 17:33:25 +01:00
|
|
|
|
2016-12-16 15:26:39 +01:00
|
|
|
fn dispatch_requests<D>(mut self, mut dispatcher: D) -> SyncRound
|
|
|
|
where D: FnMut(HeadersRequest) -> Option<ReqId>
|
2016-12-15 17:33:25 +01:00
|
|
|
{
|
|
|
|
if self.pending_req.is_none() {
|
|
|
|
// beginning offset + first block expected after last header we have.
|
|
|
|
let start = (self.start_block.0 + 1)
|
2017-01-20 12:41:59 +01:00
|
|
|
+ self.sparse_headers.len() as u64 * (self.skip + 1);
|
2016-12-15 17:33:25 +01:00
|
|
|
|
2017-01-20 12:41:59 +01:00
|
|
|
let max = self.pivots - self.sparse_headers.len();
|
2017-01-11 14:39:03 +01:00
|
|
|
|
2016-12-15 17:33:25 +01:00
|
|
|
let headers_request = HeadersRequest {
|
|
|
|
start: start.into(),
|
2017-01-11 14:39:03 +01:00
|
|
|
max: max,
|
2017-01-20 12:41:59 +01:00
|
|
|
skip: self.skip,
|
2016-12-15 17:33:25 +01:00
|
|
|
reverse: false,
|
|
|
|
};
|
|
|
|
|
2016-12-16 14:53:36 +01:00
|
|
|
if let Some(req_id) = dispatcher(headers_request.clone()) {
|
2017-01-11 14:39:03 +01:00
|
|
|
trace!(target: "sync", "Requesting scaffold: {} headers forward from {}, skip={}",
|
2017-01-20 12:41:59 +01:00
|
|
|
max, start, self.skip);
|
2017-01-11 14:39:03 +01:00
|
|
|
|
2016-12-16 14:53:36 +01:00
|
|
|
self.pending_req = Some((req_id, headers_request));
|
2016-12-15 17:33:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SyncRound::Start(self)
|
|
|
|
}
|
2016-12-14 22:57:30 +01:00
|
|
|
}
|
|
|
|
|
2016-12-14 23:25:51 +01:00
|
|
|
/// Sync round state machine.
|
|
|
|
pub enum SyncRound {
|
2016-12-14 22:57:30 +01:00
|
|
|
/// Beginning a sync round.
|
2016-12-14 23:25:51 +01:00
|
|
|
Start(RoundStart),
|
2016-12-14 22:57:30 +01:00
|
|
|
/// Fetching intermediate blocks during a sync round.
|
|
|
|
Fetch(Fetcher),
|
2017-01-11 14:39:03 +01:00
|
|
|
/// Aborted + Sequential headers
|
|
|
|
Abort(AbortReason, VecDeque<Header>),
|
2016-12-14 22:57:30 +01:00
|
|
|
}
|
|
|
|
|
2016-12-14 23:25:51 +01:00
|
|
|
impl SyncRound {
|
2017-01-11 14:39:03 +01:00
|
|
|
fn abort(reason: AbortReason, remaining: VecDeque<Header>) -> Self {
|
2017-01-20 12:41:59 +01:00
|
|
|
trace!(target: "sync", "Aborting sync round: {:?}. To drain: {}", reason, remaining.len());
|
2016-12-14 23:25:51 +01:00
|
|
|
|
2017-01-11 14:39:03 +01:00
|
|
|
SyncRound::Abort(reason, remaining)
|
2016-12-14 23:25:51 +01:00
|
|
|
}
|
|
|
|
|
2017-01-20 12:41:59 +01:00
|
|
|
/// Begin sync rounds from a starting block, but not to go past a given target
|
|
|
|
pub fn begin(start: (u64, H256), target: (u64, H256)) -> Self {
|
|
|
|
if target.0 <= start.0 {
|
|
|
|
SyncRound::abort(AbortReason::TargetReached, VecDeque::new())
|
|
|
|
} else {
|
|
|
|
SyncRound::Start(RoundStart::new(start, target))
|
|
|
|
}
|
2016-12-16 14:53:36 +01:00
|
|
|
}
|
|
|
|
|
2016-12-14 23:25:51 +01:00
|
|
|
/// Process an answer to a request. Unknown requests will be ignored.
|
2016-12-15 15:50:36 +01:00
|
|
|
pub fn process_response<R: ResponseContext>(self, ctx: &R) -> Self {
|
2016-12-14 22:57:30 +01:00
|
|
|
match self {
|
2016-12-15 15:50:36 +01:00
|
|
|
SyncRound::Start(round_start) => round_start.process_response(ctx),
|
|
|
|
SyncRound::Fetch(fetcher) => fetcher.process_response(ctx),
|
|
|
|
other => other,
|
2016-12-14 22:57:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-14 23:25:51 +01:00
|
|
|
/// Return unfulfilled requests from disconnected peer. Unknown requests will be ignored.
|
2016-12-15 15:50:36 +01:00
|
|
|
pub fn requests_abandoned(self, abandoned: &[ReqId]) -> Self {
|
2016-12-15 16:19:28 +01:00
|
|
|
match self {
|
|
|
|
SyncRound::Start(round_start) => round_start.requests_abandoned(abandoned),
|
|
|
|
SyncRound::Fetch(fetcher) => fetcher.requests_abandoned(abandoned),
|
|
|
|
other => other,
|
|
|
|
}
|
2016-12-14 22:57:30 +01:00
|
|
|
}
|
|
|
|
|
2016-12-14 23:25:51 +01:00
|
|
|
/// Dispatch pending requests. The dispatcher provided will attempt to
|
|
|
|
/// find a suitable peer to serve the request.
|
2016-12-15 17:33:25 +01:00
|
|
|
// TODO: have dispatcher take capabilities argument? and return an error as
|
|
|
|
// to why no suitable peer can be found? (no buffer, no chain heads that high, etc)
|
2016-12-15 15:50:36 +01:00
|
|
|
pub fn dispatch_requests<D>(self, dispatcher: D) -> Self
|
2016-12-16 15:26:39 +01:00
|
|
|
where D: FnMut(HeadersRequest) -> Option<ReqId>
|
2016-12-14 22:57:30 +01:00
|
|
|
{
|
2016-12-15 17:33:25 +01:00
|
|
|
match self {
|
|
|
|
SyncRound::Start(round_start) => round_start.dispatch_requests(dispatcher),
|
|
|
|
SyncRound::Fetch(fetcher) => fetcher.dispatch_requests(dispatcher),
|
|
|
|
other => other,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-15 21:51:08 +01:00
|
|
|
/// Drain up to a maximum number (None -> all) of headers (continuous, starting with a child of
|
2016-12-15 17:33:25 +01:00
|
|
|
/// the round start block) from the round, starting a new one once finished.
|
2016-12-15 21:51:08 +01:00
|
|
|
pub fn drain(self, v: &mut Vec<Header>, max: Option<usize>) -> Self {
|
2016-12-15 17:33:25 +01:00
|
|
|
match self {
|
|
|
|
SyncRound::Fetch(fetcher) => fetcher.drain(v, max),
|
2017-01-11 14:39:03 +01:00
|
|
|
SyncRound::Abort(reason, mut remaining) => {
|
|
|
|
let len = ::std::cmp::min(max.unwrap_or(usize::max_value()), remaining.len());
|
|
|
|
v.extend(remaining.drain(..len));
|
|
|
|
SyncRound::Abort(reason, remaining)
|
|
|
|
}
|
2016-12-15 17:33:25 +01:00
|
|
|
other => other,
|
|
|
|
}
|
2016-12-14 22:57:30 +01:00
|
|
|
}
|
|
|
|
}
|
2017-01-11 14:39:03 +01:00
|
|
|
|
|
|
|
impl fmt::Debug for SyncRound {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
match *self {
|
|
|
|
SyncRound::Start(ref state) => write!(f, "Scaffolding from {:?}", state.start_block),
|
|
|
|
SyncRound::Fetch(ref fetcher) => write!(f, "Filling scaffold up to {:?}", fetcher.end),
|
|
|
|
SyncRound::Abort(ref reason, ref remaining) =>
|
|
|
|
write!(f, "Aborted: {:?}, {} remain", reason, remaining.len()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-01-20 12:41:59 +01:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::scaffold_params;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn scaffold_config() {
|
|
|
|
// within a certain distance of the head, we download
|
|
|
|
// sequentially.
|
|
|
|
assert_eq!(scaffold_params(1), (0, 1));
|
|
|
|
assert_eq!(scaffold_params(6), (0, 6));
|
|
|
|
|
|
|
|
// when scaffolds are useful, download enough frames to get
|
|
|
|
// within a close distance of the goal.
|
|
|
|
assert_eq!(scaffold_params(1000), (255, 4));
|
|
|
|
assert_eq!(scaffold_params(1024), (255, 4));
|
|
|
|
}
|
|
|
|
}
|