diff --git a/crates/examples/infra/mod.rs b/crates/examples/infra/mod.rs index 3624d44ed5..2462215dd9 100755 --- a/crates/examples/infra/mod.rs +++ b/crates/examples/infra/mod.rs @@ -30,7 +30,7 @@ use hotshot::{ BlockPayload, NodeImplementation, }, types::SystemContextHandle, - MarketplaceConfig, Memberships, SystemContext, + MarketplaceConfig, SystemContext, }; use hotshot_example_types::{ auction_results_provider_types::TestAuctionResultsProvider, @@ -55,7 +55,7 @@ use hotshot_types::{ traits::{ block_contents::{BlockHeader, TestableBlock}, election::Membership, - network::{ConnectedNetwork, Topic}, + network::ConnectedNetwork, node_implementation::{ConsensusTime, NodeType, Versions}, states::TestableState, }, @@ -381,22 +381,9 @@ pub trait RunDa< let da_nodes = config.config.known_da_nodes.clone(); - // Create the quorum membership from all nodes - let quorum_membership = ::Membership::new( - all_nodes.clone(), - all_nodes.clone(), - Topic::Global, - ); - // Create the quorum membership from all nodes, specifying the committee // as the known da nodes - let da_membership = - ::Membership::new(all_nodes.clone(), da_nodes, Topic::Da); - - let memberships = Memberships { - quorum_membership: quorum_membership.clone(), - da_membership, - }; + let memberships = ::Membership::new(all_nodes, da_nodes); let marketplace_config = MarketplaceConfig { auction_results_provider: TestAuctionResultsProvider::::default().into(), @@ -544,7 +531,6 @@ pub trait RunDa< let num_eligible_leaders = context .hotshot .memberships - .quorum_membership .committee_leaders(TYPES::View::genesis(), TYPES::Epoch::genesis()) .len(); let total_num_views = usize::try_from(consensus.locked_view().u64()).unwrap(); @@ -752,7 +738,8 @@ where // Create the qurorum membership from the list of known nodes let all_nodes = config.config.known_nodes_with_stake.clone(); - let quorum_membership = TYPES::Membership::new(all_nodes.clone(), all_nodes, Topic::Global); + let da_nodes = config.config.known_da_nodes.clone(); + let quorum_membership = TYPES::Membership::new(all_nodes, da_nodes); // Derive the bind address let bind_address = diff --git a/crates/hotshot/src/lib.rs b/crates/hotshot/src/lib.rs index 3441cd93c8..83d110e380 100644 --- a/crates/hotshot/src/lib.rs +++ b/crates/hotshot/src/lib.rs @@ -92,15 +92,6 @@ pub struct MarketplaceConfig> { pub fallback_builder_url: Url, } -/// Bundle of all the memberships a consensus instance uses -#[derive(Clone)] -pub struct Memberships { - /// The entire quorum - pub quorum_membership: TYPES::Membership, - /// The DA nodes - pub da_membership: TYPES::Membership, -} - /// Holds the state needed to participate in `HotShot` consensus pub struct SystemContext, V: Versions> { /// The public key of this node @@ -116,7 +107,7 @@ pub struct SystemContext, V: Versi pub network: Arc, /// Memberships used by consensus - pub memberships: Arc>, + pub memberships: Arc, /// the metrics that the implementor is using. metrics: Arc, @@ -207,7 +198,7 @@ impl, V: Versions> SystemContext::PrivateKey, nonce: u64, config: HotShotConfig, - memberships: Memberships, + memberships: TYPES::Membership, network: Arc, initializer: HotShotInitializer, metrics: ConsensusMetricsValue, @@ -260,7 +251,7 @@ impl, V: Versions> SystemContext::PrivateKey, nonce: u64, config: HotShotConfig, - memberships: Memberships, + memberships: TYPES::Membership, network: Arc, initializer: HotShotInitializer, metrics: ConsensusMetricsValue, @@ -507,7 +498,6 @@ impl, V: Versions> SystemContext, V: Versions> SystemContext, V: Versions> SystemContext::PrivateKey, node_id: u64, config: HotShotConfig, - memberships: Memberships, + memberships: TYPES::Membership, network: Arc, initializer: HotShotInitializer, metrics: ConsensusMetricsValue, @@ -767,7 +757,7 @@ where private_key: ::PrivateKey, nonce: u64, config: HotShotConfig, - memberships: Memberships, + memberships: TYPES::Membership, network: Arc, initializer: HotShotInitializer, metrics: ConsensusMetricsValue, diff --git a/crates/hotshot/src/tasks/mod.rs b/crates/hotshot/src/tasks/mod.rs index b19ba62106..7e94aea326 100644 --- a/crates/hotshot/src/tasks/mod.rs +++ b/crates/hotshot/src/tasks/mod.rs @@ -46,7 +46,7 @@ use vbs::version::StaticVersionType; use crate::{ tasks::task_state::CreateTaskState, types::SystemContextHandle, ConsensusApi, ConsensusMetricsValue, ConsensusTaskRegistry, HotShotConfig, HotShotInitializer, - MarketplaceConfig, Memberships, NetworkTaskRegistry, SignatureKey, SystemContext, Versions, + MarketplaceConfig, NetworkTaskRegistry, SignatureKey, SystemContext, Versions, }; /// event for global event stream @@ -82,7 +82,7 @@ pub fn add_response_task, V: Versi ) { let state = NetworkResponseState::::new( handle.hotshot.consensus(), - handle.hotshot.memberships.quorum_membership.clone().into(), + (*handle.hotshot.memberships).clone().into(), handle.public_key().clone(), handle.private_key().clone(), handle.hotshot.id, @@ -190,15 +190,13 @@ pub fn add_network_event_task< >( handle: &mut SystemContextHandle, network: Arc, - quorum_membership: TYPES::Membership, - da_membership: TYPES::Membership, + membership: TYPES::Membership, ) { let network_state: NetworkEventTaskState<_, V, _, _> = NetworkEventTaskState { network, view: TYPES::View::genesis(), epoch: TYPES::Epoch::genesis(), - quorum_membership, - da_membership, + membership, storage: Arc::clone(&handle.storage()), consensus: OuterConsensus::new(handle.consensus()), upgrade_lock: handle.hotshot.upgrade_lock.clone(), @@ -323,7 +321,7 @@ where private_key: ::PrivateKey, nonce: u64, config: HotShotConfig, - memberships: Memberships, + memberships: TYPES::Membership, network: Arc, initializer: HotShotInitializer, metrics: ConsensusMetricsValue, @@ -518,15 +516,8 @@ where /// Adds the `NetworkEventTaskState` tasks possibly modifying them as well. fn add_network_event_tasks(&self, handle: &mut SystemContextHandle) { let network = Arc::clone(&handle.network); - let quorum_membership = handle.memberships.quorum_membership.clone(); - let da_membership = handle.memberships.da_membership.clone(); - - self.add_network_event_task( - handle, - Arc::clone(&network), - quorum_membership.clone(), - da_membership, - ); + + self.add_network_event_task(handle, Arc::clone(&network), (*handle.memberships).clone()); } /// Adds a `NetworkEventTaskState` task. Can be reimplemented to modify its behaviour. @@ -534,10 +525,9 @@ where &self, handle: &mut SystemContextHandle, channel: Arc<>::Network>, - quorum_membership: TYPES::Membership, - da_membership: TYPES::Membership, + membership: TYPES::Membership, ) { - add_network_event_task(handle, channel, quorum_membership, da_membership); + add_network_event_task(handle, channel, membership); } } @@ -570,13 +560,9 @@ pub async fn add_network_message_and_request_receiver_tasks< pub fn add_network_event_tasks, V: Versions>( handle: &mut SystemContextHandle, ) { - let quorum_membership = handle.memberships.quorum_membership.clone(); - let da_membership = handle.memberships.da_membership.clone(); - add_network_event_task( handle, Arc::clone(&handle.network), - quorum_membership, - da_membership, + (*handle.memberships).clone(), ); } diff --git a/crates/hotshot/src/tasks/task_state.rs b/crates/hotshot/src/tasks/task_state.rs index 7d3d085281..3c4e81585b 100644 --- a/crates/hotshot/src/tasks/task_state.rs +++ b/crates/hotshot/src/tasks/task_state.rs @@ -51,7 +51,7 @@ impl, V: Versions> CreateTaskState consensus: OuterConsensus::new(handle.hotshot.consensus()), view: handle.cur_view().await, delay: handle.hotshot.config.data_request_delay, - da_membership: handle.hotshot.memberships.da_membership.clone(), + membership: (*handle.hotshot.memberships).clone(), public_key: handle.public_key().clone(), private_key: handle.private_key().clone(), id: handle.hotshot.id, @@ -71,7 +71,7 @@ impl, V: Versions> CreateTaskState output_event_stream: handle.hotshot.external_event_stream.0.clone(), cur_view: handle.cur_view().await, cur_epoch: handle.cur_epoch().await, - quorum_membership: handle.hotshot.memberships.quorum_membership.clone().into(), + quorum_membership: (*handle.hotshot.memberships).clone().into(), vote_collectors: BTreeMap::default(), public_key: handle.public_key().clone(), private_key: handle.private_key().clone(), @@ -92,7 +92,7 @@ impl, V: Versions> CreateTaskState output_event_stream: handle.hotshot.external_event_stream.0.clone(), cur_view: handle.cur_view().await, cur_epoch: handle.cur_epoch().await, - quorum_membership: handle.hotshot.memberships.quorum_membership.clone().into(), + membership: (*handle.hotshot.memberships).clone().into(), network: Arc::clone(&handle.hotshot.network), vote_collector: None.into(), public_key: handle.public_key().clone(), @@ -121,7 +121,7 @@ impl, V: Versions> CreateTaskState cur_view: handle.cur_view().await, cur_epoch: handle.cur_epoch().await, network: Arc::clone(&handle.hotshot.network), - membership: handle.hotshot.memberships.quorum_membership.clone().into(), + membership: (*handle.hotshot.memberships).clone().into(), public_key: handle.public_key().clone(), private_key: handle.private_key().clone(), id: handle.hotshot.id, @@ -137,9 +137,8 @@ impl, V: Versions> CreateTaskState Self { consensus: OuterConsensus::new(handle.hotshot.consensus()), output_event_stream: handle.hotshot.external_event_stream.0.clone(), - da_membership: handle.hotshot.memberships.da_membership.clone().into(), + membership: (*handle.hotshot.memberships).clone().into(), network: Arc::clone(&handle.hotshot.network), - quorum_membership: handle.hotshot.memberships.quorum_membership.clone().into(), cur_view: handle.cur_view().await, cur_epoch: handle.cur_epoch().await, vote_collectors: BTreeMap::default(), @@ -163,7 +162,7 @@ impl, V: Versions> CreateTaskState cur_view, next_view: cur_view, cur_epoch: handle.cur_epoch().await, - membership: handle.hotshot.memberships.quorum_membership.clone().into(), + membership: (*handle.hotshot.memberships).clone().into(), public_key: handle.public_key().clone(), private_key: handle.private_key().clone(), num_timeouts_tracked: 0, @@ -190,7 +189,7 @@ impl, V: Versions> CreateTaskState consensus: OuterConsensus::new(handle.hotshot.consensus()), cur_view: handle.cur_view().await, cur_epoch: handle.cur_epoch().await, - membership: handle.hotshot.memberships.quorum_membership.clone().into(), + membership: (*handle.hotshot.memberships).clone().into(), public_key: handle.public_key().clone(), private_key: handle.private_key().clone(), instance_state: handle.hotshot.instance_state(), @@ -231,8 +230,7 @@ impl, V: Versions> CreateTaskState latest_voted_view: handle.cur_view().await, vote_dependencies: BTreeMap::new(), network: Arc::clone(&handle.hotshot.network), - quorum_membership: handle.hotshot.memberships.quorum_membership.clone().into(), - da_membership: handle.hotshot.memberships.da_membership.clone().into(), + membership: (*handle.hotshot.memberships).clone().into(), output_event_stream: handle.hotshot.external_event_stream.0.clone(), id: handle.hotshot.id, storage: Arc::clone(&handle.storage), @@ -254,7 +252,7 @@ impl, V: Versions> CreateTaskState proposal_dependencies: BTreeMap::new(), consensus: OuterConsensus::new(consensus), instance_state: handle.hotshot.instance_state(), - quorum_membership: handle.hotshot.memberships.quorum_membership.clone().into(), + quorum_membership: (*handle.hotshot.memberships).clone().into(), public_key: handle.public_key().clone(), private_key: handle.private_key().clone(), storage: Arc::clone(&handle.storage), @@ -281,7 +279,7 @@ impl, V: Versions> CreateTaskState consensus: OuterConsensus::new(consensus), cur_view: handle.cur_view().await, cur_epoch: handle.cur_epoch().await, - quorum_membership: handle.hotshot.memberships.quorum_membership.clone().into(), + quorum_membership: (*handle.hotshot.memberships).clone().into(), timeout: handle.hotshot.config.next_view_timeout, output_event_stream: handle.hotshot.external_event_stream.0.clone(), storage: Arc::clone(&handle.storage), @@ -305,7 +303,7 @@ impl, V: Versions> CreateTaskState private_key: handle.private_key().clone(), instance_state: handle.hotshot.instance_state(), network: Arc::clone(&handle.hotshot.network), - quorum_membership: handle.hotshot.memberships.quorum_membership.clone().into(), + membership: (*handle.hotshot.memberships).clone().into(), vote_collectors: BTreeMap::default(), timeout_vote_collectors: BTreeMap::default(), cur_view: handle.cur_view().await, diff --git a/crates/hotshot/src/traits/election/randomized_committee.rs b/crates/hotshot/src/traits/election/randomized_committee.rs index 89a36f9030..2b721a66e0 100644 --- a/crates/hotshot/src/traits/election/randomized_committee.rs +++ b/crates/hotshot/src/traits/election/randomized_committee.rs @@ -9,7 +9,6 @@ use std::{cmp::max, collections::BTreeMap, num::NonZeroU64}; use hotshot_types::{ traits::{ election::Membership, - network::Topic, node_implementation::NodeType, signature_key::{SignatureKey, StakeTableEntryType}, }, @@ -32,12 +31,16 @@ pub struct RandomizedCommittee { /// The nodes on the committee and their stake stake_table: Vec<::StakeTableEntry>, + /// The nodes on the committee and their stake + da_stake_table: Vec<::StakeTableEntry>, + /// The nodes on the committee and their stake, indexed by public key indexed_stake_table: BTreeMap::StakeTableEntry>, - /// The network topic of the committee - committee_topic: Topic, + /// The nodes on the committee and their stake, indexed by public key + indexed_da_stake_table: + BTreeMap::StakeTableEntry>, } impl Membership for RandomizedCommittee { @@ -45,13 +48,12 @@ impl Membership for RandomizedCommittee { /// Create a new election fn new( - eligible_leaders: Vec::SignatureKey>>, committee_members: Vec::SignatureKey>>, - committee_topic: Topic, + da_members: Vec::SignatureKey>>, ) -> Self { // For each eligible leader, get the stake table entry let eligible_leaders: Vec<::StakeTableEntry> = - eligible_leaders + committee_members .iter() .map(|member| member.stake_table_entry.clone()) .filter(|entry| entry.stake() > U256::zero()) @@ -65,6 +67,13 @@ impl Membership for RandomizedCommittee { .filter(|entry| entry.stake() > U256::zero()) .collect(); + // For each member, get the stake table entry + let da_members: Vec<::StakeTableEntry> = 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: BTreeMap< TYPES::SignatureKey, @@ -74,11 +83,21 @@ impl Membership for RandomizedCommittee { .map(|entry| (TYPES::SignatureKey::public_key(entry), entry.clone())) .collect(); + // Index the stake table by public key + let indexed_da_stake_table: BTreeMap< + TYPES::SignatureKey, + ::StakeTableEntry, + > = da_members + .iter() + .map(|entry| (TYPES::SignatureKey::public_key(entry), entry.clone())) + .collect(); + Self { eligible_leaders, stake_table: members, + da_stake_table: da_members, indexed_stake_table, - committee_topic, + indexed_da_stake_table, } } @@ -90,6 +109,14 @@ impl Membership for RandomizedCommittee { self.stake_table.clone() } + /// Get the stake table for the current view + fn da_stake_table( + &self, + _epoch: ::Epoch, + ) -> Vec<<::SignatureKey as SignatureKey>::StakeTableEntry> { + self.da_stake_table.clone() + } + /// Get all members of the committee for the current view fn committee_members( &self, @@ -102,6 +129,18 @@ impl Membership for RandomizedCommittee { .collect() } + /// Get all members of the committee for the current view + fn da_committee_members( + &self, + _view_number: ::View, + _epoch: ::Epoch, + ) -> std::collections::BTreeSet<::SignatureKey> { + self.da_stake_table + .iter() + .map(TYPES::SignatureKey::public_key) + .collect() + } + /// Get all eligible leaders of the committee for the current view fn committee_leaders( &self, @@ -124,6 +163,16 @@ impl Membership for RandomizedCommittee { self.indexed_stake_table.get(pub_key).cloned() } + /// Get the stake table entry for a public key + fn da_stake( + &self, + pub_key: &::SignatureKey, + _epoch: ::Epoch, + ) -> Option<::StakeTableEntry> { + // Only return the stake if it is above zero + self.indexed_da_stake_table.get(pub_key).cloned() + } + /// Check if a node has stake in the committee fn has_stake( &self, @@ -135,11 +184,22 @@ impl Membership for RandomizedCommittee { .is_some_and(|x| x.stake() > U256::zero()) } - /// Get the network topic for the committee - fn committee_topic(&self) -> Topic { - self.committee_topic.clone() + /// Check if a node has stake in the committee + fn has_da_stake( + &self, + pub_key: &::SignatureKey, + _epoch: ::Epoch, + ) -> bool { + self.indexed_da_stake_table + .get(pub_key) + .is_some_and(|x| x.stake() > U256::zero()) } + // /// Get the network topic for the committee + // fn committee_topic(&self) -> Topic { + // self.committee_topic.clone() + // } + /// Index the vector of public keys with the current view number fn lookup_leader( &self, @@ -161,12 +221,20 @@ impl Membership for RandomizedCommittee { fn total_nodes(&self, _epoch: ::Epoch) -> usize { self.stake_table.len() } - + /// Get the total number of nodes in the committee + fn da_total_nodes(&self, _epoch: ::Epoch) -> usize { + self.da_stake_table.len() + } /// Get the voting success threshold for the committee fn success_threshold(&self) -> NonZeroU64 { NonZeroU64::new(((self.stake_table.len() as u64 * 2) / 3) + 1).unwrap() } + /// Get the voting success threshold for the committee + fn da_success_threshold(&self) -> NonZeroU64 { + NonZeroU64::new(((self.da_stake_table.len() as u64 * 2) / 3) + 1).unwrap() + } + /// Get the voting failure threshold for the committee fn failure_threshold(&self) -> NonZeroU64 { NonZeroU64::new(((self.stake_table.len() as u64) / 3) + 1).unwrap() diff --git a/crates/hotshot/src/traits/election/static_committee.rs b/crates/hotshot/src/traits/election/static_committee.rs index 200ed530dd..fa904c66cf 100644 --- a/crates/hotshot/src/traits/election/static_committee.rs +++ b/crates/hotshot/src/traits/election/static_committee.rs @@ -9,7 +9,6 @@ use std::{cmp::max, collections::BTreeMap, num::NonZeroU64}; use hotshot_types::{ traits::{ election::Membership, - network::Topic, node_implementation::NodeType, signature_key::{SignatureKey, StakeTableEntryType}, }, @@ -19,7 +18,6 @@ use primitive_types::U256; use utils::anytrace::Result; #[derive(Clone, Debug, Eq, PartialEq, Hash)] - /// The static committee election pub struct StaticCommittee { /// The nodes eligible for leadership. @@ -30,12 +28,16 @@ pub struct StaticCommittee { /// The nodes on the committee and their stake stake_table: Vec<::StakeTableEntry>, + /// The nodes on the committee and their stake + da_stake_table: Vec<::StakeTableEntry>, + /// The nodes on the committee and their stake, indexed by public key indexed_stake_table: BTreeMap::StakeTableEntry>, - /// The network topic of the committee - committee_topic: Topic, + /// The nodes on the committee and their stake, indexed by public key + indexed_da_stake_table: + BTreeMap::StakeTableEntry>, } impl Membership for StaticCommittee { @@ -43,13 +45,12 @@ impl Membership for StaticCommittee { /// Create a new election fn new( - eligible_leaders: Vec::SignatureKey>>, committee_members: Vec::SignatureKey>>, - committee_topic: Topic, + da_members: Vec::SignatureKey>>, ) -> Self { // For each eligible leader, get the stake table entry let eligible_leaders: Vec<::StakeTableEntry> = - eligible_leaders + committee_members .iter() .map(|member| member.stake_table_entry.clone()) .filter(|entry| entry.stake() > U256::zero()) @@ -63,6 +64,13 @@ impl Membership for StaticCommittee { .filter(|entry| entry.stake() > U256::zero()) .collect(); + // For each member, get the stake table entry + let da_members: Vec<::StakeTableEntry> = 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: BTreeMap< TYPES::SignatureKey, @@ -72,11 +80,21 @@ impl Membership for StaticCommittee { .map(|entry| (TYPES::SignatureKey::public_key(entry), entry.clone())) .collect(); + // Index the stake table by public key + let indexed_da_stake_table: BTreeMap< + TYPES::SignatureKey, + ::StakeTableEntry, + > = da_members + .iter() + .map(|entry| (TYPES::SignatureKey::public_key(entry), entry.clone())) + .collect(); + Self { eligible_leaders, stake_table: members, + da_stake_table: da_members, indexed_stake_table, - committee_topic, + indexed_da_stake_table, } } @@ -88,6 +106,14 @@ impl Membership for StaticCommittee { self.stake_table.clone() } + /// Get the stake table for the current view + fn da_stake_table( + &self, + _epoch: ::Epoch, + ) -> Vec<<::SignatureKey as SignatureKey>::StakeTableEntry> { + self.da_stake_table.clone() + } + /// Get all members of the committee for the current view fn committee_members( &self, @@ -100,6 +126,18 @@ impl Membership for StaticCommittee { .collect() } + /// Get all members of the committee for the current view + fn da_committee_members( + &self, + _view_number: ::View, + _epoch: ::Epoch, + ) -> std::collections::BTreeSet<::SignatureKey> { + self.da_stake_table + .iter() + .map(TYPES::SignatureKey::public_key) + .collect() + } + /// Get all eligible leaders of the committee for the current view fn committee_leaders( &self, @@ -122,6 +160,16 @@ impl Membership for StaticCommittee { self.indexed_stake_table.get(pub_key).cloned() } + /// Get the DA stake table entry for a public key + fn da_stake( + &self, + pub_key: &::SignatureKey, + _epoch: ::Epoch, + ) -> Option<::StakeTableEntry> { + // Only return the stake if it is above zero + self.indexed_da_stake_table.get(pub_key).cloned() + } + /// Check if a node has stake in the committee fn has_stake( &self, @@ -133,9 +181,15 @@ impl Membership for StaticCommittee { .is_some_and(|x| x.stake() > U256::zero()) } - /// Get the network topic for the committee - fn committee_topic(&self) -> Topic { - self.committee_topic.clone() + /// Check if a node has stake in the committee + fn has_da_stake( + &self, + pub_key: &::SignatureKey, + _epoch: ::Epoch, + ) -> bool { + self.indexed_da_stake_table + .get(pub_key) + .is_some_and(|x| x.stake() > U256::zero()) } /// Index the vector of public keys with the current view number @@ -155,11 +209,21 @@ impl Membership for StaticCommittee { self.stake_table.len() } + /// Get the total number of DA nodes in the committee + fn da_total_nodes(&self, _epoch: ::Epoch) -> usize { + self.da_stake_table.len() + } + /// Get the voting success threshold for the committee fn success_threshold(&self) -> NonZeroU64 { NonZeroU64::new(((self.stake_table.len() as u64 * 2) / 3) + 1).unwrap() } + /// Get the voting success threshold for the committee + fn da_success_threshold(&self) -> NonZeroU64 { + NonZeroU64::new(((self.da_stake_table.len() as u64 * 2) / 3) + 1).unwrap() + } + /// Get the voting failure threshold for the committee fn failure_threshold(&self) -> NonZeroU64 { NonZeroU64::new(((self.stake_table.len() as u64) / 3) + 1).unwrap() diff --git a/crates/hotshot/src/traits/election/static_committee_leader_two_views.rs b/crates/hotshot/src/traits/election/static_committee_leader_two_views.rs index 922a815e68..41ed1d046e 100644 --- a/crates/hotshot/src/traits/election/static_committee_leader_two_views.rs +++ b/crates/hotshot/src/traits/election/static_committee_leader_two_views.rs @@ -9,7 +9,6 @@ use std::{collections::BTreeMap, num::NonZeroU64}; use hotshot_types::{ traits::{ election::Membership, - network::Topic, node_implementation::NodeType, signature_key::{SignatureKey, StakeTableEntryType}, }, @@ -30,12 +29,16 @@ pub struct StaticCommitteeLeaderForTwoViews { /// The nodes on the committee and their stake stake_table: Vec<::StakeTableEntry>, + /// The nodes on the committee and their stake + da_stake_table: Vec<::StakeTableEntry>, + /// The nodes on the committee and their stake, indexed by public key indexed_stake_table: BTreeMap::StakeTableEntry>, - /// The network topic of the committee - committee_topic: Topic, + /// The nodes on the committee and their stake, indexed by public key + indexed_da_stake_table: + BTreeMap::StakeTableEntry>, } impl Membership for StaticCommitteeLeaderForTwoViews { @@ -43,13 +46,12 @@ impl Membership for StaticCommitteeLeaderForTwoViews::SignatureKey>>, committee_members: Vec::SignatureKey>>, - committee_topic: Topic, + da_members: Vec::SignatureKey>>, ) -> Self { // For each eligible leader, get the stake table entry let eligible_leaders: Vec<::StakeTableEntry> = - eligible_leaders + committee_members .iter() .map(|member| member.stake_table_entry.clone()) .filter(|entry| entry.stake() > U256::zero()) @@ -63,6 +65,13 @@ impl Membership for StaticCommitteeLeaderForTwoViews U256::zero()) .collect(); + // For each member, get the stake table entry + let da_members: Vec<::StakeTableEntry> = 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: BTreeMap< TYPES::SignatureKey, @@ -72,11 +81,21 @@ impl Membership for StaticCommitteeLeaderForTwoViews::StakeTableEntry, + > = da_members + .iter() + .map(|entry| (TYPES::SignatureKey::public_key(entry), entry.clone())) + .collect(); + Self { eligible_leaders, stake_table: members, + da_stake_table: da_members, indexed_stake_table, - committee_topic, + indexed_da_stake_table, } } @@ -88,6 +107,14 @@ impl Membership for StaticCommitteeLeaderForTwoViews::Epoch, + ) -> Vec<<::SignatureKey as SignatureKey>::StakeTableEntry> { + self.da_stake_table.clone() + } + /// Get all members of the committee for the current view fn committee_members( &self, @@ -100,6 +127,18 @@ impl Membership for StaticCommitteeLeaderForTwoViews::View, + _epoch: ::Epoch, + ) -> std::collections::BTreeSet<::SignatureKey> { + self.da_stake_table + .iter() + .map(TYPES::SignatureKey::public_key) + .collect() + } + /// Get all eligible leaders of the committee for the current view fn committee_leaders( &self, @@ -122,6 +161,16 @@ impl Membership for StaticCommitteeLeaderForTwoViews::SignatureKey, + _epoch: ::Epoch, + ) -> Option<::StakeTableEntry> { + // Only return the stake if it is above zero + self.indexed_da_stake_table.get(pub_key).cloned() + } + /// Check if a node has stake in the committee fn has_stake( &self, @@ -133,9 +182,15 @@ impl Membership for StaticCommitteeLeaderForTwoViews U256::zero()) } - /// Get the network topic for the committee - fn committee_topic(&self) -> Topic { - self.committee_topic.clone() + /// Check if a node has stake in the committee + fn has_da_stake( + &self, + pub_key: &::SignatureKey, + _epoch: ::Epoch, + ) -> bool { + self.indexed_da_stake_table + .get(pub_key) + .is_some_and(|x| x.stake() > U256::zero()) } /// Index the vector of public keys with the current view number @@ -156,11 +211,21 @@ impl Membership for StaticCommitteeLeaderForTwoViews::Epoch) -> usize { + self.da_stake_table.len() + } + /// Get the voting success threshold for the committee fn success_threshold(&self) -> NonZeroU64 { NonZeroU64::new(((self.stake_table.len() as u64 * 2) / 3) + 1).unwrap() } + /// Get the voting success threshold for the committee + fn da_success_threshold(&self) -> NonZeroU64 { + NonZeroU64::new(((self.da_stake_table.len() as u64 * 2) / 3) + 1).unwrap() + } + /// Get the voting failure threshold for the committee fn failure_threshold(&self) -> NonZeroU64 { NonZeroU64::new(((self.stake_table.len() as u64) / 3) + 1).unwrap() diff --git a/crates/hotshot/src/types/handle.rs b/crates/hotshot/src/types/handle.rs index 4719b0cde2..7b1fd5a424 100644 --- a/crates/hotshot/src/types/handle.rs +++ b/crates/hotshot/src/types/handle.rs @@ -35,7 +35,7 @@ use hotshot_types::{ }; use tracing::instrument; -use crate::{traits::NodeImplementation, types::Event, Memberships, SystemContext, Versions}; +use crate::{traits::NodeImplementation, types::Event, SystemContext, Versions}; /// Event streaming handle for a [`SystemContext`] instance running in the background /// @@ -69,7 +69,7 @@ pub struct SystemContextHandle, V: pub network: Arc, /// Memberships used by consensus - pub memberships: Arc>, + pub memberships: Arc, /// Number of blocks in an epoch, zero means there are no epochs pub epoch_height: u64, @@ -157,7 +157,7 @@ impl + 'static, V: Versions> signed_proposal_request.commit().as_ref(), )?; - let mem = self.memberships.quorum_membership.clone(); + let mem = (*self.memberships).clone(); let receiver = self.internal_event_stream.1.activate_cloned(); let sender = self.internal_event_stream.0.clone(); Ok(async move { @@ -327,7 +327,6 @@ impl + 'static, V: Versions> ) -> Result { self.hotshot .memberships - .quorum_membership .leader(view_number, epoch_number) .context("Failed to lookup leader") } diff --git a/crates/libp2p-networking/src/network/transport.rs b/crates/libp2p-networking/src/network/transport.rs index 7e5987e306..0f3bdf80a9 100644 --- a/crates/libp2p-networking/src/network/transport.rs +++ b/crates/libp2p-networking/src/network/transport.rs @@ -522,9 +522,7 @@ mod test { use hotshot_example_types::node_types::TestTypes; use hotshot_types::{ - light_client::StateVerKey, - signature_key::BLSPubKey, - traits::{network::Topic, signature_key::SignatureKey}, + light_client::StateVerKey, signature_key::BLSPubKey, traits::signature_key::SignatureKey, PeerConfig, }; use libp2p::{core::transport::dummy::DummyTransport, quic::Connection}; @@ -643,11 +641,8 @@ mod test { stake_table_entry: keypair.0.stake_table_entry(1), state_ver_key: StateVerKey::default(), }; - let stake_table = ::Membership::new( - vec![peer_config.clone()], - vec![peer_config], - Topic::Global, - ); + let stake_table = + ::Membership::new(vec![peer_config.clone()], vec![peer_config]); // Verify the authentication message let result = MockStakeTableAuth::verify_peer_authentication( @@ -672,7 +667,7 @@ mod test { let mut stream = cursor_from!(auth_message); // Create an empty stake table - let stake_table = ::Membership::new(vec![], vec![], Topic::Global); + let stake_table = ::Membership::new(vec![], vec![]); // Verify the authentication message let result = MockStakeTableAuth::verify_peer_authentication( @@ -708,11 +703,8 @@ mod test { stake_table_entry: keypair.0.stake_table_entry(1), state_ver_key: StateVerKey::default(), }; - let stake_table = ::Membership::new( - vec![peer_config.clone()], - vec![peer_config], - Topic::Global, - ); + let stake_table = + ::Membership::new(vec![peer_config.clone()], vec![peer_config]); // Check against the malicious peer ID let result = MockStakeTableAuth::verify_peer_authentication( diff --git a/crates/task-impls/src/consensus/handlers.rs b/crates/task-impls/src/consensus/handlers.rs index 8c60392cf5..684c3b6e34 100644 --- a/crates/task-impls/src/consensus/handlers.rs +++ b/crates/task-impls/src/consensus/handlers.rs @@ -45,7 +45,7 @@ pub(crate) async fn handle_quorum_vote_recv< .await .is_leaf_extended(vote.data.leaf_commit); let we_are_leader = task_state - .quorum_membership + .membership .leader(vote.view_number() + 1, task_state.cur_epoch)? == task_state.public_key; ensure!( @@ -60,7 +60,7 @@ pub(crate) async fn handle_quorum_vote_recv< &mut task_state.vote_collectors, vote, task_state.public_key.clone(), - &task_state.quorum_membership, + &task_state.membership, task_state.cur_epoch, task_state.id, &event, @@ -87,7 +87,7 @@ pub(crate) async fn handle_timeout_vote_recv< // Are we the leader for this view? ensure!( task_state - .quorum_membership + .membership .leader(vote.view_number() + 1, task_state.cur_epoch)? == task_state.public_key, info!( @@ -100,7 +100,7 @@ pub(crate) async fn handle_timeout_vote_recv< &mut task_state.timeout_vote_collectors, vote, task_state.public_key.clone(), - &task_state.quorum_membership, + &task_state.membership, task_state.cur_epoch, task_state.id, &event, @@ -131,7 +131,7 @@ pub async fn send_high_qc ensure!( task_state - .quorum_membership + .membership .has_stake(&task_state.public_key, task_state.cur_epoch), debug!("We were not chosen for the consensus committee for view {view_number:?}") ); @@ -324,7 +324,7 @@ pub(crate) async fn handle_timeout .number_of_timeouts .add(1); if task_state - .quorum_membership + .membership .leader(view_number, task_state.cur_epoch)? == task_state.public_key { diff --git a/crates/task-impls/src/consensus/mod.rs b/crates/task-impls/src/consensus/mod.rs index 0ed04d30fa..c865ed82e1 100644 --- a/crates/task-impls/src/consensus/mod.rs +++ b/crates/task-impls/src/consensus/mod.rs @@ -47,7 +47,7 @@ pub struct ConsensusTaskState, V: pub network: Arc, /// Membership for Quorum Certs/votes - pub quorum_membership: Arc, + pub membership: Arc, /// A map of `QuorumVote` collector tasks. pub vote_collectors: VoteCollectorsMap, QuorumCertificate2, V>, diff --git a/crates/task-impls/src/da.rs b/crates/task-impls/src/da.rs index edaa5123e5..68df39136f 100644 --- a/crates/task-impls/src/da.rs +++ b/crates/task-impls/src/da.rs @@ -52,13 +52,10 @@ pub struct DaTaskState, V: Version /// Reference to consensus. Leader will require a read lock on this. pub consensus: OuterConsensus, - /// Membership for the DA committee - pub da_membership: Arc, - - /// Membership for the quorum committee - /// We need this only for calculating the proper VID scheme + /// Membership for the DA committee and quorum committee. + /// We need the latter only for calculating the proper VID scheme /// from the number of nodes in the quorum. - pub quorum_membership: Arc, + pub membership: Arc, /// The underlying network pub network: Arc, @@ -124,7 +121,7 @@ impl, V: Versions> DaTaskState, V: Versions> DaTaskState, V: Versions> DaTaskState, V: Versions> DaTaskState, V: Versions> DaTaskState, V: Versions> DaTaskState( let justify_qc = proposal.data.justify_qc.clone(); if !justify_qc - .is_valid_cert(quorum_membership.as_ref(), cur_epoch, upgrade_lock) + .is_valid_cert( + quorum_membership.stake_table(cur_epoch), + quorum_membership.success_threshold(), + upgrade_lock, + ) .await { bail!("Invalid justify_qc in proposal for view {}", *view_number); @@ -675,11 +679,14 @@ pub(crate) async fn validate_proposal_view_and_certs< "Timeout certificate for view {} was not for the immediately preceding view", *view_number ); + ensure!( timeout_cert .is_valid_cert( - validation_info.quorum_membership.as_ref(), - validation_info.cur_epoch, + validation_info + .quorum_membership + .stake_table(validation_info.cur_epoch), + validation_info.quorum_membership.success_threshold(), &validation_info.upgrade_lock ) .await, @@ -699,8 +706,10 @@ pub(crate) async fn validate_proposal_view_and_certs< ensure!( view_sync_cert .is_valid_cert( - validation_info.quorum_membership.as_ref(), - validation_info.cur_epoch, + validation_info + .quorum_membership + .stake_table(validation_info.cur_epoch), + validation_info.quorum_membership.success_threshold(), &validation_info.upgrade_lock ) .await, diff --git a/crates/task-impls/src/network.rs b/crates/task-impls/src/network.rs index bf23787c05..7cc8dceca0 100644 --- a/crates/task-impls/src/network.rs +++ b/crates/task-impls/src/network.rs @@ -25,7 +25,7 @@ use hotshot_types::{ traits::{ election::Membership, network::{ - BroadcastDelay, ConnectedNetwork, RequestKind, ResponseMessage, TransmitType, + BroadcastDelay, ConnectedNetwork, RequestKind, ResponseMessage, Topic, TransmitType, ViewMessage, }, node_implementation::{ConsensusTime, NodeType, Versions}, @@ -208,10 +208,8 @@ pub struct NetworkEventTaskState< pub view: TYPES::View, /// epoch number pub epoch: TYPES::Epoch, - /// quorum for the network - pub quorum_membership: TYPES::Membership, - /// da for the network - pub da_membership: TYPES::Membership, + /// network memberships + pub membership: TYPES::Membership, /// Storage to store actionable events pub storage: Arc>, /// Shared consensus state @@ -391,7 +389,7 @@ impl< HotShotEvent::QuorumVoteSend(vote) => { *maybe_action = Some(HotShotAction::Vote); let view_number = vote.view_number() + 1; - let leader = match self.quorum_membership.leader(view_number, self.epoch) { + let leader = match self.membership.leader(view_number, self.epoch) { Ok(l) => l, Err(e) => { tracing::warn!( @@ -452,7 +450,7 @@ impl< HotShotEvent::DaVoteSend(vote) => { *maybe_action = Some(HotShotAction::DaVote); let view_number = vote.view_number(); - let leader = match self.quorum_membership.leader(view_number, self.epoch) { + let leader = match self.membership.leader(view_number, self.epoch) { Ok(l) => l, Err(e) => { tracing::warn!( @@ -484,7 +482,7 @@ impl< } HotShotEvent::ViewSyncPreCommitVoteSend(vote) => { let view_number = vote.view_number() + vote.date().relay; - let leader = match self.quorum_membership.leader(view_number, self.epoch) { + let leader = match self.membership.leader(view_number, self.epoch) { Ok(l) => l, Err(e) => { tracing::warn!( @@ -507,7 +505,7 @@ impl< HotShotEvent::ViewSyncCommitVoteSend(vote) => { *maybe_action = Some(HotShotAction::ViewSyncVote); let view_number = vote.view_number() + vote.date().relay; - let leader = match self.quorum_membership.leader(view_number, self.epoch) { + let leader = match self.membership.leader(view_number, self.epoch) { Ok(l) => l, Err(e) => { tracing::warn!( @@ -530,7 +528,7 @@ impl< HotShotEvent::ViewSyncFinalizeVoteSend(vote) => { *maybe_action = Some(HotShotAction::ViewSyncVote); let view_number = vote.view_number() + vote.date().relay; - let leader = match self.quorum_membership.leader(view_number, self.epoch) { + let leader = match self.membership.leader(view_number, self.epoch) { Ok(l) => l, Err(e) => { tracing::warn!( @@ -574,7 +572,7 @@ impl< HotShotEvent::TimeoutVoteSend(vote) => { *maybe_action = Some(HotShotAction::Vote); let view_number = vote.view_number() + 1; - let leader = match self.quorum_membership.leader(view_number, self.epoch) { + let leader = match self.membership.leader(view_number, self.epoch) { Ok(l) => l, Err(e) => { tracing::warn!( @@ -603,7 +601,7 @@ impl< HotShotEvent::UpgradeVoteSend(vote) => { tracing::error!("Sending upgrade vote!"); let view_number = vote.view_number(); - let leader = match self.quorum_membership.leader(view_number, self.epoch) { + let leader = match self.membership.leader(view_number, self.epoch) { Ok(l) => l, Err(e) => { tracing::warn!( @@ -630,7 +628,7 @@ impl< self.cancel_tasks(view); let net = Arc::clone(&self.network); let epoch = self.epoch.u64(); - let mem = self.quorum_membership.clone(); + let mem = self.membership.clone(); spawn(async move { net.update_view::(view.saturating_sub(1), epoch, &mem) .await; @@ -676,10 +674,10 @@ impl< kind: message_kind, }; let view_number = message.kind.view_number(); - let committee_topic = self.quorum_membership.committee_topic(); + let committee_topic = Topic::Global; let da_committee = self - .da_membership - .committee_members(view_number, self.epoch); + .membership + .da_committee_members(view_number, self.epoch); let network = Arc::clone(&self.network); let storage = Arc::clone(&self.storage); let consensus = OuterConsensus::new(Arc::clone(&self.consensus.inner_consensus)); @@ -798,7 +796,7 @@ pub mod test { &mut sender, &mut message_kind, &mut transmit, - &self.quorum_membership, + &self.membership, ); self.spawn_transmit_task(message_kind, maybe_action, transmit, sender); } diff --git a/crates/task-impls/src/quorum_proposal/handlers.rs b/crates/task-impls/src/quorum_proposal/handlers.rs index f8d0d93d75..55f283bcd8 100644 --- a/crates/task-impls/src/quorum_proposal/handlers.rs +++ b/crates/task-impls/src/quorum_proposal/handlers.rs @@ -25,6 +25,7 @@ use hotshot_types::{ simple_certificate::{QuorumCertificate2, UpgradeCertificate}, traits::{ block_contents::BlockHeader, + election::Membership, node_implementation::{ConsensusTime, NodeType}, signature_key::SignatureKey, }, @@ -124,8 +125,10 @@ impl ProposalDependencyHandle { if let HotShotEvent::HighQcRecv(qc, _sender) = event.as_ref() { if qc .is_valid_cert( - self.quorum_membership.as_ref(), - TYPES::Epoch::new(0), + // TODO take epoch from `qc` + // https://github.com/EspressoSystems/HotShot/issues/3917 + self.quorum_membership.stake_table(TYPES::Epoch::new(0)), + self.quorum_membership.success_threshold(), &self.upgrade_lock, ) .await diff --git a/crates/task-impls/src/quorum_proposal/mod.rs b/crates/task-impls/src/quorum_proposal/mod.rs index f7e50806d0..d226677eb1 100644 --- a/crates/task-impls/src/quorum_proposal/mod.rs +++ b/crates/task-impls/src/quorum_proposal/mod.rs @@ -443,8 +443,8 @@ impl, V: Versions> ensure!( certificate .is_valid_cert( - self.quorum_membership.as_ref(), - epoch_number, + self.quorum_membership.stake_table(epoch_number), + self.quorum_membership.success_threshold(), &self.upgrade_lock ) .await, @@ -507,8 +507,8 @@ impl, V: Versions> let epoch_number = self.consensus.read().await.cur_epoch(); ensure!( qc.is_valid_cert( - self.quorum_membership.as_ref(), - epoch_number, + self.quorum_membership.stake_table(epoch_number), + self.quorum_membership.success_threshold(), &self.upgrade_lock ) .await, diff --git a/crates/task-impls/src/quorum_proposal_recv/handlers.rs b/crates/task-impls/src/quorum_proposal_recv/handlers.rs index f5fe251367..73f6addef0 100644 --- a/crates/task-impls/src/quorum_proposal_recv/handlers.rs +++ b/crates/task-impls/src/quorum_proposal_recv/handlers.rs @@ -154,8 +154,10 @@ pub(crate) async fn handle_quorum_proposal_recv< if !justify_qc .is_valid_cert( - validation_info.quorum_membership.as_ref(), - validation_info.cur_epoch, + validation_info + .quorum_membership + .stake_table(validation_info.cur_epoch), + validation_info.quorum_membership.success_threshold(), &validation_info.upgrade_lock, ) .await diff --git a/crates/task-impls/src/quorum_vote/mod.rs b/crates/task-impls/src/quorum_vote/mod.rs index 20e851fa14..d1079197f8 100644 --- a/crates/task-impls/src/quorum_vote/mod.rs +++ b/crates/task-impls/src/quorum_vote/mod.rs @@ -269,11 +269,8 @@ pub struct QuorumVoteTaskState, V: /// The underlying network pub network: Arc, - /// Membership for Quorum certs/votes. - pub quorum_membership: Arc, - - /// Membership for DA committee certs/votes. - pub da_membership: Arc, + /// Membership for Quorum certs/votes and DA committee certs/votes. + pub membership: Arc, /// Output events to application pub output_event_stream: async_broadcast::Sender>, @@ -377,7 +374,7 @@ impl, V: Versions> QuorumVoteTaskS private_key: self.private_key.clone(), consensus: OuterConsensus::new(Arc::clone(&self.consensus.inner_consensus)), instance_state: Arc::clone(&self.instance_state), - quorum_membership: Arc::clone(&self.quorum_membership), + quorum_membership: Arc::clone(&self.membership), storage: Arc::clone(&self.storage), view_number, sender: event_sender.clone(), @@ -479,8 +476,12 @@ impl, V: Versions> QuorumVoteTaskS let cur_epoch = self.consensus.read().await.cur_epoch(); // Validate the DAC. ensure!( - cert.is_valid_cert(self.da_membership.as_ref(), cur_epoch, &self.upgrade_lock) - .await, + cert.is_valid_cert( + self.membership.da_stake_table(cur_epoch), + self.membership.da_success_threshold(), + &self.upgrade_lock + ) + .await, warn!("Invalid DAC") ); @@ -518,16 +519,16 @@ impl, V: Versions> QuorumVoteTaskS // ensure that the VID share was sent by a DA member OR the view leader ensure!( - self.da_membership - .committee_members(view, cur_epoch) + self.membership + .da_committee_members(view, cur_epoch) .contains(sender) - || *sender == self.quorum_membership.leader(view, cur_epoch)?, + || *sender == self.membership.leader(view, cur_epoch)?, "VID share was not sent by a DA member or the view leader." ); // NOTE: `verify_share` returns a nested `Result`, so we must check both the inner // and outer results - match vid_scheme(self.quorum_membership.total_nodes(cur_epoch)).verify_share( + match vid_scheme(self.membership.total_nodes(cur_epoch)).verify_share( &disperse.data.share, &disperse.data.common, payload_commitment, @@ -641,7 +642,7 @@ impl, V: Versions> QuorumVoteTaskS OuterConsensus::new(Arc::clone(&self.consensus.inner_consensus)), event_sender.clone(), event_receiver.clone().deactivate(), - Arc::clone(&self.quorum_membership), + Arc::clone(&self.membership), self.public_key.clone(), self.private_key.clone(), self.upgrade_lock.clone(), @@ -684,7 +685,7 @@ impl, V: Versions> QuorumVoteTaskS .is_leaf_extended(proposed_leaf.commit()); if let Err(e) = submit_vote::( event_sender.clone(), - Arc::clone(&self.quorum_membership), + Arc::clone(&self.membership), self.public_key.clone(), self.private_key.clone(), self.upgrade_lock.clone(), diff --git a/crates/task-impls/src/request.rs b/crates/task-impls/src/request.rs index 6e9384f3ea..a41b04f5d5 100644 --- a/crates/task-impls/src/request.rs +++ b/crates/task-impls/src/request.rs @@ -59,8 +59,8 @@ pub struct NetworkRequestState> { pub view: TYPES::View, /// Delay before requesting peers pub delay: Duration, - /// DA Membership - pub da_membership: TYPES::Membership, + /// Membership (Here containing only DA) + pub membership: TYPES::Membership, /// This nodes public key pub public_key: TYPES::SignatureKey, /// This nodes private/signing key, used to sign requests. @@ -180,15 +180,15 @@ impl> NetworkRequestState = self - .da_membership - .committee_members(view, epoch) + .membership + .da_committee_members(view, epoch) .into_iter() .collect(); // Randomize the recipients so all replicas don't overload the same 1 recipients diff --git a/crates/task-impls/src/view_sync.rs b/crates/task-impls/src/view_sync.rs index dbb190be9f..efb87f9cd5 100644 --- a/crates/task-impls/src/view_sync.rs +++ b/crates/task-impls/src/view_sync.rs @@ -533,7 +533,11 @@ impl ViewSyncReplicaTaskState { // If certificate is not valid, return current state if !certificate - .is_valid_cert(self.membership.as_ref(), self.cur_epoch, &self.upgrade_lock) + .is_valid_cert( + self.membership.stake_table(self.cur_epoch), + self.membership.failure_threshold(), + &self.upgrade_lock, + ) .await { tracing::error!("Not valid view sync cert! {:?}", certificate.data()); @@ -615,7 +619,11 @@ impl ViewSyncReplicaTaskState { // If certificate is not valid, return current state if !certificate - .is_valid_cert(self.membership.as_ref(), self.cur_epoch, &self.upgrade_lock) + .is_valid_cert( + self.membership.stake_table(self.cur_epoch), + self.membership.success_threshold(), + &self.upgrade_lock, + ) .await { tracing::error!("Not valid view sync cert! {:?}", certificate.data()); @@ -708,7 +716,11 @@ impl ViewSyncReplicaTaskState { // If certificate is not valid, return current state if !certificate - .is_valid_cert(self.membership.as_ref(), self.cur_epoch, &self.upgrade_lock) + .is_valid_cert( + self.membership.stake_table(self.cur_epoch), + self.membership.success_threshold(), + &self.upgrade_lock, + ) .await { tracing::error!("Not valid view sync cert! {:?}", certificate.data()); diff --git a/crates/task-impls/src/vote_collection.rs b/crates/task-impls/src/vote_collection.rs index b58f74dd7e..e0fc96040b 100644 --- a/crates/task-impls/src/vote_collection.rs +++ b/crates/task-impls/src/vote_collection.rs @@ -43,7 +43,7 @@ pub type VoteCollectorsMap = pub struct VoteCollectionTaskState< TYPES: NodeType, VOTE: Vote, - CERT: Certificate + Debug, + CERT: Certificate + Debug, V: Versions, > { /// Public key for this node. @@ -72,7 +72,7 @@ pub struct VoteCollectionTaskState< pub trait AggregatableVote< TYPES: NodeType, VOTE: Vote, - CERT: Certificate, + CERT: Certificate, > { /// return the leader for this votes @@ -92,7 +92,7 @@ pub trait AggregatableVote< impl< TYPES: NodeType, VOTE: Vote + AggregatableVote, - CERT: Certificate + Clone + Debug, + CERT: Certificate + Clone + Debug, V: Versions, > VoteCollectionTaskState { @@ -153,7 +153,7 @@ pub trait HandleVoteEvent where TYPES: NodeType, VOTE: Vote + AggregatableVote, - CERT: Certificate + Debug, + CERT: Certificate + Debug, { /// Handle a vote event /// @@ -204,7 +204,7 @@ where + std::marker::Send + std::marker::Sync + 'static, - CERT: Certificate + CERT: Certificate + Debug + std::marker::Send + std::marker::Sync @@ -242,7 +242,11 @@ where pub async fn handle_vote< TYPES: NodeType, VOTE: Vote + AggregatableVote + Send + Sync + 'static, - CERT: Certificate + Debug + Send + Sync + 'static, + CERT: Certificate + + Debug + + Send + + Sync + + 'static, V: Versions, >( collectors: &mut VoteCollectorsMap, diff --git a/crates/testing/src/byzantine/byzantine_behaviour.rs b/crates/testing/src/byzantine/byzantine_behaviour.rs index 0ffc97df37..449629d5ca 100644 --- a/crates/testing/src/byzantine/byzantine_behaviour.rs +++ b/crates/testing/src/byzantine/byzantine_behaviour.rs @@ -339,15 +339,13 @@ impl + std::fmt::Debug, V: Version &self, handle: &mut SystemContextHandle, network: Arc<>::Network>, - quorum_membership: TYPES::Membership, - da_membership: TYPES::Membership, + membership: TYPES::Membership, ) { let network_state: NetworkEventTaskState<_, V, _, _> = NetworkEventTaskState { network, view: TYPES::View::genesis(), epoch: TYPES::Epoch::genesis(), - quorum_membership, - da_membership, + membership, storage: Arc::clone(&handle.storage()), consensus: OuterConsensus::new(handle.consensus()), upgrade_lock: handle.hotshot.upgrade_lock.clone(), diff --git a/crates/testing/src/helpers.rs b/crates/testing/src/helpers.rs index d27c5fe165..6759d17e69 100644 --- a/crates/testing/src/helpers.rs +++ b/crates/testing/src/helpers.rs @@ -13,7 +13,7 @@ use committable::Committable; use hotshot::{ traits::{NodeImplementation, TestableNodeImplementation}, types::{SignatureKey, SystemContextHandle}, - HotShotInitializer, Memberships, SystemContext, + HotShotInitializer, SystemContext, }; use hotshot_example_types::{ auction_results_provider_types::TestAuctionResultsProvider, @@ -33,7 +33,6 @@ use hotshot_types::{ block_contents::vid_commitment, consensus_api::ConsensusApi, election::Membership, - network::Topic, node_implementation::{NodeType, Versions}, }, utils::{View, ViewInner}, @@ -109,17 +108,10 @@ pub async fn build_system_handle_from_launcher< let private_key = validator_config.private_key.clone(); let public_key = validator_config.public_key.clone(); - let all_nodes = config.known_nodes_with_stake.clone(); - let da_nodes = config.known_da_nodes.clone(); - - let memberships = Memberships { - quorum_membership: TYPES::Membership::new( - all_nodes.clone(), - all_nodes.clone(), - Topic::Global, - ), - da_membership: TYPES::Membership::new(all_nodes, da_nodes, Topic::Da), - }; + let memberships = TYPES::Membership::new( + config.known_nodes_with_stake.clone(), + config.known_da_nodes.clone(), + ); SystemContext::init( public_key, @@ -145,7 +137,7 @@ pub async fn build_cert< V: Versions, DATAType: Committable + Clone + Eq + Hash + Serialize + Debug + 'static, VOTE: Vote, - CERT: Certificate, + CERT: Certificate, >( data: DATAType, membership: &TYPES::Membership, @@ -210,7 +202,7 @@ pub async fn build_assembled_sig< TYPES: NodeType, V: Versions, VOTE: Vote, - CERT: Certificate, + CERT: Certificate, DATAType: Committable + Clone + Eq + Hash + Serialize + Debug + 'static, >( data: &DATAType, @@ -219,7 +211,7 @@ pub async fn build_assembled_sig< epoch: TYPES::Epoch, upgrade_lock: &UpgradeLock, ) -> ::QcType { - let stake_table = membership.stake_table(epoch); + let stake_table = CERT::stake_table(membership, epoch); let real_qc_pp: ::QcParams = ::public_parameter( stake_table.clone(), @@ -365,8 +357,7 @@ pub fn build_vid_proposal( #[allow(clippy::too_many_arguments)] pub async fn build_da_certificate( - quorum_membership: &::Membership, - da_membership: &::Membership, + membership: &::Membership, view_number: TYPES::View, epoch_number: TYPES::Epoch, transactions: Vec, @@ -376,10 +367,8 @@ pub async fn build_da_certificate( ) -> DaCertificate { let encoded_transactions = TestTransaction::encode(&transactions); - let da_payload_commitment = vid_commitment( - &encoded_transactions, - quorum_membership.total_nodes(epoch_number), - ); + let da_payload_commitment = + vid_commitment(&encoded_transactions, membership.total_nodes(epoch_number)); let da_data = DaData { payload_commit: da_payload_commitment, @@ -387,7 +376,7 @@ pub async fn build_da_certificate( build_cert::, DaCertificate>( da_data, - da_membership, + membership, view_number, epoch_number, public_key, diff --git a/crates/testing/src/test_builder.rs b/crates/testing/src/test_builder.rs index 4773e597e0..5bd6f38d79 100644 --- a/crates/testing/src/test_builder.rs +++ b/crates/testing/src/test_builder.rs @@ -11,7 +11,7 @@ use hotshot::{ tasks::EventTransformerState, traits::{NetworkReliability, NodeImplementation, TestableNodeImplementation}, types::SystemContextHandle, - HotShotInitializer, MarketplaceConfig, Memberships, SystemContext, TwinsHandlerState, + HotShotInitializer, MarketplaceConfig, SystemContext, TwinsHandlerState, }; use hotshot_example_types::{ auction_results_provider_types::TestAuctionResultsProvider, state_types::TestInstanceState, @@ -175,7 +175,7 @@ pub async fn create_test_handle< metadata: TestDescription, node_id: u64, network: Network, - memberships: Memberships, + memberships: TYPES::Membership, config: HotShotConfig, storage: I::Storage, marketplace_config: MarketplaceConfig, diff --git a/crates/testing/src/test_runner.rs b/crates/testing/src/test_runner.rs index 95f494808f..414e6e0f4b 100644 --- a/crates/testing/src/test_runner.rs +++ b/crates/testing/src/test_runner.rs @@ -17,7 +17,7 @@ use futures::future::join_all; use hotshot::{ traits::TestableNodeImplementation, types::{Event, SystemContextHandle}, - HotShotInitializer, MarketplaceConfig, Memberships, SystemContext, + HotShotInitializer, MarketplaceConfig, SystemContext, }; use hotshot_example_types::{ auction_results_provider_types::TestAuctionResultsProvider, @@ -34,7 +34,7 @@ use hotshot_types::{ simple_certificate::QuorumCertificate, traits::{ election::Membership, - network::{ConnectedNetwork, Topic}, + network::ConnectedNetwork, node_implementation::{ConsensusTime, NodeImplementation, NodeType, Versions}, }, HotShotConfig, ValidatorConfig, @@ -419,17 +419,10 @@ where self.next_node_id += 1; tracing::debug!("launch node {}", i); - let all_nodes = config.known_nodes_with_stake.clone(); - let da_nodes = config.known_da_nodes.clone(); - - let memberships = Memberships { - quorum_membership: ::Membership::new( - all_nodes.clone(), - all_nodes.clone(), - Topic::Global, - ), - da_membership: ::Membership::new(all_nodes, da_nodes, Topic::Da), - }; + let memberships = ::Membership::new( + config.known_nodes_with_stake.clone(), + config.known_da_nodes.clone(), + ); config.builder_urls = builder_urls .clone() .try_into() @@ -584,7 +577,7 @@ where pub async fn add_node_with_config( node_id: u64, network: Network, - memberships: Memberships, + memberships: TYPES::Membership, initializer: HotShotInitializer, config: HotShotConfig, validator_config: ValidatorConfig, @@ -617,7 +610,7 @@ where pub async fn add_node_with_config_and_channels( node_id: u64, network: Network, - memberships: Memberships, + memberships: TYPES::Membership, initializer: HotShotInitializer, config: HotShotConfig, validator_config: ValidatorConfig, @@ -667,7 +660,7 @@ pub struct LateNodeContextParameters, + pub memberships: TYPES::Membership, /// The config associated with this node. pub config: HotShotConfig, diff --git a/crates/testing/src/view_generator.rs b/crates/testing/src/view_generator.rs index 2da7829fc9..8ff60cf949 100644 --- a/crates/testing/src/view_generator.rs +++ b/crates/testing/src/view_generator.rs @@ -54,8 +54,7 @@ pub struct TestView { pub leaf: Leaf2, pub view_number: ViewNumber, pub epoch_number: EpochNumber, - pub quorum_membership: ::Membership, - pub da_membership: ::Membership, + pub membership: ::Membership, pub vid_disperse: Proposal>, pub vid_proposal: ( Vec>>, @@ -72,10 +71,7 @@ pub struct TestView { } impl TestView { - pub async fn genesis( - quorum_membership: &::Membership, - da_membership: &::Membership, - ) -> Self { + pub async fn genesis(membership: &::Membership) -> Self { let genesis_view = ViewNumber::new(1); let genesis_epoch = EpochNumber::new(1); let upgrade_lock = UpgradeLock::new(); @@ -100,14 +96,11 @@ impl TestView { let leader_public_key = public_key; - let payload_commitment = da_payload_commitment::( - quorum_membership, - transactions.clone(), - genesis_epoch, - ); + let payload_commitment = + da_payload_commitment::(membership, transactions.clone(), genesis_epoch); let (vid_disperse, vid_proposal) = build_vid_proposal( - quorum_membership, + membership, genesis_view, genesis_epoch, transactions.clone(), @@ -115,8 +108,7 @@ impl TestView { ); let da_certificate = build_da_certificate( - quorum_membership, - da_membership, + membership, genesis_view, genesis_epoch, transactions.clone(), @@ -190,8 +182,7 @@ impl TestView { leaf, view_number: genesis_view, epoch_number: genesis_epoch, - quorum_membership: quorum_membership.clone(), - da_membership: da_membership.clone(), + membership: membership.clone(), vid_disperse, vid_proposal: (vid_proposal, public_key), da_certificate, @@ -219,8 +210,7 @@ impl TestView { // test view here. let next_view = max(old_view, self.view_number) + 1; - let quorum_membership = &self.quorum_membership; - let da_membership = &self.da_membership; + let membership = &self.membership; let transactions = &self.transactions; @@ -247,14 +237,11 @@ impl TestView { &metadata, ); - let payload_commitment = da_payload_commitment::( - quorum_membership, - transactions.clone(), - self.epoch_number, - ); + let payload_commitment = + da_payload_commitment::(membership, transactions.clone(), self.epoch_number); let (vid_disperse, vid_proposal) = build_vid_proposal( - quorum_membership, + membership, next_view, self.epoch_number, transactions.clone(), @@ -262,8 +249,7 @@ impl TestView { ); let da_certificate = build_da_certificate::( - quorum_membership, - da_membership, + membership, next_view, self.epoch_number, transactions.clone(), @@ -281,7 +267,7 @@ impl TestView { QuorumCertificate2, >( quorum_data, - quorum_membership, + membership, old_view, self.epoch_number, &old_public_key, @@ -299,7 +285,7 @@ impl TestView { UpgradeCertificate, >( data.clone(), - quorum_membership, + membership, next_view, self.epoch_number, &public_key, @@ -322,7 +308,7 @@ impl TestView { ViewSyncFinalizeCertificate2, >( data.clone(), - quorum_membership, + membership, next_view, self.epoch_number, &public_key, @@ -345,7 +331,7 @@ impl TestView { TimeoutCertificate, >( data.clone(), - quorum_membership, + membership, next_view, self.epoch_number, &public_key, @@ -425,8 +411,7 @@ impl TestView { leaf, view_number: next_view, epoch_number: self.epoch_number, - quorum_membership: quorum_membership.clone(), - da_membership: self.da_membership.clone(), + membership: self.membership.clone(), vid_disperse, vid_proposal: (vid_proposal, public_key), da_certificate, @@ -501,19 +486,14 @@ impl TestView { pub struct TestViewGenerator { pub current_view: Option, - pub quorum_membership: ::Membership, - pub da_membership: ::Membership, + pub membership: ::Membership, } impl TestViewGenerator { - pub fn generate( - quorum_membership: ::Membership, - da_membership: ::Membership, - ) -> Self { + pub fn generate(membership: ::Membership) -> Self { TestViewGenerator { current_view: None, - quorum_membership, - da_membership, + membership, } } @@ -590,14 +570,13 @@ impl Stream for TestViewGenerator { type Item = TestView; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let qm = &self.quorum_membership.clone(); - let da = &self.da_membership.clone(); + let mem = &self.membership.clone(); let curr_view = &self.current_view.clone(); let mut fut = if let Some(ref view) = curr_view { async move { TestView::next_view(view).await }.boxed() } else { - async move { TestView::genesis(qm, da).await }.boxed() + async move { TestView::genesis(mem).await }.boxed() }; match fut.as_mut().poll(cx) { diff --git a/crates/testing/tests/tests_1/da_task.rs b/crates/testing/tests/tests_1/da_task.rs index 96a7de771d..6f4c2a38df 100644 --- a/crates/testing/tests/tests_1/da_task.rs +++ b/crates/testing/tests/tests_1/da_task.rs @@ -39,8 +39,8 @@ async fn test_da_task() { let handle = build_system_handle::(2) .await .0; - let quorum_membership = handle.hotshot.memberships.quorum_membership.clone(); - let da_membership = handle.hotshot.memberships.da_membership.clone(); + + let membership = (*handle.hotshot.memberships).clone(); // Make some empty encoded transactions, we just care about having a commitment handy for the // later calls. We need the VID commitment to be able to propose later. @@ -48,14 +48,10 @@ async fn test_da_task() { let encoded_transactions = Arc::from(TestTransaction::encode(&transactions)); let (payload_commit, precompute) = precompute_vid_commitment( &encoded_transactions, - handle - .hotshot - .memberships - .quorum_membership - .total_nodes(EpochNumber::new(0)), + handle.hotshot.memberships.total_nodes(EpochNumber::new(0)), ); - let mut generator = TestViewGenerator::generate(quorum_membership.clone(), da_membership); + let mut generator = TestViewGenerator::generate(membership.clone()); let mut proposals = Vec::new(); let mut leaders = Vec::new(); @@ -98,7 +94,7 @@ async fn test_da_task() { }, ViewNumber::new(2), vec1::vec1![null_block::builder_fee::( - quorum_membership.total_nodes(EpochNumber::new(0)), + membership.total_nodes(EpochNumber::new(0)), ::Base::VERSION, *ViewNumber::new(2), ) @@ -139,8 +135,7 @@ async fn test_da_task_storage_failure() { // Set the error flag here for the system handle. This causes it to emit an error on append. handle.storage().write().await.should_return_err = true; - let quorum_membership = handle.hotshot.memberships.quorum_membership.clone(); - let da_membership = handle.hotshot.memberships.da_membership.clone(); + let membership = (*handle.hotshot.memberships).clone(); // Make some empty encoded transactions, we just care about having a commitment handy for the // later calls. We need the VID commitment to be able to propose later. @@ -148,14 +143,10 @@ async fn test_da_task_storage_failure() { let encoded_transactions = Arc::from(TestTransaction::encode(&transactions)); let (payload_commit, precompute) = precompute_vid_commitment( &encoded_transactions, - handle - .hotshot - .memberships - .quorum_membership - .total_nodes(EpochNumber::new(0)), + handle.hotshot.memberships.total_nodes(EpochNumber::new(0)), ); - let mut generator = TestViewGenerator::generate(quorum_membership.clone(), da_membership); + let mut generator = TestViewGenerator::generate(membership.clone()); let mut proposals = Vec::new(); let mut leaders = Vec::new(); @@ -198,7 +189,7 @@ async fn test_da_task_storage_failure() { }, ViewNumber::new(2), vec1::vec1![null_block::builder_fee::( - quorum_membership.total_nodes(EpochNumber::new(0)), + membership.total_nodes(EpochNumber::new(0)), ::Base::VERSION, *ViewNumber::new(2), ) diff --git a/crates/testing/tests/tests_1/message.rs b/crates/testing/tests/tests_1/message.rs index 5e0ff49eaa..c8869bc3a5 100644 --- a/crates/testing/tests/tests_1/message.rs +++ b/crates/testing/tests/tests_1/message.rs @@ -66,6 +66,7 @@ async fn test_certificate2_validity() { use hotshot_testing::{helpers::build_system_handle, view_generator::TestViewGenerator}; use hotshot_types::{ data::{EpochNumber, Leaf, Leaf2}, + traits::election::Membership, traits::node_implementation::ConsensusTime, vote::Certificate, }; @@ -76,10 +77,9 @@ async fn test_certificate2_validity() { let handle = build_system_handle::(node_id) .await .0; - let quorum_membership = handle.hotshot.memberships.quorum_membership.clone(); - let da_membership = handle.hotshot.memberships.da_membership.clone(); + let membership = (*handle.hotshot.memberships).clone(); - let mut generator = TestViewGenerator::generate(quorum_membership.clone(), da_membership); + let mut generator = TestViewGenerator::generate(membership.clone()); let mut proposals = Vec::new(); let mut leaders = Vec::new(); @@ -104,8 +104,8 @@ async fn test_certificate2_validity() { assert!( qc.is_valid_cert( - &quorum_membership, - EpochNumber::new(0), + membership.stake_table(EpochNumber::new(0)), + membership.success_threshold(), &handle.hotshot.upgrade_lock ) .await @@ -113,8 +113,8 @@ async fn test_certificate2_validity() { assert!( qc2.is_valid_cert( - &quorum_membership, - EpochNumber::new(0), + membership.stake_table(EpochNumber::new(0)), + membership.success_threshold(), &handle.hotshot.upgrade_lock ) .await diff --git a/crates/testing/tests/tests_1/network_task.rs b/crates/testing/tests/tests_1/network_task.rs index 9a539f89e4..a4e1c29a03 100644 --- a/crates/testing/tests/tests_1/network_task.rs +++ b/crates/testing/tests/tests_1/network_task.rs @@ -36,7 +36,6 @@ async fn test_network_task() { use std::collections::BTreeMap; use futures::StreamExt; - use hotshot_types::traits::network::Topic; hotshot::helpers::initialize_logging(); @@ -59,15 +58,13 @@ async fn test_network_task() { let all_nodes = config.known_nodes_with_stake.clone(); - let membership = - ::Membership::new(all_nodes.clone(), all_nodes, Topic::Global); + let membership = ::Membership::new(all_nodes.clone(), all_nodes); let network_state: NetworkEventTaskState, _> = NetworkEventTaskState { network: network.clone(), view: ViewNumber::new(0), epoch: EpochNumber::new(0), - quorum_membership: membership.clone(), - da_membership: membership.clone(), + membership: membership.clone(), upgrade_lock: upgrade_lock.clone(), storage, consensus, @@ -79,7 +76,7 @@ async fn test_network_task() { let task = Task::new(network_state, tx.clone(), rx); task_reg.run_task(task); - let mut generator = TestViewGenerator::generate(membership.clone(), membership); + let mut generator = TestViewGenerator::generate(membership); let view = generator.next().await.unwrap(); let (out_tx_internal, mut out_rx_internal) = async_broadcast::broadcast(10); @@ -208,7 +205,6 @@ async fn test_network_storage_fail() { use std::collections::BTreeMap; use futures::StreamExt; - use hotshot_types::traits::network::Topic; hotshot::helpers::initialize_logging(); @@ -231,15 +227,13 @@ async fn test_network_storage_fail() { let all_nodes = config.known_nodes_with_stake.clone(); let upgrade_lock = UpgradeLock::::new(); - let membership = - ::Membership::new(all_nodes.clone(), all_nodes, Topic::Global); + let membership = ::Membership::new(all_nodes.clone(), all_nodes); let network_state: NetworkEventTaskState, _> = NetworkEventTaskState { network: network.clone(), view: ViewNumber::new(0), epoch: EpochNumber::new(0), - quorum_membership: membership.clone(), - da_membership: membership.clone(), + membership: membership.clone(), upgrade_lock: upgrade_lock.clone(), storage, consensus, @@ -251,7 +245,7 @@ async fn test_network_storage_fail() { let task = Task::new(network_state, tx.clone(), rx); task_reg.run_task(task); - let mut generator = TestViewGenerator::generate(membership.clone(), membership); + let mut generator = TestViewGenerator::generate(membership); let view = generator.next().await.unwrap(); let (out_tx_internal, mut out_rx_internal): (Sender>>, _) = diff --git a/crates/testing/tests/tests_1/quorum_proposal_recv_task.rs b/crates/testing/tests/tests_1/quorum_proposal_recv_task.rs index 7a1636d9c6..97742d0deb 100644 --- a/crates/testing/tests/tests_1/quorum_proposal_recv_task.rs +++ b/crates/testing/tests/tests_1/quorum_proposal_recv_task.rs @@ -32,6 +32,7 @@ use hotshot_types::{ request_response::ProposalRequestPayload, traits::{ consensus_api::ConsensusApi, + election::Membership, node_implementation::{ConsensusTime, NodeType}, signature_key::SignatureKey, ValidatedState, @@ -53,12 +54,11 @@ async fn test_quorum_proposal_recv_task() { let handle = build_system_handle::(2) .await .0; - let quorum_membership = handle.hotshot.memberships.quorum_membership.clone(); - let da_membership = handle.hotshot.memberships.da_membership.clone(); + let membership = (*handle.hotshot.memberships).clone(); let consensus = handle.hotshot.consensus(); let mut consensus_writer = consensus.write().await; - let mut generator = TestViewGenerator::generate(quorum_membership.clone(), da_membership); + let mut generator = TestViewGenerator::generate(membership.clone()); let mut proposals = Vec::new(); let mut leaders = Vec::new(); let mut votes = Vec::new(); @@ -129,12 +129,11 @@ async fn test_quorum_proposal_recv_task_liveness_check() { let handle = build_system_handle::(4) .await .0; - let quorum_membership = handle.hotshot.memberships.quorum_membership.clone(); - let da_membership = handle.hotshot.memberships.da_membership.clone(); + let membership = (*handle.hotshot.memberships).clone(); let consensus = handle.hotshot.consensus(); let mut consensus_writer = consensus.write().await; - let mut generator = TestViewGenerator::generate(quorum_membership.clone(), da_membership); + let mut generator = TestViewGenerator::generate(membership); let mut proposals = Vec::new(); let mut leaders = Vec::new(); let mut votes = Vec::new(); diff --git a/crates/testing/tests/tests_1/quorum_proposal_task.rs b/crates/testing/tests/tests_1/quorum_proposal_task.rs index f522d28f7c..0124e10b5a 100644 --- a/crates/testing/tests/tests_1/quorum_proposal_task.rs +++ b/crates/testing/tests/tests_1/quorum_proposal_task.rs @@ -50,16 +50,16 @@ async fn test_quorum_proposal_task_quorum_proposal_view_1() { let handle = build_system_handle::(node_id) .await .0; - let quorum_membership = handle.hotshot.memberships.quorum_membership.clone(); - let da_membership = handle.hotshot.memberships.da_membership.clone(); + + let membership = (*handle.hotshot.memberships).clone(); let payload_commitment = build_payload_commitment::( - &quorum_membership, + &membership, ViewNumber::new(node_id), EpochNumber::new(1), ); - let mut generator = TestViewGenerator::generate(quorum_membership.clone(), da_membership); + let mut generator = TestViewGenerator::generate(membership.clone()); let mut proposals = Vec::new(); let mut leaders = Vec::new(); @@ -90,7 +90,7 @@ async fn test_quorum_proposal_task_quorum_proposal_view_1() { let genesis_cert = proposals[0].data.justify_qc.clone(); let builder_commitment = BuilderCommitment::from_raw_digest(sha2::Sha256::new().finalize()); let builder_fee = null_block::builder_fee::( - quorum_membership.total_nodes(EpochNumber::new(1)), + membership.total_nodes(EpochNumber::new(1)), ::Base::VERSION, *ViewNumber::new(1), ) @@ -144,10 +144,10 @@ async fn test_quorum_proposal_task_quorum_proposal_view_gt_1() { let handle = build_system_handle::(node_id) .await .0; - let quorum_membership = handle.hotshot.memberships.quorum_membership.clone(); - let da_membership = handle.hotshot.memberships.da_membership.clone(); - let mut generator = TestViewGenerator::generate(quorum_membership.clone(), da_membership); + let membership = (*handle.hotshot.memberships).clone(); + + let mut generator = TestViewGenerator::generate(membership.clone()); let mut proposals = Vec::new(); let mut leaders = Vec::new(); @@ -182,7 +182,7 @@ async fn test_quorum_proposal_task_quorum_proposal_view_gt_1() { let builder_commitment = BuilderCommitment::from_raw_digest(sha2::Sha256::new().finalize()); let builder_fee = null_block::builder_fee::( - quorum_membership.total_nodes(EpochNumber::new(1)), + membership.total_nodes(EpochNumber::new(1)), ::Base::VERSION, *ViewNumber::new(1), ) @@ -193,7 +193,7 @@ async fn test_quorum_proposal_task_quorum_proposal_view_gt_1() { Qc2Formed(either::Left(genesis_cert.clone())), SendPayloadCommitmentAndMetadata( build_payload_commitment::( - &quorum_membership, + &membership, ViewNumber::new(1), EpochNumber::new(1) ), @@ -212,7 +212,7 @@ async fn test_quorum_proposal_task_quorum_proposal_view_gt_1() { Qc2Formed(either::Left(proposals[1].data.justify_qc.clone())), SendPayloadCommitmentAndMetadata( build_payload_commitment::( - &quorum_membership, + &membership, ViewNumber::new(2), EpochNumber::new(1) ), @@ -229,7 +229,7 @@ async fn test_quorum_proposal_task_quorum_proposal_view_gt_1() { Qc2Formed(either::Left(proposals[2].data.justify_qc.clone())), SendPayloadCommitmentAndMetadata( build_payload_commitment::( - &quorum_membership, + &membership, ViewNumber::new(3), EpochNumber::new(1) ), @@ -246,7 +246,7 @@ async fn test_quorum_proposal_task_quorum_proposal_view_gt_1() { Qc2Formed(either::Left(proposals[3].data.justify_qc.clone())), SendPayloadCommitmentAndMetadata( build_payload_commitment::( - &quorum_membership, + &membership, ViewNumber::new(4), EpochNumber::new(1) ), @@ -263,7 +263,7 @@ async fn test_quorum_proposal_task_quorum_proposal_view_gt_1() { Qc2Formed(either::Left(proposals[4].data.justify_qc.clone())), SendPayloadCommitmentAndMetadata( build_payload_commitment::( - &quorum_membership, + &membership, ViewNumber::new(5), EpochNumber::new(1) ), @@ -308,17 +308,16 @@ async fn test_quorum_proposal_task_qc_timeout() { let handle = build_system_handle::(node_id) .await .0; - let quorum_membership = handle.hotshot.memberships.quorum_membership.clone(); - let da_membership = handle.hotshot.memberships.da_membership.clone(); + let membership = (*handle.hotshot.memberships).clone(); let payload_commitment = build_payload_commitment::( - &quorum_membership, + &membership, ViewNumber::new(node_id), EpochNumber::new(1), ); let builder_commitment = BuilderCommitment::from_raw_digest(sha2::Sha256::new().finalize()); - let mut generator = TestViewGenerator::generate(quorum_membership.clone(), da_membership); + let mut generator = TestViewGenerator::generate(membership.clone()); let mut proposals = Vec::new(); let mut leaders = Vec::new(); @@ -360,7 +359,7 @@ async fn test_quorum_proposal_task_qc_timeout() { }, ViewNumber::new(3), vec1![null_block::builder_fee::( - quorum_membership.total_nodes(EpochNumber::new(1)), + membership.total_nodes(EpochNumber::new(1)), ::Base::VERSION, *ViewNumber::new(3), ) @@ -396,17 +395,17 @@ async fn test_quorum_proposal_task_view_sync() { let handle = build_system_handle::(node_id) .await .0; - let quorum_membership = handle.hotshot.memberships.quorum_membership.clone(); - let da_membership = handle.hotshot.memberships.da_membership.clone(); + + let membership = (*handle.hotshot.memberships).clone(); let payload_commitment = build_payload_commitment::( - &quorum_membership, + &membership, ViewNumber::new(node_id), EpochNumber::new(1), ); let builder_commitment = BuilderCommitment::from_raw_digest(sha2::Sha256::new().finalize()); - let mut generator = TestViewGenerator::generate(quorum_membership.clone(), da_membership); + let mut generator = TestViewGenerator::generate(membership.clone()); let mut proposals = Vec::new(); let mut leaders = Vec::new(); @@ -450,7 +449,7 @@ async fn test_quorum_proposal_task_view_sync() { }, ViewNumber::new(2), vec1![null_block::builder_fee::( - quorum_membership.total_nodes(EpochNumber::new(1)), + membership.total_nodes(EpochNumber::new(1)), ::Base::VERSION, *ViewNumber::new(2), ) @@ -484,10 +483,10 @@ async fn test_quorum_proposal_task_liveness_check() { let handle = build_system_handle::(node_id) .await .0; - let quorum_membership = handle.hotshot.memberships.quorum_membership.clone(); - let da_membership = handle.hotshot.memberships.da_membership.clone(); - let mut generator = TestViewGenerator::generate(quorum_membership.clone(), da_membership); + let membership = (*handle.hotshot.memberships).clone(); + + let mut generator = TestViewGenerator::generate(membership.clone()); let mut proposals = Vec::new(); let mut leaders = Vec::new(); @@ -517,7 +516,7 @@ async fn test_quorum_proposal_task_liveness_check() { let builder_commitment = BuilderCommitment::from_raw_digest(sha2::Sha256::new().finalize()); let builder_fee = null_block::builder_fee::( - quorum_membership.total_nodes(EpochNumber::new(1)), + membership.total_nodes(EpochNumber::new(1)), ::Base::VERSION, *ViewNumber::new(1), ) @@ -532,7 +531,7 @@ async fn test_quorum_proposal_task_liveness_check() { Qc2Formed(either::Left(genesis_cert.clone())), SendPayloadCommitmentAndMetadata( build_payload_commitment::( - &quorum_membership, + &membership, ViewNumber::new(1), EpochNumber::new(1) ), @@ -551,7 +550,7 @@ async fn test_quorum_proposal_task_liveness_check() { Qc2Formed(either::Left(proposals[1].data.justify_qc.clone())), SendPayloadCommitmentAndMetadata( build_payload_commitment::( - &quorum_membership, + &membership, ViewNumber::new(2), EpochNumber::new(1) ), @@ -568,7 +567,7 @@ async fn test_quorum_proposal_task_liveness_check() { Qc2Formed(either::Left(proposals[2].data.justify_qc.clone())), SendPayloadCommitmentAndMetadata( build_payload_commitment::( - &quorum_membership, + &membership, ViewNumber::new(3), EpochNumber::new(1) ), @@ -585,7 +584,7 @@ async fn test_quorum_proposal_task_liveness_check() { Qc2Formed(either::Left(proposals[3].data.justify_qc.clone())), SendPayloadCommitmentAndMetadata( build_payload_commitment::( - &quorum_membership, + &membership, ViewNumber::new(4), EpochNumber::new(1) ), @@ -602,7 +601,7 @@ async fn test_quorum_proposal_task_liveness_check() { Qc2Formed(either::Left(proposals[4].data.justify_qc.clone())), SendPayloadCommitmentAndMetadata( build_payload_commitment::( - &quorum_membership, + &membership, ViewNumber::new(5), EpochNumber::new(1) ), @@ -643,10 +642,9 @@ async fn test_quorum_proposal_task_with_incomplete_events() { let handle = build_system_handle::(2) .await .0; - let quorum_membership = handle.hotshot.memberships.quorum_membership.clone(); - let da_membership = handle.hotshot.memberships.da_membership.clone(); + let membership = (*handle.hotshot.memberships).clone(); - let mut generator = TestViewGenerator::generate(quorum_membership.clone(), da_membership); + let mut generator = TestViewGenerator::generate(membership); let mut proposals = Vec::new(); let mut leaders = Vec::new(); diff --git a/crates/testing/tests/tests_1/quorum_vote_task.rs b/crates/testing/tests/tests_1/quorum_vote_task.rs index f5c5940b20..b5d079f56c 100644 --- a/crates/testing/tests/tests_1/quorum_vote_task.rs +++ b/crates/testing/tests/tests_1/quorum_vote_task.rs @@ -44,10 +44,10 @@ async fn test_quorum_vote_task_success() { let handle = build_system_handle::(2) .await .0; - let quorum_membership = handle.hotshot.memberships.quorum_membership.clone(); - let da_membership = handle.hotshot.memberships.da_membership.clone(); - let mut generator = TestViewGenerator::generate(quorum_membership.clone(), da_membership); + let membership = (*handle.hotshot.memberships).clone(); + + let mut generator = TestViewGenerator::generate(membership.clone()); let mut proposals = Vec::new(); let mut leaves = Vec::new(); @@ -111,10 +111,10 @@ async fn test_quorum_vote_task_miss_dependency() { let handle = build_system_handle::(2) .await .0; - let quorum_membership = handle.hotshot.memberships.quorum_membership.clone(); - let da_membership = handle.hotshot.memberships.da_membership.clone(); - let mut generator = TestViewGenerator::generate(quorum_membership.clone(), da_membership); + let membership = (*handle.hotshot.memberships).clone(); + + let mut generator = TestViewGenerator::generate(membership.clone()); let mut proposals = Vec::new(); let mut leaders = Vec::new(); @@ -195,10 +195,10 @@ async fn test_quorum_vote_task_incorrect_dependency() { let handle = build_system_handle::(2) .await .0; - let quorum_membership = handle.hotshot.memberships.quorum_membership.clone(); - let da_membership = handle.hotshot.memberships.da_membership.clone(); - let mut generator = TestViewGenerator::generate(quorum_membership.clone(), da_membership); + let membership = (*handle.hotshot.memberships).clone(); + + let mut generator = TestViewGenerator::generate(membership); let mut proposals = Vec::new(); let mut leaves = Vec::new(); diff --git a/crates/testing/tests/tests_1/transaction_task.rs b/crates/testing/tests/tests_1/transaction_task.rs index 0a4877fcc1..43773e63da 100644 --- a/crates/testing/tests/tests_1/transaction_task.rs +++ b/crates/testing/tests/tests_1/transaction_task.rs @@ -39,10 +39,11 @@ async fn test_transaction_task_leader_two_views_in_a_row() { EpochNumber::new(1), )); input.push(HotShotEvent::Shutdown); - let quorum_membership = handle.hotshot.memberships.quorum_membership.clone(); - let (_, precompute_data) = - precompute_vid_commitment(&[], quorum_membership.total_nodes(EpochNumber::new(0))); + let (_, precompute_data) = precompute_vid_commitment( + &[], + handle.hotshot.memberships.total_nodes(EpochNumber::new(0)), + ); // current view let mut exp_packed_bundle = PackedBundle::new( @@ -53,7 +54,7 @@ async fn test_transaction_task_leader_two_views_in_a_row() { current_view, vec1::vec1![ null_block::builder_fee::( - quorum_membership.total_nodes(EpochNumber::new(0)), + handle.hotshot.memberships.total_nodes(EpochNumber::new(0)), ::Base::VERSION, *ViewNumber::new(4), ) diff --git a/crates/testing/tests/tests_1/upgrade_task_with_proposal.rs b/crates/testing/tests/tests_1/upgrade_task_with_proposal.rs index 0c706efd02..44833d7727 100644 --- a/crates/testing/tests/tests_1/upgrade_task_with_proposal.rs +++ b/crates/testing/tests/tests_1/upgrade_task_with_proposal.rs @@ -58,8 +58,6 @@ async fn test_upgrade_task_with_proposal() { let handle = build_system_handle::(3) .await .0; - let quorum_membership = handle.hotshot.memberships.quorum_membership.clone(); - let da_membership = handle.hotshot.memberships.da_membership.clone(); let other_handles = futures::future::join_all((0..=9).map(build_system_handle)).await; @@ -85,7 +83,9 @@ async fn test_upgrade_task_with_proposal() { let consensus = handle.hotshot.consensus(); let mut consensus_writer = consensus.write().await; - let mut generator = TestViewGenerator::generate(quorum_membership.clone(), da_membership); + let membership = (*handle.hotshot.memberships).clone(); + + let mut generator = TestViewGenerator::generate(membership.clone()); for view in (&mut generator).take(1).collect::>().await { proposals.push(view.quorum_proposal.clone()); @@ -126,7 +126,7 @@ async fn test_upgrade_task_with_proposal() { let genesis_cert = proposals[0].data.justify_qc.clone(); let builder_commitment = BuilderCommitment::from_raw_digest(sha2::Sha256::new().finalize()); let builder_fee = null_block::builder_fee::( - quorum_membership.total_nodes(EpochNumber::new(1)), + membership.total_nodes(EpochNumber::new(1)), ::Base::VERSION, *ViewNumber::new(1), ) @@ -153,7 +153,7 @@ async fn test_upgrade_task_with_proposal() { Qc2Formed(either::Left(genesis_cert.clone())), SendPayloadCommitmentAndMetadata( build_payload_commitment::( - &quorum_membership, + &membership, ViewNumber::new(1), EpochNumber::new(1) ), @@ -172,7 +172,7 @@ async fn test_upgrade_task_with_proposal() { Qc2Formed(either::Left(proposals[1].data.justify_qc.clone())), SendPayloadCommitmentAndMetadata( build_payload_commitment::( - &quorum_membership, + &membership, ViewNumber::new(2), EpochNumber::new(1) ), @@ -190,7 +190,7 @@ async fn test_upgrade_task_with_proposal() { Qc2Formed(either::Left(proposals[2].data.justify_qc.clone())), SendPayloadCommitmentAndMetadata( build_payload_commitment::( - &quorum_membership, + &membership, ViewNumber::new(3), EpochNumber::new(1) ), diff --git a/crates/testing/tests/tests_1/upgrade_task_with_vote.rs b/crates/testing/tests/tests_1/upgrade_task_with_vote.rs index cc942451f1..5390f56e03 100644 --- a/crates/testing/tests/tests_1/upgrade_task_with_vote.rs +++ b/crates/testing/tests/tests_1/upgrade_task_with_vote.rs @@ -48,8 +48,6 @@ async fn test_upgrade_task_with_vote() { let handle = build_system_handle::(2) .await .0; - let quorum_membership = handle.hotshot.memberships.quorum_membership.clone(); - let da_membership = handle.hotshot.memberships.da_membership.clone(); let old_version = Version { major: 0, minor: 1 }; let new_version = Version { major: 0, minor: 2 }; @@ -72,7 +70,9 @@ async fn test_upgrade_task_with_vote() { let consensus = handle.hotshot.consensus().clone(); let mut consensus_writer = consensus.write().await; - let mut generator = TestViewGenerator::generate(quorum_membership.clone(), da_membership); + let membership = (*handle.hotshot.memberships).clone(); + let mut generator = TestViewGenerator::generate(membership); + for view in (&mut generator).take(2).collect::>().await { proposals.push(view.quorum_proposal.clone()); votes.push(view.create_quorum_vote(&handle).await); diff --git a/crates/testing/tests/tests_1/vid_task.rs b/crates/testing/tests/tests_1/vid_task.rs index 8d8daf4c5e..81f9ac2999 100644 --- a/crates/testing/tests/tests_1/vid_task.rs +++ b/crates/testing/tests/tests_1/vid_task.rs @@ -45,11 +45,10 @@ async fn test_vid_task() { .0; let pub_key = handle.public_key(); - // quorum membership for VID share distribution - let quorum_membership = handle.hotshot.memberships.quorum_membership.clone(); + let membership = (*handle.hotshot.memberships).clone(); let mut vid = vid_scheme_from_view_number::( - &quorum_membership, + &membership, ViewNumber::new(0), EpochNumber::new(0), ); @@ -90,7 +89,7 @@ async fn test_vid_task() { let vid_disperse = VidDisperse::from_membership( message.data.view_number, vid_disperse, - &quorum_membership, + &membership, EpochNumber::new(0), ); @@ -110,7 +109,7 @@ async fn test_vid_task() { }, ViewNumber::new(2), vec1::vec1![null_block::builder_fee::( - quorum_membership.total_nodes(EpochNumber::new(0)), + membership.total_nodes(EpochNumber::new(0)), ::Base::VERSION, *ViewNumber::new(2), ) @@ -132,7 +131,7 @@ async fn test_vid_task() { }, ViewNumber::new(2), vec1![null_block::builder_fee::( - quorum_membership.total_nodes(EpochNumber::new(0)), + membership.total_nodes(EpochNumber::new(0)), ::Base::VERSION, *ViewNumber::new(2), ) diff --git a/crates/testing/tests/tests_1/vote_dependency_handle.rs b/crates/testing/tests/tests_1/vote_dependency_handle.rs index 938dbbffa8..9de2531361 100644 --- a/crates/testing/tests/tests_1/vote_dependency_handle.rs +++ b/crates/testing/tests/tests_1/vote_dependency_handle.rs @@ -36,10 +36,9 @@ async fn test_vote_dependency_handle() { let handle = build_system_handle::(node_id) .await .0; - let quorum_membership = handle.hotshot.memberships.quorum_membership.clone(); - let da_membership = handle.hotshot.memberships.da_membership.clone(); + let membership = (*handle.hotshot.memberships).clone(); - let mut generator = TestViewGenerator::generate(quorum_membership.clone(), da_membership); + let mut generator = TestViewGenerator::generate(membership.clone()); // Generate our state for the test let mut proposals = Vec::new(); @@ -90,7 +89,7 @@ async fn test_vote_dependency_handle() { private_key: handle.private_key().clone(), consensus: OuterConsensus::new(consensus.clone()), instance_state: handle.hotshot.instance_state(), - quorum_membership: handle.hotshot.memberships.quorum_membership.clone().into(), + quorum_membership: (*handle.hotshot.memberships).clone().into(), storage: Arc::clone(&handle.storage()), view_number, sender: event_sender.clone(), diff --git a/crates/types/src/simple_certificate.rs b/crates/types/src/simple_certificate.rs index 17fd4aa1c2..271d5b0729 100644 --- a/crates/types/src/simple_certificate.rs +++ b/crates/types/src/simple_certificate.rs @@ -10,6 +10,7 @@ use std::{ fmt::{self, Debug, Display, Formatter}, hash::Hash, marker::PhantomData, + num::NonZeroU64, sync::Arc, }; @@ -23,8 +24,9 @@ use crate::{ data::serialize_signature2, message::UpgradeLock, simple_vote::{ - DaData, QuorumData, QuorumData2, TimeoutData, UpgradeProposalData, VersionedVoteData, - ViewSyncCommitData, ViewSyncFinalizeData, ViewSyncPreCommitData, Voteable, + DaData, QuorumData, QuorumData2, QuorumMaker, TimeoutData, UpgradeProposalData, + VersionedVoteData, ViewSyncCommitData, ViewSyncFinalizeData, ViewSyncPreCommitData, + Voteable, }, traits::{ election::Membership, @@ -123,14 +125,14 @@ impl> Certificate - for SimpleCertificate +impl> Certificate + for SimpleCertificate { - type Voteable = VOTEABLE; + type Voteable = DaData; type Threshold = THRESHOLD; fn create_signed_certificate( - vote_commitment: Commitment>, + vote_commitment: Commitment>, data: Self::Voteable, sig: ::QcType, view: TYPES::View, @@ -145,18 +147,103 @@ impl> _pd: PhantomData, } } - async fn is_valid_cert, V: Versions>( + async fn is_valid_cert( &self, + stake_table: Vec<::StakeTableEntry>, + threshold: NonZeroU64, + upgrade_lock: &UpgradeLock, + ) -> bool { + if self.view_number == TYPES::View::genesis() { + return true; + } + let real_qc_pp = ::public_parameter( + stake_table, + U256::from(u64::from(threshold)), + ); + let Ok(commit) = self.data_commitment(upgrade_lock).await else { + return false; + }; + ::check( + &real_qc_pp, + commit.as_ref(), + self.signatures.as_ref().unwrap(), + ) + } + /// Proxy's to `Membership.stake` + fn stake_table_entry>( membership: &MEMBERSHIP, + pub_key: &TYPES::SignatureKey, epoch: TYPES::Epoch, + ) -> Option<::StakeTableEntry> { + membership.da_stake(pub_key, epoch) + } + + /// Proxy's to `Membership.da_stake_table` + fn stake_table>( + membership: &MEMBERSHIP, + epoch: TYPES::Epoch, + ) -> Vec<::StakeTableEntry> { + membership.da_stake_table(epoch) + } + /// Proxy's to `Membership.da_total_nodes` + fn total_nodes>( + membership: &MEMBERSHIP, + epoch: TYPES::Epoch, + ) -> usize { + membership.da_total_nodes(epoch) + } + fn threshold>(membership: &MEMBERSHIP) -> u64 { + membership.da_success_threshold().into() + } + fn data(&self) -> &Self::Voteable { + &self.data + } + async fn data_commitment( + &self, + upgrade_lock: &UpgradeLock, + ) -> Result>> { + Ok( + VersionedVoteData::new(self.data.clone(), self.view_number, upgrade_lock) + .await? + .commit(), + ) + } +} + +impl> + Certificate for SimpleCertificate +{ + type Voteable = VOTEABLE; + type Threshold = THRESHOLD; + + fn create_signed_certificate( + vote_commitment: Commitment>, + data: Self::Voteable, + sig: ::QcType, + view: TYPES::View, + ) -> Self { + let vote_commitment_bytes: [u8; 32] = vote_commitment.into(); + + SimpleCertificate { + data, + vote_commitment: Commitment::from_raw(vote_commitment_bytes), + view_number: view, + signatures: Some(sig), + _pd: PhantomData, + } + } + async fn is_valid_cert( + &self, + stake_table: Vec<::StakeTableEntry>, + threshold: NonZeroU64, upgrade_lock: &UpgradeLock, ) -> bool { if self.view_number == TYPES::View::genesis() { return true; } let real_qc_pp = ::public_parameter( - membership.stake_table(epoch), - U256::from(Self::threshold(membership)), + stake_table, + U256::from(u64::from(threshold)), ); let Ok(commit) = self.data_commitment(upgrade_lock).await else { return false; @@ -170,6 +257,30 @@ impl> fn threshold>(membership: &MEMBERSHIP) -> u64 { THRESHOLD::threshold(membership) } + + fn stake_table_entry>( + membership: &MEMBERSHIP, + pub_key: &TYPES::SignatureKey, + epoch: TYPES::Epoch, + ) -> Option<::StakeTableEntry> { + membership.stake(pub_key, epoch) + } + + fn stake_table>( + membership: &MEMBERSHIP, + epoch: TYPES::Epoch, + ) -> Vec<::StakeTableEntry> { + membership.stake_table(epoch) + } + + /// Proxy's to `Membership.total_nodes` + fn total_nodes>( + membership: &MEMBERSHIP, + epoch: TYPES::Epoch, + ) -> usize { + membership.total_nodes(epoch) + } + fn data(&self) -> &Self::Voteable { &self.data } @@ -232,8 +343,12 @@ impl UpgradeCertificate { ) -> Result<()> { if let Some(ref cert) = upgrade_certificate { ensure!( - cert.is_valid_cert(quorum_membership, epoch, upgrade_lock) - .await, + cert.is_valid_cert( + quorum_membership.stake_table(epoch), + quorum_membership.upgrade_threshold(), + upgrade_lock + ) + .await, "Invalid upgrade certificate." ); Ok(()) diff --git a/crates/types/src/simple_vote.rs b/crates/types/src/simple_vote.rs index 013653a959..35997d3725 100644 --- a/crates/types/src/simple_vote.rs +++ b/crates/types/src/simple_vote.rs @@ -24,6 +24,9 @@ use crate::{ vote::{HasViewNumber, Vote}, }; +/// Marker that data should use the quorum cert type +pub(crate) trait QuorumMaker {} + #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)] /// Data used for a yes vote. #[serde(bound(deserialize = ""))] @@ -50,12 +53,7 @@ pub struct TimeoutData { /// View the timeout is for pub view: TYPES::View, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)] -/// Data used for a VID vote. -pub struct VidData { - /// Commitment to the block payload the VID vote is on. - pub payload_commit: VidCommitment, -} + #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)] /// Data used for a Pre Commit vote. pub struct ViewSyncPreCommitData { @@ -119,6 +117,14 @@ mod sealed { impl Sealed for C {} } +impl QuorumMaker for QuorumData {} +impl QuorumMaker for QuorumData2 {} +impl QuorumMaker for TimeoutData {} +impl QuorumMaker for ViewSyncPreCommitData {} +impl QuorumMaker for ViewSyncCommitData {} +impl QuorumMaker for ViewSyncFinalizeData {} +impl QuorumMaker for UpgradeProposalData {} + /// A simple yes vote over some votable type. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)] pub struct SimpleVote { @@ -288,14 +294,6 @@ impl Committable for DaData { } } -impl Committable for VidData { - fn commit(&self) -> Commitment { - committable::RawCommitmentBuilder::new("VID data") - .var_size_bytes(self.payload_commit.as_ref()) - .finalize() - } -} - impl Committable for UpgradeProposalData { fn commit(&self) -> Commitment { let builder = committable::RawCommitmentBuilder::new("Upgrade data"); diff --git a/crates/types/src/traits/election.rs b/crates/types/src/traits/election.rs index aa313fc64a..04aa76ccb4 100644 --- a/crates/types/src/traits/election.rs +++ b/crates/types/src/traits/election.rs @@ -9,7 +9,7 @@ use std::{collections::BTreeSet, fmt::Debug, num::NonZeroU64}; use utils::anytrace::Result; -use super::{network::Topic, node_implementation::NodeType}; +use super::node_implementation::NodeType; use crate::{traits::signature_key::SignatureKey, PeerConfig}; /// A protocol for determining membership in and participating in a committee. @@ -21,9 +21,8 @@ pub trait Membership: Clone + Debug + Send + Sync { fn new( // Note: eligible_leaders is currently a hack because the DA leader == the quorum leader // but they should not have voting power. - eligible_leaders: Vec>, - committee_members: Vec>, - committee_topic: Topic, + stake_committee_members: Vec>, + da_committee_members: Vec>, ) -> Self; /// Get all participants in the committee (including their stake) for a specific epoch @@ -32,6 +31,12 @@ pub trait Membership: Clone + Debug + Send + Sync { epoch: TYPES::Epoch, ) -> Vec<::StakeTableEntry>; + /// Get all participants in the committee (including their stake) for a specific epoch + fn da_stake_table( + &self, + epoch: TYPES::Epoch, + ) -> Vec<::StakeTableEntry>; + /// Get all participants in the committee for a specific view for a specific epoch fn committee_members( &self, @@ -39,6 +44,13 @@ pub trait Membership: Clone + Debug + Send + Sync { epoch: TYPES::Epoch, ) -> BTreeSet; + /// Get all participants in the committee for a specific view for a specific epoch + fn da_committee_members( + &self, + view_number: TYPES::View, + epoch: TYPES::Epoch, + ) -> BTreeSet; + /// Get all leaders in the committee for a specific view for a specific epoch fn committee_leaders( &self, @@ -54,9 +66,20 @@ pub trait Membership: Clone + Debug + Send + Sync { epoch: TYPES::Epoch, ) -> Option<::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: TYPES::Epoch, + ) -> Option<::StakeTableEntry>; + /// See if a node has stake in the committee in a specific epoch fn has_stake(&self, pub_key: &TYPES::SignatureKey, epoch: 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: TYPES::Epoch) -> bool; + /// The leader of the committee for view `view_number` in `epoch`. /// /// Note: this function uses a HotShot-internal error type. @@ -74,6 +97,9 @@ pub trait Membership: Clone + Debug + Send + Sync { /// 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( @@ -82,15 +108,18 @@ pub trait Membership: Clone + Debug + Send + Sync { epoch: TYPES::Epoch, ) -> std::result::Result; - /// Get the network topic for the committee - fn committee_topic(&self) -> Topic; - /// Returns the number of total nodes in the committee in an epoch `epoch` fn total_nodes(&self, epoch: TYPES::Epoch) -> usize; + /// Returns the number of total DA nodes in the committee in an epoch `epoch` + fn da_total_nodes(&self, epoch: TYPES::Epoch) -> usize; + /// Returns the threshold for a specific `Membership` implementation fn success_threshold(&self) -> NonZeroU64; + /// Returns the DA threshold for a specific `Membership` implementation + fn da_success_threshold(&self) -> NonZeroU64; + /// Returns the threshold for a specific `Membership` implementation fn failure_threshold(&self) -> NonZeroU64; diff --git a/crates/types/src/vote.rs b/crates/types/src/vote.rs index 6291599004..bdff9d4bb5 100644 --- a/crates/types/src/vote.rs +++ b/crates/types/src/vote.rs @@ -9,6 +9,7 @@ use std::{ collections::{BTreeMap, HashMap}, marker::PhantomData, + num::NonZeroU64, }; use bitvec::{bitvec, vec::BitVec}; @@ -56,7 +57,7 @@ The certificate formed from the collection of signatures a committee. The committee is defined by the `Membership` associated type. The votes all must be over the `Commitment` associated type. */ -pub trait Certificate: HasViewNumber { +pub trait Certificate: HasViewNumber { /// The data commitment this certificate certifies. type Voteable: Voteable; @@ -72,17 +73,38 @@ pub trait Certificate: HasViewNumber { ) -> Self; /// Checks if the cert is valid in the given epoch - fn is_valid_cert, V: Versions>( + fn is_valid_cert( &self, - membership: &MEMBERSHIP, - epoch: TYPES::Epoch, + stake_table: Vec<::StakeTableEntry>, + threshold: NonZeroU64, upgrade_lock: &UpgradeLock, ) -> impl std::future::Future; /// Returns the amount of stake needed to create this certificate // TODO: Make this a static ratio of the total stake of `Membership` fn threshold>(membership: &MEMBERSHIP) -> u64; + + /// Get Stake Table from Membership implementation. + fn stake_table>( + membership: &MEMBERSHIP, + epoch: TYPES::Epoch, + ) -> Vec<::StakeTableEntry>; + + /// Get Total Nodes from Membership implementation. + fn total_nodes>( + membership: &MEMBERSHIP, + epoch: TYPES::Epoch, + ) -> usize; + + /// Get `StakeTableEntry` from Membership implementation. + fn stake_table_entry>( + membership: &MEMBERSHIP, + pub_key: &TYPES::SignatureKey, + epoch: TYPES::Epoch, + ) -> Option<::StakeTableEntry>; + /// Get the commitment which was voted on fn data(&self) -> &Self::Voteable; + /// Get the vote commitment which the votes commit to fn data_commitment( &self, @@ -103,7 +125,7 @@ type SignersMap = HashMap< pub struct VoteAccumulator< TYPES: NodeType, VOTE: Vote, - CERT: Certificate, + CERT: Certificate, V: Versions, > { /// Map of all signatures accumulated so far @@ -127,7 +149,7 @@ pub struct VoteAccumulator< impl< TYPES: NodeType, VOTE: Vote, - CERT: Certificate, + CERT: Certificate, V: Versions, > VoteAccumulator { @@ -161,10 +183,10 @@ impl< return Either::Left(()); } - let Some(stake_table_entry) = membership.stake(&key, epoch) else { + let Some(stake_table_entry) = CERT::stake_table_entry(membership, &key, epoch) else { return Either::Left(()); }; - let stake_table = membership.stake_table(epoch); + let stake_table = CERT::stake_table(membership, epoch); let Some(vote_node_id) = stake_table .iter() .position(|x| *x == stake_table_entry.clone()) @@ -187,7 +209,7 @@ impl< let (signers, sig_list) = self .signers .entry(vote_commitment) - .or_insert((bitvec![0; membership.total_nodes(epoch)], Vec::new())); + .or_insert((bitvec![0; CERT::total_nodes(membership, epoch)], Vec::new())); if signers.get(vote_node_id).as_deref() == Some(&true) { error!("Node id is already in signers list"); return Either::Left(()); @@ -195,7 +217,6 @@ impl< signers.set(vote_node_id, true); sig_list.push(original_signature); - // TODO: Get the stake from the stake table entry. *total_stake_casted += stake_table_entry.stake(); total_vote_map.insert(key, (vote.signature(), vote_commitment)); diff --git a/justfile b/justfile index 12ee669e29..a48c18021c 100644 --- a/justfile +++ b/justfile @@ -51,7 +51,7 @@ test-ci-5 *ARGS: echo Testing {{ARGS}} RUST_LOG=info cargo nextest run --profile ci tests_5 --lib --bins --tests --benches --workspace --no-fail-fast {{ARGS}} -test_basic: test_success test_with_failures test_network_task test_consensus_task test_da_task test_vid_task test_view_sync_task +test_basic: test_success test_with_failures test_network_task test_da_task test_vid_task test_view_sync_task test_catchup: echo Testing catchup @@ -88,10 +88,6 @@ test_memory_network: echo Testing the DA task cargo test --lib --bins --tests --benches --workspace --no-fail-fast memory_network -- --test-threads=1 --nocapture -test_consensus_task: - echo Testing the consensus task - cargo test --lib --bins --tests --benches --workspace --no-fail-fast test_consensus -- --test-threads=1 --nocapture - test_quorum_vote_task: echo Testing the quorum vote task cargo test --lib --bins --tests --benches --workspace --no-fail-fast test_quorum_vote_task -- --test-threads=1 --nocapture