light: downloader state machine stub

This commit is contained in:
Robert Habermeier 2016-12-14 22:57:30 +01:00
parent 0d466fa8d0
commit 91b8fa7039

View File

@ -0,0 +1,173 @@
// 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.
use std::collections::{HashMap, VecDeque};
use std::mem;
use ethcore::header::Header;
use light::net::{EventContext, ReqId};
use light::request::Headers as HeadersRequest;
use network::PeerId;
use rlp::{UntrustedRlp, View};
use util::{Bytes, H256, Mutex};
use super::{Error, Peer};
use super::response::{self, Constraint};
// amount of blocks between each scaffold entry.
// TODO: move these into paraeters for `RoundStart::new`?
const ROUND_SKIP: usize = 255;
// amount of scaffold frames: these are the blank spaces in "X___X___X"
const ROUND_FRAMES: u64 = 255;
// number of attempts to make to get a full scaffold for a sync round.
const SCAFFOLD_ATTEMPTS: usize = 3;
// A request for headers with a known starting header
// and a known parent hash for the last block.
struct Request {
headers: HeadersRequest,
end_parent: H256,
}
pub struct Fetcher {
sparse: Vec<Header>, // sparse header chain.
requests: VecDeque<Request>,
pending: HashMap<ReqId, Request>,
}
impl Fetcher {
// Produce a new fetcher given a sparse headerchain, in ascending order.
// The headers must be valid RLP at this point.
fn new(sparse_headers: Vec<Header>) -> Self {
let mut requests = VecDeque::with_capacity(sparse_headers.len() - 1);
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();
if diff < 2 { continue } // these headers are already adjacent.
let needed_headers = HeadersRequest {
start: high_rung.parent_hash().clone().into(),
max: diff as usize - 1,
skip: 0,
reverse: true,
};
requests.push_back(Request {
headers: needed_headers,
end_parent: low_rung.hash(),
});
}
Fetcher {
sparse: sparse_headers,
requests: requests,
pending: HashMap::new(),
}
}
fn process_response(self, req_id: ReqId, headers: &[Bytes]) -> (Downloader, Result<(), Error>) {
unimplemented!()
}
}
// Round started: get stepped header chain.
// from a start block with number X we request 256 headers stepped by 256 from
// block X + 1.
struct RoundStart {
start_block: (u64, H256),
pending_req: Option<(ReqId, HeadersRequest)>,
sparse_headers: Vec<Header>,
attempt: 0,
}
impl RoundStart {
fn new(start: (u64, H256)) -> Self {
RoundStart {
start_block: start.clone(),
pending_req: None,
sparse_headers: Vec::new(),
}
}
fn process_response(mut self, req_id: ReqId, headers: &[Bytes]) -> (Downloader, Result<(), Error>) {
let req = match self.pending_req.take() {
Some((id, req)) if req_id == id { req.clone() }
other => {
self.pending_req = other;
return (self, Ok(()))
}
};
self.attempt += 1;
let headers = match response::decode_and_verify(headers, &req) {
Ok(headers) => {
self.sparse_headers.extend(headers);
if self.sparse_headers.len() == ROUND_FRAMES + 1
|| self.attempt >= SCAFFOLD_ATTEMPTS
{
let fetcher = Fetcher::new(self.sparse_headers);
(Downloader::Fetch(fetcher), Ok(()))
}
}
}
}
}
/// Downloader state machine.
pub enum Downloader {
/// Waiting for peers.
Nothing,
/// Searching for common block with best chain.
SearchCommon,
/// Beginning a sync round.
RoundStart(RoundStart),
/// Fetching intermediate blocks during a sync round.
Fetch(Fetcher),
}
impl Downloader {
// Process an answer to a request. Unknown requests will be ignored.
fn process_response(self, req_id: ReqId, headers: &[Bytes]) -> (Self, Result<(), Error>) {
match self {
Downloader::RoundStart(round_start) => round_start.process_response(req_id, headers),
Downloader::Fetch(fetcher) => fetcher.process_response(req_id, headers),
other => (other, Ok(())),
}
}
// Return unfulfilled requests from disconnected peer. Unknown requests will be ignored.
fn requests_abandoned(self, abandoned: &[ReqId]) -> (Self, Result<(), Error>) {
}
// Dispatch pending requests. The dispatcher provided will attempt to
// find a suitable peer to serve the request.
// TODO: have dispatcher take capabilities argument?
fn dispatch_requests<D>(self, dispatcher: D) -> (Self, Result<(), Error>)
where D: Fn(HeadersRequest) -> Option<ReqId>
{
unimplemented!()
}
}