Explicit sync API for downloading important, possibly orphaned, forks (#3633)

* Explicit sync API

* Keep sync requests

* Don't request the finalized block we already have

* Dropping requests & docs

* Renamed a function
This commit is contained in:
Arkadiy Paronyan
2019-09-28 14:02:36 +02:00
committed by Robert Habermeier
parent 5d82f453e8
commit 3242d7f2b6
5 changed files with 183 additions and 1 deletions
+95 -1
View File
@@ -123,7 +123,10 @@ pub struct ChainSync<B: BlockT> {
queue_blocks: HashSet<B::Hash>,
/// The best block number that we are currently importing.
best_importing_number: NumberFor<B>,
/// Finality proof handler.
request_builder: Option<BoxFinalityProofRequestBuilder<B>>,
/// Explicit sync requests.
sync_requests: HashMap<B::Hash, SyncRequest<B>>,
/// A flag that caches idle state with no pending requests.
is_idle: bool,
/// A type to check incoming block announcements.
@@ -157,6 +160,11 @@ pub struct PeerInfo<B: BlockT> {
pub best_number: NumberFor<B>
}
struct SyncRequest<B: BlockT> {
number: NumberFor<B>,
peers: HashSet<PeerId>,
}
/// The state of syncing between a Peer and ourselves.
///
/// Generally two categories, "busy" or `Available`. If busy, the enum
@@ -299,6 +307,7 @@ impl<B: BlockT> ChainSync<B> {
queue_blocks: Default::default(),
best_importing_number: Zero::zero(),
request_builder,
sync_requests: Default::default(),
is_idle: false,
block_announce_validator,
}
@@ -449,6 +458,51 @@ impl<B: BlockT> ChainSync<B> {
})
}
/// Request syncing for the given block from given set of peers.
// The implementation is similar to on_block_announce with unknown parent hash.
pub fn set_sync_fork_request(&mut self, peers: Vec<PeerId>, hash: &B::Hash, number: NumberFor<B>) {
if peers.is_empty() {
if let Some(_) = self.sync_requests.remove(hash) {
debug!(target: "sync", "Cleared sync request for block {:?} with {:?}", hash, peers);
}
return;
}
debug!(target: "sync", "Explicit sync request for block {:?} with {:?}", hash, peers);
if self.is_known(&hash) {
debug!(target: "sync", "Refusing to sync known hash {:?}", hash);
return;
}
let block_status = self.client.block_status(&BlockId::Number(number - One::one()))
.unwrap_or(BlockStatus::Unknown);
if block_status == BlockStatus::InChainPruned {
trace!(target: "sync", "Refusing to sync ancient block {:?}", hash);
return;
}
self.is_idle = false;
for peer_id in &peers {
if let Some(peer) = self.peers.get_mut(peer_id) {
if let PeerSyncState::AncestorSearch(_, _) = peer.state {
continue;
}
if number > peer.best_number {
peer.best_number = number;
peer.best_hash = hash.clone();
}
}
}
self.sync_requests
.entry(hash.clone())
.or_insert_with(|| SyncRequest {
number,
peers: Default::default(),
})
.peers.extend(peers);
}
/// Get an iterator over all scheduled justification requests.
pub fn justification_requests(&mut self) -> impl Iterator<Item = (PeerId, BlockRequest<B>)> + '_ {
let peers = &mut self.peers;
@@ -508,13 +562,21 @@ impl<B: BlockT> ChainSync<B> {
}
let blocks = &mut self.blocks;
let attrs = &self.required_block_attributes;
let sync_requests = &self.sync_requests;
let mut have_requests = false;
let last_finalized = self.client.info().chain.finalized_number;
let best_queued = self.best_queued_number;
let iter = self.peers.iter_mut().filter_map(move |(id, peer)| {
if !peer.state.is_available() {
trace!(target: "sync", "Peer {} is busy", id);
return None
}
if let Some((range, req)) = peer_block_request(id, peer, blocks, attrs) {
if let Some((hash, req)) = explicit_sync_request(id, sync_requests, best_queued, last_finalized, attrs) {
trace!(target: "sync", "Downloading explicitly requested block {:?} from {}", hash, id);
peer.state = PeerSyncState::DownloadingStale(hash);
have_requests = true;
Some((id.clone(), req))
} else if let Some((range, req)) = peer_block_request(id, peer, blocks, attrs) {
peer.state = PeerSyncState::DownloadingNew(range.start);
trace!(target: "sync", "New block request for {}", id);
have_requests = true;
@@ -860,6 +922,9 @@ impl<B: BlockT> ChainSync<B> {
self.best_queued_number = number;
self.best_queued_hash = *hash;
}
if let Some(_) = self.sync_requests.remove(&hash) {
trace!(target: "sync", "Completed explicit sync request {:?}", hash);
}
// Update common blocks
for (n, peer) in self.peers.iter_mut() {
if let PeerSyncState::AncestorSearch(_, _) = peer.state {
@@ -1232,3 +1297,32 @@ fn peer_block_request<B: BlockT>(
None
}
}
/// Get pending explicit sync request for a peer.
fn explicit_sync_request<B: BlockT>(
id: &PeerId,
requests: &HashMap<B::Hash, SyncRequest<B>>,
best_num: NumberFor<B>,
finalized: NumberFor<B>,
attributes: &message::BlockAttributes,
) -> Option<(B::Hash, BlockRequest<B>)>
{
for (hash, r) in requests {
if !r.peers.contains(id) {
continue
}
if r.number <= best_num {
trace!(target: "sync", "Downloading requested fork {:?} from {}", hash, id);
return Some((hash.clone(), message::generic::BlockRequest {
id: 0,
fields: attributes.clone(),
from: message::FromBlock::Hash(hash.clone()),
to: None,
direction: message::Direction::Descending,
max: Some((r.number - finalized).saturated_into::<u32>()), // up to the last finalized block
}))
}
}
None
}