drain prepared headers from sync round
This commit is contained in:
parent
71e96aca10
commit
f776f48023
@ -33,8 +33,8 @@ use util::{Bytes, H256, Mutex};
|
||||
use super::response;
|
||||
|
||||
// amount of blocks between each scaffold entry.
|
||||
// TODO: move these into paraeters for `RoundStart::new`?
|
||||
const ROUND_SKIP: usize = 255;
|
||||
// TODO: move these into parameters for `RoundStart::new`?
|
||||
const ROUND_SKIP: u64 = 255;
|
||||
|
||||
// amount of scaffold frames: these are the blank spaces in "X___X___X"
|
||||
const ROUND_FRAMES: usize = 255;
|
||||
@ -94,13 +94,16 @@ pub struct Fetcher {
|
||||
complete_requests: HashMap<H256, SubchainRequest>,
|
||||
pending: HashMap<ReqId, SubchainRequest>,
|
||||
scaffold_contributors: Vec<PeerId>,
|
||||
ready: VecDeque<Header>,
|
||||
end: (u64, H256),
|
||||
}
|
||||
|
||||
impl Fetcher {
|
||||
// Produce a new fetcher given a sparse headerchain, in ascending order along
|
||||
// with a list of peers who helped produce the chain.
|
||||
// The headers must be valid RLP at this point.
|
||||
fn new(sparse_headers: Vec<Header>, contributors: Vec<PeerId>) -> Self {
|
||||
// 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.
|
||||
fn new(sparse_headers: Vec<Header>, contributors: Vec<PeerId>) -> SyncRound {
|
||||
let mut requests = BinaryHeap::with_capacity(sparse_headers.len() - 1);
|
||||
|
||||
for pair in sparse_headers.windows(2) {
|
||||
@ -128,12 +131,44 @@ impl Fetcher {
|
||||
});
|
||||
}
|
||||
|
||||
Fetcher {
|
||||
let end = match sparse_headers.last().map(|h| (h.number(), h.hash())) {
|
||||
Some(end) => end,
|
||||
None => return SyncRound::abort(AbortReason::BadScaffold(contributors)),
|
||||
};
|
||||
|
||||
SyncRound::Fetch(Fetcher {
|
||||
sparse: sparse_headers.into(),
|
||||
requests: requests,
|
||||
complete_requests: HashMap::new(),
|
||||
pending: HashMap::new(),
|
||||
scaffold_contributors: contributors,
|
||||
ready: VecDeque::new(),
|
||||
end: end,
|
||||
})
|
||||
}
|
||||
|
||||
// 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"))
|
||||
}
|
||||
}
|
||||
|
||||
@ -209,6 +244,40 @@ impl Fetcher {
|
||||
// TODO: track failure rate and potentially abort.
|
||||
SyncRound::Fetch(self)
|
||||
}
|
||||
|
||||
fn dispatch_requests<D>(mut self, dispatcher: D) -> SyncRound
|
||||
where D: Fn(HeadersRequest) -> Option<ReqId>
|
||||
{
|
||||
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)
|
||||
}
|
||||
|
||||
fn drain(mut self, headers: &mut Vec<Header>, max: usize) -> SyncRound {
|
||||
self.collect_ready();
|
||||
|
||||
let max = ::std::cmp::min(max, self.ready.len());
|
||||
headers.extend(self.ready.drain(0..max));
|
||||
|
||||
if self.sparse.is_empty() && self.ready.is_empty() {
|
||||
SyncRound::Start(RoundStart::new(self.end))
|
||||
} else {
|
||||
SyncRound::Fetch(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Round started: get stepped header chain.
|
||||
@ -233,14 +302,16 @@ impl RoundStart {
|
||||
}
|
||||
}
|
||||
|
||||
// called on failed attempt. may trigger a transition.
|
||||
// called on failed attempt. may trigger a transition after a number of attempts.
|
||||
// a failed attempt is defined as:
|
||||
// - any time we try to make a request to a peer and fail
|
||||
// - any time a peer returns invalid or incomplete response
|
||||
fn failed_attempt(mut self) -> SyncRound {
|
||||
self.attempt += 1;
|
||||
|
||||
if self.attempt >= SCAFFOLD_ATTEMPTS {
|
||||
if self.sparse_headers.len() > 1 {
|
||||
let fetcher = Fetcher::new(self.sparse_headers, self.contributors.into_iter().collect());
|
||||
SyncRound::Fetch(fetcher)
|
||||
Fetcher::new(self.sparse_headers, self.contributors.into_iter().collect())
|
||||
} else {
|
||||
SyncRound::Abort(AbortReason::NoResponses)
|
||||
}
|
||||
@ -260,6 +331,12 @@ impl RoundStart {
|
||||
|
||||
match response::decode_and_verify(ctx.data(), &req) {
|
||||
Ok(headers) => {
|
||||
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?
|
||||
}
|
||||
|
||||
self.contributors.insert(ctx.responder());
|
||||
self.sparse_headers.extend(headers);
|
||||
|
||||
@ -267,8 +344,7 @@ impl RoundStart {
|
||||
trace!(target: "sync", "Beginning fetch of blocks between {} sparse headers",
|
||||
self.sparse_headers.len());
|
||||
|
||||
let fetcher = Fetcher::new(self.sparse_headers, self.contributors.into_iter().collect());
|
||||
return SyncRound::Fetch(fetcher);
|
||||
return Fetcher::new(self.sparse_headers, self.contributors.into_iter().collect());
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
@ -294,6 +370,31 @@ impl RoundStart {
|
||||
None => SyncRound::Start(self),
|
||||
}
|
||||
}
|
||||
|
||||
fn dispatch_requests<D>(mut self, dispatcher: D) -> SyncRound
|
||||
where D: Fn(HeadersRequest) -> Option<ReqId>
|
||||
{
|
||||
if self.pending_req.is_none() {
|
||||
|
||||
// beginning offset + first block expected after last header we have.
|
||||
let start = (self.start_block.0 + 1)
|
||||
+ self.sparse_headers.len() as u64 * (ROUND_SKIP + 1);
|
||||
|
||||
let headers_request = HeadersRequest {
|
||||
start: start.into(),
|
||||
max: (ROUND_FRAMES - 1) - self.sparse_headers.len(),
|
||||
skip: ROUND_SKIP,
|
||||
reverse: false,
|
||||
};
|
||||
|
||||
match dispatcher(headers_request.clone()) {
|
||||
Some(req_id) => self.pending_req = Some((req_id, headers_request)),
|
||||
None => return self.failed_attempt(),
|
||||
}
|
||||
}
|
||||
|
||||
SyncRound::Start(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sync round state machine.
|
||||
@ -333,10 +434,24 @@ impl SyncRound {
|
||||
|
||||
/// Dispatch pending requests. The dispatcher provided will attempt to
|
||||
/// find a suitable peer to serve the request.
|
||||
// TODO: have dispatcher take capabilities argument?
|
||||
// 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)
|
||||
pub fn dispatch_requests<D>(self, dispatcher: D) -> Self
|
||||
where D: Fn(HeadersRequest) -> Option<ReqId>
|
||||
{
|
||||
unimplemented!()
|
||||
match self {
|
||||
SyncRound::Start(round_start) => round_start.dispatch_requests(dispatcher),
|
||||
SyncRound::Fetch(fetcher) => fetcher.dispatch_requests(dispatcher),
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
|
||||
/// Drain up to a maximum number of headers (continuous, starting with a child of
|
||||
/// the round start block) from the round, starting a new one once finished.
|
||||
pub fn drain(self, v: &mut Vec<Header>, max: usize) -> Self {
|
||||
match self {
|
||||
SyncRound::Fetch(fetcher) => fetcher.drain(v, max),
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user