use std::{
cmp::max,
sync::mpsc::{Receiver, Sender, TryRecvError},
thread::{self, JoinHandle},
time::{Duration, Instant, SystemTime},
};
use ed25519_dalek::VerifyingKey;
use crate::{
block_tree::{accessors::public::BlockTreeCamera, pluggables::KVStore},
events::{Event, ReceiveSyncRequestEvent, SendSyncResponseEvent},
networking::{network::Network, receiving::BlockSyncServerStub, sending::SenderHandle},
types::{
crypto_primitives::Keypair,
data_types::{BlockHeight, ChainID},
},
};
use super::messages::{BlockSyncAdvertiseMessage, BlockSyncRequest, BlockSyncResponse};
pub struct BlockSyncServer<N: Network + 'static, K: KVStore> {
config: BlockSyncServerConfiguration,
block_tree_camera: BlockTreeCamera<K>,
last_advertisement: Instant,
receiver: BlockSyncServerStub,
sender: SenderHandle<N>,
shutdown_signal: Receiver<()>,
event_publisher: Option<Sender<Event>>,
}
impl<N: Network + 'static, K: KVStore> BlockSyncServer<N, K> {
pub(crate) fn new(
config: BlockSyncServerConfiguration,
block_tree_camera: BlockTreeCamera<K>,
requests: Receiver<(VerifyingKey, BlockSyncRequest)>,
network: N,
shutdown_signal: Receiver<()>,
event_publisher: Option<Sender<Event>>,
) -> Self {
Self {
config,
block_tree_camera,
last_advertisement: Instant::now(),
receiver: BlockSyncServerStub::new(requests),
sender: SenderHandle::new(network),
shutdown_signal,
event_publisher,
}
}
pub(crate) fn start(mut self) -> JoinHandle<()> {
thread::spawn(move || loop {
match self.shutdown_signal.try_recv() {
Ok(()) => return,
Err(TryRecvError::Empty) => (),
Err(TryRecvError::Disconnected) => {
unreachable!("The Block Sync Server's `shutdown_signal` channel no longer has any senders connected to it")
}
}
if let Ok((
origin,
BlockSyncRequest {
start_height,
limit,
chain_id,
},
)) = self.receiver.recv_request()
{
if chain_id != self.config.chain_id {
continue;
}
Event::ReceiveSyncRequest(ReceiveSyncRequestEvent {
timestamp: SystemTime::now(),
peer: origin,
start_height,
limit,
})
.publish(&self.event_publisher);
let bt_snapshot = self.block_tree_camera.snapshot();
let blocks_res = bt_snapshot.blocks_from_height_to_newest(
start_height,
max(limit, self.config.request_limit),
);
let highest_pc_res = bt_snapshot.highest_pc();
match (blocks_res, highest_pc_res) {
(Ok(blocks), Ok(highest_pc)) => {
self.sender.send(
origin,
BlockSyncResponse {
blocks: blocks.clone(),
highest_pc: highest_pc.clone(),
},
);
Event::SendSyncResponse(SendSyncResponseEvent {
timestamp: SystemTime::now(),
peer: origin,
blocks,
highest_pc: highest_pc,
})
.publish(&self.event_publisher)
}
_ => {
}
}
}
if Instant::now() - self.last_advertisement >= self.config.advertise_time {
let highest_pc = self
.block_tree_camera
.snapshot()
.highest_pc()
.expect("Could not obtain the highest PC!");
let highest_committed_block_height = match self
.block_tree_camera
.snapshot()
.highest_committed_block_height()
.expect("Could not obtain the highest committed block height!")
{
Some(height) => height,
None => BlockHeight::new(0),
};
let advertise_pc_msg = BlockSyncAdvertiseMessage::advertise_pc(highest_pc);
self.sender.broadcast(advertise_pc_msg);
let advertise_block_msg = BlockSyncAdvertiseMessage::advertise_block(
&self.config.keypair,
self.config.chain_id,
highest_committed_block_height,
);
self.sender.broadcast(advertise_block_msg);
self.last_advertisement = Instant::now()
}
thread::yield_now();
})
}
}
pub(crate) struct BlockSyncServerConfiguration {
pub(crate) chain_id: ChainID,
pub(crate) keypair: Keypair,
pub(crate) request_limit: u32,
pub(crate) advertise_time: Duration,
}