diff --git a/contracts/rust/adapter/src/stake_table.rs b/contracts/rust/adapter/src/stake_table.rs index cdd8ecf912..1ea6e98077 100644 --- a/contracts/rust/adapter/src/stake_table.rs +++ b/contracts/rust/adapter/src/stake_table.rs @@ -26,6 +26,7 @@ use hotshot_types::{ signature_key::BLSPubKey, stake_table::StakeTableEntry, traits::signature_key::SignatureKey as _, + PeerConfig, }; use serde::{Deserialize, Serialize}; use std::str::FromStr; @@ -216,6 +217,18 @@ impl From for StakeTableEntry { } } +impl From for PeerConfig { + fn from(value: NodeInfoJf) -> Self { + Self { + stake_table_entry: StakeTableEntry { + stake_key: value.stake_table_key, + stake_amount: U256::from(1), // dummy stake amount + }, + state_ver_key: value.state_ver_key, + } + } +} + impl From for NodeInfoJf { fn from(value: NodeInfo) -> Self { let NodeInfo { diff --git a/hotshot-types/src/traits/election.rs b/hotshot-types/src/traits/election.rs index 2473f6b357..be75a06ff4 100644 --- a/hotshot-types/src/traits/election.rs +++ b/hotshot-types/src/traits/election.rs @@ -16,9 +16,6 @@ use crate::{drb::DrbResult, traits::signature_key::SignatureKey, PeerConfig}; #[async_trait] /// A protocol for determining membership in and participating in a committee. pub trait Membership: 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 @@ -31,34 +28,34 @@ pub trait Membership: Debug + Send + Sync { fn stake_table( &self, epoch: Option, - ) -> Vec<::StakeTableEntry>; + ) -> Result>>; /// Get all participants in the committee (including their stake) for a specific epoch fn da_stake_table( &self, epoch: Option, - ) -> Vec<::StakeTableEntry>; + ) -> Result>>; /// Get all participants in the committee for a specific view for a specific epoch fn committee_members( &self, view_number: TYPES::View, epoch: Option, - ) -> BTreeSet; + ) -> Result>; /// 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, - ) -> BTreeSet; + ) -> Result>; /// Get all leaders in the committee for a specific view for a specific epoch fn committee_leaders( &self, view_number: TYPES::View, epoch: Option, - ) -> BTreeSet; + ) -> Result>; /// Get the stake table entry for a public key, returns `None` if the /// key is not in the table for a specific epoch @@ -77,10 +74,15 @@ pub trait Membership: Debug + Send + Sync { ) -> Option<::StakeTableEntry>; /// See if a node has stake in the committee in a specific epoch - fn has_stake(&self, pub_key: &TYPES::SignatureKey, epoch: Option) -> bool; + fn has_stake(&self, pub_key: &TYPES::SignatureKey, epoch: Option) + -> Result; /// See if a node has stake in the committee in a specific epoch - fn has_da_stake(&self, pub_key: &TYPES::SignatureKey, epoch: Option) -> bool; + fn has_da_stake( + &self, + pub_key: &TYPES::SignatureKey, + epoch: Option, + ) -> Result; /// The leader of the committee for view `view_number` in `epoch`. /// @@ -112,25 +114,25 @@ pub trait Membership: Debug + Send + Sync { &self, view: TYPES::View, epoch: Option, - ) -> std::result::Result; + ) -> Result; /// Returns the number of total nodes in the committee in an epoch `epoch` - fn total_nodes(&self, epoch: Option) -> usize; + fn total_nodes(&self, epoch: Option) -> Result; /// Returns the number of total DA nodes in the committee in an epoch `epoch` - fn da_total_nodes(&self, epoch: Option) -> usize; + fn da_total_nodes(&self, epoch: Option) -> Result; /// Returns the threshold for a specific `Membership` implementation - fn success_threshold(&self, epoch: Option) -> NonZeroU64; + fn success_threshold(&self, epoch: Option) -> Result; /// Returns the DA threshold for a specific `Membership` implementation - fn da_success_threshold(&self, epoch: Option) -> NonZeroU64; + fn da_success_threshold(&self, epoch: Option) -> Result; /// Returns the threshold for a specific `Membership` implementation - fn failure_threshold(&self, epoch: Option) -> NonZeroU64; + fn failure_threshold(&self, epoch: Option) -> Result; /// Returns the threshold required to upgrade the network protocol - fn upgrade_threshold(&self, epoch: Option) -> NonZeroU64; + fn upgrade_threshold(&self, epoch: Option) -> Result; #[allow(clippy::type_complexity)] /// Handles notifications that a new epoch root has been created @@ -146,13 +148,6 @@ pub trait Membership: Debug + Send + Sync { 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> { - None - } - /// Called to notify the Membership when a new DRB result has been calculated. /// Observes the same semantics as add_epoch_root fn add_drb_result(&mut self, _epoch: TYPES::Epoch, _drb_result: DrbResult); diff --git a/types/src/v0/impls/stake_table.rs b/types/src/v0/impls/stake_table.rs index 9bbf03daad..6befeaeed7 100644 --- a/types/src/v0/impls/stake_table.rs +++ b/types/src/v0/impls/stake_table.rs @@ -80,8 +80,8 @@ impl StakeTables { StakeTableChange::Remove(_) => None, }); - let mut consensus_stake_table: Vec> = vec![]; - let mut da_members: Vec> = vec![]; + let mut consensus_stake_table: Vec> = vec![]; + let mut da_members: Vec> = vec![]; for node in currently_staking { consensus_stake_table.push(node.clone().into()); if node.da { @@ -111,7 +111,7 @@ pub struct EpochCommittees { contract_address: Option
, /// Randomized committees, filled when we receive the DrbResult - randomized_committees: BTreeMap>>, + randomized_committees: BTreeMap>>, } #[derive(Debug, Clone, PartialEq)] @@ -135,19 +135,19 @@ struct Committee { /// The nodes eligible for leadership. /// NOTE: This is currently a hack because the DA leader needs to be the quorum /// leader but without voting rights. - eligible_leaders: Vec>, + eligible_leaders: Vec>, /// Keys for nodes participating in the network - stake_table: Vec>, + stake_table: Vec>, /// Keys for DA members - da_members: Vec>, + da_members: Vec>, /// Stake entries indexed by public key, for efficient lookup. - indexed_stake_table: HashMap>, + indexed_stake_table: HashMap>, /// DA entries indexed by public key, for efficient lookup. - indexed_da_members: HashMap>, + indexed_da_members: HashMap>, } impl EpochCommittees { @@ -162,28 +162,37 @@ impl EpochCommittees { // more subtlety when start fetching only the events since last update. let stake_table = st.stake_table.0.clone(); - let da_members = st.da_members.0.clone(); let indexed_stake_table: HashMap = st .stake_table .0 .iter() - .map(|entry| (PubKey::public_key(entry), entry.clone())) + .map(|peer_config| { + ( + PubKey::public_key(&peer_config.stake_table_entry), + peer_config.clone(), + ) + }) .collect(); let indexed_da_members: HashMap = st .da_members .0 .iter() - .map(|entry| (PubKey::public_key(entry), entry.clone())) + .map(|peer_config| { + ( + PubKey::public_key(&peer_config.stake_table_entry), + peer_config.clone(), + ) + }) .collect(); let eligible_leaders: Vec<_> = st .stake_table .0 .into_iter() - .filter(|entry| entry.stake() > U256::zero()) + .filter(|peer_config| peer_config.stake_table_entry.stake() > U256::zero()) .collect(); let committee = Committee { @@ -210,34 +219,44 @@ impl EpochCommittees { // For each eligible leader, get the stake table entry let eligible_leaders: Vec<_> = committee_members .iter() - .map(|member| member.stake_table_entry.clone()) - .filter(|entry| entry.stake() > U256::zero()) + .map(|member| member.clone()) + .filter(|peer_config| peer_config.stake_table_entry.stake() > U256::zero()) .collect(); // For each member, get the stake table entry let stake_table: Vec<_> = committee_members .iter() - .map(|member| member.stake_table_entry.clone()) - .filter(|entry| entry.stake() > U256::zero()) + .map(|member| member.clone()) + .filter(|peer_config| peer_config.stake_table_entry.stake() > U256::zero()) .collect(); // For each member, get the stake table entry let da_members: Vec<_> = da_members .iter() - .map(|member| member.stake_table_entry.clone()) - .filter(|entry| entry.stake() > U256::zero()) + .map(|member| member.clone()) + .filter(|peer_config| peer_config.stake_table_entry.stake() > U256::zero()) .collect(); // Index the stake table by public key let indexed_stake_table: HashMap = stake_table .iter() - .map(|entry| (PubKey::public_key(entry), entry.clone())) + .map(|peer_config| { + ( + PubKey::public_key(&peer_config.stake_table_entry), + peer_config.clone(), + ) + }) .collect(); // Index the stake table by public key let indexed_da_members: HashMap = da_members .iter() - .map(|entry| (PubKey::public_key(entry), entry.clone())) + .map(|peer_config| { + ( + PubKey::public_key(&peer_config.stake_table_entry), + peer_config.clone(), + ) + }) .collect(); let members = Committee { @@ -278,87 +297,27 @@ pub struct LeaderLookupError; #[async_trait] impl Membership for EpochCommittees { - type Error = LeaderLookupError; - // DO NOT USE. Dummy constructor to comply w/ trait. fn new( // TODO remove `new` from trait and remove this fn as well. // https://github.com/EspressoSystems/HotShot/commit/fcb7d54a4443e29d643b3bbc53761856aef4de8b - committee_members: Vec>, - da_members: Vec>, + _committee_members: Vec>, + _da_members: Vec>, ) -> Self { - // For each eligible leader, get the stake table entry - let eligible_leaders: Vec<_> = committee_members - .iter() - .map(|member| member.stake_table_entry.clone()) - .filter(|entry| entry.stake() > U256::zero()) - .collect(); - - // For each member, get the stake table entry - let stake_table: Vec<_> = committee_members - .iter() - .map(|member| member.stake_table_entry.clone()) - .filter(|entry| entry.stake() > U256::zero()) - .collect(); - - // For each member, get the stake table entry - let da_members: Vec<_> = da_members - .iter() - .map(|member| member.stake_table_entry.clone()) - .filter(|entry| entry.stake() > U256::zero()) - .collect(); - - // Index the stake table by public key - let indexed_stake_table: HashMap = stake_table - .iter() - .map(|entry| (PubKey::public_key(entry), entry.clone())) - .collect(); - - // Index the stake table by public key - let indexed_da_members: HashMap = da_members - .iter() - .map(|entry| (PubKey::public_key(entry), entry.clone())) - .collect(); - - let members = Committee { - eligible_leaders, - stake_table, - da_members, - indexed_stake_table, - indexed_da_members, - }; - - let mut map = HashMap::new(); - map.insert(Epoch::genesis(), members.clone()); - // TODO: remove this, workaround for hotshot asking for stake tables from epoch 1 - map.insert(Epoch::genesis() + 1u64, members.clone()); - - Self { - non_epoch_committee: members, - state: map, - _epoch_size: 12, - l1_client: L1Client::new(vec![Url::from_str("http:://ab.b").unwrap()]) - .expect("Failed to create L1 client"), - contract_address: None, - randomized_committees: BTreeMap::new(), - } + panic!("This function has been repalce with new_stake()"); } /// Get the stake table for the current view - fn stake_table(&self, epoch: Option) -> Vec> { - if let Some(st) = self.state(&epoch) { - st.stake_table.clone() - } else { - vec![] - } + fn stake_table(&self, epoch: Option) -> anyhow::Result>> { + self.state(&epoch) + .context(format!("failed to get state for epoch={epoch}")) } /// Get the stake table for the current view - fn da_stake_table(&self, epoch: Option) -> Vec> { - if let Some(sc) = self.state(&epoch) { - sc.da_members.clone() - } else { - vec![] - } + fn da_stake_table(&self, epoch: Option) -> anyhow::Result>> { + let sc = self + .state(&epoch) + .context(format!("failed to get state for epoch={epoch}"))?; + Ok(sc.da_member) } /// Get all members of the committee for the current view @@ -366,7 +325,7 @@ impl Membership for EpochCommittees { &self, _view_number: ::View, epoch: Option, - ) -> BTreeSet { + ) -> anyhow::Result> { if let Some(sc) = self.state(&epoch) { sc.indexed_stake_table.clone().into_keys().collect() } else { @@ -379,7 +338,7 @@ impl Membership for EpochCommittees { &self, _view_number: ::View, epoch: Option, - ) -> BTreeSet { + ) -> anyhow::Result> { if let Some(sc) = self.state(&epoch) { sc.indexed_da_members.clone().into_keys().collect() } else { @@ -392,7 +351,7 @@ impl Membership for EpochCommittees { &self, _view_number: ::View, epoch: Option, - ) -> BTreeSet { + ) -> anyhow::Result> { self.state(&epoch) .unwrap() .eligible_leaders @@ -402,28 +361,28 @@ impl Membership for EpochCommittees { } /// Get the stake table entry for a public key - fn stake(&self, pub_key: &PubKey, epoch: Option) -> Option> { + fn stake(&self, pub_key: &PubKey, epoch: Option) -> Option> { // Only return the stake if it is above zero self.state(&epoch) .and_then(|h| h.indexed_stake_table.get(pub_key).cloned()) } /// Get the DA stake table entry for a public key - fn da_stake(&self, pub_key: &PubKey, epoch: Option) -> Option> { + fn da_stake(&self, pub_key: &PubKey, epoch: Option) -> Option> { // Only return the stake if it is above zero self.state(&epoch) .and_then(|h| h.indexed_da_members.get(pub_key).cloned()) } /// Check if a node has stake in the committee - fn has_stake(&self, pub_key: &PubKey, epoch: Option) -> bool { + fn has_stake(&self, pub_key: &PubKey, epoch: Option) -> anyhow::Result { self.state(&epoch) .and_then(|h| h.indexed_stake_table.get(pub_key)) .is_some_and(|x| x.stake() > U256::zero()) } /// Check if a node has stake in the committee - fn has_da_stake(&self, pub_key: &PubKey, epoch: Option) -> bool { + fn has_da_stake(&self, pub_key: &PubKey, epoch: Option) -> anyhow::Result { self.state(&epoch) .and_then(|h| h.indexed_da_members.get(pub_key)) .is_some_and(|x| x.stake() > U256::zero()) @@ -434,7 +393,7 @@ impl Membership for EpochCommittees { &self, view_number: ::View, epoch: Option, - ) -> Result { + ) -> anyhow::Result { if let Some(epoch) = epoch { let Some(randomized_committee) = self.randomized_committees.get(&epoch) else { tracing::error!( @@ -458,40 +417,40 @@ impl Membership for EpochCommittees { } /// Get the total number of nodes in the committee - fn total_nodes(&self, epoch: Option) -> usize { + fn total_nodes(&self, epoch: Option) -> anyhow::Result { self.state(&epoch) .map(|sc| sc.stake_table.len()) .unwrap_or_default() } /// Get the total number of DA nodes in the committee - fn da_total_nodes(&self, epoch: Option) -> usize { + fn da_total_nodes(&self, epoch: Option) -> anyhow::Result { self.state(&epoch) .map(|sc: &Committee| sc.da_members.len()) .unwrap_or_default() } /// Get the voting success threshold for the committee - fn success_threshold(&self, epoch: Option) -> NonZeroU64 { + fn success_threshold(&self, epoch: Option) -> anyhow::Result { let quorum_len = self.state(&epoch).unwrap().stake_table.len(); NonZeroU64::new(((quorum_len as u64 * 2) / 3) + 1).unwrap() } /// Get the voting success threshold for the committee - fn da_success_threshold(&self, epoch: Option) -> NonZeroU64 { + fn da_success_threshold(&self, epoch: Option) -> anyhow::Result { let da_len = self.state(&epoch).unwrap().da_members.len(); NonZeroU64::new(((da_len as u64 * 2) / 3) + 1).unwrap() } /// Get the voting failure threshold for the committee - fn failure_threshold(&self, epoch: Option) -> NonZeroU64 { + fn failure_threshold(&self, epoch: Option) -> anyhow::Result { let quorum_len = self.state(&epoch).unwrap().stake_table.len(); NonZeroU64::new(((quorum_len as u64) / 3) + 1).unwrap() } /// Get the voting upgrade threshold for the committee - fn upgrade_threshold(&self, epoch: Option) -> NonZeroU64 { + fn upgrade_threshold(&self, epoch: Option) -> anyhow::Result { let quorum_len = self.state(&epoch).unwrap().indexed_stake_table.len(); NonZeroU64::new(max( diff --git a/types/src/v0/v0_3/stake_table.rs b/types/src/v0/v0_3/stake_table.rs index 336093f3a1..08a63da211 100644 --- a/types/src/v0/v0_3/stake_table.rs +++ b/types/src/v0/v0_3/stake_table.rs @@ -1,7 +1,7 @@ use crate::PubKey; use derive_more::derive::{From, Into}; use hotshot_contract_adapter::stake_table::NodeInfoJf; -use hotshot_types::{network::PeerConfigKeys, stake_table::StakeTableEntry}; +use hotshot_types::{network::PeerConfigKeys, stake_table::StakeTableEntry, PeerConfig}; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize, From)] @@ -13,11 +13,11 @@ pub struct CombinedStakeTable(Vec>); #[derive(Clone, Debug, From, Into)] /// NewType to disambiguate DA Membership -pub struct DAMembers(pub Vec>); +pub struct DAMembers(pub Vec>); #[derive(Clone, Debug, From, Into)] /// NewType to disambiguate StakeTable -pub struct StakeTable(pub Vec>); +pub struct StakeTable(pub Vec>); #[derive(Clone, Debug)] pub struct StakeTables {