1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
// 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 <https://mit-license.org/>.
//! The election trait, used to decide which node is the leader and determine if a vote is valid.
use std::{collections::BTreeSet, fmt::Debug, num::NonZeroU64};
use async_trait::async_trait;
use utils::anytrace::Result;
use super::node_implementation::NodeType;
use crate::{traits::signature_key::SignatureKey, PeerConfig};
#[async_trait]
/// A protocol for determining membership in and participating in a committee.
pub trait Membership<TYPES: NodeType>: Debug + Send + Sync {
/// The error type returned by methods like `lookup_leader`.
type Error: std::fmt::Display;
/// Create a committee
fn new(
// Note: eligible_leaders is currently a hack because the DA leader == the quorum leader
// but they should not have voting power.
stake_committee_members: Vec<PeerConfig<TYPES::SignatureKey>>,
da_committee_members: Vec<PeerConfig<TYPES::SignatureKey>>,
) -> Self;
/// Get all participants in the committee (including their stake) for a specific epoch
fn stake_table(
&self,
epoch: Option<TYPES::Epoch>,
) -> Vec<<TYPES::SignatureKey as SignatureKey>::StakeTableEntry>;
/// Get all participants in the committee (including their stake) for a specific epoch
fn da_stake_table(
&self,
epoch: Option<TYPES::Epoch>,
) -> Vec<<TYPES::SignatureKey as SignatureKey>::StakeTableEntry>;
/// Get all participants in the committee for a specific view for a specific epoch
fn committee_members(
&self,
view_number: TYPES::View,
epoch: Option<TYPES::Epoch>,
) -> BTreeSet<TYPES::SignatureKey>;
/// Get all participants in the committee for a specific view for a specific epoch
fn da_committee_members(
&self,
view_number: TYPES::View,
epoch: Option<TYPES::Epoch>,
) -> BTreeSet<TYPES::SignatureKey>;
/// Get all leaders in the committee for a specific view for a specific epoch
fn committee_leaders(
&self,
view_number: TYPES::View,
epoch: Option<TYPES::Epoch>,
) -> BTreeSet<TYPES::SignatureKey>;
/// Get the stake table entry for a public key, returns `None` if the
/// key is not in the table for a specific epoch
fn stake(
&self,
pub_key: &TYPES::SignatureKey,
epoch: Option<TYPES::Epoch>,
) -> Option<<TYPES::SignatureKey as SignatureKey>::StakeTableEntry>;
/// Get the DA stake table entry for a public key, returns `None` if the
/// key is not in the table for a specific epoch
fn da_stake(
&self,
pub_key: &TYPES::SignatureKey,
epoch: Option<TYPES::Epoch>,
) -> Option<<TYPES::SignatureKey as SignatureKey>::StakeTableEntry>;
/// See if a node has stake in the committee in a specific epoch
fn has_stake(&self, pub_key: &TYPES::SignatureKey, epoch: Option<TYPES::Epoch>) -> bool;
/// See if a node has stake in the committee in a specific epoch
fn has_da_stake(&self, pub_key: &TYPES::SignatureKey, epoch: Option<TYPES::Epoch>) -> bool;
/// The leader of the committee for view `view_number` in `epoch`.
///
/// Note: this function uses a HotShot-internal error type.
/// You should implement `lookup_leader`, rather than implementing this function directly.
///
/// # Errors
/// Returns an error if the leader cannot be calculated.
fn leader(
&self,
view: TYPES::View,
epoch: Option<TYPES::Epoch>,
) -> Result<TYPES::SignatureKey> {
use utils::anytrace::*;
self.lookup_leader(view, epoch).wrap().context(info!(
"Failed to get leader for view {view} in epoch {epoch}"
))
}
/// The leader of the committee for view `view_number` in `epoch`.
///
/// Note: There is no such thing as a DA leader, so any consumer
/// requiring a leader should call this.
///
/// # Errors
/// Returns an error if the leader cannot be calculated
fn lookup_leader(
&self,
view: TYPES::View,
epoch: Option<TYPES::Epoch>,
) -> std::result::Result<TYPES::SignatureKey, Self::Error>;
/// Returns the number of total nodes in the committee in an epoch `epoch`
fn total_nodes(&self, epoch: Option<TYPES::Epoch>) -> usize;
/// Returns the number of total DA nodes in the committee in an epoch `epoch`
fn da_total_nodes(&self, epoch: Option<TYPES::Epoch>) -> usize;
/// Returns the threshold for a specific `Membership` implementation
fn success_threshold(&self, epoch: Option<TYPES::Epoch>) -> NonZeroU64;
/// Returns the DA threshold for a specific `Membership` implementation
fn da_success_threshold(&self, epoch: Option<TYPES::Epoch>) -> NonZeroU64;
/// Returns the threshold for a specific `Membership` implementation
fn failure_threshold(&self, epoch: Option<TYPES::Epoch>) -> NonZeroU64;
/// Returns the threshold required to upgrade the network protocol
fn upgrade_threshold(&self, epoch: Option<TYPES::Epoch>) -> NonZeroU64;
#[allow(clippy::type_complexity)]
/// Handles notifications that a new epoch root has been created
/// Is called under a read lock to the Membership. Return a callback
/// with Some to have that callback invoked under a write lock.
///
/// #3967 REVIEW NOTE: this is only called if epoch is Some. Is there any reason to do otherwise?
async fn add_epoch_root(
&self,
_epoch: TYPES::Epoch,
_block_header: TYPES::BlockHeader,
) -> Option<Box<dyn FnOnce(&mut Self) + Send>> {
None
}
#[allow(clippy::type_complexity)]
/// Called after add_epoch_root runs and any callback has been invoked.
/// Causes a read lock to be reacquired for this functionality.
async fn sync_l1(&self) -> Option<Box<dyn FnOnce(&mut Self) + Send>> {
None
}
}