diff --git a/crates/hotshot/src/traits/election.rs b/crates/hotshot/src/traits/election.rs index 4f9212705f..427ed12629 100644 --- a/crates/hotshot/src/traits/election.rs +++ b/crates/hotshot/src/traits/election.rs @@ -6,6 +6,8 @@ //! elections used for consensus +/// Dynamic leader election with epochs. +pub mod dynamic; /// leader completely randomized every view pub mod randomized_committee; /// static (round robin) committee election diff --git a/crates/hotshot/src/traits/election/dynamic.rs b/crates/hotshot/src/traits/election/dynamic.rs new file mode 100644 index 0000000000..1f354bd8f4 --- /dev/null +++ b/crates/hotshot/src/traits/election/dynamic.rs @@ -0,0 +1,68 @@ +// Copyright (c) 2021-2024 Espresso Systems (espressosys.com) +// This file is part of the HotShot repository. + +// You should have received a copy of the MIT License +// along with the HotShot repository. If not, see . + +use anyhow::{Context, Result}; +use hotshot_types::traits::{node_implementation::NodeType, signature_key::SignatureKey}; +use sha2::{Digest, Sha256}; + +/// Time a DRB calculation will take, in terms of number of views. +const DRB_CALCULATION_NUM_VIEW: u64 = 300; + +// TODO: Replace `hashes_per_second` parameter with a const once we benchmark the hash time. +// +/// Difficulty level of the DRB calculation. +/// +/// Represents the number of times the hash function will be repeatedly called. +/// +/// # Arguments +/// * `hashes_per_second` - Number of hashes that can be completed in a second. +/// * `view_timeout` - Timeout duration for a view, in seconds. +#[must_use] +pub fn difficulty_level(hashes_per_second: u64, view_timeout: u64) -> u64 { + hashes_per_second * view_timeout * DRB_CALCULATION_NUM_VIEW +} + +// TODO: Replace `hashes_per_second` parameter with a const once we benchmark the hash time. +// +/// Compute the DRB result for the leader rotation. +/// +/// This is to be started two epochs in advance. +/// +/// # Arguments +/// * `qc_signature` - Aggregated signature in the current quorum proposal. +/// * `view_timeout` - Timeout duration for a view, in seconds. +/// +/// # Errors +/// * If failed to serialize the QC signature. +pub fn compute_drb( + qc_signature: &::QcType, + hashes_per_second: u64, + view_timeout: u64, +) -> Result { + // Hash the QC signature. + let mut hash = bincode::serialize(&qc_signature) + .with_context(|| "Failed to serialize the QC signature.")?; + let difficulty_level = difficulty_level(hashes_per_second, view_timeout); + for _iter in 0..difficulty_level { + hash = Sha256::digest(hash).to_vec(); + } + + // Convert the hash to a seed as the DRB result. + let mut seed = [0u8; 8]; + seed.copy_from_slice(&hash[..size_of::()]); + Ok(usize::from_le_bytes(seed)) +} + +/// Use the DRB result to get the leader. +#[must_use] +pub fn leader( + stake_table: &[::StakeTableEntry], + drb: usize, +) -> TYPES::SignatureKey { + let index = drb % stake_table.len(); + let res = stake_table[index].clone(); + TYPES::SignatureKey::public_key(&res) +}