| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431 |
- /* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2024 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
- use std::sync::Arc;
- use async_trait::async_trait;
- use log::{debug, error};
- use smol::Executor;
- use darkfi::{
- blockchain::{BlockInfo, Header, HeaderHash},
- impl_p2p_message,
- net::{
- ChannelPtr, Message, MessageSubscription, ProtocolBase, ProtocolBasePtr,
- ProtocolJobsManager, ProtocolJobsManagerPtr,
- },
- validator::{consensus::Proposal, ValidatorPtr},
- Result,
- };
- use darkfi_serial::{SerialDecodable, SerialEncodable};
- // Constant defining how many blocks we send during syncing.
- pub const BATCH: usize = 10;
- /// Structure represening a request to ask a node for their current
- /// canonical(finalized) tip block hash, if they are synced. We also
- /// include our own tip, so they can verify we follow the same sequence.
- #[derive(Debug, SerialEncodable, SerialDecodable)]
- pub struct TipRequest {
- /// Canonical(finalized) tip block hash
- pub tip: HeaderHash,
- }
- impl_p2p_message!(TipRequest, "tiprequest");
- /// Structure representing the response to `TipRequest`,
- /// containing a boolean flag to indicate if we are synced,
- /// and our canonical(finalized) tip block height and hash.
- #[derive(Debug, SerialEncodable, SerialDecodable)]
- pub struct TipResponse {
- /// Flag indicating the node is synced
- pub synced: bool,
- /// Canonical(finalized) tip block height
- pub height: Option<u32>,
- /// Canonical(finalized) tip block hash
- pub hash: Option<HeaderHash>,
- }
- impl_p2p_message!(TipResponse, "tipresponse");
- /// Structure represening a request to ask a node for up to `BATCH` headers before
- /// the provided header height.
- #[derive(Debug, SerialEncodable, SerialDecodable)]
- pub struct HeaderSyncRequest {
- /// Header height
- pub height: u32,
- }
- impl_p2p_message!(HeaderSyncRequest, "headersyncrequest");
- /// Structure representing the response to `HeaderSyncRequest`,
- /// containing up to `BATCH` headers before the requested block height.
- #[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
- pub struct HeaderSyncResponse {
- /// Response headers
- pub headers: Vec<Header>,
- }
- impl_p2p_message!(HeaderSyncResponse, "headersyncresponse");
- /// Structure represening a request to ask a node for up to`BATCH` blocks
- /// of provided headers.
- #[derive(Debug, SerialEncodable, SerialDecodable)]
- pub struct SyncRequest {
- /// Header hashes
- pub headers: Vec<HeaderHash>,
- }
- impl_p2p_message!(SyncRequest, "syncrequest");
- /// Structure representing the response to `SyncRequest`,
- /// containing up to `BATCH` blocks after the requested block height.
- #[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
- pub struct SyncResponse {
- /// Response blocks
- pub blocks: Vec<BlockInfo>,
- }
- impl_p2p_message!(SyncResponse, "syncresponse");
- /// Structure represening a request to ask a node a fork sequence.
- /// If we include a specific fork tip, they have to return its sequence,
- /// otherwise they respond with their best fork sequence.
- /// We also include our own canonical(finalized) tip, so they can verify
- /// we follow the same sequence.
- #[derive(Debug, SerialEncodable, SerialDecodable)]
- pub struct ForkSyncRequest {
- /// Canonical(finalized) tip block hash
- pub tip: HeaderHash,
- /// Optional fork tip block hash
- pub fork_tip: Option<HeaderHash>,
- }
- impl_p2p_message!(ForkSyncRequest, "forksyncrequest");
- /// Structure representing the response to `ForkSyncRequest`,
- /// containing the requested fork sequence.
- #[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
- pub struct ForkSyncResponse {
- /// Response fork proposals
- pub proposals: Vec<Proposal>,
- }
- impl_p2p_message!(ForkSyncResponse, "forksyncresponse");
- pub struct ProtocolSync {
- tip_sub: MessageSubscription<TipRequest>,
- header_sub: MessageSubscription<HeaderSyncRequest>,
- request_sub: MessageSubscription<SyncRequest>,
- fork_request_sub: MessageSubscription<ForkSyncRequest>,
- jobsman: ProtocolJobsManagerPtr,
- validator: ValidatorPtr,
- channel: ChannelPtr,
- }
- impl ProtocolSync {
- pub async fn init(channel: ChannelPtr, validator: ValidatorPtr) -> Result<ProtocolBasePtr> {
- debug!(
- target: "darkfid::proto::protocol_sync::init",
- "Adding ProtocolSync to the protocol registry"
- );
- let msg_subsystem = channel.message_subsystem();
- msg_subsystem.add_dispatch::<TipRequest>().await;
- msg_subsystem.add_dispatch::<TipResponse>().await;
- msg_subsystem.add_dispatch::<HeaderSyncRequest>().await;
- msg_subsystem.add_dispatch::<HeaderSyncResponse>().await;
- msg_subsystem.add_dispatch::<SyncRequest>().await;
- msg_subsystem.add_dispatch::<SyncResponse>().await;
- msg_subsystem.add_dispatch::<ForkSyncRequest>().await;
- msg_subsystem.add_dispatch::<ForkSyncResponse>().await;
- let tip_sub = channel.subscribe_msg::<TipRequest>().await?;
- let header_sub = channel.subscribe_msg::<HeaderSyncRequest>().await?;
- let request_sub = channel.subscribe_msg::<SyncRequest>().await?;
- let fork_request_sub = channel.subscribe_msg::<ForkSyncRequest>().await?;
- Ok(Arc::new(Self {
- tip_sub,
- header_sub,
- request_sub,
- fork_request_sub,
- jobsman: ProtocolJobsManager::new("SyncProtocol", channel.clone()),
- validator,
- channel,
- }))
- }
- async fn handle_receive_tip_request(self: Arc<Self>) -> Result<()> {
- debug!(target: "darkfid::proto::protocol_sync::handle_receive_tip_request", "START");
- loop {
- let request = match self.tip_sub.receive().await {
- Ok(v) => v,
- Err(e) => {
- debug!(
- target: "darkfid::proto::protocol_sync::handle_receive_tip_request",
- "recv fail: {}",
- e
- );
- continue
- }
- };
- // Check if node has finished syncing its blockchain
- let response = if !*self.validator.synced.read().await {
- debug!(
- target: "darkfid::proto::protocol_sync::handle_receive_tip_request",
- "Node still syncing blockchain, skipping..."
- );
- TipResponse { synced: false, height: None, hash: None }
- } else {
- // Check we follow the same sequence
- match self.validator.blockchain.blocks.contains(&request.tip) {
- Ok(contains) => {
- if !contains {
- debug!(
- target: "darkfid::proto::protocol_sync::handle_receive_tip_request",
- "Node doesn't follow request sequence"
- );
- continue
- }
- }
- Err(e) => {
- error!(
- target: "darkfid::proto::protocol_sync::handle_receive_tip_request",
- "block_store.contains fail: {}",
- e
- );
- continue
- }
- }
- // Grab our current tip and return it
- let tip = match self.validator.blockchain.last() {
- Ok(v) => v,
- Err(e) => {
- error!(
- target: "darkfid::proto::protocol_sync::handle_receive_tip_request",
- "blockchain.last fail: {}",
- e
- );
- continue
- }
- };
- TipResponse { synced: true, height: Some(tip.0), hash: Some(tip.1) }
- };
- if let Err(e) = self.channel.send(&response).await {
- error!(
- target: "darkfid::proto::protocol_sync::handle_receive_tip_request",
- "channel send fail: {}",
- e
- )
- };
- }
- }
- async fn handle_receive_header_request(self: Arc<Self>) -> Result<()> {
- debug!(target: "darkfid::proto::protocol_sync::handle_receive_header_request", "START");
- loop {
- let request = match self.header_sub.receive().await {
- Ok(v) => v,
- Err(e) => {
- debug!(
- target: "darkfid::proto::protocol_sync::handle_receive_header_request",
- "recv fail: {}",
- e
- );
- continue
- }
- };
- // Check if node has finished syncing its blockchain
- if !*self.validator.synced.read().await {
- debug!(
- target: "darkfid::proto::protocol_sync::handle_receive_header_request",
- "Node still syncing blockchain, skipping..."
- );
- continue
- }
- let headers = match self.validator.blockchain.get_headers_before(request.height, BATCH)
- {
- Ok(v) => v,
- Err(e) => {
- error!(
- target: "darkfid::proto::protocol_sync::handle_receive_header_request",
- "get_headers_before fail: {}",
- e
- );
- continue
- }
- };
- let response = HeaderSyncResponse { headers };
- if let Err(e) = self.channel.send(&response).await {
- error!(
- target: "darkfid::proto::protocol_sync::handle_receive_header_request",
- "channel send fail: {}",
- e
- )
- };
- }
- }
- async fn handle_receive_request(self: Arc<Self>) -> Result<()> {
- debug!(target: "darkfid::proto::protocol_sync::handle_receive_request", "START");
- loop {
- let request = match self.request_sub.receive().await {
- Ok(v) => v,
- Err(e) => {
- debug!(
- target: "darkfid::proto::protocol_sync::handle_receive_request",
- "recv fail: {}",
- e
- );
- continue
- }
- };
- // Check if node has finished syncing its blockchain
- if !*self.validator.synced.read().await {
- debug!(
- target: "darkfid::proto::protocol_sync::handle_receive_request",
- "Node still syncing blockchain, skipping..."
- );
- continue
- }
- // Check if request exists the configured limit
- if request.headers.len() > BATCH {
- debug!(
- target: "darkfid::proto::protocol_sync::handle_receive_request",
- "Node requested more blocks than allowed."
- );
- continue
- }
- let blocks = match self.validator.blockchain.get_blocks_by_hash(&request.headers) {
- Ok(v) => v,
- Err(e) => {
- error!(
- target: "darkfid::proto::protocol_sync::handle_receive_request",
- "get_blocks_after fail: {}",
- e
- );
- continue
- }
- };
- let response = SyncResponse { blocks };
- if let Err(e) = self.channel.send(&response).await {
- error!(
- target: "darkfid::proto::protocol_sync::handle_receive_request",
- "channel send fail: {}",
- e
- )
- };
- }
- }
- async fn handle_receive_fork_request(self: Arc<Self>) -> Result<()> {
- debug!(target: "darkfid::proto::protocol_sync::handle_receive_fork_request", "START");
- loop {
- let request = match self.fork_request_sub.receive().await {
- Ok(v) => v,
- Err(e) => {
- debug!(
- target: "darkfid::proto::protocol_sync::handle_receive_fork_request",
- "recv fail: {}",
- e
- );
- continue
- }
- };
- // Check if node has finished syncing its blockchain
- if !*self.validator.synced.read().await {
- debug!(
- target: "darkfid::proto::protocol_sync::handle_receive_fork_request",
- "Node still syncing blockchain, skipping..."
- );
- continue
- }
- debug!(target: "darkfid::proto::protocol_sync::handle_receive_request", "Received request: {request:?}");
- // If a fork tip is provided, grab its fork proposals sequence.
- // Otherwise, grab best fork proposals sequence.
- let proposals = match request.fork_tip {
- Some(fork_tip) => {
- self.validator.consensus.get_fork_proposals(request.tip, fork_tip).await
- }
- None => self.validator.consensus.get_best_fork_proposals(request.tip).await,
- };
- let proposals = match proposals {
- Ok(p) => p,
- Err(e) => {
- debug!(
- target: "darkfid::proto::protocol_sync::handle_receive_request",
- "Getting fork proposals failed: {}",
- e
- );
- continue
- }
- };
- let response = ForkSyncResponse { proposals };
- debug!(target: "darkfid::proto::protocol_sync::handle_receive_request", "Response: {response:?}");
- if let Err(e) = self.channel.send(&response).await {
- debug!(
- target: "darkfid::proto::protocol_sync::handle_receive_fork_request",
- "channel send fail: {}",
- e
- )
- };
- }
- }
- }
- #[async_trait]
- impl ProtocolBase for ProtocolSync {
- async fn start(self: Arc<Self>, executor: Arc<Executor<'_>>) -> Result<()> {
- debug!(target: "darkfid::proto::protocol_sync::start", "START");
- self.jobsman.clone().start(executor.clone());
- self.jobsman
- .clone()
- .spawn(self.clone().handle_receive_tip_request(), executor.clone())
- .await;
- self.jobsman
- .clone()
- .spawn(self.clone().handle_receive_header_request(), executor.clone())
- .await;
- self.jobsman.clone().spawn(self.clone().handle_receive_request(), executor.clone()).await;
- self.jobsman
- .clone()
- .spawn(self.clone().handle_receive_fork_request(), executor.clone())
- .await;
- debug!(target: "darkfid::proto::protocol_sync::start", "END");
- Ok(())
- }
- fn name(&self) -> &'static str {
- "ProtocolSync"
- }
- }
|