afdc4fea1d
Fix a small sync bug which can mis-classify newly connected peers.
109 lines
4.0 KiB
Rust
109 lines
4.0 KiB
Rust
use super::manager::SLOT_IMPORT_TOLERANCE;
|
|
use crate::router::processor::status_message;
|
|
use beacon_chain::{BeaconChain, BeaconChainTypes};
|
|
use eth2_libp2p::rpc::*;
|
|
use eth2_libp2p::SyncInfo;
|
|
use std::ops::Sub;
|
|
use std::sync::Arc;
|
|
use types::{Epoch, Hash256, Slot};
|
|
|
|
/// Keeps track of syncing information for known connected peers.
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub struct PeerSyncInfo {
|
|
pub fork_digest: [u8; 4],
|
|
pub finalized_root: Hash256,
|
|
pub finalized_epoch: Epoch,
|
|
pub head_root: Hash256,
|
|
pub head_slot: Slot,
|
|
}
|
|
|
|
/// The type of peer relative to our current state.
|
|
pub enum PeerSyncType {
|
|
/// The peer is on our chain and is fully synced with respect to our chain.
|
|
FullySynced,
|
|
/// The peer has a greater knowledge of the chain than us that warrants a full sync.
|
|
Advanced,
|
|
/// A peer is behind in the sync and not useful to us for downloading blocks.
|
|
Behind,
|
|
}
|
|
|
|
impl From<StatusMessage> for PeerSyncInfo {
|
|
fn from(status: StatusMessage) -> PeerSyncInfo {
|
|
PeerSyncInfo {
|
|
fork_digest: status.fork_digest,
|
|
finalized_root: status.finalized_root,
|
|
finalized_epoch: status.finalized_epoch,
|
|
head_root: status.head_root,
|
|
head_slot: status.head_slot,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Into<SyncInfo> for PeerSyncInfo {
|
|
fn into(self) -> SyncInfo {
|
|
SyncInfo {
|
|
status_head_slot: self.head_slot,
|
|
status_head_root: self.head_root,
|
|
status_finalized_epoch: self.finalized_epoch,
|
|
status_finalized_root: self.finalized_root,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PeerSyncInfo {
|
|
/// Derives the peer sync information from a beacon chain.
|
|
pub fn from_chain<T: BeaconChainTypes>(chain: &Arc<BeaconChain<T>>) -> Option<PeerSyncInfo> {
|
|
Some(Self::from(status_message(chain)?))
|
|
}
|
|
|
|
/// Given another peer's `PeerSyncInfo` this will determine how useful that peer is to us in
|
|
/// regards to syncing. This returns the peer sync type that can then be handled by the
|
|
/// `SyncManager`.
|
|
pub fn peer_sync_type(&self, remote_peer_sync_info: &PeerSyncInfo) -> PeerSyncType {
|
|
// check if the peer is fully synced with our current chain
|
|
if self.is_fully_synced_peer(remote_peer_sync_info) {
|
|
PeerSyncType::FullySynced
|
|
}
|
|
// if not, check if the peer is ahead of our chain
|
|
else if self.is_advanced_peer(remote_peer_sync_info) {
|
|
PeerSyncType::Advanced
|
|
} else {
|
|
// the peer must be behind and not useful
|
|
PeerSyncType::Behind
|
|
}
|
|
}
|
|
|
|
/// Determines if another peer is fully synced with the current peer.
|
|
///
|
|
/// A fully synced peer is a peer whose finalized epoch and hash match our own and their
|
|
/// head is within SLOT_IMPORT_TOLERANCE of our own.
|
|
/// In this case we ignore any batch/range syncing.
|
|
fn is_fully_synced_peer(&self, remote: &PeerSyncInfo) -> bool {
|
|
// ensure we are on the same chain, with minor differing heads
|
|
if remote.finalized_epoch == self.finalized_epoch
|
|
&& remote.finalized_root == self.finalized_root
|
|
{
|
|
// that we are within SLOT_IMPORT_TOLERANCE of our two heads
|
|
if (self.head_slot >= remote.head_slot
|
|
&& self.head_slot.sub(remote.head_slot).as_usize() <= SLOT_IMPORT_TOLERANCE)
|
|
|| (self.head_slot < remote.head_slot
|
|
&& remote.head_slot.sub(self.head_slot).as_usize() <= SLOT_IMPORT_TOLERANCE)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
false
|
|
}
|
|
|
|
/// Determines if a peer has more knowledge about the current chain than we do.
|
|
///
|
|
/// There are two conditions here.
|
|
/// 1) The peer could have a head slot that is greater
|
|
/// than SLOT_IMPORT_TOLERANCE of our current head.
|
|
/// 2) The peer has a greater finalized slot/epoch than our own.
|
|
fn is_advanced_peer(&self, remote: &PeerSyncInfo) -> bool {
|
|
remote.head_slot.sub(self.head_slot).as_usize() > SLOT_IMPORT_TOLERANCE
|
|
|| self.finalized_epoch < remote.finalized_epoch
|
|
}
|
|
}
|