// This file is part of Bizinikiwi. // 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 . //! [`SyncingStrategy`] defines an interface [`crate::engine::SyncingEngine`] uses as a specific //! syncing algorithm. //! //! A few different strategies are provided by Bizinikiwi out of the box with custom strategies //! possible too. pub mod chain_sync; mod disconnected_peers; pub mod pezkuwi; pub mod state; pub mod state_sync; pub mod warp; use crate::{ pending_responses::ResponseFuture, service::network::NetworkServiceHandle, types::{BadPeer, SyncStatus}, }; use pezsc_consensus::{BlockImportError, BlockImportStatus, IncomingBlock}; use pezsc_network::ProtocolName; use pezsc_network_common::sync::message::BlockAnnounce; use pezsc_network_types::PeerId; use pezsp_blockchain::Error as ClientError; use pezsp_consensus::BlockOrigin; use pezsp_runtime::{ traits::{Block as BlockT, NumberFor}, Justifications, }; use std::any::Any; /// Syncing strategy for syncing engine to use pub trait SyncingStrategy: Send where B: BlockT, { /// Notify syncing state machine that a new sync peer has connected. fn add_peer(&mut self, peer_id: PeerId, best_hash: B::Hash, best_number: NumberFor); /// Notify that a sync peer has disconnected. fn remove_peer(&mut self, peer_id: &PeerId); /// Submit a validated block announcement. /// /// Returns new best hash & best number of the peer if they are updated. #[must_use] fn on_validated_block_announce( &mut self, is_best: bool, peer_id: PeerId, announce: &BlockAnnounce, ) -> Option<(B::Hash, NumberFor)>; /// Configure an explicit fork sync request in case external code has detected that there is a /// stale fork missing. /// /// Note that this function should not be used for recent blocks. /// Sync should be able to download all the recent forks normally. /// /// Passing empty `peers` set effectively removes the sync request. fn set_sync_fork_request(&mut self, peers: Vec, hash: &B::Hash, number: NumberFor); /// Request extra justification. fn request_justification(&mut self, hash: &B::Hash, number: NumberFor); /// Clear extra justification requests. fn clear_justification_requests(&mut self); /// Report a justification import (successful or not). fn on_justification_import(&mut self, hash: B::Hash, number: NumberFor, success: bool); /// Process generic response. /// /// Strategy has to create opaque response and should be to downcast it back into concrete type /// internally. Failure to downcast is an implementation bug. fn on_generic_response( &mut self, peer_id: &PeerId, key: StrategyKey, protocol_name: ProtocolName, response: Box, ); /// A batch of blocks that have been processed, with or without errors. /// /// Call this when a batch of blocks that have been processed by the import queue, with or /// without errors. fn on_blocks_processed( &mut self, imported: usize, count: usize, results: Vec<(Result>, BlockImportError>, B::Hash)>, ); /// Notify a syncing strategy that a block has been finalized. fn on_block_finalized(&mut self, hash: &B::Hash, number: NumberFor); /// Inform sync about a new best imported block. fn update_chain_info(&mut self, best_hash: &B::Hash, best_number: NumberFor); // Are we in major sync mode? fn is_major_syncing(&self) -> bool; /// Get the number of peers known to the syncing strategy. fn num_peers(&self) -> usize; /// Returns the current sync status. fn status(&self) -> SyncStatus; /// Get the total number of downloaded blocks. fn num_downloaded_blocks(&self) -> usize; /// Get an estimate of the number of parallel sync requests. fn num_sync_requests(&self) -> usize; /// Get actions that should be performed by the owner on the strategy's behalf #[must_use] fn actions( &mut self, // TODO: Consider making this internal property of the strategy network_service: &NetworkServiceHandle, ) -> Result>, ClientError>; } /// The key identifying a specific strategy for responses routing. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct StrategyKey(&'static str); impl StrategyKey { /// Instantiate opaque strategy key. pub const fn new(key: &'static str) -> Self { Self(key) } } pub enum SyncingAction { /// Start request to peer. StartRequest { peer_id: PeerId, key: StrategyKey, request: ResponseFuture, // Whether to remove obsolete pending responses. remove_obsolete: bool, }, /// Drop stale request. CancelRequest { peer_id: PeerId, key: StrategyKey }, /// Peer misbehaved. Disconnect, report it and cancel any requests to it. DropPeer(BadPeer), /// Import blocks. ImportBlocks { origin: BlockOrigin, blocks: Vec> }, /// Import justifications. ImportJustifications { peer_id: PeerId, hash: B::Hash, number: NumberFor, justifications: Justifications, }, /// Strategy finished. Nothing to do, this is handled by `PezkuwiSyncingStrategy`. Finished, } // Note: Ideally we can deduce this information with #[derive(derive_more::Debug)]. // However, we'd need a bump to the latest version 2 of the crate. impl std::fmt::Debug for SyncingAction where B: BlockT, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match &self { Self::StartRequest { peer_id, key, remove_obsolete, .. } => { write!( f, "StartRequest {{ peer_id: {:?}, key: {:?}, remove_obsolete: {:?} }}", peer_id, key, remove_obsolete ) }, Self::CancelRequest { peer_id, key } => { write!(f, "CancelRequest {{ peer_id: {:?}, key: {:?} }}", peer_id, key) }, Self::DropPeer(peer) => write!(f, "DropPeer({:?})", peer), Self::ImportBlocks { blocks, .. } => write!(f, "ImportBlocks({:?})", blocks), Self::ImportJustifications { hash, number, .. } => { write!(f, "ImportJustifications({:?}, {:?})", hash, number) }, Self::Finished => write!(f, "Finished"), } } } impl SyncingAction { /// Returns `true` if the syncing action has completed. pub fn is_finished(&self) -> bool { matches!(self, SyncingAction::Finished) } #[cfg(test)] pub(crate) fn name(&self) -> &'static str { match self { Self::StartRequest { .. } => "StartRequest", Self::CancelRequest { .. } => "CancelRequest", Self::DropPeer(_) => "DropPeer", Self::ImportBlocks { .. } => "ImportBlocks", Self::ImportJustifications { .. } => "ImportJustifications", Self::Finished => "Finished", } } }