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 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 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(chain: &Arc>) -> Option { 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 } }