// 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>, BadPeer>; /// Signal that a new best block has been imported. fn update_chain_info(&mut self, best_hash: &Block::Hash, best_number: NumberFor); /// Schedule a justification request for the given block. fn request_justification(&mut self, hash: &Block::Hash, number: NumberFor); /// Clear all pending justification requests. fn clear_justification_requests(&mut self); /// Request syncing for the given block from given set of peers. fn set_sync_fork_request( &mut self, peers: Vec, hash: &Block::Hash, number: NumberFor, ); /// Handle a response from the remote to a block request that we made. /// /// `request` must be the original request that triggered `response`. /// or `None` if data comes from the block announcement. /// /// If this corresponds to a valid block, this outputs the block that /// must be imported in the import queue. fn on_block_data( &mut self, who: &PeerId, request: Option>, response: BlockResponse, ) -> Result, BadPeer>; /// Handle a response from the remote to a justification request that we made. /// /// `request` must be the original request that triggered `response`. fn on_block_justification( &mut self, who: PeerId, response: BlockResponse, ) -> Result, BadPeer>; /// Call this when a justification has been processed by the import queue, /// with or without errors. fn on_justification_import( &mut self, hash: Block::Hash, number: NumberFor, success: bool, ); /// Notify about finalization of the given block. fn on_block_finalized(&mut self, hash: &Block::Hash, number: NumberFor); /// Push a block announce validation. /// /// It is required that [`ChainSync::poll_block_announce_validation`] is called /// to check for finished block announce validations. fn push_block_announce_validation( &mut self, who: PeerId, hash: Block::Hash, announce: BlockAnnounce, is_best: bool, ); /// Poll block announce validation. /// /// Block announce validations can be pushed by using /// [`ChainSync::push_block_announce_validation`]. /// /// This should be polled until it returns [`Poll::Pending`]. fn poll_block_announce_validation( &mut self, cx: &mut std::task::Context<'_>, ) -> Poll>; /// Call when a peer has disconnected. /// Canceled obsolete block request may result in some blocks being ready for /// import, so this functions checks for such blocks and returns them. fn peer_disconnected(&mut self, who: &PeerId); /// Return some key metrics. fn metrics(&self) -> Metrics; /// Access blocks from implementation-specific block response. fn block_response_into_blocks( &self, request: &BlockRequest, response: OpaqueBlockResponse, ) -> Result>, String>; /// Advance the state of `ChainSync` /// /// Internally calls [`ChainSync::poll_block_announce_validation()`] and /// this function should be polled until it returns [`Poll::Pending`] to /// consume all pending events. fn poll( &mut self, cx: &mut std::task::Context, ) -> Poll>; /// Send block request to peer fn send_block_request(&mut self, who: PeerId, request: BlockRequest); }