From 572e6a2acbca78e60955e6e3da93b77181f4e05d Mon Sep 17 00:00:00 2001 From: tbro Date: Thu, 7 Nov 2024 17:33:17 -0600 Subject: [PATCH] replace calls to Memberships Delete Memberships and replace functionality. Add some methods to `Membership` trait to deal w/ collapsing into one type both kinds of memberships (stake and DA). * avoid passing membership into `is_valid_cert * for DA, avoid proxying threshold through `Threshold` trait * remove `Topic` param from `Membership::new * Split cert impls by marker (#3891) * add membership methods to Cert trait * remove non-existent tests from justfile --- crates/examples/infra/mod.rs | 22 +- crates/hotshot/src/lib.rs | 22 +- crates/hotshot/src/tasks/mod.rs | 37 +-- crates/hotshot/src/tasks/task_state.rs | 27 +- .../traits/election/randomized_committee.rs | 118 +++++++- .../src/traits/election/static_committee.rs | 108 +++++++- .../static_committee_leader_two_views.rs | 109 +++++++- crates/hotshot/src/types/handle.rs | 259 +++++++++--------- .../src/network/transport.rs | 18 +- crates/task-impls/src/consensus/handlers.rs | 16 +- crates/task-impls/src/consensus/mod.rs | 8 +- crates/task-impls/src/da.rs | 25 +- crates/task-impls/src/helpers.rs | 19 +- crates/task-impls/src/network.rs | 32 +-- .../src/quorum_proposal/handlers.rs | 9 +- crates/task-impls/src/quorum_proposal/mod.rs | 22 +- .../src/quorum_proposal_recv/handlers.rs | 6 +- crates/task-impls/src/quorum_vote/mod.rs | 29 +- crates/task-impls/src/request.rs | 6 +- crates/task-impls/src/upgrade.rs | 13 +- crates/task-impls/src/view_sync.rs | 18 +- crates/task-impls/src/vote_collection.rs | 16 +- .../src/byzantine/byzantine_behaviour.rs | 6 +- crates/testing/src/helpers.rs | 35 +-- crates/testing/src/test_builder.rs | 4 +- crates/testing/src/test_runner.rs | 25 +- crates/testing/src/view_generator.rs | 65 ++--- crates/testing/tests/tests_1/da_task.rs | 27 +- crates/testing/tests/tests_1/network_task.rs | 18 +- .../tests_1/quorum_proposal_recv_task.rs | 11 +- .../tests/tests_1/quorum_proposal_task.rs | 70 +++-- .../testing/tests/tests_1/quorum_vote_task.rs | 18 +- .../testing/tests/tests_1/transaction_task.rs | 9 +- .../tests_1/upgrade_task_with_proposal.rs | 14 +- .../tests/tests_1/upgrade_task_with_vote.rs | 6 +- crates/testing/tests/tests_1/vid_task.rs | 11 +- .../tests/tests_1/vote_dependency_handle.rs | 7 +- crates/types/src/simple_certificate.rs | 134 ++++++++- crates/types/src/simple_vote.rs | 26 +- crates/types/src/traits/election.rs | 63 ++++- crates/types/src/vote.rs | 41 ++- justfile | 6 +- 42 files changed, 951 insertions(+), 584 deletions(-) diff --git a/crates/examples/infra/mod.rs b/crates/examples/infra/mod.rs index 9e031c02fa..c278875e36 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.clone(), 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,7 @@ 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 quorum_membership = TYPES::Membership::new(all_nodes.clone(), all_nodes); // Derive the bind address let bind_address = diff --git a/crates/hotshot/src/lib.rs b/crates/hotshot/src/lib.rs index 9f6d8591e5..3b84bae2fb 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 bb9f7ed630..0483d81884 100644 --- a/crates/hotshot/src/tasks/mod.rs +++ b/crates/hotshot/src/tasks/mod.rs @@ -48,6 +48,11 @@ use crate::{ ConsensusMetricsValue, ConsensusTaskRegistry, HotShotConfig, HotShotInitializer, MarketplaceConfig, Memberships, NetworkTaskRegistry, SignatureKey, SystemContext, Versions, }; +use crate::{ + tasks::task_state::CreateTaskState, types::SystemContextHandle, ConsensusApi, + ConsensusMetricsValue, ConsensusTaskRegistry, HotShotConfig, HotShotInitializer, + MarketplaceConfig, NetworkTaskRegistry, SignatureKey, SystemContext, Versions, +}; /// event for global event stream #[derive(Clone, Debug)] @@ -82,7 +87,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 +195,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 +326,7 @@ where private_key: ::PrivateKey, nonce: u64, config: HotShotConfig, - memberships: Memberships, + memberships: TYPES::Membership, network: Arc, initializer: HotShotInitializer, metrics: ConsensusMetricsValue, @@ -518,15 +521,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 +530,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 +565,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 536e6239a6..4ae570526a 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(), + da_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(), + membership: (*handle.hotshot.memberships).clone().into(), network: Arc::clone(&handle.hotshot.network), vote_collectors: BTreeMap::default(), public_key: handle.public_key().clone(), @@ -93,7 +93,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(), @@ -123,7 +123,7 @@ impl, V: Versions> CreateTaskState cur_epoch: handle.cur_epoch().await, vote_collector: None, 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, @@ -139,9 +139,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(), @@ -166,7 +165,7 @@ impl, V: Versions> CreateTaskState next_view: cur_view, 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(), num_timeouts_tracked: 0, @@ -194,7 +193,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(), instance_state: handle.hotshot.instance_state(), @@ -235,8 +234,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), @@ -260,8 +258,7 @@ impl, V: Versions> CreateTaskState output_event_stream: handle.hotshot.external_event_stream.0.clone(), consensus: OuterConsensus::new(consensus), instance_state: handle.hotshot.instance_state(), - timeout_membership: handle.hotshot.memberships.quorum_membership.clone().into(), - quorum_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(), storage: Arc::clone(&handle.storage), @@ -288,7 +285,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), @@ -312,9 +309,7 @@ impl, V: Versions> CreateTaskState private_key: handle.private_key().clone(), instance_state: handle.hotshot.instance_state(), network: Arc::clone(&handle.hotshot.network), - timeout_membership: handle.hotshot.memberships.quorum_membership.clone().into(), - quorum_membership: handle.hotshot.memberships.quorum_membership.clone().into(), - committee_membership: handle.hotshot.memberships.da_membership.clone().into(), + membership: (*handle.hotshot.memberships).clone().into(), vote_collectors: BTreeMap::default(), timeout_vote_collectors: BTreeMap::default(), storage: Arc::clone(&handle.storage), diff --git a/crates/hotshot/src/traits/election/randomized_committee.rs b/crates/hotshot/src/traits/election/randomized_committee.rs index 401a3102e2..d45a0784d9 100644 --- a/crates/hotshot/src/traits/election/randomized_committee.rs +++ b/crates/hotshot/src/traits/election/randomized_committee.rs @@ -10,7 +10,6 @@ use ethereum_types::U256; use hotshot_types::{ traits::{ election::Membership, - network::Topic, node_implementation::NodeType, signature_key::{SignatureKey, StakeTableEntryType}, }, @@ -28,16 +27,22 @@ pub struct RandomizedCommittee { /// NOTE: This is currently a hack because the DA leader needs to be the quorum /// leader but without voting rights. eligible_leaders: Vec<::StakeTableEntry>, + /// The nodes eligible for leadership. + da_eligible_leaders: Vec<::StakeTableEntry>, /// 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 +50,20 @@ 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()) + .collect(); + + // For each eligible leader, get the stake table entry + let da_eligible_leaders: Vec<::StakeTableEntry> = + da_members .iter() .map(|member| member.stake_table_entry.clone()) .filter(|entry| entry.stake() > U256::zero()) @@ -65,6 +77,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 +93,22 @@ 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, + da_eligible_leaders, stake_table: members, + da_stake_table: da_members, indexed_stake_table, - committee_topic, + indexed_da_stake_table, } } @@ -90,6 +120,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 +140,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 +174,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 +195,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, @@ -157,16 +228,41 @@ impl Membership for RandomizedCommittee { Ok(TYPES::SignatureKey::public_key(&res)) } + /// Index the vector of public keys with the current view number + fn lookup_da_leader( + &self, + view_number: TYPES::View, + _epoch: ::Epoch, + ) -> Result { + let mut rng: StdRng = rand::SeedableRng::seed_from_u64(*view_number); + + let randomized_view_number: u64 = rng.gen_range(0..=u64::MAX); + #[allow(clippy::cast_possible_truncation)] + let index = randomized_view_number as usize % self.da_eligible_leaders.len(); + + let res = self.da_eligible_leaders[index].clone(); + + Ok(TYPES::SignatureKey::public_key(&res)) + } + /// Get the total number of nodes in the committee 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 258a2cfeda..2c2307faad 100644 --- a/crates/hotshot/src/traits/election/static_committee.rs +++ b/crates/hotshot/src/traits/election/static_committee.rs @@ -10,7 +10,6 @@ use ethereum_types::U256; use hotshot_types::{ traits::{ election::Membership, - network::Topic, node_implementation::NodeType, signature_key::{SignatureKey, StakeTableEntryType}, }, @@ -19,23 +18,28 @@ use hotshot_types::{ use utils::anytrace::Result; #[derive(Clone, Debug, Eq, PartialEq, Hash)] - /// The static committee election pub struct StaticCommittee { /// The nodes eligible for leadership. /// NOTE: This is currently a hack because the DA leader needs to be the quorum /// leader but without voting rights. eligible_leaders: Vec<::StakeTableEntry>, + /// The nodes eligible for leadership. + da_eligible_leaders: Vec<::StakeTableEntry>, /// 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 +47,19 @@ 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()) + .collect(); + + let da_eligible_leaders: Vec<::StakeTableEntry> = + da_members .iter() .map(|member| member.stake_table_entry.clone()) .filter(|entry| entry.stake() > U256::zero()) @@ -63,6 +73,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 +89,22 @@ 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, + da_eligible_leaders, stake_table: members, + da_stake_table: da_members, indexed_stake_table, - committee_topic, + indexed_da_stake_table, } } @@ -88,6 +116,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 +136,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 +170,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 +191,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 @@ -150,16 +214,38 @@ impl Membership for StaticCommittee { Ok(TYPES::SignatureKey::public_key(&res)) } + /// Index the vector of public keys with the current view number + fn lookup_da_leader( + &self, + view_number: TYPES::View, + _epoch: ::Epoch, + ) -> Result { + #[allow(clippy::cast_possible_truncation)] + let index = *view_number as usize % self.da_eligible_leaders.len(); + let res = self.da_eligible_leaders[index].clone(); + Ok(TYPES::SignatureKey::public_key(&res)) + } + /// Get the total number of nodes in the committee fn total_nodes(&self, _epoch: ::Epoch) -> usize { 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 8658744e71..d4c265dd79 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 @@ -10,7 +10,6 @@ use ethereum_types::U256; use hotshot_types::{ traits::{ election::Membership, - network::Topic, node_implementation::NodeType, signature_key::{SignatureKey, StakeTableEntryType}, }, @@ -26,16 +25,22 @@ pub struct StaticCommitteeLeaderForTwoViews { /// NOTE: This is currently a hack because the DA leader needs to be the quorum /// leader but without voting rights. eligible_leaders: Vec<::StakeTableEntry>, + /// The nodes eligible for leadership. + da_eligible_leaders: Vec<::StakeTableEntry>, /// 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 +48,20 @@ 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()) + .collect(); + + // For each eligible leader, get the stake table entry + let da_eligible_leaders: Vec<::StakeTableEntry> = + da_members .iter() .map(|member| member.stake_table_entry.clone()) .filter(|entry| entry.stake() > U256::zero()) @@ -63,6 +75,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 +91,22 @@ impl Membership for StaticCommitteeLeaderForTwoViews::StakeTableEntry, + > = da_members + .iter() + .map(|entry| (TYPES::SignatureKey::public_key(entry), entry.clone())) + .collect(); + Self { eligible_leaders, + da_eligible_leaders, stake_table: members, + da_stake_table: da_members, indexed_stake_table, - committee_topic, + indexed_da_stake_table, } } @@ -88,6 +118,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 +138,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 +172,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 +193,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 @@ -151,16 +217,39 @@ impl Membership for StaticCommitteeLeaderForTwoViews::Epoch, + ) -> Result { + let index = + usize::try_from((*view_number / 2) % self.da_eligible_leaders.len() as u64).unwrap(); + let res = self.da_eligible_leaders[index].clone(); + + Ok(TYPES::SignatureKey::public_key(&res)) + } + /// Get the total number of nodes in the committee fn total_nodes(&self, _epoch: ::Epoch) -> usize { 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/types/handle.rs b/crates/hotshot/src/types/handle.rs index 19fcb97f1d..4ee68d4e31 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, @@ -80,19 +80,19 @@ impl + 'static, V: Versions> { /// Adds a hotshot consensus-related task to the `SystemContextHandle`. pub fn add_task> + 'static>(&mut self, task_state: S) { - let task = Task::new( - task_state, - self.internal_event_stream.0.clone(), - self.internal_event_stream.1.activate_cloned(), - ); + let task = Task::new( + task_state, + self.internal_event_stream.0.clone(), + self.internal_event_stream.1.activate_cloned(), + ); - self.consensus_registry.run_task(task); - } + self.consensus_registry.run_task(task); +} /// obtains a stream to expose to the user pub fn event_stream(&self) -> impl Stream> { - self.output_event_stream.1.activate_cloned() - } + self.output_event_stream.1.activate_cloned() +} /// Message other participents with a serialized message from the application /// Receivers of this message will get an `Event::ExternalMessageReceived` via @@ -101,35 +101,35 @@ impl + 'static, V: Versions> /// # Errors /// Errors if serializing the request fails, or the request fails to be sent pub async fn send_external_message( - &self, - msg: Vec, - recipients: RecipientList, - ) -> Result<()> { - let message = Message { - sender: self.public_key().clone(), - kind: MessageKind::External(msg), - }; - let serialized_message = self.hotshot.upgrade_lock.serialize(&message).await?; - - match recipients { - RecipientList::Broadcast => { - self.network - .broadcast_message(serialized_message, Topic::Global, BroadcastDelay::None) - .await?; - } - RecipientList::Direct(recipient) => { - self.network - .direct_message(serialized_message, recipient) - .await?; - } - RecipientList::Many(recipients) => { - self.network - .da_broadcast_message(serialized_message, recipients, BroadcastDelay::None) - .await?; - } + &self, + msg: Vec, + recipients: RecipientList, +) -> Result<()> { + let message = Message { + sender: self.public_key().clone(), + kind: MessageKind::External(msg), + }; + let serialized_message = self.hotshot.upgrade_lock.serialize(&message).await?; + + match recipients { + RecipientList::Broadcast => { + self.network + .broadcast_message(serialized_message, Topic::Global, BroadcastDelay::None) + .await?; + } + RecipientList::Direct(recipient) => { + self.network + .direct_message(serialized_message, recipient) + .await?; + } + RecipientList::Many(recipients) => { + self.network + .da_broadcast_message(serialized_message, recipients, BroadcastDelay::None) + .await?; } - Ok(()) } + Ok(()) +} /// Request a proposal from the all other nodes. Will block until some node /// returns a valid proposal with the requested commitment. If nobody has the @@ -138,70 +138,70 @@ impl + 'static, V: Versions> /// # Errors /// Errors if signing the request for proposal fails pub fn request_proposal( - &self, - view: TYPES::View, - epoch: TYPES::Epoch, - leaf_commitment: Commitment>, - ) -> Result>>>> - { - // We need to be able to sign this request before submitting it to the network. Compute the - // payload first. - let signed_proposal_request = ProposalRequestPayload { - view_number: view, - key: self.public_key().clone(), - }; - - // Finally, compute the signature for the payload. - let signature = TYPES::SignatureKey::sign( - self.private_key(), - signed_proposal_request.commit().as_ref(), - )?; - - let mem = self.memberships.quorum_membership.clone(); - let receiver = self.internal_event_stream.1.activate_cloned(); - let sender = self.internal_event_stream.0.clone(); - Ok(async move { - // First, broadcast that we need a proposal - broadcast_event( - HotShotEvent::QuorumProposalRequestSend(signed_proposal_request, signature).into(), - &sender, - ) - .await; - loop { - let hs_event = EventDependency::new( - receiver.clone(), - Box::new(move |event| { - let event = event.as_ref(); - if let HotShotEvent::QuorumProposalResponseRecv(quorum_proposal) = event { - quorum_proposal.data.view_number() == view - } else { - false - } - }), - ) - .completed() - .await - .ok_or(anyhow!("Event dependency failed to get event"))?; - - // Then, if it's `Some`, make sure that the data is correct - - if let HotShotEvent::QuorumProposalResponseRecv(quorum_proposal) = hs_event.as_ref() - { - // Make sure that the quorum_proposal is valid - if let Err(err) = quorum_proposal.validate_signature(&mem, epoch) { - tracing::warn!("Invalid Proposal Received after Request. Err {:?}", err); - continue; - } - let proposed_leaf = Leaf2::from_quorum_proposal(&quorum_proposal.data); - let commit = proposed_leaf.commit(); - if commit == leaf_commitment { - return Ok(quorum_proposal.clone()); + &self, + view: TYPES::View, + epoch: TYPES::Epoch, + leaf_commitment: Commitment>, +) -> Result>>>> +{ + // We need to be able to sign this request before submitting it to the network. Compute the + // payload first. + let signed_proposal_request = ProposalRequestPayload { + view_number: view, + key: self.public_key().clone(), + }; + + // Finally, compute the signature for the payload. + let signature = TYPES::SignatureKey::sign( + self.private_key(), + signed_proposal_request.commit().as_ref(), + )?; + + 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 { + // First, broadcast that we need a proposal + broadcast_event( + HotShotEvent::QuorumProposalRequestSend(signed_proposal_request, signature).into(), + &sender, + ) + .await; + loop { + let hs_event = EventDependency::new( + receiver.clone(), + Box::new(move |event| { + let event = event.as_ref(); + if let HotShotEvent::QuorumProposalResponseRecv(quorum_proposal) = event { + quorum_proposal.data.view_number() == view + } else { + false } - tracing::warn!("Proposal receied from request has different commitment than expected.\nExpected = {:?}\nReceived{:?}", leaf_commitment, commit); + }), + ) + .completed() + .await + .ok_or(anyhow!("Event dependency failed to get event"))?; + + // Then, if it's `Some`, make sure that the data is correct + + if let HotShotEvent::QuorumProposalResponseRecv(quorum_proposal) = hs_event.as_ref() + { + // Make sure that the quorum_proposal is valid + if let Err(err) = quorum_proposal.validate_signature(&mem, epoch) { + tracing::warn!("Invalid Proposal Received after Request. Err {:?}", err); + continue; } + let proposed_leaf = Leaf2::from_quorum_proposal(&quorum_proposal.data); + let commit = proposed_leaf.commit(); + if commit == leaf_commitment { + return Ok(quorum_proposal.clone()); + } + tracing::warn!("Proposal receied from request has different commitment than expected.\nExpected = {:?}\nReceived{:?}", leaf_commitment, commit); } - }) - } + } + }) +} /// HACK so we can know the types when running tests... /// there are two cleaner solutions: @@ -234,8 +234,8 @@ impl + 'static, V: Versions> /// # Panics /// If the internal consensus is in an inconsistent state. pub async fn decided_state(&self) -> Arc { - self.hotshot.decided_state().await - } + self.hotshot.decided_state().await +} /// Get the validated state from a given `view`. /// @@ -245,16 +245,16 @@ impl + 'static, V: Versions> /// [`decided_state`](Self::decided_state)) or if there is no path for the requested /// view to ever be decided. pub async fn state(&self, view: TYPES::View) -> Option> { - self.hotshot.state(view).await - } + self.hotshot.state(view).await +} /// Get the last decided leaf of the [`SystemContext`] instance. /// /// # Panics /// If the internal consensus is in an inconsistent state. pub async fn decided_leaf(&self) -> Leaf2 { - self.hotshot.decided_leaf().await - } + self.hotshot.decided_leaf().await +} /// Tries to get the most recent decided leaf, returning instantly /// if we can't acquire the lock. @@ -275,11 +275,11 @@ impl + 'static, V: Versions> /// Will return a [`HotShotError`] if some error occurs in the underlying /// [`SystemContext`] instance. pub async fn submit_transaction( - &self, - tx: TYPES::Transaction, - ) -> Result<(), HotShotError> { - self.hotshot.publish_transaction_async(tx).await - } + &self, + tx: TYPES::Transaction, +) -> Result<(), HotShotError> { + self.hotshot.publish_transaction_async(tx).await +} /// Get the underlying consensus state for this [`SystemContext`] #[must_use] @@ -289,25 +289,25 @@ impl + 'static, V: Versions> /// Shut down the the inner hotshot and wait until all background threads are closed. pub async fn shut_down(&mut self) { - // this is required because `SystemContextHandle` holds an inactive receiver and - // `broadcast_direct` below can wait indefinitely - self.internal_event_stream.0.set_await_active(false); - let _ = self - .internal_event_stream - .0 - .broadcast_direct(Arc::new(HotShotEvent::Shutdown)) - .await - .inspect_err(|err| tracing::error!("Failed to send shutdown event: {err}")); - - tracing::error!("Shutting down the network!"); - self.hotshot.network.shut_down().await; - - tracing::error!("Shutting down network tasks!"); - self.network_registry.shutdown().await; - - tracing::error!("Shutting down consensus!"); - self.consensus_registry.shutdown().await; - } + // this is required because `SystemContextHandle` holds an inactive receiver and + // `broadcast_direct` below can wait indefinitely + self.internal_event_stream.0.set_await_active(false); + let _ = self + .internal_event_stream + .0 + .broadcast_direct(Arc::new(HotShotEvent::Shutdown)) + .await + .inspect_err(|err| tracing::error!("Failed to send shutdown event: {err}")); + + tracing::error!("Shutting down the network!"); + self.hotshot.network.shut_down().await; + + tracing::error!("Shutting down network tasks!"); + self.network_registry.shutdown().await; + + tracing::error!("Shutting down consensus!"); + self.consensus_registry.shutdown().await; +} /// return the timeout for a view of the underlying `SystemContext` #[must_use] @@ -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..bdd34fff1a 100644 --- a/crates/libp2p-networking/src/network/transport.rs +++ b/crates/libp2p-networking/src/network/transport.rs @@ -524,7 +524,7 @@ mod test { use hotshot_types::{ light_client::StateVerKey, signature_key::BLSPubKey, - traits::{network::Topic, signature_key::SignatureKey}, + traits::signature_key::SignatureKey, PeerConfig, }; use libp2p::{core::transport::dummy::DummyTransport, quic::Connection}; @@ -643,11 +643,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 +669,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 +705,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 1dd319fed9..3305e56212 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 - .timeout_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 - .timeout_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 6def6ebd82..9c45ac1c5e 100644 --- a/crates/task-impls/src/consensus/mod.rs +++ b/crates/task-impls/src/consensus/mod.rs @@ -47,14 +47,8 @@ pub struct ConsensusTaskState, V: /// The underlying network pub network: Arc, - /// Membership for Timeout votes/certs - pub timeout_membership: Arc, - /// Membership for Quorum Certs/votes - pub quorum_membership: Arc, - - /// Membership for DA committee Votes/certs - pub committee_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..e7b213e3b1 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 a4c972b95f..addf03433e 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 94f2b12207..567e6a4fe1 100644 --- a/crates/task-impls/src/quorum_proposal/handlers.rs +++ b/crates/task-impls/src/quorum_proposal/handlers.rs @@ -9,6 +9,7 @@ use std::{ marker::PhantomData, + num::NonZeroU64, sync::Arc, time::{Duration, Instant}, }; @@ -25,6 +26,7 @@ use hotshot_types::{ simple_certificate::{QuorumCertificate2, UpgradeCertificate}, traits::{ block_contents::BlockHeader, + election::Membership, node_implementation::{ConsensusTime, NodeType}, signature_key::SignatureKey, }, @@ -124,8 +126,11 @@ 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), + self.quorum_membership.stake_table(TYPES::Epoch::new(0)), + NonZeroU64::new(QuorumCertificate::::threshold( + self.quorum_membership.as_ref(), + )) + .unwrap(), &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 9951f63e6d..4b2bdf650b 100644 --- a/crates/task-impls/src/quorum_proposal/mod.rs +++ b/crates/task-impls/src/quorum_proposal/mod.rs @@ -4,7 +4,7 @@ // You should have received a copy of the MIT License // along with the HotShot repository. If not, see . -use std::{collections::BTreeMap, sync::Arc, time::Instant}; +use std::{collections::BTreeMap, num::NonZeroU64, sync::Arc, time::Instant}; use async_broadcast::{Receiver, Sender}; use async_lock::RwLock; @@ -54,11 +54,8 @@ pub struct QuorumProposalTaskState /// Immutable instance state pub instance_state: Arc, - /// Membership for Timeout votes/certs - pub timeout_membership: Arc, - /// Membership for Quorum Certs/votes - pub quorum_membership: Arc, + pub membership: Arc, /// Our public key pub public_key: TYPES::SignatureKey, @@ -287,7 +284,7 @@ impl, V: Versions> ) -> Result<()> { // Don't even bother making the task if we are not entitled to propose anyway. ensure!( - self.quorum_membership.leader(view_number, epoch_number)? == self.public_key, + self.membership.leader(view_number, epoch_number)? == self.public_key, debug!("We are not the leader of the next view") ); @@ -316,7 +313,7 @@ impl, V: Versions> view_number, sender: event_sender, receiver: event_receiver, - quorum_membership: Arc::clone(&self.quorum_membership), + quorum_membership: Arc::clone(&self.membership), public_key: self.public_key.clone(), private_key: self.private_key.clone(), instance_state: Arc::clone(&self.instance_state), @@ -453,8 +450,8 @@ impl, V: Versions> ensure!( certificate .is_valid_cert( - self.quorum_membership.as_ref(), - epoch_number, + self.membership.stake_table(epoch_number), + self.membership.success_threshold(), &self.upgrade_lock ) .await, @@ -517,8 +514,11 @@ 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.membership.stake_table(epoch_number), + NonZeroU64::new(QuorumCertificate::::threshold( + self.membership.as_ref() + )) + .unwrap(), &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 393488402f..cac86ebbc8 100644 --- a/crates/task-impls/src/quorum_vote/mod.rs +++ b/crates/task-impls/src/quorum_vote/mod.rs @@ -270,11 +270,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>, @@ -378,7 +375,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(), @@ -480,8 +477,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") ); @@ -519,16 +520,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, @@ -642,7 +643,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(), @@ -680,7 +681,7 @@ impl, V: Versions> QuorumVoteTaskS 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 1a565a85de..9962adf7c9 100644 --- a/crates/task-impls/src/request.rs +++ b/crates/task-impls/src/request.rs @@ -180,15 +180,15 @@ impl> NetworkRequestState = self .da_membership - .committee_members(view, epoch) + .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/upgrade.rs b/crates/task-impls/src/upgrade.rs index c5e9735701..245f1729ae 100644 --- a/crates/task-impls/src/upgrade.rs +++ b/crates/task-impls/src/upgrade.rs @@ -49,7 +49,7 @@ pub struct UpgradeTaskState, V: Ve pub cur_epoch: TYPES::Epoch, /// Membership for Quorum Certs/votes - pub quorum_membership: Arc, + pub membership: Arc, /// The underlying network pub network: Arc, @@ -182,7 +182,7 @@ impl, V: Versions> UpgradeTaskStat ); // We then validate that the proposal was issued by the leader for the view. - let view_leader_key = self.quorum_membership.leader(view, self.cur_epoch)?; + let view_leader_key = self.membership.leader(view, self.cur_epoch)?; ensure!( view_leader_key == *sender, info!( @@ -226,12 +226,11 @@ impl, V: Versions> UpgradeTaskStat { let view = vote.view_number(); ensure!( - self.quorum_membership.leader(view, self.cur_epoch)? == self.public_key, + self.membership.leader(view, self.cur_epoch)? == self.public_key, debug!( "We are not the leader for view {} are we leader for next view? {}", *view, - self.quorum_membership.leader(view + 1, self.cur_epoch)? - == self.public_key + self.membership.leader(view + 1, self.cur_epoch)? == self.public_key ) ); } @@ -240,7 +239,7 @@ impl, V: Versions> UpgradeTaskStat &mut self.vote_collectors, vote, self.public_key.clone(), - &self.quorum_membership, + &self.membership, self.cur_epoch, self.id, &event, @@ -273,7 +272,7 @@ impl, V: Versions> UpgradeTaskStat && time >= self.start_proposing_time && time < self.stop_proposing_time && !self.upgraded().await - && self.quorum_membership.leader( + && self.membership.leader( TYPES::View::new(view + UPGRADE_PROPOSE_OFFSET), self.cur_epoch, )? == self.public_key diff --git a/crates/task-impls/src/view_sync.rs b/crates/task-impls/src/view_sync.rs index 9e8b852789..57061d54c0 100644 --- a/crates/task-impls/src/view_sync.rs +++ b/crates/task-impls/src/view_sync.rs @@ -546,7 +546,11 @@ impl, V: Versions> // 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()); @@ -628,7 +632,11 @@ impl, V: Versions> // 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()); @@ -721,7 +729,11 @@ impl, V: Versions> // 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 6023f5cdfb..bd14d19705 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 49f28e2504..f102871ce8 100644 --- a/crates/testing/src/helpers.rs +++ b/crates/testing/src/helpers.rs @@ -14,7 +14,7 @@ use ethereum_types::U256; use hotshot::{ traits::{NodeImplementation, TestableNodeImplementation}, types::{SignatureKey, SystemContextHandle}, - HotShotInitializer, Memberships, SystemContext, + HotShotInitializer, SystemContext, }; use hotshot_example_types::{ auction_results_provider_types::TestAuctionResultsProvider, @@ -34,7 +34,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 3460795db0..8aea6ac728 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 associted with this node. pub config: HotShotConfig, diff --git a/crates/testing/src/view_generator.rs b/crates/testing/src/view_generator.rs index 53a0d74b7e..e36ed0ac05 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/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 0cef44ab81..5ea457f884 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), ) @@ -154,7 +154,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) ), @@ -173,7 +173,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) ), @@ -191,7 +191,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 f22d658d34..5d53d3a2e5 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 9c75b294a3..8901ea0d88 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,7 +24,7 @@ use crate::{ data::serialize_signature2, message::UpgradeLock, simple_vote::{ - DaData, QuorumData, QuorumData2, TimeoutData, UpgradeProposalData, VersionedVoteData, + DaData, QuorumData, QuorumData2, QuorumMaker, TimeoutData, UpgradeProposalData, VersionedVoteData, ViewSyncCommitData, ViewSyncFinalizeData, ViewSyncPreCommitData, Voteable, }, traits::{ @@ -123,14 +124,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 +146,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 +256,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 +342,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..430689a4f7 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 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..c9adbd471f 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. @@ -72,6 +95,21 @@ pub trait Membership: Clone + Debug + Send + Sync { )) } + /// The DA leader of the committee for view `view_number` in `epoch`. + /// + /// Note: this function uses a HotShot-internal error type. + /// You should implement `lookup_leader`, rather than implementing this function directly. + /// + /// # Errors + /// Returns an error if the leader cannot be calculated. + fn da_leader(&self, view: TYPES::View, epoch: TYPES::Epoch) -> Result { + use utils::anytrace::*; + + self.lookup_da_leader(view, epoch).wrap().context(info!( + "Failed to get leader for view {view} in epoch {epoch}" + )) + } + /// The leader of the committee for view `view_number` in `epoch`. /// /// # Errors @@ -82,15 +120,28 @@ 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; + /// The leader of the DA committee for view `view_number` in `epoch`. + /// + /// # Errors + /// Returns an error if the leader cannot be calculated + fn lookup_da_leader( + &self, + view: TYPES::View, + epoch: TYPES::Epoch, + ) -> std::result::Result; /// 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 1275e172e0..18e9d73d5c 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