use std::{
sync::{Arc, Mutex},
time::Duration,
};
use borsh::BorshDeserialize;
use ed25519_dalek::SigningKey;
use hotstuff_rs::{
events::{
CommitBlockEvent, InsertBlockEvent, PhaseVoteEvent, ReceiveProposalEvent,
UpdateHighestPCEvent,
},
replica::{Configuration, Replica, ReplicaSpec},
types::{
data_types::{BufferSize, ChainID, EpochLength, ViewNumber},
update_sets::{AppStateUpdates, ValidatorSetUpdates},
validator_set::{ValidatorSet, ValidatorSetState},
},
};
use crate::common::{
mem_db::MemDB,
network::NetworkStub,
number_app::{NumberApp, NumberAppTransaction},
verifying_key_bytes::VerifyingKeyBytes,
};
use super::logging::{first_seven_base64_chars, log_with_context};
pub(crate) struct Node {
verifying_key: VerifyingKeyBytes,
tx_queue: Arc<Mutex<Vec<NumberAppTransaction>>>,
replica: Replica<MemDB>,
}
impl Node {
pub(crate) fn new(
keypair: SigningKey,
network_stub: NetworkStub,
init_as_updates: AppStateUpdates,
init_vs_updates: ValidatorSetUpdates,
) -> Node {
let kv_store = MemDB::new();
let mut init_vs = ValidatorSet::new();
init_vs.apply_updates(&init_vs_updates);
let init_vs_state = ValidatorSetState::new(init_vs.clone(), init_vs, None, true);
Replica::initialize(kv_store.clone(), init_as_updates, init_vs_state);
let verifying_key = keypair.verifying_key().to_bytes();
let tx_queue = Arc::new(Mutex::new(Vec::new()));
let configuration = Configuration::builder()
.me(keypair)
.chain_id(ChainID::new(0))
.block_sync_request_limit(10)
.block_sync_server_advertise_time(Duration::new(10, 0))
.block_sync_response_timeout(Duration::new(3, 0))
.block_sync_blacklist_expiry_time(Duration::new(10, 0))
.block_sync_trigger_min_view_difference(2)
.block_sync_trigger_timeout(Duration::new(60, 0))
.progress_msg_buffer_capacity(BufferSize::new(1024))
.epoch_length(EpochLength::new(50))
.max_view_time(Duration::from_millis(2000))
.log_events(false)
.build();
let replica = ReplicaSpec::builder()
.app(NumberApp::new(tx_queue.clone()))
.network(network_stub)
.kv_store(kv_store)
.configuration(configuration)
.on_insert_block(insert_block_handler(verifying_key))
.on_receive_proposal(receive_proposal_handler(verifying_key))
.on_commit_block(commit_block_handler(verifying_key))
.on_update_highest_pc(update_highest_pc_handler(verifying_key))
.on_phase_vote(phase_vote_handler(verifying_key))
.build()
.start();
Node {
verifying_key,
replica,
tx_queue,
}
}
pub(crate) fn submit_transaction(&mut self, txn: NumberAppTransaction) {
self.tx_queue.lock().unwrap().push(txn);
}
pub(crate) fn number(&self) -> u32 {
NumberApp::number(self.replica.block_tree_camera().snapshot())
}
pub(crate) fn committed_validator_set(&self) -> ValidatorSet {
self.replica
.block_tree_camera()
.snapshot()
.committed_validator_set()
.expect("should have been able to get the committed validator set from the block tree")
}
pub(crate) fn highest_view_entered(&self) -> ViewNumber {
self.replica
.block_tree_camera()
.snapshot()
.highest_view_entered()
.expect("should have been able to get the highest view entered from the block tree")
}
pub(crate) fn verifying_key(&self) -> VerifyingKeyBytes {
self.verifying_key
}
}
fn insert_block_handler(
verifying_key: VerifyingKeyBytes,
) -> impl Fn(&InsertBlockEvent) + Send + 'static {
move |insert_block_event| {
log_with_context(
Some(verifying_key),
&format!(
"Inserted Block, block hash: {}",
first_seven_base64_chars(&insert_block_event.block.hash.bytes())
),
);
}
}
fn receive_proposal_handler(
verifying_key: VerifyingKeyBytes,
) -> impl Fn(&ReceiveProposalEvent) + Send + 'static {
move |receive_proposal_event| {
let txn = Vec::<NumberAppTransaction>::deserialize(
&mut &*receive_proposal_event.proposal.block.data.vec()[0]
.bytes()
.as_slice(),
)
.unwrap();
let txn_printable = if txn.is_empty() {
String::from("no transactions")
} else {
let all: Vec<String> = txn
.iter()
.map(|tx| match tx {
NumberAppTransaction::Increment => String::from("Increment"),
NumberAppTransaction::SetValidator(_, _) => String::from("Set Validator"),
NumberAppTransaction::DeleteValidator(_) => String::from("Delete Validator"),
})
.collect();
all.join(", ")
};
log_with_context(
Some(verifying_key),
&format!("Received Proposal, origin: {}, view: {}, block hash: {}, block height: {}, transactions: {}",
first_seven_base64_chars(&receive_proposal_event.origin.to_bytes()),
receive_proposal_event.proposal.view,
first_seven_base64_chars(&receive_proposal_event.proposal.block.hash.bytes()),
receive_proposal_event.proposal.block.height.clone(),
txn_printable
)
);
}
}
fn commit_block_handler(
verifying_key: VerifyingKeyBytes,
) -> impl Fn(&CommitBlockEvent) + Send + 'static {
move |commit_block_event: &CommitBlockEvent| {
log_with_context(
Some(verifying_key),
&format!(
"Committed Block, block hash: {}",
first_seven_base64_chars(&commit_block_event.block.bytes())
),
);
}
}
fn update_highest_pc_handler(
verifying_key: VerifyingKeyBytes,
) -> impl Fn(&UpdateHighestPCEvent) + Send + 'static {
move |update_highest_pc_event| {
log_with_context(
Some(verifying_key),
&format!(
"Updated Highest PC, block hash: {}, view: {}, phase: {:?}, no. of signatures: {}",
first_seven_base64_chars(&update_highest_pc_event.highest_pc.block.bytes()),
update_highest_pc_event.highest_pc.view,
update_highest_pc_event.highest_pc.phase,
update_highest_pc_event
.highest_pc
.signatures
.iter()
.filter(|sig| sig.is_some())
.count()
),
);
}
}
fn phase_vote_handler(
verifying_key: VerifyingKeyBytes,
) -> impl Fn(&PhaseVoteEvent) + Send + 'static {
move |phase_vote_event: &PhaseVoteEvent| {
log_with_context(
Some(verifying_key),
&format!(
"Phase Voted, block hash: {}, view: {}, phase: {:?}",
first_seven_base64_chars(&phase_vote_event.vote.block.bytes()),
phase_vote_event.vote.view,
phase_vote_event.vote.phase,
),
);
}
}