/* 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;
}
}