// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This program 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.
// This program 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 this program. If not, see .
//! Abstract interfaces and data structures related to network sync.
pub mod message;
pub mod metrics;
pub mod warp;
use crate::{role::Roles, types::ReputationChange};
use futures::Stream;
use libp2p_identity::PeerId;
use message::{BlockAnnounce, BlockData, BlockRequest, BlockResponse};
use sc_consensus::{import_queue::RuntimeOrigin, IncomingBlock};
use sp_consensus::BlockOrigin;
use sp_runtime::{
traits::{Block as BlockT, NumberFor},
Justifications,
};
use warp::WarpSyncProgress;
use std::{any::Any, fmt, fmt::Formatter, pin::Pin, sync::Arc, task::Poll};
/// The sync status of a peer we are trying to sync with
#[derive(Debug)]
pub struct PeerInfo {
/// Their best block hash.
pub best_hash: Block::Hash,
/// Their best block number.
pub best_number: NumberFor,
}
/// Info about a peer's known state (both full and light).
#[derive(Clone, Debug)]
pub struct ExtendedPeerInfo {
/// Roles
pub roles: Roles,
/// Peer best block hash
pub best_hash: B::Hash,
/// Peer best block number
pub best_number: NumberFor,
}
/// Reported sync state.
#[derive(Clone, Eq, PartialEq, Debug)]
pub enum SyncState {
/// Initial sync is complete, keep-up sync is active.
Idle,
/// Actively catching up with the chain.
Downloading { target: BlockNumber },
/// All blocks are downloaded and are being imported.
Importing { target: BlockNumber },
}
impl SyncState {
/// Are we actively catching up with the chain?
pub fn is_major_syncing(&self) -> bool {
!matches!(self, SyncState::Idle)
}
}
/// Reported state download progress.
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct StateDownloadProgress {
/// Estimated download percentage.
pub percentage: u32,
/// Total state size in bytes downloaded so far.
pub size: u64,
}
/// Syncing status and statistics.
#[derive(Debug, Clone)]
pub struct SyncStatus {
/// Current global sync state.
pub state: SyncState>,
/// Target sync block number.
pub best_seen_block: Option>,
/// Number of peers participating in syncing.
pub num_peers: u32,
/// Number of peers known to `SyncingEngine` (both full and light).
pub num_connected_peers: u32,
/// Number of blocks queued for import
pub queued_blocks: u32,
/// State sync status in progress, if any.
pub state_sync: Option,
/// Warp sync in progress, if any.
pub warp_sync: Option>,
}
/// A peer did not behave as expected and should be reported.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BadPeer(pub PeerId, pub ReputationChange);
impl fmt::Display for BadPeer {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Bad peer {}; Reputation change: {:?}", self.0, self.1)
}
}
impl std::error::Error for BadPeer {}
/// Result of [`ChainSync::on_block_data`].
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum OnBlockData {
/// The block should be imported.
Import(BlockOrigin, Vec>),
/// A new block request needs to be made to the given peer.
Request(PeerId, BlockRequest),
/// Continue processing events.
Continue,
}
/// Result of [`ChainSync::on_block_justification`].
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum OnBlockJustification {
/// The justification needs no further handling.
Nothing,
/// The justification should be imported.
Import {
peer: PeerId,
hash: Block::Hash,
number: NumberFor,
justifications: Justifications,
},
}
/// Result of `ChainSync::on_state_data`.
#[derive(Debug)]
pub enum OnStateData {
/// The block and state that should be imported.
Import(BlockOrigin, IncomingBlock),
/// A new state request needs to be made to the given peer.
Continue,
}
/// Block or justification request polled from `ChainSync`
#[derive(Debug)]
pub enum ImportResult {
BlockImport(BlockOrigin, Vec>),
JustificationImport(RuntimeOrigin, B::Hash, NumberFor, Justifications),
}
/// Value polled from `ChainSync`
#[derive(Debug)]
pub enum PollResult {
Import(ImportResult),
Announce(PollBlockAnnounceValidation),
}
/// Result of [`ChainSync::poll_block_announce_validation`].
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PollBlockAnnounceValidation {
/// The announcement failed at validation.
///
/// The peer reputation should be decreased.
Failure {
/// Who sent the processed block announcement?
who: PeerId,
/// Should the peer be disconnected?
disconnect: bool,
},
/// The announcement does not require further handling.
Nothing {
/// Who sent the processed block announcement?
who: PeerId,
/// Was this their new best block?
is_best: bool,
/// The announcement.
announce: BlockAnnounce,
},
/// The block announcement should be skipped.
Skip,
}
/// Sync operation mode.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum SyncMode {
/// Full block download and verification.
Full,
/// Download blocks and the latest state.
LightState {
/// Skip state proof download and verification.
skip_proofs: bool,
/// Download indexed transactions for recent blocks.
storage_chain_mode: bool,
},
/// Warp sync - verify authority set transitions and the latest state.
Warp,
}
impl SyncMode {
/// Returns `true` if `self` is [`Self::Warp`].
pub fn is_warp(&self) -> bool {
matches!(self, Self::Warp)
}
/// Returns `true` if `self` is [`Self::LightState`].
pub fn light_state(&self) -> bool {
matches!(self, Self::LightState { .. })
}
}
impl Default for SyncMode {
fn default() -> Self {
Self::Full
}
}
#[derive(Debug)]
pub struct Metrics {
pub queued_blocks: u32,
pub fork_targets: u32,
pub justifications: metrics::Metrics,
}
#[derive(Debug)]
pub enum PeerRequest {
Block(BlockRequest),
State,
WarpProof,
}
/// Wrapper for implementation-specific state request.
///
/// NOTE: Implementation must be able to encode and decode it for network purposes.
pub struct OpaqueStateRequest(pub Box);
impl fmt::Debug for OpaqueStateRequest {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("OpaqueStateRequest").finish()
}
}
/// Wrapper for implementation-specific state response.
///
/// NOTE: Implementation must be able to encode and decode it for network purposes.
pub struct OpaqueStateResponse(pub Box);
impl fmt::Debug for OpaqueStateResponse {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("OpaqueStateResponse").finish()
}
}
/// Wrapper for implementation-specific block request.
///
/// NOTE: Implementation must be able to encode and decode it for network purposes.
pub struct OpaqueBlockRequest(pub Box);
impl fmt::Debug for OpaqueBlockRequest {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("OpaqueBlockRequest").finish()
}
}
/// Wrapper for implementation-specific block response.
///
/// NOTE: Implementation must be able to encode and decode it for network purposes.
pub struct OpaqueBlockResponse(pub Box);
impl fmt::Debug for OpaqueBlockResponse {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("OpaqueBlockResponse").finish()
}
}
/// Provides high-level status of syncing.
#[async_trait::async_trait]
pub trait SyncStatusProvider: Send + Sync {
/// Get high-level view of the syncing status.
async fn status(&self) -> Result, ()>;
}
#[async_trait::async_trait]
impl SyncStatusProvider for Arc
where
T: ?Sized,
T: SyncStatusProvider,
Block: BlockT,
{
async fn status(&self) -> Result, ()> {
T::status(self).await
}
}
/// Syncing-related events that other protocols can subscribe to.
pub enum SyncEvent {
/// Peer that the syncing implementation is tracking connected.
PeerConnected(PeerId),
/// Peer that the syncing implementation was tracking disconnected.
PeerDisconnected(PeerId),
}
pub trait SyncEventStream: Send + Sync {
/// Subscribe to syncing-related events.
fn event_stream(&self, name: &'static str) -> Pin + Send>>;
}
impl SyncEventStream for Arc
where
T: ?Sized,
T: SyncEventStream,
{
fn event_stream(&self, name: &'static str) -> Pin + Send>> {
T::event_stream(self, name)
}
}
/// Something that represents the syncing strategy to download past and future blocks of the chain.
pub trait ChainSync: Send {
/// Returns the state of the sync of the given peer.
///
/// Returns `None` if the peer is unknown.
fn peer_info(&self, who: &PeerId) -> Option>;
/// Returns the current sync status.
fn status(&self) -> SyncStatus;
/// Number of active forks requests. This includes
/// requests that are pending or could be issued right away.
fn num_sync_requests(&self) -> usize;
/// Number of downloaded blocks.
fn num_downloaded_blocks(&self) -> usize;
/// Returns the current number of peers stored within this state machine.
fn num_peers(&self) -> usize;
/// Returns the number of peers we're connected to and that are being queried.
fn num_active_peers(&self) -> usize;
/// Handle a new connected peer.
///
/// Call this method whenever we connect to a new peer.
fn new_peer(
&mut self,
who: PeerId,
best_hash: Block::Hash,
best_number: NumberFor,
) -> Result