/* This file is part of DarkFi (https://dark.fi) * * Copyright (C) 2020-2025 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 . */ use std::sync::Arc; use async_trait::async_trait; use tracing::{debug, error}; use darkfi::{ blockchain::{BlockInfo, Header, HeaderHash}, impl_p2p_message, net::{ metering::MeteringConfiguration, protocol::protocol_generic::{ ProtocolGenericAction, ProtocolGenericHandler, ProtocolGenericHandlerPtr, }, session::SESSION_DEFAULT, Message, P2pPtr, }, system::ExecutorPtr, util::time::NanoTimestamp, validator::{consensus::Proposal, ValidatorPtr}, Error, Result, }; use darkfi_serial::{SerialDecodable, SerialEncodable}; // Constant defining max elements we send in vectors during syncing. pub const BATCH: usize = 20; // TODO: Fine tune // Protocol metering configuration. // Since all messages are synchronous(request -> response) we will define // strict rules to prevent spamming. // Each message score will be 1, with a threshold of 20 and expiry time of 5. // Check ../tests/metering.rs for each message max bytes definition. const PROTOCOL_SYNC_METERING_CONFIGURATION: MeteringConfiguration = MeteringConfiguration { threshold: 20, sleep_step: 500, expiry_time: NanoTimestamp::from_secs(5), }; /// Structure represening a request to ask a node for their current /// canonical(confirmed) tip block hash, if they are synced. We also /// include our own tip, so they can verify we follow the same sequence. #[derive(Clone, Debug, SerialEncodable, SerialDecodable)] pub struct TipRequest { /// Canonical(confirmed) tip block hash pub tip: HeaderHash, } impl_p2p_message!(TipRequest, "tiprequest", 32, 1, PROTOCOL_SYNC_METERING_CONFIGURATION); /// Structure representing the response to `TipRequest`, /// containing a boolean flag to indicate if we are synced, /// and our canonical(confirmed) tip block height and hash. #[derive(Clone, Debug, SerialEncodable, SerialDecodable)] pub struct TipResponse { /// Flag indicating the node is synced pub synced: bool, /// Canonical(confirmed) tip block height pub height: Option, /// Canonical(confirmed) tip block hash pub hash: Option, } impl_p2p_message!(TipResponse, "tipresponse", 39, 1, PROTOCOL_SYNC_METERING_CONFIGURATION); /// Structure represening a request to ask a node for up to `BATCH` headers before /// the provided header height. #[derive(Clone, Debug, SerialEncodable, SerialDecodable)] pub struct HeaderSyncRequest { /// Header height pub height: u32, } impl_p2p_message!( HeaderSyncRequest, "headersyncrequest", 4, 1, PROTOCOL_SYNC_METERING_CONFIGURATION ); /// Structure representing the response to `HeaderSyncRequest`, /// containing up to `BATCH` headers before the requested block height. #[derive(Clone, Debug, SerialEncodable, SerialDecodable)] pub struct HeaderSyncResponse { /// Response headers pub headers: Vec
, } impl_p2p_message!( HeaderSyncResponse, "headersyncresponse", 8192, // We leave some headroom for merge mining data 1, PROTOCOL_SYNC_METERING_CONFIGURATION ); /// Structure represening a request to ask a node for up to`BATCH` blocks /// of provided headers. #[derive(Clone, Debug, SerialEncodable, SerialDecodable)] pub struct SyncRequest { /// Header hashes pub headers: Vec, } impl_p2p_message!(SyncRequest, "syncrequest", 641, 1, PROTOCOL_SYNC_METERING_CONFIGURATION); /// Structure representing the response to `SyncRequest`, /// containing up to `BATCH` blocks after the requested block height. #[derive(Clone, Debug, SerialEncodable, SerialDecodable)] pub struct SyncResponse { /// Response blocks pub blocks: Vec, } impl_p2p_message!(SyncResponse, "syncresponse", 0, 1, PROTOCOL_SYNC_METERING_CONFIGURATION); /// 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(confirmed) tip, so they can verify /// we follow the same sequence. #[derive(Clone, Debug, SerialEncodable, SerialDecodable)] pub struct ForkSyncRequest { /// Canonical(confirmed) tip block hash pub tip: HeaderHash, /// Optional fork tip block hash pub fork_tip: Option, } impl_p2p_message!(ForkSyncRequest, "forksyncrequest", 65, 1, PROTOCOL_SYNC_METERING_CONFIGURATION); /// Structure representing the response to `ForkSyncRequest`, /// containing the requested fork sequence, up to `BATCH` proposals. #[derive(Clone, Debug, SerialEncodable, SerialDecodable)] pub struct ForkSyncResponse { /// Response fork proposals pub proposals: Vec, } impl_p2p_message!(ForkSyncResponse, "forksyncresponse", 0, 1, PROTOCOL_SYNC_METERING_CONFIGURATION); /// Structure represening a request to ask a node a fork header for the /// requested height. The fork is identified by the provided header hash. #[derive(Clone, Debug, SerialEncodable, SerialDecodable)] pub struct ForkHeaderHashRequest { /// Header height pub height: u32, /// Block header hash to identify the fork pub fork_header: HeaderHash, } impl_p2p_message!( ForkHeaderHashRequest, "forkheaderhashrequest", 36, 1, PROTOCOL_SYNC_METERING_CONFIGURATION ); /// Structure representing the response to `ForkHeaderHashRequest`, /// containing the requested fork header hash, if it was found. #[derive(Clone, Debug, SerialEncodable, SerialDecodable)] pub struct ForkHeaderHashResponse { /// Response fork block header hash pub fork_header: Option, } impl_p2p_message!( ForkHeaderHashResponse, "forkheaderhashresponse", 33, 1, PROTOCOL_SYNC_METERING_CONFIGURATION ); /// Structure represening a request to ask a node for up to `BATCH` /// fork headers for provided header hashes. The fork is identified /// by the provided header hash. #[derive(Clone, Debug, SerialEncodable, SerialDecodable)] pub struct ForkHeadersRequest { /// Header hashes pub headers: Vec, /// Block header hash to identify the fork pub fork_header: HeaderHash, } impl_p2p_message!( ForkHeadersRequest, "forkheadersrequest", 673, 1, PROTOCOL_SYNC_METERING_CONFIGURATION ); /// Structure representing the response to `ForkHeadersRequest`, /// containing up to `BATCH` fork headers. #[derive(Clone, Debug, SerialEncodable, SerialDecodable)] pub struct ForkHeadersResponse { /// Response headers pub headers: Vec
, } impl_p2p_message!( ForkHeadersResponse, "forkheadersresponse", 8192, // We leave some headroom for merge mining data 1, PROTOCOL_SYNC_METERING_CONFIGURATION ); /// Structure represening a request to ask a node for up to `BATCH` /// fork proposals for provided header hashes. The fork is identified /// by the provided header hash. #[derive(Clone, Debug, SerialEncodable, SerialDecodable)] pub struct ForkProposalsRequest { /// Header hashes pub headers: Vec, /// Block header hash to identify the fork pub fork_header: HeaderHash, } impl_p2p_message!( ForkProposalsRequest, "forkproposalsrequest", 673, 1, PROTOCOL_SYNC_METERING_CONFIGURATION ); /// Structure representing the response to `ForkProposalsRequest`, /// containing up to `BATCH` fork headers. #[derive(Clone, Debug, SerialEncodable, SerialDecodable)] pub struct ForkProposalsResponse { /// Response proposals pub proposals: Vec, } impl_p2p_message!( ForkProposalsResponse, "forkproposalsresponse", 0, 1, PROTOCOL_SYNC_METERING_CONFIGURATION ); /// Atomic pointer to the `ProtocolSync` handler. pub type ProtocolSyncHandlerPtr = Arc; /// Handler managing all `ProtocolSync` messages, over generic P2P protocols. pub struct ProtocolSyncHandler { /// The generic handler for `TipRequest` messages. tip_handler: ProtocolGenericHandlerPtr, /// The generic handler for `HeaderSyncRequest` messages. header_handler: ProtocolGenericHandlerPtr, /// The generic handler for `SyncRequest` messages. sync_handler: ProtocolGenericHandlerPtr, /// The generic handler for `ForkSyncRequest` messages. fork_sync_handler: ProtocolGenericHandlerPtr, /// The generic handler for `ForkHeaderHashRequest` messages. fork_header_hash_handler: ProtocolGenericHandlerPtr, /// The generic handler for `ForkHeadersRequest` messages. fork_headers_handler: ProtocolGenericHandlerPtr, /// The generic handler for `ForkProposalsRequest` messages. fork_proposals_handler: ProtocolGenericHandlerPtr, } impl ProtocolSyncHandler { /// Initialize the generic prototocol handlers for all `ProtocolSync` messages /// and register them to the provided P2P network, using the default session flag. pub async fn init(p2p: &P2pPtr) -> ProtocolSyncHandlerPtr { debug!( target: "darkfid::proto::protocol_sync::init", "Adding all sync protocols to the protocol registry" ); let tip_handler = ProtocolGenericHandler::new(p2p, "ProtocolSyncTip", SESSION_DEFAULT).await; let header_handler = ProtocolGenericHandler::new(p2p, "ProtocolSyncHeader", SESSION_DEFAULT).await; let sync_handler = ProtocolGenericHandler::new(p2p, "ProtocolSync", SESSION_DEFAULT).await; let fork_sync_handler = ProtocolGenericHandler::new(p2p, "ProtocolSyncFork", SESSION_DEFAULT).await; let fork_header_hash_handler = ProtocolGenericHandler::new(p2p, "ProtocolSyncForkHeaderHash", SESSION_DEFAULT).await; let fork_headers_handler = ProtocolGenericHandler::new(p2p, "ProtocolSyncForkHeaders", SESSION_DEFAULT).await; let fork_proposals_handler = ProtocolGenericHandler::new(p2p, "ProtocolSyncForkProposals", SESSION_DEFAULT).await; Arc::new(Self { tip_handler, header_handler, sync_handler, fork_sync_handler, fork_header_hash_handler, fork_headers_handler, fork_proposals_handler, }) } /// Start all `ProtocolSync` background tasks. pub async fn start(&self, executor: &ExecutorPtr, validator: &ValidatorPtr) -> Result<()> { debug!( target: "darkfid::proto::protocol_sync::start", "Starting sync protocols handlers tasks..." ); self.tip_handler.task.clone().start( handle_receive_tip_request(self.tip_handler.clone(), validator.clone()), |res| async move { match res { Ok(()) | Err(Error::DetachedTaskStopped) => { /* Do nothing */ } Err(e) => error!(target: "darkfid::proto::protocol_sync::start", "Failed starting ProtocolSyncTip handler task: {e}"), } }, Error::DetachedTaskStopped, executor.clone(), ); self.header_handler.task.clone().start( handle_receive_header_request(self.header_handler.clone(), validator.clone()), |res| async move { match res { Ok(()) | Err(Error::DetachedTaskStopped) => { /* Do nothing */ } Err(e) => error!(target: "darkfid::proto::protocol_sync::start", "Failed starting ProtocolSyncHeader handler task: {e}"), } }, Error::DetachedTaskStopped, executor.clone(), ); self.sync_handler.task.clone().start( handle_receive_request(self.sync_handler.clone(), validator.clone()), |res| async move { match res { Ok(()) | Err(Error::DetachedTaskStopped) => { /* Do nothing */ } Err(e) => error!(target: "darkfid::proto::protocol_sync::start", "Failed starting ProtocolSync handler task: {e}"), } }, Error::DetachedTaskStopped, executor.clone(), ); self.fork_sync_handler.task.clone().start( handle_receive_fork_request(self.fork_sync_handler.clone(), validator.clone()), |res| async move { match res { Ok(()) | Err(Error::DetachedTaskStopped) => { /* Do nothing */ } Err(e) => error!(target: "darkfid::proto::protocol_sync::start", "Failed starting ProtocolSyncFork handler task: {e}"), } }, Error::DetachedTaskStopped, executor.clone(), ); self.fork_header_hash_handler.task.clone().start( handle_receive_fork_header_hash_request(self.fork_header_hash_handler.clone(), validator.clone()), |res| async move { match res { Ok(()) | Err(Error::DetachedTaskStopped) => { /* Do nothing */ } Err(e) => error!(target: "darkfid::proto::protocol_sync::start", "Failed starting ProtocolSyncForkHeaderHash handler task: {e}"), } }, Error::DetachedTaskStopped, executor.clone(), ); self.fork_headers_handler.task.clone().start( handle_receive_fork_headers_request(self.fork_headers_handler.clone(), validator.clone()), |res| async move { match res { Ok(()) | Err(Error::DetachedTaskStopped) => { /* Do nothing */ } Err(e) => error!(target: "darkfid::proto::protocol_sync::start", "Failed starting ProtocolSyncForkHeaders handler task: {e}"), } }, Error::DetachedTaskStopped, executor.clone(), ); self.fork_proposals_handler.task.clone().start( handle_receive_fork_proposals_request(self.fork_proposals_handler.clone(), validator.clone()), |res| async move { match res { Ok(()) | Err(Error::DetachedTaskStopped) => { /* Do nothing */ } Err(e) => error!(target: "darkfid::proto::protocol_sync::start", "Failed starting ProtocolSyncForkProposals handler task: {e}"), } }, Error::DetachedTaskStopped, executor.clone(), ); debug!( target: "darkfid::proto::protocol_sync::start", "Sync protocols handlers tasks started!" ); Ok(()) } /// Stop all `ProtocolSync` background tasks. pub async fn stop(&self) { debug!(target: "darkfid::proto::protocol_sync::stop", "Terminating sync protocols handlers tasks..."); self.tip_handler.task.stop().await; self.header_handler.task.stop().await; self.sync_handler.task.stop().await; self.fork_sync_handler.task.stop().await; self.fork_header_hash_handler.task.stop().await; self.fork_headers_handler.task.stop().await; self.fork_proposals_handler.task.stop().await; debug!(target: "darkfid::proto::protocol_sync::stop", "Sync protocols handlers tasks terminated!"); } } /// Background handler function for ProtocolSyncTip. async fn handle_receive_tip_request( handler: ProtocolGenericHandlerPtr, validator: ValidatorPtr, ) -> Result<()> { debug!(target: "darkfid::proto::protocol_sync::handle_receive_tip_request", "START"); loop { // Wait for a new tip request message let (channel, request) = match handler.receiver.recv().await { Ok(r) => r, Err(e) => { debug!( target: "darkfid::proto::protocol_sync::handle_receive_tip_request", "recv fail: {e}" ); continue } }; debug!(target: "darkfid::proto::protocol_sync::handle_receive_tip_request", "Received request: {request:?}"); // Check if node has finished syncing its blockchain if !*validator.synced.read().await { debug!( target: "darkfid::proto::protocol_sync::handle_receive_tip_request", "Node still syncing blockchain" ); handler .send_action( channel, ProtocolGenericAction::Response(TipResponse { synced: false, height: None, hash: None, }), ) .await; continue } // Check we follow the same sequence match 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" ); handler .send_action( channel, ProtocolGenericAction::Response(TipResponse { synced: true, height: None, hash: None, }), ) .await; continue } } Err(e) => { error!( target: "darkfid::proto::protocol_sync::handle_receive_tip_request", "block_store.contains fail: {e}" ); handler.send_action(channel, ProtocolGenericAction::Skip).await; continue } } // Grab our current tip and return it let tip = match validator.blockchain.last() { Ok(v) => v, Err(e) => { error!( target: "darkfid::proto::protocol_sync::handle_receive_tip_request", "blockchain.last fail: {e}" ); handler.send_action(channel, ProtocolGenericAction::Skip).await; continue } }; // Send response handler .send_action( channel, ProtocolGenericAction::Response(TipResponse { synced: true, height: Some(tip.0), hash: Some(tip.1), }), ) .await; } } /// Background handler function for ProtocolSyncHeader. async fn handle_receive_header_request( handler: ProtocolGenericHandlerPtr, validator: ValidatorPtr, ) -> Result<()> { debug!(target: "darkfid::proto::protocol_sync::handle_receive_header_request", "START"); loop { // Wait for a new header request message let (channel, request) = match handler.receiver.recv().await { Ok(r) => r, 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 !*validator.synced.read().await { debug!( target: "darkfid::proto::protocol_sync::handle_receive_header_request", "Node still syncing blockchain, skipping..." ); handler.send_action(channel, ProtocolGenericAction::Skip).await; continue } debug!(target: "darkfid::proto::protocol_sync::handle_receive_header_request", "Received request: {request:?}"); // Grab the corresponding headers let headers = match 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}" ); handler.send_action(channel, ProtocolGenericAction::Skip).await; continue } }; // Send response handler .send_action(channel, ProtocolGenericAction::Response(HeaderSyncResponse { headers })) .await; } } /// Background handler function for ProtocolSync. async fn handle_receive_request( handler: ProtocolGenericHandlerPtr, validator: ValidatorPtr, ) -> Result<()> { debug!(target: "darkfid::proto::protocol_sync::handle_receive_request", "START"); loop { // Wait for a new sync request message let (channel, request) = match handler.receiver.recv().await { Ok(r) => r, Err(e) => { debug!( target: "darkfid::proto::protocol_sync::handle_receive_request", "recv fail: {e}" ); continue } }; // Check if node has finished syncing its blockchain if !*validator.synced.read().await { debug!( target: "darkfid::proto::protocol_sync::handle_receive_request", "Node still syncing blockchain, skipping..." ); handler.send_action(channel, ProtocolGenericAction::Skip).await; 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." ); handler.send_action(channel, ProtocolGenericAction::Skip).await; continue } debug!(target: "darkfid::proto::protocol_sync::handle_receive_request", "Received request: {request:?}"); // Grab the corresponding blocks let blocks = match 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}" ); handler.send_action(channel, ProtocolGenericAction::Skip).await; continue } }; // Send response handler .send_action(channel, ProtocolGenericAction::Response(SyncResponse { blocks })) .await; } } /// Background handler function for ProtocolSyncFork. async fn handle_receive_fork_request( handler: ProtocolGenericHandlerPtr, validator: ValidatorPtr, ) -> Result<()> { debug!(target: "darkfid::proto::protocol_sync::handle_receive_fork_request", "START"); loop { // Wait for a new fork sync request message let (channel, request) = match handler.receiver.recv().await { Ok(r) => r, 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 !*validator.synced.read().await { debug!( target: "darkfid::proto::protocol_sync::handle_receive_fork_request", "Node still syncing blockchain, skipping..." ); handler.send_action(channel, ProtocolGenericAction::Skip).await; continue } debug!(target: "darkfid::proto::protocol_sync::handle_receive_fork_request", "Received request: {request:?}"); // Retrieve proposals sequence let proposals = match validator .consensus .get_fork_proposals_after(request.tip, request.fork_tip, BATCH as u32) .await { Ok(p) => p, Err(e) => { debug!( target: "darkfid::proto::protocol_sync::handle_receive_fork_request", "Getting fork proposals failed: {e}" ); handler.send_action(channel, ProtocolGenericAction::Skip).await; continue } }; // Send response handler .send_action(channel, ProtocolGenericAction::Response(ForkSyncResponse { proposals })) .await; } } /// Background handler function for ProtocolSyncForkHeaderHash. async fn handle_receive_fork_header_hash_request( handler: ProtocolGenericHandlerPtr, validator: ValidatorPtr, ) -> Result<()> { debug!(target: "darkfid::proto::protocol_sync::handle_receive_fork_header_hash_request", "START"); loop { // Wait for a new fork header hash request message let (channel, request) = match handler.receiver.recv().await { Ok(r) => r, Err(e) => { debug!( target: "darkfid::proto::protocol_sync::handle_receive_fork_header_hash_request", "recv fail: {e}" ); continue } }; // Check if node has finished syncing its blockchain if !*validator.synced.read().await { debug!( target: "darkfid::proto::protocol_sync::handle_receive_fork_header_hash_request", "Node still syncing blockchain, skipping..." ); handler.send_action(channel, ProtocolGenericAction::Skip).await; continue } debug!(target: "darkfid::proto::protocol_sync::handle_receive_fork_header_hash_request", "Received request: {request:?}"); // Retrieve fork header let fork_header = match validator .consensus .get_fork_header_hash(request.height, &request.fork_header) .await { Ok(h) => h, Err(e) => { debug!( target: "darkfid::proto::protocol_sync::handle_receive_fork_header_hash_request", "Getting fork header hash failed: {e}" ); handler.send_action(channel, ProtocolGenericAction::Skip).await; continue } }; // Send response if header was found if fork_header.is_some() { handler .send_action( channel, ProtocolGenericAction::Response(ForkHeaderHashResponse { fork_header }), ) .await; continue } // If header wasn't found in a fork, check canonical if let Err(e) = validator.blockchain.headers.get(&[request.fork_header], true) { debug!( target: "darkfid::proto::protocol_sync::handle_receive_fork_header_hash_request", "Getting fork header hash failed: {e}" ); handler.send_action(channel, ProtocolGenericAction::Skip).await; continue }; let response = match validator.blockchain.blocks.get_order(&[request.height], false) { Ok(h) => ProtocolGenericAction::Response(ForkHeaderHashResponse { fork_header: h[0] }), Err(e) => { debug!( target: "darkfid::proto::protocol_sync::handle_receive_fork_header_hash_request", "Getting fork header hash failed: {e}" ); ProtocolGenericAction::Skip } }; // Send response handler.send_action(channel, response).await; } } /// Background handler function for ProtocolSyncForkHeaders. async fn handle_receive_fork_headers_request( handler: ProtocolGenericHandlerPtr, validator: ValidatorPtr, ) -> Result<()> { debug!(target: "darkfid::proto::protocol_sync::handle_receive_fork_headers_request", "START"); loop { // Wait for a new fork header hash request message let (channel, request) = match handler.receiver.recv().await { Ok(r) => r, Err(e) => { debug!( target: "darkfid::proto::protocol_sync::handle_receive_fork_headers_request", "recv fail: {e}" ); continue } }; // Check if node has finished syncing its blockchain if !*validator.synced.read().await { debug!( target: "darkfid::proto::protocol_sync::handle_receive_fork_headers_request", "Node still syncing blockchain, skipping..." ); handler.send_action(channel, ProtocolGenericAction::Skip).await; continue } // Check if request exists the configured limit if request.headers.len() > BATCH { debug!( target: "darkfid::proto::protocol_sync::handle_receive_fork_headers_request", "Node requested more headers than allowed." ); handler.send_action(channel, ProtocolGenericAction::Skip).await; continue } debug!(target: "darkfid::proto::protocol_sync::handle_receive_fork_headers_request", "Received request: {request:?}"); // Retrieve fork headers let headers = match validator .consensus .get_fork_headers(&request.headers, &request.fork_header) .await { Ok(h) => h, Err(e) => { debug!( target: "darkfid::proto::protocol_sync::handle_receive_fork_headers_request", "Getting fork headers failed: {e}" ); handler.send_action(channel, ProtocolGenericAction::Skip).await; continue } }; // Send response if headers were found if !headers.is_empty() { handler .send_action( channel, ProtocolGenericAction::Response(ForkHeadersResponse { headers }), ) .await; continue } // If headers weren't found in a fork, check canonical if let Err(e) = validator.blockchain.headers.get(&[request.fork_header], true) { debug!( target: "darkfid::proto::protocol_sync::handle_receive_fork_headers_request", "Getting fork header hash failed: {e}" ); handler.send_action(channel, ProtocolGenericAction::Skip).await; continue }; let response = match validator.blockchain.headers.get(&request.headers, true) { Ok(h) => ProtocolGenericAction::Response(ForkHeadersResponse { headers: h.iter().map(|x| x.clone().unwrap()).collect(), }), Err(e) => { debug!( target: "darkfid::proto::protocol_sync::handle_receive_fork_headers_request", "Getting fork headers failed: {e}" ); ProtocolGenericAction::Skip } }; // Send response handler.send_action(channel, response).await; } } /// Background handler function for ProtocolSyncForkProposals. async fn handle_receive_fork_proposals_request( handler: ProtocolGenericHandlerPtr, validator: ValidatorPtr, ) -> Result<()> { debug!(target: "darkfid::proto::protocol_sync::handle_receive_fork_proposals_request", "START"); loop { // Wait for a new fork header hash request message let (channel, request) = match handler.receiver.recv().await { Ok(r) => r, Err(e) => { debug!( target: "darkfid::proto::protocol_sync::handle_receive_fork_proposals_request", "recv fail: {e}" ); continue } }; // Check if node has finished syncing its blockchain if !*validator.synced.read().await { debug!( target: "darkfid::proto::protocol_sync::handle_receive_fork_proposals_request", "Node still syncing blockchain, skipping..." ); handler.send_action(channel, ProtocolGenericAction::Skip).await; continue } // Check if request exists the configured limit if request.headers.len() > BATCH { debug!( target: "darkfid::proto::protocol_sync::handle_receive_fork_proposals_request", "Node requested more proposals than allowed." ); handler.send_action(channel, ProtocolGenericAction::Skip).await; continue } debug!(target: "darkfid::proto::protocol_sync::handle_receive_fork_proposals_request", "Received request: {request:?}"); // Retrieve fork proposals let proposals = match validator .consensus .get_fork_proposals(&request.headers, &request.fork_header) .await { Ok(p) => p, Err(e) => { debug!( target: "darkfid::proto::protocol_sync::handle_receive_fork_proposals_request", "Getting fork proposals failed: {e}" ); handler.send_action(channel, ProtocolGenericAction::Skip).await; continue } }; // Send response if proposals were found if !proposals.is_empty() { handler .send_action( channel, ProtocolGenericAction::Response(ForkProposalsResponse { proposals }), ) .await; continue } // If proposals weren't found in a fork, check canonical if let Err(e) = validator.blockchain.headers.get(&[request.fork_header], true) { debug!( target: "darkfid::proto::protocol_sync::handle_receive_fork_proposals_request", "Getting fork header hash failed: {e}" ); handler.send_action(channel, ProtocolGenericAction::Skip).await; continue }; let response = match validator.blockchain.get_blocks_by_hash(&request.headers) { Ok(blocks) => { let mut proposals = Vec::with_capacity(blocks.len()); for block in blocks { proposals.push(Proposal::new(block)); } ProtocolGenericAction::Response(ForkProposalsResponse { proposals }) } Err(e) => { debug!( target: "darkfid::proto::protocol_sync::handle_receive_fork_proposals_request", "Getting fork proposals failed: {e}" ); ProtocolGenericAction::Skip } }; // Send response handler.send_action(channel, response).await; } }