From 64559d97fd3120c3a4a5ee06c92fc1c1c366baa0 Mon Sep 17 00:00:00 2001 From: pls148 <184445976+pls148@users.noreply.github.com> Date: Tue, 7 Jan 2025 17:00:05 -0800 Subject: [PATCH] 3967 Option for everybody --- crates/example-types/src/node_types.rs | 4 +- crates/example-types/src/storage_types.rs | 18 +- crates/examples/infra/mod.rs | 7 +- crates/hotshot/src/lib.rs | 32 +- crates/hotshot/src/tasks/mod.rs | 2 +- .../traits/election/randomized_committee.rs | 34 +-- .../election/randomized_committee_members.rs | 288 +++++++++++------- .../src/traits/election/static_committee.rs | 34 +-- .../static_committee_leader_two_views.rs | 34 +-- .../traits/election/two_static_committees.rs | 66 ++-- .../src/traits/networking/combined_network.rs | 2 +- .../src/traits/networking/libp2p_network.rs | 4 +- crates/hotshot/src/types/handle.rs | 4 +- .../src/network/transport.rs | 10 +- crates/task-impls/src/consensus/handlers.rs | 60 ++-- crates/task-impls/src/consensus/mod.rs | 16 +- crates/task-impls/src/da.rs | 4 +- crates/task-impls/src/events.rs | 4 +- crates/task-impls/src/helpers.rs | 10 +- crates/task-impls/src/network.rs | 4 +- .../src/quorum_proposal/handlers.rs | 32 +- crates/task-impls/src/quorum_proposal/mod.rs | 20 +- .../src/quorum_proposal_recv/handlers.rs | 22 +- .../src/quorum_proposal_recv/mod.rs | 4 +- crates/task-impls/src/quorum_vote/handlers.rs | 63 ++-- crates/task-impls/src/quorum_vote/mod.rs | 18 +- crates/task-impls/src/request.rs | 9 +- crates/task-impls/src/response.rs | 6 +- crates/task-impls/src/transactions.rs | 24 +- crates/task-impls/src/upgrade.rs | 4 +- crates/task-impls/src/vid.rs | 22 +- crates/task-impls/src/view_sync.rs | 8 +- crates/task-impls/src/vote_collection.rs | 31 +- .../src/byzantine/byzantine_behaviour.rs | 2 +- crates/testing/src/helpers.rs | 21 +- crates/testing/src/overall_safety_task.rs | 21 +- crates/testing/src/spinning_task.rs | 2 +- crates/testing/src/test_runner.rs | 4 +- crates/testing/src/view_generator.rs | 24 +- crates/testing/tests/tests_1/da_task.rs | 34 +-- crates/testing/tests/tests_1/message.rs | 8 +- crates/testing/tests/tests_1/network_task.rs | 8 +- .../tests_1/quorum_proposal_recv_task.rs | 8 +- .../tests/tests_1/quorum_proposal_task.rs | 67 ++-- .../testing/tests/tests_1/quorum_vote_task.rs | 8 +- .../testing/tests/tests_1/transaction_task.rs | 13 +- .../tests_1/upgrade_task_with_proposal.rs | 13 +- .../tests/tests_1/upgrade_task_with_vote.rs | 10 +- crates/testing/tests/tests_1/vid_task.rs | 22 +- .../testing/tests/tests_1/view_sync_task.rs | 20 +- .../tests/tests_1/vote_dependency_handle.rs | 4 +- .../testing/tests/tests_3/byzantine_tests.rs | 2 +- crates/types/src/consensus.rs | 28 +- crates/types/src/data.rs | 69 +++-- crates/types/src/message.rs | 20 +- crates/types/src/simple_certificate.rs | 46 +-- crates/types/src/simple_vote.rs | 46 +-- crates/types/src/traits/election.rs | 40 +-- crates/types/src/traits/network.rs | 2 +- crates/types/src/utils.rs | 34 ++- crates/types/src/vote.rs | 10 +- 61 files changed, 852 insertions(+), 634 deletions(-) diff --git a/crates/example-types/src/node_types.rs b/crates/example-types/src/node_types.rs index 0b1a0989c1..528203a65b 100644 --- a/crates/example-types/src/node_types.rs +++ b/crates/example-types/src/node_types.rs @@ -333,7 +333,7 @@ mod tests { /// Dummy data used for test struct TestData { data: u64, - epoch: TYPES::Epoch, + epoch: Option, } impl Committable for TestData { @@ -353,7 +353,7 @@ mod tests { let data = TestData { data: 10, - epoch: EpochNumber::new(0), + epoch: None, }; let view_0 = ::View::new(0); diff --git a/crates/example-types/src/storage_types.rs b/crates/example-types/src/storage_types.rs index b5f3610f21..cabd6dabe7 100644 --- a/crates/example-types/src/storage_types.rs +++ b/crates/example-types/src/storage_types.rs @@ -55,7 +55,7 @@ pub struct TestStorageState { next_epoch_high_qc2: Option>, action: TYPES::View, - epoch: TYPES::Epoch, + epoch: Option, } impl Default for TestStorageState { @@ -71,7 +71,7 @@ impl Default for TestStorageState { next_epoch_high_qc2: None, high_qc2: None, action: TYPES::View::genesis(), - epoch: TYPES::Epoch::genesis(), + epoch: None, } } } @@ -112,19 +112,24 @@ impl TestStorage { ) -> BTreeMap>> { self.inner.read().await.proposals2.clone() } + pub async fn high_qc_cloned(&self) -> Option> { self.inner.read().await.high_qc2.clone() } + pub async fn next_epoch_high_qc_cloned(&self) -> Option> { self.inner.read().await.next_epoch_high_qc2.clone() } + pub async fn decided_upgrade_certificate(&self) -> Option> { self.decided_upgrade_certificate.read().await.clone() } + pub async fn last_actioned_view(&self) -> TYPES::View { self.inner.read().await.action } - pub async fn last_actioned_epoch(&self) -> TYPES::Epoch { + + pub async fn last_actioned_epoch(&self) -> Option { self.inner.read().await.epoch } } @@ -177,6 +182,7 @@ impl Storage for TestStorage { .insert(proposal.data.view_number, proposal.clone()); Ok(()) } + async fn append_da2( &self, proposal: &Proposal>, @@ -192,6 +198,7 @@ impl Storage for TestStorage { .insert(proposal.data.view_number, proposal.clone()); Ok(()) } + async fn append_proposal( &self, proposal: &Proposal>, @@ -206,6 +213,7 @@ impl Storage for TestStorage { .insert(proposal.data.view_number, proposal.clone()); Ok(()) } + async fn append_proposal2( &self, proposal: &Proposal>, @@ -274,6 +282,7 @@ impl Storage for TestStorage { } Ok(()) } + async fn update_next_epoch_high_qc2( &self, new_next_epoch_high_qc: hotshot_types::simple_certificate::NextEpochQuorumCertificate2< @@ -294,6 +303,7 @@ impl Storage for TestStorage { } Ok(()) } + async fn update_undecided_state( &self, _leaves: CommitmentMap>, @@ -305,6 +315,7 @@ impl Storage for TestStorage { Self::run_delay_settings_from_config(&self.delay_config).await; Ok(()) } + async fn update_undecided_state2( &self, _leaves: CommitmentMap>, @@ -316,6 +327,7 @@ impl Storage for TestStorage { Self::run_delay_settings_from_config(&self.delay_config).await; Ok(()) } + async fn update_decided_upgrade_certificate( &self, decided_upgrade_certificate: Option>, diff --git a/crates/examples/infra/mod.rs b/crates/examples/infra/mod.rs index 85f161fffd..8395f7324f 100755 --- a/crates/examples/infra/mod.rs +++ b/crates/examples/infra/mod.rs @@ -524,11 +524,12 @@ pub trait RunDa< .memberships .read() .await - .committee_leaders(TYPES::View::genesis(), TYPES::Epoch::genesis()) + .committee_leaders(TYPES::View::genesis(), None) .len(); let consensus_lock = context.hotshot.consensus(); - let consensus = consensus_lock.read().await; - let total_num_views = usize::try_from(consensus.locked_view().u64()).unwrap(); + let consensus_reader = consensus_lock.read().await; + let total_num_views = usize::try_from(consensus_reader.locked_view().u64()).unwrap(); + drop(consensus_reader); // `failed_num_views` could include uncommitted views let failed_num_views = total_num_views - num_successful_commits; // When posting to the orchestrator, note that the total number of views also include un-finalized views. diff --git a/crates/hotshot/src/lib.rs b/crates/hotshot/src/lib.rs index ea213a6338..aa5e938fb0 100644 --- a/crates/hotshot/src/lib.rs +++ b/crates/hotshot/src/lib.rs @@ -122,7 +122,7 @@ pub struct SystemContext, V: Versi start_view: TYPES::View, /// The epoch to enter when first starting consensus - start_epoch: TYPES::Epoch, + start_epoch: Option, /// Access to the output event stream. output_event_stream: (Sender>, InactiveReceiver>), @@ -291,10 +291,11 @@ impl, V: Versions> SystemContext, V: Versions> SystemContext, V: Versions> SystemContext { /// Starting view number that should be equivalent to the view the node shut down with last. start_view: TYPES::View, /// Starting epoch number that should be equivalent to the epoch the node shut down with last. - start_epoch: TYPES::Epoch, + start_epoch: Option, /// The view we last performed an action in. An action is Proposing or voting for /// Either the quorum or DA. actioned_view: TYPES::View, @@ -1035,11 +1044,12 @@ impl HotShotInitializer { let high_qc = QuorumCertificate2::genesis::(&validated_state, &instance_state).await; Ok(Self { - inner: Leaf2::genesis(&validated_state, &instance_state).await, + // CHECK IN REVIEW: does it make sense for this Leaf2 to be with_epoch=false? + inner: Leaf2::genesis::(&validated_state, &instance_state).await, validated_state: Some(Arc::new(validated_state)), state_delta: Some(Arc::new(state_delta)), start_view: TYPES::View::new(0), - start_epoch: TYPES::Epoch::new(0), + start_epoch: None, actioned_view: TYPES::View::new(0), saved_proposals: BTreeMap::new(), high_qc, @@ -1064,7 +1074,7 @@ impl HotShotInitializer { instance_state: TYPES::InstanceState, validated_state: Option>, start_view: TYPES::View, - start_epoch: TYPES::Epoch, + start_epoch: Option, actioned_view: TYPES::View, saved_proposals: BTreeMap>>, high_qc: QuorumCertificate2, diff --git a/crates/hotshot/src/tasks/mod.rs b/crates/hotshot/src/tasks/mod.rs index d32a671b94..1e57c887fe 100644 --- a/crates/hotshot/src/tasks/mod.rs +++ b/crates/hotshot/src/tasks/mod.rs @@ -197,7 +197,7 @@ pub fn add_network_event_task< let network_state: NetworkEventTaskState<_, V, _, _> = NetworkEventTaskState { network, view: TYPES::View::genesis(), - epoch: TYPES::Epoch::genesis(), + epoch: None, membership, storage: Arc::clone(&handle.storage()), consensus: OuterConsensus::new(handle.consensus()), diff --git a/crates/hotshot/src/traits/election/randomized_committee.rs b/crates/hotshot/src/traits/election/randomized_committee.rs index 4046123553..80d9185841 100644 --- a/crates/hotshot/src/traits/election/randomized_committee.rs +++ b/crates/hotshot/src/traits/election/randomized_committee.rs @@ -104,7 +104,7 @@ impl Membership for RandomizedCommittee { /// Get the stake table for the current view fn stake_table( &self, - _epoch: ::Epoch, + _epoch: Option<::Epoch>, ) -> Vec<<::SignatureKey as SignatureKey>::StakeTableEntry> { self.stake_table.clone() } @@ -112,7 +112,7 @@ impl Membership for RandomizedCommittee { /// Get the stake table for the current view fn da_stake_table( &self, - _epoch: ::Epoch, + _epoch: Option<::Epoch>, ) -> Vec<<::SignatureKey as SignatureKey>::StakeTableEntry> { self.da_stake_table.clone() } @@ -121,7 +121,7 @@ impl Membership for RandomizedCommittee { fn committee_members( &self, _view_number: ::View, - _epoch: ::Epoch, + _epoch: Option<::Epoch>, ) -> std::collections::BTreeSet<::SignatureKey> { self.stake_table .iter() @@ -133,7 +133,7 @@ impl Membership for RandomizedCommittee { fn da_committee_members( &self, _view_number: ::View, - _epoch: ::Epoch, + _epoch: Option<::Epoch>, ) -> std::collections::BTreeSet<::SignatureKey> { self.da_stake_table .iter() @@ -145,7 +145,7 @@ impl Membership for RandomizedCommittee { fn committee_leaders( &self, _view_number: ::View, - _epoch: ::Epoch, + _epoch: Option<::Epoch>, ) -> std::collections::BTreeSet<::SignatureKey> { self.eligible_leaders .iter() @@ -157,7 +157,7 @@ impl Membership for RandomizedCommittee { fn stake( &self, pub_key: &::SignatureKey, - _epoch: ::Epoch, + _epoch: Option<::Epoch>, ) -> Option<::StakeTableEntry> { // Only return the stake if it is above zero self.indexed_stake_table.get(pub_key).cloned() @@ -167,7 +167,7 @@ impl Membership for RandomizedCommittee { fn da_stake( &self, pub_key: &::SignatureKey, - _epoch: ::Epoch, + _epoch: Option<::Epoch>, ) -> Option<::StakeTableEntry> { // Only return the stake if it is above zero self.indexed_da_stake_table.get(pub_key).cloned() @@ -177,7 +177,7 @@ impl Membership for RandomizedCommittee { fn has_stake( &self, pub_key: &::SignatureKey, - _epoch: ::Epoch, + _epoch: Option<::Epoch>, ) -> bool { self.indexed_stake_table .get(pub_key) @@ -188,7 +188,7 @@ impl Membership for RandomizedCommittee { fn has_da_stake( &self, pub_key: &::SignatureKey, - _epoch: ::Epoch, + _epoch: Option<::Epoch>, ) -> bool { self.indexed_da_stake_table .get(pub_key) @@ -203,8 +203,8 @@ impl Membership for RandomizedCommittee { /// Index the vector of public keys with the current view number fn lookup_leader( &self, - view_number: TYPES::View, - _epoch: ::Epoch, + view_number: ::View, + _epoch: Option<::Epoch>, ) -> Result { let mut rng: StdRng = rand::SeedableRng::seed_from_u64(*view_number); @@ -218,30 +218,30 @@ impl Membership for RandomizedCommittee { } /// Get the total number of nodes in the committee - fn total_nodes(&self, _epoch: ::Epoch) -> usize { + fn total_nodes(&self, _epoch: Option<::Epoch>) -> usize { self.stake_table.len() } /// Get the total number of nodes in the committee - fn da_total_nodes(&self, _epoch: ::Epoch) -> usize { + fn da_total_nodes(&self, _epoch: Option<::Epoch>) -> usize { self.da_stake_table.len() } /// Get the voting success threshold for the committee - fn success_threshold(&self, _epoch: ::Epoch) -> NonZeroU64 { + fn success_threshold(&self, _epoch: Option<::Epoch>) -> 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, _epoch: ::Epoch) -> NonZeroU64 { + fn da_success_threshold(&self, _epoch: Option<::Epoch>) -> 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, _epoch: ::Epoch) -> NonZeroU64 { + fn failure_threshold(&self, _epoch: Option<::Epoch>) -> NonZeroU64 { NonZeroU64::new(((self.stake_table.len() as u64) / 3) + 1).unwrap() } /// Get the voting upgrade threshold for the committee - fn upgrade_threshold(&self, _epoch: ::Epoch) -> NonZeroU64 { + fn upgrade_threshold(&self, _epoch: Option<::Epoch>) -> NonZeroU64 { NonZeroU64::new(max( (self.stake_table.len() as u64 * 9) / 10, ((self.stake_table.len() as u64 * 2) / 3) + 1, diff --git a/crates/hotshot/src/traits/election/randomized_committee_members.rs b/crates/hotshot/src/traits/election/randomized_committee_members.rs index 5c85ad9c07..f5a60641e4 100644 --- a/crates/hotshot/src/traits/election/randomized_committee_members.rs +++ b/crates/hotshot/src/traits/election/randomized_committee_members.rs @@ -127,68 +127,90 @@ impl Membership /// Get the stake table for the current view fn stake_table( &self, - epoch: ::Epoch, + epoch: Option<::Epoch>, ) -> Vec<<::SignatureKey as SignatureKey>::StakeTableEntry> { - let filter = self.make_quorum_filter(epoch); - //self.stake_table.clone()s - self.stake_table - .iter() - .enumerate() - .filter(|(idx, _)| filter.contains(idx)) - .map(|(_, v)| v.clone()) - .collect() + if let Some(epoch) = epoch { + let filter = self.make_quorum_filter(epoch); + //self.stake_table.clone()s + self.stake_table + .iter() + .enumerate() + .filter(|(idx, _)| filter.contains(idx)) + .map(|(_, v)| v.clone()) + .collect() + } else { + self.stake_table.clone() + } } /// Get the da stake table for the current view fn da_stake_table( &self, - epoch: ::Epoch, + epoch: Option<::Epoch>, ) -> Vec<<::SignatureKey as SignatureKey>::StakeTableEntry> { - let filter = self.make_da_quorum_filter(epoch); - //self.stake_table.clone()s - self.da_stake_table - .iter() - .enumerate() - .filter(|(idx, _)| filter.contains(idx)) - .map(|(_, v)| v.clone()) - .collect() + if let Some(epoch) = epoch { + let filter = self.make_da_quorum_filter(epoch); + //self.stake_table.clone()s + self.da_stake_table + .iter() + .enumerate() + .filter(|(idx, _)| filter.contains(idx)) + .map(|(_, v)| v.clone()) + .collect() + } else { + self.da_stake_table.clone() + } } /// Get all members of the committee for the current view fn committee_members( &self, _view_number: ::View, - epoch: ::Epoch, + epoch: Option<::Epoch>, ) -> BTreeSet<::SignatureKey> { - let filter = self.make_quorum_filter(epoch); - self.stake_table - .iter() - .enumerate() - .filter(|(idx, _)| filter.contains(idx)) - .map(|(_, v)| TYPES::SignatureKey::public_key(v)) - .collect() + if let Some(epoch) = epoch { + let filter = self.make_quorum_filter(epoch); + self.stake_table + .iter() + .enumerate() + .filter(|(idx, _)| filter.contains(idx)) + .map(|(_, v)| TYPES::SignatureKey::public_key(v)) + .collect() + } else { + self.stake_table + .iter() + .map(TYPES::SignatureKey::public_key) + .collect() + } } /// Get all members of the committee for the current view fn da_committee_members( &self, _view_number: ::View, - epoch: ::Epoch, + epoch: Option<::Epoch>, ) -> BTreeSet<::SignatureKey> { - let filter = self.make_da_quorum_filter(epoch); - self.da_stake_table - .iter() - .enumerate() - .filter(|(idx, _)| filter.contains(idx)) - .map(|(_, v)| TYPES::SignatureKey::public_key(v)) - .collect() + if let Some(epoch) = epoch { + let filter = self.make_da_quorum_filter(epoch); + self.da_stake_table + .iter() + .enumerate() + .filter(|(idx, _)| filter.contains(idx)) + .map(|(_, v)| TYPES::SignatureKey::public_key(v)) + .collect() + } else { + 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, view_number: ::View, - epoch: ::Epoch, + epoch: Option<::Epoch>, ) -> BTreeSet<::SignatureKey> { self.committee_members(view_number, epoch) } @@ -197,23 +219,27 @@ impl Membership fn stake( &self, pub_key: &::SignatureKey, - epoch: ::Epoch, + epoch: Option<::Epoch>, ) -> Option<::StakeTableEntry> { - let filter = self.make_quorum_filter(epoch); - let actual_members: BTreeSet<_> = self - .stake_table - .iter() - .enumerate() - .filter(|(idx, _)| filter.contains(idx)) - .map(|(_, v)| TYPES::SignatureKey::public_key(v)) - .collect(); + if let Some(epoch) = epoch { + let filter = self.make_quorum_filter(epoch); + let actual_members: BTreeSet<_> = self + .stake_table + .iter() + .enumerate() + .filter(|(idx, _)| filter.contains(idx)) + .map(|(_, v)| TYPES::SignatureKey::public_key(v)) + .collect(); - if actual_members.contains(pub_key) { - // Only return the stake if it is above zero - self.indexed_stake_table.get(pub_key).cloned() + if actual_members.contains(pub_key) { + // Only return the stake if it is above zero + self.indexed_stake_table.get(pub_key).cloned() + } else { + // Skip members which aren't included based on the quorum filter + None + } } else { - // Skip members which aren't included based on the quorum filter - None + self.indexed_stake_table.get(pub_key).cloned() } } @@ -221,23 +247,27 @@ impl Membership fn da_stake( &self, pub_key: &::SignatureKey, - epoch: ::Epoch, + epoch: Option<::Epoch>, ) -> Option<::StakeTableEntry> { - let filter = self.make_da_quorum_filter(epoch); - let actual_members: BTreeSet<_> = self - .da_stake_table - .iter() - .enumerate() - .filter(|(idx, _)| filter.contains(idx)) - .map(|(_, v)| TYPES::SignatureKey::public_key(v)) - .collect(); + if let Some(epoch) = epoch { + let filter = self.make_da_quorum_filter(epoch); + let actual_members: BTreeSet<_> = self + .da_stake_table + .iter() + .enumerate() + .filter(|(idx, _)| filter.contains(idx)) + .map(|(_, v)| TYPES::SignatureKey::public_key(v)) + .collect(); - if actual_members.contains(pub_key) { - // Only return the stake if it is above zero - self.indexed_da_stake_table.get(pub_key).cloned() + if actual_members.contains(pub_key) { + // Only return the stake if it is above zero + self.indexed_da_stake_table.get(pub_key).cloned() + } else { + // Skip members which aren't included based on the quorum filter + None + } } else { - // Skip members which aren't included based on the quorum filter - None + self.indexed_da_stake_table.get(pub_key).cloned() } } @@ -245,24 +275,30 @@ impl Membership fn has_stake( &self, pub_key: &::SignatureKey, - epoch: ::Epoch, + epoch: Option<::Epoch>, ) -> bool { - let filter = self.make_quorum_filter(epoch); - let actual_members: BTreeSet<_> = self - .stake_table - .iter() - .enumerate() - .filter(|(idx, _)| filter.contains(idx)) - .map(|(_, v)| TYPES::SignatureKey::public_key(v)) - .collect(); + if let Some(epoch) = epoch { + let filter = self.make_quorum_filter(epoch); + let actual_members: BTreeSet<_> = self + .stake_table + .iter() + .enumerate() + .filter(|(idx, _)| filter.contains(idx)) + .map(|(_, v)| TYPES::SignatureKey::public_key(v)) + .collect(); - if actual_members.contains(pub_key) { + if actual_members.contains(pub_key) { + self.indexed_stake_table + .get(pub_key) + .is_some_and(|x| x.stake() > U256::zero()) + } else { + // Skip members which aren't included based on the quorum filter + false + } + } else { self.indexed_stake_table .get(pub_key) .is_some_and(|x| x.stake() > U256::zero()) - } else { - // Skip members which aren't included based on the quorum filter - false } } @@ -270,83 +306,109 @@ impl Membership fn has_da_stake( &self, pub_key: &::SignatureKey, - epoch: ::Epoch, + epoch: Option<::Epoch>, ) -> bool { - let filter = self.make_da_quorum_filter(epoch); - let actual_members: BTreeSet<_> = self - .da_stake_table - .iter() - .enumerate() - .filter(|(idx, _)| filter.contains(idx)) - .map(|(_, v)| TYPES::SignatureKey::public_key(v)) - .collect(); + if let Some(epoch) = epoch { + let filter = self.make_da_quorum_filter(epoch); + let actual_members: BTreeSet<_> = self + .da_stake_table + .iter() + .enumerate() + .filter(|(idx, _)| filter.contains(idx)) + .map(|(_, v)| TYPES::SignatureKey::public_key(v)) + .collect(); - if actual_members.contains(pub_key) { + if actual_members.contains(pub_key) { + self.indexed_da_stake_table + .get(pub_key) + .is_some_and(|x| x.stake() > U256::zero()) + } else { + // Skip members which aren't included based on the quorum filter + false + } + } else { self.indexed_da_stake_table .get(pub_key) .is_some_and(|x| x.stake() > U256::zero()) - } else { - // Skip members which aren't included based on the quorum filter - false } } /// Index the vector of public keys with the current view number fn lookup_leader( &self, - view_number: TYPES::View, - epoch: ::Epoch, + view_number: ::View, + epoch: Option<::Epoch>, ) -> Result { - let filter = self.make_quorum_filter(epoch); - let leader_vec: Vec<_> = self - .stake_table - .iter() - .enumerate() - .filter(|(idx, _)| filter.contains(idx)) - .map(|(_, v)| v.clone()) - .collect(); + if let Some(epoch) = epoch { + let filter = self.make_quorum_filter(epoch); + let leader_vec: Vec<_> = self + .stake_table + .iter() + .enumerate() + .filter(|(idx, _)| filter.contains(idx)) + .map(|(_, v)| v.clone()) + .collect(); + + 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 % leader_vec.len(); - let mut rng: StdRng = rand::SeedableRng::seed_from_u64(*view_number); + let res = leader_vec[index].clone(); - let randomized_view_number: u64 = rng.gen_range(0..=u64::MAX); - #[allow(clippy::cast_possible_truncation)] - let index = randomized_view_number as usize % leader_vec.len(); + Ok(TYPES::SignatureKey::public_key(&res)) + } else { + 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.eligible_leaders.len(); - let res = leader_vec[index].clone(); + let res = self.eligible_leaders[index].clone(); - Ok(TYPES::SignatureKey::public_key(&res)) + Ok(TYPES::SignatureKey::public_key(&res)) + } } /// Get the total number of nodes in the committee - fn total_nodes(&self, epoch: ::Epoch) -> usize { - self.make_quorum_filter(epoch).len() + fn total_nodes(&self, epoch: Option<::Epoch>) -> usize { + if let Some(epoch) = epoch { + self.make_quorum_filter(epoch).len() + } else { + self.stake_table.len() + } } /// Get the total number of nodes in the committee - fn da_total_nodes(&self, epoch: ::Epoch) -> usize { - self.make_da_quorum_filter(epoch).len() + fn da_total_nodes(&self, epoch: Option<::Epoch>) -> usize { + if let Some(epoch) = epoch { + self.make_da_quorum_filter(epoch).len() + } else { + self.da_stake_table.len() + } } /// Get the voting success threshold for the committee - fn success_threshold(&self, epoch: ::Epoch) -> NonZeroU64 { + fn success_threshold(&self, epoch: Option<::Epoch>) -> NonZeroU64 { let len = self.total_nodes(epoch); NonZeroU64::new(((len as u64 * 2) / 3) + 1).unwrap() } /// Get the voting success threshold for the committee - fn da_success_threshold(&self, epoch: ::Epoch) -> NonZeroU64 { + fn da_success_threshold(&self, epoch: Option<::Epoch>) -> NonZeroU64 { let len = self.da_total_nodes(epoch); NonZeroU64::new(((len as u64 * 2) / 3) + 1).unwrap() } /// Get the voting failure threshold for the committee - fn failure_threshold(&self, epoch: ::Epoch) -> NonZeroU64 { + fn failure_threshold(&self, epoch: Option<::Epoch>) -> NonZeroU64 { let len = self.total_nodes(epoch); NonZeroU64::new(((len as u64) / 3) + 1).unwrap() } /// Get the voting upgrade threshold for the committee - fn upgrade_threshold(&self, epoch: ::Epoch) -> NonZeroU64 { + fn upgrade_threshold(&self, epoch: Option<::Epoch>) -> NonZeroU64 { let len = self.total_nodes(epoch); NonZeroU64::new(max((len as u64 * 9) / 10, ((len as u64 * 2) / 3) + 1)).unwrap() } diff --git a/crates/hotshot/src/traits/election/static_committee.rs b/crates/hotshot/src/traits/election/static_committee.rs index b6010c174d..deae814155 100644 --- a/crates/hotshot/src/traits/election/static_committee.rs +++ b/crates/hotshot/src/traits/election/static_committee.rs @@ -101,7 +101,7 @@ impl Membership for StaticCommittee { /// Get the stake table for the current view fn stake_table( &self, - _epoch: ::Epoch, + _epoch: Option<::Epoch>, ) -> Vec<<::SignatureKey as SignatureKey>::StakeTableEntry> { self.stake_table.clone() } @@ -109,7 +109,7 @@ impl Membership for StaticCommittee { /// Get the stake table for the current view fn da_stake_table( &self, - _epoch: ::Epoch, + _epoch: Option<::Epoch>, ) -> Vec<<::SignatureKey as SignatureKey>::StakeTableEntry> { self.da_stake_table.clone() } @@ -118,7 +118,7 @@ impl Membership for StaticCommittee { fn committee_members( &self, _view_number: ::View, - _epoch: ::Epoch, + _epoch: Option<::Epoch>, ) -> std::collections::BTreeSet<::SignatureKey> { self.stake_table .iter() @@ -130,7 +130,7 @@ impl Membership for StaticCommittee { fn da_committee_members( &self, _view_number: ::View, - _epoch: ::Epoch, + _epoch: Option<::Epoch>, ) -> std::collections::BTreeSet<::SignatureKey> { self.da_stake_table .iter() @@ -142,7 +142,7 @@ impl Membership for StaticCommittee { fn committee_leaders( &self, _view_number: ::View, - _epoch: ::Epoch, + _epoch: Option<::Epoch>, ) -> std::collections::BTreeSet<::SignatureKey> { self.eligible_leaders .iter() @@ -154,7 +154,7 @@ impl Membership for StaticCommittee { fn stake( &self, pub_key: &::SignatureKey, - _epoch: ::Epoch, + _epoch: Option<::Epoch>, ) -> Option<::StakeTableEntry> { // Only return the stake if it is above zero self.indexed_stake_table.get(pub_key).cloned() @@ -164,7 +164,7 @@ impl Membership for StaticCommittee { fn da_stake( &self, pub_key: &::SignatureKey, - _epoch: ::Epoch, + _epoch: Option<::Epoch>, ) -> Option<::StakeTableEntry> { // Only return the stake if it is above zero self.indexed_da_stake_table.get(pub_key).cloned() @@ -174,7 +174,7 @@ impl Membership for StaticCommittee { fn has_stake( &self, pub_key: &::SignatureKey, - _epoch: ::Epoch, + _epoch: Option<::Epoch>, ) -> bool { self.indexed_stake_table .get(pub_key) @@ -185,7 +185,7 @@ impl Membership for StaticCommittee { fn has_da_stake( &self, pub_key: &::SignatureKey, - _epoch: ::Epoch, + _epoch: Option<::Epoch>, ) -> bool { self.indexed_da_stake_table .get(pub_key) @@ -195,8 +195,8 @@ impl Membership for StaticCommittee { /// Index the vector of public keys with the current view number fn lookup_leader( &self, - view_number: TYPES::View, - _epoch: ::Epoch, + view_number: ::View, + _epoch: Option<::Epoch>, ) -> Result { #[allow(clippy::cast_possible_truncation)] let index = *view_number as usize % self.eligible_leaders.len(); @@ -205,32 +205,32 @@ impl Membership for StaticCommittee { } /// Get the total number of nodes in the committee - fn total_nodes(&self, _epoch: ::Epoch) -> usize { + fn total_nodes(&self, _epoch: Option<::Epoch>) -> usize { self.stake_table.len() } /// Get the total number of DA nodes in the committee - fn da_total_nodes(&self, _epoch: ::Epoch) -> usize { + fn da_total_nodes(&self, _epoch: Option<::Epoch>) -> usize { self.da_stake_table.len() } /// Get the voting success threshold for the committee - fn success_threshold(&self, _epoch: TYPES::Epoch) -> NonZeroU64 { + fn success_threshold(&self, _epoch: Option<::Epoch>) -> 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, _epoch: TYPES::Epoch) -> NonZeroU64 { + fn da_success_threshold(&self, _epoch: Option<::Epoch>) -> 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, _epoch: TYPES::Epoch) -> NonZeroU64 { + fn failure_threshold(&self, _epoch: Option<::Epoch>) -> NonZeroU64 { NonZeroU64::new(((self.stake_table.len() as u64) / 3) + 1).unwrap() } /// Get the voting upgrade threshold for the committee - fn upgrade_threshold(&self, _epoch: TYPES::Epoch) -> NonZeroU64 { + fn upgrade_threshold(&self, _epoch: Option<::Epoch>) -> NonZeroU64 { let len = self.stake_table.len(); NonZeroU64::new(max((len as u64 * 9) / 10, ((len as u64 * 2) / 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 d2635cc273..60f98684b9 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 @@ -102,7 +102,7 @@ impl Membership for StaticCommitteeLeaderForTwoViews::Epoch, + _epoch: Option<::Epoch>, ) -> Vec<<::SignatureKey as SignatureKey>::StakeTableEntry> { self.stake_table.clone() } @@ -110,7 +110,7 @@ impl Membership for StaticCommitteeLeaderForTwoViews::Epoch, + _epoch: Option<::Epoch>, ) -> Vec<<::SignatureKey as SignatureKey>::StakeTableEntry> { self.da_stake_table.clone() } @@ -119,7 +119,7 @@ impl Membership for StaticCommitteeLeaderForTwoViews::View, - _epoch: ::Epoch, + _epoch: Option<::Epoch>, ) -> std::collections::BTreeSet<::SignatureKey> { self.stake_table .iter() @@ -131,7 +131,7 @@ impl Membership for StaticCommitteeLeaderForTwoViews::View, - _epoch: ::Epoch, + _epoch: Option<::Epoch>, ) -> std::collections::BTreeSet<::SignatureKey> { self.da_stake_table .iter() @@ -143,7 +143,7 @@ impl Membership for StaticCommitteeLeaderForTwoViews::View, - _epoch: ::Epoch, + _epoch: Option<::Epoch>, ) -> std::collections::BTreeSet<::SignatureKey> { self.eligible_leaders .iter() @@ -155,7 +155,7 @@ impl Membership for StaticCommitteeLeaderForTwoViews::SignatureKey, - _epoch: ::Epoch, + _epoch: Option<::Epoch>, ) -> Option<::StakeTableEntry> { // Only return the stake if it is above zero self.indexed_stake_table.get(pub_key).cloned() @@ -165,7 +165,7 @@ impl Membership for StaticCommitteeLeaderForTwoViews::SignatureKey, - _epoch: ::Epoch, + _epoch: Option<::Epoch>, ) -> Option<::StakeTableEntry> { // Only return the stake if it is above zero self.indexed_da_stake_table.get(pub_key).cloned() @@ -175,7 +175,7 @@ impl Membership for StaticCommitteeLeaderForTwoViews::SignatureKey, - _epoch: ::Epoch, + _epoch: Option<::Epoch>, ) -> bool { self.indexed_stake_table .get(pub_key) @@ -186,7 +186,7 @@ impl Membership for StaticCommitteeLeaderForTwoViews::SignatureKey, - _epoch: ::Epoch, + _epoch: Option<::Epoch>, ) -> bool { self.indexed_da_stake_table .get(pub_key) @@ -196,8 +196,8 @@ impl Membership for StaticCommitteeLeaderForTwoViews::Epoch, + view_number: ::View, + _epoch: Option<::Epoch>, ) -> Result { let index = usize::try_from((*view_number / 2) % self.eligible_leaders.len() as u64).unwrap(); @@ -207,32 +207,32 @@ impl Membership for StaticCommitteeLeaderForTwoViews::Epoch) -> usize { + fn total_nodes(&self, _epoch: Option<::Epoch>) -> usize { self.stake_table.len() } /// Get the total number of DA nodes in the committee - fn da_total_nodes(&self, _epoch: ::Epoch) -> usize { + fn da_total_nodes(&self, _epoch: Option<::Epoch>) -> usize { self.da_stake_table.len() } /// Get the voting success threshold for the committee - fn success_threshold(&self, _epoch: TYPES::Epoch) -> NonZeroU64 { + fn success_threshold(&self, _epoch: Option<::Epoch>) -> 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, _epoch: TYPES::Epoch) -> NonZeroU64 { + fn da_success_threshold(&self, _epoch: Option<::Epoch>) -> 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, _epoch: TYPES::Epoch) -> NonZeroU64 { + fn failure_threshold(&self, _epoch: Option<::Epoch>) -> NonZeroU64 { NonZeroU64::new(((self.stake_table.len() as u64) / 3) + 1).unwrap() } /// Get the voting upgrade threshold for the committee - fn upgrade_threshold(&self, _epoch: TYPES::Epoch) -> NonZeroU64 { + fn upgrade_threshold(&self, _epoch: Option<::Epoch>) -> NonZeroU64 { NonZeroU64::new(((self.stake_table.len() as u64 * 9) / 10) + 1).unwrap() } } diff --git a/crates/hotshot/src/traits/election/two_static_committees.rs b/crates/hotshot/src/traits/election/two_static_committees.rs index 25af69905c..846bc86616 100644 --- a/crates/hotshot/src/traits/election/two_static_committees.rs +++ b/crates/hotshot/src/traits/election/two_static_committees.rs @@ -178,9 +178,9 @@ impl Membership for TwoStaticCommittees { /// Get the stake table for the current view fn stake_table( &self, - epoch: ::Epoch, + epoch: Option<::Epoch>, ) -> Vec<<::SignatureKey as SignatureKey>::StakeTableEntry> { - if *epoch != 0 && *epoch % 2 == 0 { + if epoch.is_some_and(|e| *e % 2 == 0) { self.stake_table.0.clone() } else { self.stake_table.1.clone() @@ -190,9 +190,9 @@ impl Membership for TwoStaticCommittees { /// Get the stake table for the current view fn da_stake_table( &self, - epoch: ::Epoch, + epoch: Option<::Epoch>, ) -> Vec<<::SignatureKey as SignatureKey>::StakeTableEntry> { - if *epoch != 0 && *epoch % 2 == 0 { + if epoch.is_some_and(|e| *e % 2 == 0) { self.da_stake_table.0.clone() } else { self.da_stake_table.1.clone() @@ -203,9 +203,9 @@ impl Membership for TwoStaticCommittees { fn committee_members( &self, _view_number: ::View, - epoch: ::Epoch, + epoch: Option<::Epoch>, ) -> std::collections::BTreeSet<::SignatureKey> { - if *epoch != 0 && *epoch % 2 == 0 { + if epoch.is_some_and(|e| *e % 2 == 0) { self.stake_table .0 .iter() @@ -224,9 +224,9 @@ impl Membership for TwoStaticCommittees { fn da_committee_members( &self, _view_number: ::View, - epoch: ::Epoch, + epoch: Option<::Epoch>, ) -> std::collections::BTreeSet<::SignatureKey> { - if *epoch != 0 && *epoch % 2 == 0 { + if epoch.is_some_and(|e| *e % 2 == 0) { self.da_stake_table .0 .iter() @@ -245,9 +245,9 @@ impl Membership for TwoStaticCommittees { fn committee_leaders( &self, _view_number: ::View, - epoch: ::Epoch, + epoch: Option<::Epoch>, ) -> std::collections::BTreeSet<::SignatureKey> { - if *epoch != 0 && *epoch % 2 == 0 { + if epoch.is_some_and(|e| *e % 2 == 0) { self.eligible_leaders .0 .iter() @@ -266,10 +266,10 @@ impl Membership for TwoStaticCommittees { fn stake( &self, pub_key: &::SignatureKey, - epoch: ::Epoch, + epoch: Option<::Epoch>, ) -> Option<::StakeTableEntry> { // Only return the stake if it is above zero - if *epoch != 0 && *epoch % 2 == 0 { + if epoch.is_some_and(|e| *e % 2 == 0) { self.indexed_stake_table.0.get(pub_key).cloned() } else { self.indexed_stake_table.1.get(pub_key).cloned() @@ -280,10 +280,10 @@ impl Membership for TwoStaticCommittees { fn da_stake( &self, pub_key: &::SignatureKey, - epoch: ::Epoch, + epoch: Option<::Epoch>, ) -> Option<::StakeTableEntry> { // Only return the stake if it is above zero - if *epoch != 0 && *epoch % 2 == 0 { + if epoch.is_some_and(|e| *e % 2 == 0) { self.indexed_da_stake_table.0.get(pub_key).cloned() } else { self.indexed_da_stake_table.1.get(pub_key).cloned() @@ -294,9 +294,9 @@ impl Membership for TwoStaticCommittees { fn has_stake( &self, pub_key: &::SignatureKey, - epoch: ::Epoch, + epoch: Option<::Epoch>, ) -> bool { - if *epoch != 0 && *epoch % 2 == 0 { + if epoch.is_some_and(|e| *e % 2 == 0) { self.indexed_stake_table .0 .get(pub_key) @@ -313,9 +313,9 @@ impl Membership for TwoStaticCommittees { fn has_da_stake( &self, pub_key: &::SignatureKey, - epoch: ::Epoch, + epoch: Option<::Epoch>, ) -> bool { - if *epoch != 0 && *epoch % 2 == 0 { + if epoch.is_some_and(|e| *e % 2 == 0) { self.indexed_da_stake_table .0 .get(pub_key) @@ -331,10 +331,10 @@ impl Membership for TwoStaticCommittees { /// Index the vector of public keys with the current view number fn lookup_leader( &self, - view_number: TYPES::View, - epoch: ::Epoch, + view_number: ::View, + epoch: Option<::Epoch>, ) -> Result { - if *epoch != 0 && *epoch % 2 == 0 { + if epoch.is_some_and(|e| *e % 2 == 0) { #[allow(clippy::cast_possible_truncation)] let index = *view_number as usize % self.eligible_leaders.0.len(); let res = self.eligible_leaders.0[index].clone(); @@ -348,8 +348,8 @@ impl Membership for TwoStaticCommittees { } /// Get the total number of nodes in the committee - fn total_nodes(&self, epoch: ::Epoch) -> usize { - if *epoch != 0 && *epoch % 2 == 0 { + fn total_nodes(&self, epoch: Option<::Epoch>) -> usize { + if epoch.is_some_and(|e| *e % 2 == 0) { self.stake_table.0.len() } else { self.stake_table.1.len() @@ -357,8 +357,8 @@ impl Membership for TwoStaticCommittees { } /// Get the total number of DA nodes in the committee - fn da_total_nodes(&self, epoch: ::Epoch) -> usize { - if *epoch != 0 && *epoch % 2 == 0 { + fn da_total_nodes(&self, epoch: Option<::Epoch>) -> usize { + if epoch.is_some_and(|e| *e % 2 == 0) { self.da_stake_table.0.len() } else { self.da_stake_table.1.len() @@ -366,8 +366,8 @@ impl Membership for TwoStaticCommittees { } /// Get the voting success threshold for the committee - fn success_threshold(&self, epoch: TYPES::Epoch) -> NonZeroU64 { - if *epoch != 0 && *epoch % 2 == 0 { + fn success_threshold(&self, epoch: Option<::Epoch>) -> NonZeroU64 { + if epoch.is_some_and(|e| *e % 2 == 0) { NonZeroU64::new(((self.stake_table.0.len() as u64 * 2) / 3) + 1).unwrap() } else { NonZeroU64::new(((self.stake_table.1.len() as u64 * 2) / 3) + 1).unwrap() @@ -375,8 +375,8 @@ impl Membership for TwoStaticCommittees { } /// Get the voting success threshold for the committee - fn da_success_threshold(&self, epoch: TYPES::Epoch) -> NonZeroU64 { - if *epoch != 0 && *epoch % 2 == 0 { + fn da_success_threshold(&self, epoch: Option) -> NonZeroU64 { + if epoch.is_some_and(|e| *e % 2 == 0) { NonZeroU64::new(((self.da_stake_table.0.len() as u64 * 2) / 3) + 1).unwrap() } else { NonZeroU64::new(((self.da_stake_table.1.len() as u64 * 2) / 3) + 1).unwrap() @@ -384,8 +384,8 @@ impl Membership for TwoStaticCommittees { } /// Get the voting failure threshold for the committee - fn failure_threshold(&self, epoch: TYPES::Epoch) -> NonZeroU64 { - if *epoch != 0 && *epoch % 2 == 0 { + fn failure_threshold(&self, epoch: Option<::Epoch>) -> NonZeroU64 { + if epoch.is_some_and(|e| *e % 2 == 0) { NonZeroU64::new(((self.stake_table.0.len() as u64) / 3) + 1).unwrap() } else { NonZeroU64::new(((self.stake_table.1.len() as u64) / 3) + 1).unwrap() @@ -393,8 +393,8 @@ impl Membership for TwoStaticCommittees { } /// Get the voting upgrade threshold for the committee - fn upgrade_threshold(&self, epoch: TYPES::Epoch) -> NonZeroU64 { - if *epoch != 0 && *epoch % 2 == 0 { + fn upgrade_threshold(&self, epoch: Option<::Epoch>) -> NonZeroU64 { + if epoch.is_some_and(|e| *e % 2 == 0) { NonZeroU64::new(max( (self.stake_table.0.len() as u64 * 9) / 10, ((self.stake_table.0.len() as u64 * 2) / 3) + 1, diff --git a/crates/hotshot/src/traits/networking/combined_network.rs b/crates/hotshot/src/traits/networking/combined_network.rs index 7021c3e892..298500b473 100644 --- a/crates/hotshot/src/traits/networking/combined_network.rs +++ b/crates/hotshot/src/traits/networking/combined_network.rs @@ -478,7 +478,7 @@ impl ConnectedNetwork for CombinedNetworks async fn update_view<'a, T>( &'a self, view: u64, - epoch: u64, + epoch: Option, membership: Arc>, ) where T: NodeType + 'a, diff --git a/crates/hotshot/src/traits/networking/libp2p_network.rs b/crates/hotshot/src/traits/networking/libp2p_network.rs index b832f751b7..d288c3e360 100644 --- a/crates/hotshot/src/traits/networking/libp2p_network.rs +++ b/crates/hotshot/src/traits/networking/libp2p_network.rs @@ -991,13 +991,13 @@ impl ConnectedNetwork for Libp2pNetwork { async fn update_view<'a, TYPES>( &'a self, view: u64, - epoch: u64, + epoch: Option, membership: Arc>, ) where TYPES: NodeType + 'a, { let future_view = ::View::new(view) + LOOK_AHEAD; - let epoch = ::Epoch::new(epoch); + let epoch = epoch.map(::Epoch::new); let future_leader = match membership.read().await.leader(future_view, epoch) { Ok(l) => l, diff --git a/crates/hotshot/src/types/handle.rs b/crates/hotshot/src/types/handle.rs index d1d45473c1..f29f31afb7 100644 --- a/crates/hotshot/src/types/handle.rs +++ b/crates/hotshot/src/types/handle.rs @@ -325,7 +325,7 @@ impl + 'static, V: Versions> pub async fn leader( &self, view_number: TYPES::View, - epoch_number: TYPES::Epoch, + epoch_number: Option, ) -> Result { self.hotshot .memberships @@ -365,7 +365,7 @@ impl + 'static, V: Versions> /// Wrapper to get the epoch number this node is on. #[instrument(skip_all, target = "SystemContextHandle", fields(id = self.hotshot.id))] - pub async fn cur_epoch(&self) -> TYPES::Epoch { + pub async fn cur_epoch(&self) -> Option { self.hotshot.consensus.read().await.cur_epoch() } diff --git a/crates/libp2p-networking/src/network/transport.rs b/crates/libp2p-networking/src/network/transport.rs index 458b7f032b..ca27124ea4 100644 --- a/crates/libp2p-networking/src/network/transport.rs +++ b/crates/libp2p-networking/src/network/transport.rs @@ -10,9 +10,7 @@ use anyhow::{ensure, Context, Result as AnyhowResult}; use async_lock::RwLock; use futures::{future::poll_fn, AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; use hotshot_types::traits::{ - election::Membership, - node_implementation::{ConsensusTime, NodeType}, - signature_key::SignatureKey, + election::Membership, node_implementation::NodeType, signature_key::SignatureKey, }; use libp2p::{ core::{ @@ -137,11 +135,7 @@ impl StakeTableAuthentica } // Check if the public key is in the stake table - if !stake_table - .read() - .await - .has_stake(&public_key, Types::Epoch::new(0)) - { + if !stake_table.read().await.has_stake(&public_key, None) { return Err(anyhow::anyhow!("Peer not in stake table")); } } diff --git a/crates/task-impls/src/consensus/handlers.rs b/crates/task-impls/src/consensus/handlers.rs index 437e066ea0..bc346fa209 100644 --- a/crates/task-impls/src/consensus/handlers.rs +++ b/crates/task-impls/src/consensus/handlers.rs @@ -78,26 +78,30 @@ pub(crate) async fn handle_quorum_vote_recv< ) .await?; - // If the vote sender belongs to the next epoch, collect it separately to form the second QC - let has_stake = task_state - .membership - .read() - .await - .has_stake(&vote.signing_key(), vote.epoch() + 1); - if has_stake { - handle_vote( - &mut task_state.next_epoch_vote_collectors, - &vote.clone().into(), - task_state.public_key.clone(), - &task_state.membership, - vote.data.epoch, - task_state.id, - &event, - sender, - &task_state.upgrade_lock, - transition_indicator, - ) - .await?; + // #3967 REVIEW NOTE: Is this right to disable this behavior if epochs are disabled? How do we + // START having epochs then? + if let Some(vote_epoch) = vote.epoch() { + // If the vote sender belongs to the next epoch, collect it separately to form the second QC + let has_stake = task_state + .membership + .read() + .await + .has_stake(&vote.signing_key(), Some(vote_epoch + 1)); + if has_stake { + handle_vote( + &mut task_state.next_epoch_vote_collectors, + &vote.clone().into(), + task_state.public_key.clone(), + &task_state.membership, + vote.data.epoch, + task_state.id, + &event, + sender, + &task_state.upgrade_lock, + transition_indicator, + ) + .await?; + } } Ok(()) @@ -166,7 +170,7 @@ pub async fn send_high_qc( new_view_number: TYPES::View, - epoch_number: TYPES::Epoch, + epoch_number: Option, sender: &Sender>>, task_state: &mut ConsensusTaskState, ) -> Result<()> { if epoch_number > task_state.cur_epoch { task_state.cur_epoch = epoch_number; - let _ = task_state - .consensus - .write() - .await - .update_epoch(epoch_number); - tracing::info!("Progress: entered epoch {:>6}", *epoch_number); + if let Some(new_epoch) = epoch_number { + let _ = task_state.consensus.write().await.update_epoch(new_epoch); + tracing::info!("Progress: entered epoch {:>6}", *new_epoch); + } } ensure!( @@ -318,7 +320,7 @@ pub(crate) async fn handle_view_change< #[instrument(skip_all)] pub(crate) async fn handle_timeout, V: Versions>( view_number: TYPES::View, - epoch: TYPES::Epoch, + epoch: Option, sender: &Sender>>, task_state: &mut ConsensusTaskState, ) -> Result<()> { diff --git a/crates/task-impls/src/consensus/mod.rs b/crates/task-impls/src/consensus/mod.rs index fe25a9ec2e..1eb5a8cff2 100644 --- a/crates/task-impls/src/consensus/mod.rs +++ b/crates/task-impls/src/consensus/mod.rs @@ -75,7 +75,7 @@ pub struct ConsensusTaskState, V: pub cur_view_time: i64, /// The epoch number that this node is currently executing in. - pub cur_epoch: TYPES::Epoch, + pub cur_epoch: Option, /// Output events to application pub output_event_stream: async_broadcast::Sender>, @@ -101,7 +101,7 @@ pub struct ConsensusTaskState, V: impl, V: Versions> ConsensusTaskState { /// Handles a consensus event received on the event stream - #[instrument(skip_all, fields(id = self.id, cur_view = *self.cur_view, cur_epoch = *self.cur_epoch), name = "Consensus replica task", level = "error", target = "ConsensusTaskState")] + #[instrument(skip_all, fields(id = self.id, cur_view = *self.cur_view, cur_epoch = self.cur_epoch.map(|x| *x)), name = "Consensus replica task", level = "error", target = "ConsensusTaskState")] pub async fn handle( &mut self, event: Arc>, @@ -155,14 +155,18 @@ impl, V: Versions> ConsensusTaskSt "Could not find the leaf for the eQC. It shouldn't happen." ))? .height(); - let cert_epoch = TYPES::Epoch::new(epoch_from_block_number( + + // #3967 TODO FIGURE OUT HOW TO DECIDE IF cert_epoch IS NONE + // THE SOME() BELOW IS ALMOST CERTAINLY WRONG + let cert_epoch = Some(TYPES::Epoch::new(epoch_from_block_number( cert_block_number, self.epoch_height, - )); + ))); // Transition to the new epoch by sending ViewChange - tracing::info!("Entering new epoch: {:?}", cert_epoch + 1); + let next_epoch = cert_epoch.map(|x| x + 1); + tracing::info!("Entering new epoch: {:?}", next_epoch); broadcast_event( - Arc::new(HotShotEvent::ViewChange(cert_view + 1, cert_epoch + 1)), + Arc::new(HotShotEvent::ViewChange(cert_view + 1, next_epoch)), &sender, ) .await; diff --git a/crates/task-impls/src/da.rs b/crates/task-impls/src/da.rs index 0a03f09be4..f26a201f15 100644 --- a/crates/task-impls/src/da.rs +++ b/crates/task-impls/src/da.rs @@ -49,7 +49,7 @@ pub struct DaTaskState, V: Version pub cur_view: TYPES::View, /// Epoch number this node is executing in. - pub cur_epoch: TYPES::Epoch, + pub cur_epoch: Option, /// Reference to consensus. Leader will require a read lock on this. pub consensus: OuterConsensus, @@ -83,7 +83,7 @@ pub struct DaTaskState, V: Version impl, V: Versions> DaTaskState { /// main task event handler - #[instrument(skip_all, fields(id = self.id, view = *self.cur_view, epoch = *self.cur_epoch), name = "DA Main Task", level = "error", target = "DaTaskState")] + #[instrument(skip_all, fields(id = self.id, view = *self.cur_view, epoch = self.cur_epoch.map(|x| *x)), name = "DA Main Task", level = "error", target = "DaTaskState")] pub async fn handle( &mut self, event: Arc>, diff --git a/crates/task-impls/src/events.rs b/crates/task-impls/src/events.rs index 195b2137cd..20d88751f8 100644 --- a/crates/task-impls/src/events.rs +++ b/crates/task-impls/src/events.rs @@ -129,7 +129,7 @@ pub enum HotShotEvent { /// The DA leader has collected enough votes to form a DAC; emitted by the DA leader in the DA task; sent to the entire network via the networking task DacSend(DaCertificate2, TYPES::SignatureKey), /// The current view has changed; emitted by the replica in the consensus task or replica in the view sync task; received by almost all other tasks - ViewChange(TYPES::View, TYPES::Epoch), + ViewChange(TYPES::View, Option), /// Timeout for the view sync protocol; emitted by a replica in the view sync task ViewSyncTimeout(TYPES::View, u64, ViewSyncPhase), @@ -164,7 +164,7 @@ pub enum HotShotEvent { /// Trigger the start of the view sync protocol; emitted by view sync task; internal trigger only ViewSyncTrigger(TYPES::View), /// A consensus view has timed out; emitted by a replica in the consensus task; received by the view sync task; internal event only - Timeout(TYPES::View, TYPES::Epoch), + Timeout(TYPES::View, Option), /// Receive transactions from the network TransactionsRecv(Vec), /// Send transactions to the network diff --git a/crates/task-impls/src/helpers.rs b/crates/task-impls/src/helpers.rs index ed21b3d282..f8867e5d88 100644 --- a/crates/task-impls/src/helpers.rs +++ b/crates/task-impls/src/helpers.rs @@ -29,7 +29,8 @@ use hotshot_types::{ BlockPayload, ValidatedState, }, utils::{ - epoch_from_block_number, is_epoch_root, is_last_block_in_epoch, Terminator, View, ViewInner, + epoch_from_block_number, is_epoch_root, is_last_block_in_epoch, + option_epoch_from_block_number, Terminator, View, ViewInner, }, vote::{Certificate, HasViewNumber}, }; @@ -608,7 +609,7 @@ pub async fn validate_proposal_safety_and_liveness< UpgradeCertificate::validate( &proposal.data.upgrade_certificate, &validation_info.membership, - TYPES::Epoch::new(proposal_epoch), + Some(TYPES::Epoch::new(proposal_epoch)), // #3967 how do we know if proposal_epoch should be Some() or None? &validation_info.upgrade_lock, ) .await?; @@ -805,10 +806,11 @@ pub(crate) async fn validate_proposal_view_and_certs< // Validate the upgrade certificate -- this is just a signature validation. // Note that we don't do anything with the certificate directly if this passes; it eventually gets stored as part of the leaf if nothing goes wrong. { - let epoch = TYPES::Epoch::new(epoch_from_block_number( + let epoch = option_epoch_from_block_number::( + proposal.data.with_epoch, proposal.data.block_header.block_number(), validation_info.epoch_height, - )); + ); UpgradeCertificate::validate( &proposal.data.upgrade_certificate, &validation_info.membership, diff --git a/crates/task-impls/src/network.rs b/crates/task-impls/src/network.rs index 7d6664f39c..6e3321fa89 100644 --- a/crates/task-impls/src/network.rs +++ b/crates/task-impls/src/network.rs @@ -260,7 +260,7 @@ pub struct NetworkEventTaskState< pub view: TYPES::View, /// epoch number - pub epoch: TYPES::Epoch, + pub epoch: Option, /// network memberships pub membership: Arc>, @@ -839,7 +839,7 @@ impl< let keep_view = TYPES::View::new(view.saturating_sub(1)); self.cancel_tasks(keep_view); let net = Arc::clone(&self.network); - let epoch = self.epoch.u64(); + let epoch = self.epoch.map(|x| x.u64()); let mem = Arc::clone(&self.membership); spawn(async move { net.update_view::(*keep_view, epoch, mem).await; diff --git a/crates/task-impls/src/quorum_proposal/handlers.rs b/crates/task-impls/src/quorum_proposal/handlers.rs index c4922af173..9b3309ff25 100644 --- a/crates/task-impls/src/quorum_proposal/handlers.rs +++ b/crates/task-impls/src/quorum_proposal/handlers.rs @@ -28,12 +28,10 @@ use hotshot_types::{ message::Proposal, simple_certificate::{NextEpochQuorumCertificate2, QuorumCertificate2, UpgradeCertificate}, traits::{ - block_contents::BlockHeader, - election::Membership, - node_implementation::{ConsensusTime, NodeType}, + block_contents::BlockHeader, election::Membership, node_implementation::NodeType, signature_key::SignatureKey, }, - utils::{epoch_from_block_number, is_last_block_in_epoch}, + utils::{is_last_block_in_epoch, option_epoch_from_block_number}, vote::{Certificate, HasViewNumber}, }; use tracing::instrument; @@ -370,10 +368,13 @@ impl ProposalDependencyHandle { .context(warn!("Failed to construct marketplace block header"))? }; - let epoch = TYPES::Epoch::new(epoch_from_block_number( + // #3967 REVIEW NOTE: Is this the right way to decide if we're using epochs? + let epoch = option_epoch_from_block_number::( + version >= V::Epochs::VERSION, block_header.block_number(), self.epoch_height, - )); + ); + // Make sure we are the leader for the view and epoch. // We might have ended up here because we were in the epoch transition. if self @@ -400,13 +401,17 @@ impl ProposalDependencyHandle { }; let next_drb_result = if is_last_block_in_epoch(block_header.block_number(), self.epoch_height) { - self.consensus - .read() - .await - .drb_seeds_and_results - .results - .get(&epoch) - .copied() + if let Some(epoch_val) = &epoch { + self.consensus + .read() + .await + .drb_seeds_and_results + .results + .get(epoch_val) + .copied() + } else { + None + } } else { None }; @@ -418,6 +423,7 @@ impl ProposalDependencyHandle { upgrade_certificate, view_change_evidence: proposal_certificate, next_drb_result, + with_epoch: version >= V::Epochs::VERSION, }; let proposed_leaf = Leaf2::from_quorum_proposal(&proposal); diff --git a/crates/task-impls/src/quorum_proposal/mod.rs b/crates/task-impls/src/quorum_proposal/mod.rs index 9eb75188a3..597c787e7d 100644 --- a/crates/task-impls/src/quorum_proposal/mod.rs +++ b/crates/task-impls/src/quorum_proposal/mod.rs @@ -43,7 +43,7 @@ pub struct QuorumProposalTaskState pub latest_proposed_view: TYPES::View, /// Current epoch - pub cur_epoch: TYPES::Epoch, + pub cur_epoch: Option, /// Table for the in-progress proposal dependency tasks. pub proposal_dependencies: BTreeMap>, @@ -274,7 +274,7 @@ impl, V: Versions> async fn create_dependency_task_if_new( &mut self, view_number: TYPES::View, - epoch_number: TYPES::Epoch, + epoch_number: Option, event_receiver: Receiver>>, event_sender: Sender>>, event: Arc>, @@ -285,11 +285,15 @@ impl, V: Versions> membership_reader.leader(view_number, epoch_number)? == self.public_key; // If we are in the epoch transition and we are the leader in the next epoch, // we might want to start collecting dependencies for our next epoch proposal. - let leader_in_next_epoch = matches!( - epoch_transition_indicator, - EpochTransitionIndicator::InTransition - ) && membership_reader.leader(view_number, epoch_number + 1)? - == self.public_key; + + // #3967 REVIEW NOTE: is epoch_number.is_some() the right way to go here? how do we transition to epochs? + let leader_in_next_epoch = epoch_number.is_some() + && matches!( + epoch_transition_indicator, + EpochTransitionIndicator::InTransition + ) + && membership_reader.leader(view_number, epoch_number.map(|x| x + 1))? + == self.public_key; drop(membership_reader); // Don't even bother making the task if we are not entitled to propose anyway. @@ -369,7 +373,7 @@ impl, V: Versions> } /// Handles a consensus event received on the event stream - #[instrument(skip_all, fields(id = self.id, latest_proposed_view = *self.latest_proposed_view, epoch = *self.cur_epoch), name = "handle method", level = "error", target = "QuorumProposalTaskState")] + #[instrument(skip_all, fields(id = self.id, latest_proposed_view = *self.latest_proposed_view, epoch = self.cur_epoch.map(|x| *x)), name = "handle method", level = "error", target = "QuorumProposalTaskState")] pub async fn handle( &mut self, event: Arc>, diff --git a/crates/task-impls/src/quorum_proposal_recv/handlers.rs b/crates/task-impls/src/quorum_proposal_recv/handlers.rs index b08064205c..2b4715f78a 100644 --- a/crates/task-impls/src/quorum_proposal_recv/handlers.rs +++ b/crates/task-impls/src/quorum_proposal_recv/handlers.rs @@ -24,7 +24,7 @@ use hotshot_types::{ storage::Storage, ValidatedState, }, - utils::{epoch_from_block_number, View, ViewInner}, + utils::{epoch_from_block_number, option_epoch_from_block_number, View, ViewInner}, vote::{Certificate, HasViewNumber}, }; use tokio::spawn; @@ -157,10 +157,12 @@ pub(crate) async fn handle_quorum_proposal_recv< let maybe_next_epoch_justify_qc = proposal.data.next_epoch_justify_qc.clone(); let proposal_block_number = proposal.data.block_header.block_number(); - let proposal_epoch = TYPES::Epoch::new(epoch_from_block_number( + // #3967 REVIEW NOTE: Check if proposal.data.with_epoch is what we want here + let proposal_epoch = option_epoch_from_block_number::( + proposal.data.with_epoch, proposal_block_number, validation_info.epoch_height, - )); + ); let membership_reader = validation_info.membership.read().await; let membership_stake_table = membership_reader.stake_table(justify_qc.data.epoch); @@ -189,10 +191,12 @@ pub(crate) async fn handle_quorum_proposal_recv< bail!("Next epoch justify qc exists but it's not equal with justify qc."); } + // #3967 REVIEW NOTE: What do we want to do when justify_qc.data.epoch is None? let membership_reader = validation_info.membership.read().await; - let membership_next_stake_table = membership_reader.stake_table(justify_qc.data.epoch + 1); + let membership_next_stake_table = + membership_reader.stake_table(justify_qc.data.epoch.map(|x| x + 1)); let membership_next_success_threshold = - membership_reader.success_threshold(justify_qc.data.epoch + 1); + membership_reader.success_threshold(justify_qc.data.epoch.map(|x| x + 1)); drop(membership_reader); // Validate the next epoch justify qc as well @@ -302,9 +306,9 @@ pub(crate) async fn handle_quorum_proposal_recv< ); validate_proposal_liveness(proposal, &validation_info).await?; tracing::trace!( - "Sending ViewChange for view {} and epoch {}", + "Sending ViewChange for view {} and epoch {:?}", view_number, - *proposal_epoch + proposal_epoch ); broadcast_event( Arc::new(HotShotEvent::ViewChange(view_number, proposal_epoch)), @@ -325,9 +329,9 @@ pub(crate) async fn handle_quorum_proposal_recv< .await?; tracing::trace!( - "Sending ViewChange for view {} and epoch {}", + "Sending ViewChange for view {} and epoch {:?}", view_number, - *proposal_epoch + proposal_epoch ); broadcast_event( Arc::new(HotShotEvent::ViewChange(view_number, proposal_epoch)), diff --git a/crates/task-impls/src/quorum_proposal_recv/mod.rs b/crates/task-impls/src/quorum_proposal_recv/mod.rs index 14ed4edf7e..3251fa42a4 100644 --- a/crates/task-impls/src/quorum_proposal_recv/mod.rs +++ b/crates/task-impls/src/quorum_proposal_recv/mod.rs @@ -55,7 +55,7 @@ pub struct QuorumProposalRecvTaskState, /// Membership for Quorum Certs/votes pub membership: Arc>, @@ -129,7 +129,7 @@ impl, V: Versions> } /// Handles all consensus events relating to propose and vote-enabling events. - #[instrument(skip_all, fields(id = self.id, view = *self.cur_view, epoch = *self.cur_epoch), name = "Consensus replica task", level = "error")] + #[instrument(skip_all, fields(id = self.id, view = *self.cur_view, epoch = self.cur_epoch.map(|x| *x)), name = "Consensus replica task", level = "error")] #[allow(unused_variables)] pub async fn handle( &mut self, diff --git a/crates/task-impls/src/quorum_vote/handlers.rs b/crates/task-impls/src/quorum_vote/handlers.rs index fe11d69745..01da0ae7c9 100644 --- a/crates/task-impls/src/quorum_vote/handlers.rs +++ b/crates/task-impls/src/quorum_vote/handlers.rs @@ -25,7 +25,10 @@ use hotshot_types::{ storage::Storage, ValidatedState, }, - utils::{epoch_from_block_number, is_epoch_root, is_last_block_in_epoch}, + utils::{ + epoch_from_block_number, is_epoch_root, is_last_block_in_epoch, + option_epoch_from_block_number, + }, vote::HasViewNumber, }; use tokio::spawn; @@ -161,10 +164,14 @@ async fn verify_drb_result, V: Ver return Ok(()); } - let epoch = TYPES::Epoch::new(epoch_from_block_number( + // #3967 REVIEW NOTE: Check if this is the right way to decide if we're doing epochs + // Alternatively, should we just return Err() if epochs aren't happening here? Or can we assume + // that epochs are definitely happening by virtue of getting here? + let epoch = option_epoch_from_block_number::( + true, proposal.block_header.block_number(), task_state.epoch_height, - )); + ); let proposal_result = proposal .next_drb_result @@ -172,23 +179,31 @@ async fn verify_drb_result, V: Ver let membership_reader = task_state.membership.read().await; - let has_stake_current_epoch = membership_reader.has_stake(&task_state.public_key, epoch); - let has_stake_next_epoch = membership_reader.has_stake(&task_state.public_key, epoch + 1); + // #3967 REVIEW NOTE: Is this the right way to decide this? + if let Some(epoch_val) = epoch { + let has_stake_current_epoch = + membership_reader.has_stake(&task_state.public_key, Some(epoch_val)); + let has_stake_next_epoch = + membership_reader.has_stake(&task_state.public_key, Some(epoch_val + 1)); - drop(membership_reader); + drop(membership_reader); - if has_stake_current_epoch { - let computed_result = store_and_get_computed_drb_result(epoch + 1, task_state).await?; + if has_stake_current_epoch { + let computed_result = + store_and_get_computed_drb_result(epoch_val + 1, task_state).await?; - ensure!(proposal_result == computed_result, warn!("Our calculated DRB result is {:?}, which does not match the proposed DRB result of {:?}", computed_result, proposal_result)); + ensure!(proposal_result == computed_result, warn!("Our calculated DRB result is {:?}, which does not match the proposed DRB result of {:?}", computed_result, proposal_result)); - Ok(()) - } else if has_stake_next_epoch { - store_received_drb_result(epoch + 1, proposal_result, task_state).await + Ok(()) + } else if has_stake_next_epoch { + store_received_drb_result(epoch_val + 1, proposal_result, task_state).await + } else { + Err(error!( + "We are not participating in either the current or next epoch" + )) + } } else { - Err(error!( - "We are not participating in either the current or next epoch" - )) + Err(error!("Epochs are not available")) } } @@ -199,6 +214,11 @@ async fn start_drb_task, V: Versio proposal: &QuorumProposal2, task_state: &mut QuorumVoteTaskState, ) { + // #3967 REVIEW NOTE: Should we just exit early if we aren't doing epochs? + if !proposal.with_epoch { + return; + } + let current_epoch_number = TYPES::Epoch::new(epoch_from_block_number( proposal.block_header.block_number(), task_state.epoch_height, @@ -209,7 +229,7 @@ async fn start_drb_task, V: Versio .membership .read() .await - .has_stake(&task_state.public_key, current_epoch_number) + .has_stake(&task_state.public_key, Some(current_epoch_number)) { let new_epoch_number = current_epoch_number + 1; @@ -315,7 +335,7 @@ async fn store_drb_seed_and_result .membership .read() .await - .has_stake(&task_state.public_key, current_epoch_number + 1) + .has_stake(&task_state.public_key, Some(current_epoch_number + 1)) { let new_epoch_number = current_epoch_number + 2; let Ok(drb_seed_input_vec) = bincode::serialize(&proposal.justify_qc.signatures) else { @@ -615,17 +635,20 @@ pub(crate) async fn submit_vote, V extended_vote: bool, epoch_height: u64, ) -> Result<()> { - let epoch_number = TYPES::Epoch::new(epoch_from_block_number( + // #3967 REVIEW NOTE: CHECK IF leaf.with_epoch IS APPROPRIATE + let epoch_number = option_epoch_from_block_number::( + leaf.with_epoch, leaf.block_header().block_number(), epoch_height, - )); + ); let membership_reader = membership.read().await; let committee_member_in_current_epoch = membership_reader.has_stake(&public_key, epoch_number); // If the proposed leaf is for the last block in the epoch and the node is part of the quorum committee // in the next epoch, the node should vote to achieve the double quorum. + // 3967 TODO: HANDLE CASE WHERE EPOCH_NUMBER IS NONE let committee_member_in_next_epoch = is_last_block_in_epoch(leaf.height(), epoch_height) - && membership_reader.has_stake(&public_key, epoch_number + 1); + && membership_reader.has_stake(&public_key, epoch_number.map(|x| x + 1)); drop(membership_reader); ensure!( diff --git a/crates/task-impls/src/quorum_vote/mod.rs b/crates/task-impls/src/quorum_vote/mod.rs index 541c1d5fb2..3f48a27698 100644 --- a/crates/task-impls/src/quorum_vote/mod.rs +++ b/crates/task-impls/src/quorum_vote/mod.rs @@ -230,18 +230,18 @@ impl + 'static, V: Versions> Handl return; } - let current_epoch = - TYPES::Epoch::new(epoch_from_block_number(leaf.height(), self.epoch_height)); + // #3967 WRAP IN SOME() BELOW IS ALMOST CERTAINLY WRONG + let cur_epoch = Some(TYPES::Epoch::new(epoch_from_block_number( + leaf.height(), + self.epoch_height, + ))); tracing::trace!( - "Sending ViewChange for view {} and epoch {}", + "Sending ViewChange for view {} and epoch {:?}", self.view_number + 1, - *current_epoch + cur_epoch ); broadcast_event( - Arc::new(HotShotEvent::ViewChange( - self.view_number + 1, - current_epoch, - )), + Arc::new(HotShotEvent::ViewChange(self.view_number + 1, cur_epoch)), &self.sender, ) .await; @@ -750,7 +750,7 @@ impl, V: Versions> QuorumVoteTaskS broadcast_event( Arc::new(HotShotEvent::ViewChange( proposal.data.view_number() + 1, - current_epoch, + Some(current_epoch), )), &event_sender, ) diff --git a/crates/task-impls/src/request.rs b/crates/task-impls/src/request.rs index 3e1d75dff2..8c963ed64e 100644 --- a/crates/task-impls/src/request.rs +++ b/crates/task-impls/src/request.rs @@ -112,10 +112,11 @@ impl> TaskState for NetworkRequest match event.as_ref() { HotShotEvent::QuorumProposalValidated(proposal, _) => { let prop_view = proposal.data.view_number(); - let prop_epoch = TYPES::Epoch::new(epoch_from_block_number( + // #3967 REVIEW NOTE: Do we always want to have epochs here? + let prop_epoch = Some(TYPES::Epoch::new(epoch_from_block_number( proposal.data.block_header.block_number(), self.epoch_height, - )); + ))); // If we already have the VID shares for the next view, do nothing. if prop_view >= self.view @@ -162,7 +163,7 @@ impl> NetworkRequestState, sender: &Sender>>, receiver: &Receiver>>, ) { @@ -191,7 +192,7 @@ impl> NetworkRequestState>>, receiver: Receiver>>, view: TYPES::View, - epoch: TYPES::Epoch, + epoch: Option, ) { let consensus = OuterConsensus::new(Arc::clone(&self.consensus.inner_consensus)); let network = Arc::clone(&self.network); diff --git a/crates/task-impls/src/response.rs b/crates/task-impls/src/response.rs index 86543a5db9..30a413c85f 100644 --- a/crates/task-impls/src/response.rs +++ b/crates/task-impls/src/response.rs @@ -185,7 +185,11 @@ impl NetworkResponseState { } /// Makes sure the sender is allowed to send a request in the given epoch. - async fn valid_sender(&self, sender: &TYPES::SignatureKey, epoch: TYPES::Epoch) -> bool { + async fn valid_sender( + &self, + sender: &TYPES::SignatureKey, + epoch: Option, + ) -> bool { self.membership.read().await.has_stake(sender, epoch) } } diff --git a/crates/task-impls/src/transactions.rs b/crates/task-impls/src/transactions.rs index 34dd64fd23..4a577d9f4b 100644 --- a/crates/task-impls/src/transactions.rs +++ b/crates/task-impls/src/transactions.rs @@ -89,7 +89,7 @@ pub struct TransactionTaskState, V pub cur_view: TYPES::View, /// Epoch number this node is executing in. - pub cur_epoch: TYPES::Epoch, + pub cur_epoch: Option, /// Reference to consensus. Leader will require a read lock on this. pub consensus: OuterConsensus, @@ -131,7 +131,7 @@ impl, V: Versions> TransactionTask &mut self, event_stream: &Sender>>, block_view: TYPES::View, - block_epoch: TYPES::Epoch, + block_epoch: Option, ) -> Option { let version = match self.upgrade_lock.version(block_view).await { Ok(v) => v, @@ -156,7 +156,7 @@ impl, V: Versions> TransactionTask &mut self, event_stream: &Sender>>, block_view: TYPES::View, - block_epoch: TYPES::Epoch, + block_epoch: Option, ) -> Option { let version = match self.upgrade_lock.version(block_view).await { Ok(v) => v, @@ -257,7 +257,7 @@ impl, V: Versions> TransactionTask async fn produce_block_marketplace( &mut self, block_view: TYPES::View, - block_epoch: TYPES::Epoch, + block_epoch: Option, task_start_time: Instant, ) -> Result> { ensure!( @@ -361,7 +361,7 @@ impl, V: Versions> TransactionTask pub async fn null_block( &self, block_view: TYPES::View, - block_epoch: TYPES::Epoch, + block_epoch: Option, version: Version, ) -> Option> { let membership_total_nodes = self.membership.read().await.total_nodes(self.cur_epoch); @@ -394,7 +394,7 @@ impl, V: Versions> TransactionTask &mut self, event_stream: &Sender>>, block_view: TYPES::View, - block_epoch: TYPES::Epoch, + block_epoch: Option, ) -> Option { let task_start_time = Instant::now(); @@ -447,7 +447,7 @@ impl, V: Versions> TransactionTask &mut self, event_stream: &Sender>>, block_view: TYPES::View, - block_epoch: TYPES::Epoch, + block_epoch: Option, ) -> Option { if self.consensus.read().await.is_high_qc_forming_eqc() { tracing::info!("Reached end of epoch. Not getting a new block until we form an eQC."); @@ -459,7 +459,7 @@ impl, V: Versions> TransactionTask } /// main task event handler - #[instrument(skip_all, fields(id = self.id, view = *self.cur_view, epoch = *self.cur_epoch), name = "Transaction task", level = "error", target = "TransactionTaskState")] + #[instrument(skip_all, fields(id = self.id, view = *self.cur_view, epoch = self.cur_epoch.map(|x| *x)), name = "Transaction task", level = "error", target = "TransactionTaskState")] pub async fn handle( &mut self, event: Arc>, @@ -481,12 +481,16 @@ impl, V: Versions> TransactionTask HotShotEvent::ViewChange(view, epoch) => { let view = TYPES::View::new(std::cmp::max(1, **view)); let epoch = if self.epoch_height != 0 { - TYPES::Epoch::new(std::cmp::max(1, **epoch)) + // #3967 REVIEW NOTE: Double check this logic + Some(TYPES::Epoch::new(std::cmp::max( + 1, + epoch.map(|x| *x).unwrap_or(0), + ))) } else { *epoch }; ensure!( - *view > *self.cur_view && *epoch >= *self.cur_epoch, + *view > *self.cur_view && epoch >= self.cur_epoch, debug!( "Received a view change to an older view and epoch: tried to change view to {:?}\ and epoch {:?} though we are at view {:?} and epoch {:?}", diff --git a/crates/task-impls/src/upgrade.rs b/crates/task-impls/src/upgrade.rs index de1fcad3f8..591d04c9f0 100644 --- a/crates/task-impls/src/upgrade.rs +++ b/crates/task-impls/src/upgrade.rs @@ -48,7 +48,7 @@ pub struct UpgradeTaskState { pub cur_view: TYPES::View, /// Epoch number this node is executing in. - pub cur_epoch: TYPES::Epoch, + pub cur_epoch: Option, /// Membership for Quorum Certs/votes pub membership: Arc>, @@ -104,7 +104,7 @@ impl UpgradeTaskState { } /// main task event handler - #[instrument(skip_all, fields(id = self.id, view = *self.cur_view, epoch = *self.cur_epoch), name = "Upgrade Task", level = "error")] + #[instrument(skip_all, fields(id = self.id, view = *self.cur_view, epoch = self.cur_epoch.map(|x| *x)), name = "Upgrade Task", level = "error")] pub async fn handle( &mut self, event: Arc>, diff --git a/crates/task-impls/src/vid.rs b/crates/task-impls/src/vid.rs index 24603f27e1..266ee639ce 100644 --- a/crates/task-impls/src/vid.rs +++ b/crates/task-impls/src/vid.rs @@ -37,7 +37,7 @@ pub struct VidTaskState> { pub cur_view: TYPES::View, /// Epoch number this node is executing in. - pub cur_epoch: TYPES::Epoch, + pub cur_epoch: Option, /// Reference to consensus. Leader will require a read lock on this. pub consensus: OuterConsensus, @@ -63,7 +63,7 @@ pub struct VidTaskState> { impl> VidTaskState { /// main task event handler - #[instrument(skip_all, fields(id = self.id, view = *self.cur_view, epoch = *self.cur_epoch), name = "VID Main Task", level = "error", target = "VidTaskState")] + #[instrument(skip_all, fields(id = self.id, view = *self.cur_view, epoch = self.cur_epoch.map(|x| *x)), name = "VID Main Task", level = "error", target = "VidTaskState")] pub async fn handle( &mut self, event: Arc>, @@ -138,8 +138,8 @@ impl> VidTaskState { return None; }; debug!( - "publishing VID disperse for view {} and epoch {}", - *view_number, *epoch + "publishing VID disperse for view {} and epoch {:?}", + *view_number, epoch ); broadcast_event( Arc::new(HotShotEvent::VidDisperseSend( @@ -182,13 +182,15 @@ impl> VidTaskState { // We just sent a proposal for the last block in the epoch. We need to calculate // and send VID for the nodes in the next epoch so that they can vote. let proposal_view_number = proposal.data.view_number; - let sender_epoch = TYPES::Epoch::new(epoch_from_block_number( + // #3967 REVIEW NOTE: I think Some() wrap here is safe because if self.epoch_height == 0 on L179 + // then we've left + let sender_epoch = Some(TYPES::Epoch::new(epoch_from_block_number( proposed_block_number, self.epoch_height, - )); - let target_epoch = TYPES::Epoch::new( + ))); + let target_epoch = Some(TYPES::Epoch::new( epoch_from_block_number(proposed_block_number, self.epoch_height) + 1, - ); + )); let consensus_reader = self.consensus.read().await; let Some(payload) = consensus_reader.saved_payloads().get(&proposal_view_number) @@ -219,8 +221,8 @@ impl> VidTaskState { return None; }; debug!( - "publishing VID disperse for view {} and epoch {}", - *proposal_view_number, *target_epoch + "publishing VID disperse for view {} and epoch {:?}", + *proposal_view_number, target_epoch ); broadcast_event( Arc::new(HotShotEvent::VidDisperseSend( diff --git a/crates/task-impls/src/view_sync.rs b/crates/task-impls/src/view_sync.rs index 4f40516b3e..dadc7c8c70 100644 --- a/crates/task-impls/src/view_sync.rs +++ b/crates/task-impls/src/view_sync.rs @@ -71,7 +71,7 @@ pub struct ViewSyncTaskState { pub next_view: TYPES::View, /// Epoch HotShot is currently in - pub cur_epoch: TYPES::Epoch, + pub cur_epoch: Option, /// Membership for the quorum pub membership: Arc>, @@ -143,7 +143,7 @@ pub struct ViewSyncReplicaTaskState { pub next_view: TYPES::View, /// Current epoch HotShot is in - pub cur_epoch: TYPES::Epoch, + pub cur_epoch: Option, /// The relay index we are currently on pub relay: u64, @@ -255,7 +255,7 @@ impl ViewSyncTaskState { task_map.insert(view, replica_state); } - #[instrument(skip_all, fields(id = self.id, view = *self.cur_view, epoch = *self.cur_epoch), name = "View Sync Main Task", level = "error")] + #[instrument(skip_all, fields(id = self.id, view = *self.cur_view, epoch = self.cur_epoch.map(|x| *x)), name = "View Sync Main Task", level = "error")] #[allow(clippy::type_complexity)] /// Handles incoming events for the main view sync task pub async fn handle( @@ -530,7 +530,7 @@ impl ViewSyncTaskState { } impl ViewSyncReplicaTaskState { - #[instrument(skip_all, fields(id = self.id, view = *self.cur_view, epoch = *self.cur_epoch), name = "View Sync Replica Task", level = "error")] + #[instrument(skip_all, fields(id = self.id, view = *self.cur_view, epoch = self.cur_epoch.map(|x| *x)), name = "View Sync Replica Task", level = "error")] /// Handle incoming events for the view sync replica task pub async fn handle( &mut self, diff --git a/crates/task-impls/src/vote_collection.rs b/crates/task-impls/src/vote_collection.rs index 902630794a..d92c452c8b 100644 --- a/crates/task-impls/src/vote_collection.rs +++ b/crates/task-impls/src/vote_collection.rs @@ -61,7 +61,7 @@ pub struct VoteCollectionTaskState< pub view: TYPES::View, /// The epoch which we are collecting votes for - pub epoch: TYPES::Epoch, + pub epoch: Option, /// Node id pub id: u64, @@ -84,7 +84,7 @@ pub trait AggregatableVote< fn leader( &self, membership: &TYPES::Membership, - epoch: TYPES::Epoch, + epoch: Option, ) -> Result; /// return the Hotshot event for the completion of this CERT @@ -107,7 +107,7 @@ impl< pub async fn accumulate_vote( &mut self, vote: &VOTE, - sender_epoch: TYPES::Epoch, + sender_epoch: Option, event_stream: &Sender>>, ) -> Result> { ensure!( @@ -186,7 +186,7 @@ pub struct AccumulatorInfo { pub view: TYPES::View, /// Epoch of the votes we are collecting - pub epoch: TYPES::Epoch, + pub epoch: Option, /// This nodes id pub id: u64, @@ -262,7 +262,7 @@ pub async fn handle_vote< vote: &VOTE, public_key: TYPES::SignatureKey, membership: &Arc>, - epoch: TYPES::Epoch, + epoch: Option, id: u64, event: &Arc>, event_stream: &Sender>>, @@ -359,7 +359,7 @@ impl AggregatableVote, QuorumCertifica fn leader( &self, membership: &TYPES::Membership, - epoch: TYPES::Epoch, + epoch: Option, ) -> Result { membership.leader(self.view_number() + 1, epoch) } @@ -377,7 +377,7 @@ impl AggregatableVote, QuorumCertific fn leader( &self, membership: &TYPES::Membership, - epoch: TYPES::Epoch, + epoch: Option, ) -> Result { membership.leader(self.view_number() + 1, epoch) } @@ -396,7 +396,7 @@ impl fn leader( &self, membership: &TYPES::Membership, - epoch: TYPES::Epoch, + epoch: Option, ) -> Result { membership.leader(self.view_number() + 1, epoch) } @@ -414,7 +414,7 @@ impl AggregatableVote, UpgradeCertifi fn leader( &self, membership: &TYPES::Membership, - epoch: TYPES::Epoch, + epoch: Option, ) -> Result { membership.leader(self.view_number(), epoch) } @@ -432,7 +432,7 @@ impl AggregatableVote, DaCertificate2, ) -> Result { membership.leader(self.view_number(), epoch) } @@ -450,7 +450,7 @@ impl AggregatableVote, TimeoutCertif fn leader( &self, membership: &TYPES::Membership, - epoch: TYPES::Epoch, + epoch: Option, ) -> Result { membership.leader(self.view_number() + 1, epoch) } @@ -469,7 +469,7 @@ impl fn leader( &self, membership: &TYPES::Membership, - epoch: TYPES::Epoch, + epoch: Option, ) -> Result { membership.leader(self.date().round + self.date().relay, epoch) } @@ -488,7 +488,7 @@ impl fn leader( &self, membership: &TYPES::Membership, - epoch: TYPES::Epoch, + epoch: Option, ) -> Result { membership.leader(self.date().round + self.date().relay, epoch) } @@ -507,7 +507,7 @@ impl fn leader( &self, membership: &TYPES::Membership, - epoch: TYPES::Epoch, + epoch: Option, ) -> Result { membership.leader(self.date().round + self.date().relay, epoch) } @@ -555,7 +555,8 @@ impl ) -> Result>> { match event.as_ref() { HotShotEvent::QuorumVoteRecv(vote) => { - self.accumulate_vote(&vote.clone().into(), self.epoch + 1, sender) + // #3967 REVIEW NOTE: Should we error if self.epoch is None? + self.accumulate_vote(&vote.clone().into(), self.epoch.map(|x| x + 1), sender) .await } _ => Ok(None), diff --git a/crates/testing/src/byzantine/byzantine_behaviour.rs b/crates/testing/src/byzantine/byzantine_behaviour.rs index 3c0790b595..4f956f31b4 100644 --- a/crates/testing/src/byzantine/byzantine_behaviour.rs +++ b/crates/testing/src/byzantine/byzantine_behaviour.rs @@ -344,7 +344,7 @@ impl + std::fmt::Debug, V: Version let network_state: NetworkEventTaskState<_, V, _, _> = NetworkEventTaskState { network, view: TYPES::View::genesis(), - epoch: TYPES::Epoch::genesis(), + epoch: None, membership, storage: Arc::clone(&handle.storage()), consensus: OuterConsensus::new(handle.consensus()), diff --git a/crates/testing/src/helpers.rs b/crates/testing/src/helpers.rs index d0a5631fc5..04b42ebe3b 100644 --- a/crates/testing/src/helpers.rs +++ b/crates/testing/src/helpers.rs @@ -144,7 +144,7 @@ pub async fn build_cert< data: DATAType, membership: &Arc>, view: TYPES::View, - epoch: TYPES::Epoch, + epoch: Option, public_key: &TYPES::SignatureKey, private_key: &::PrivateKey, upgrade_lock: &UpgradeLock, @@ -210,7 +210,7 @@ pub async fn build_assembled_sig< data: &DATAType, membership: &Arc>, view: TYPES::View, - epoch: TYPES::Epoch, + epoch: Option, upgrade_lock: &UpgradeLock, ) -> ::QcType { let membership_reader = membership.read().await; @@ -272,7 +272,7 @@ pub fn key_pair_for_id( pub async fn vid_scheme_from_view_number( membership: &Arc>, view_number: TYPES::View, - epoch_number: TYPES::Epoch, + epoch_number: Option, ) -> VidSchemeType { let num_storage_nodes = membership .read() @@ -285,7 +285,7 @@ pub async fn vid_scheme_from_view_number( pub async fn vid_payload_commitment( membership: &Arc::Membership>>, view_number: TYPES::View, - epoch_number: TYPES::Epoch, + epoch_number: Option, transactions: Vec, ) -> VidCommitment { let mut vid = vid_scheme_from_view_number::(membership, view_number, epoch_number).await; @@ -298,7 +298,7 @@ pub async fn vid_payload_commitment( pub async fn da_payload_commitment( membership: &Arc::Membership>>, transactions: Vec, - epoch_number: TYPES::Epoch, + epoch_number: Option, ) -> VidCommitment { let encoded_transactions = TestTransaction::encode(&transactions); @@ -311,7 +311,7 @@ pub async fn da_payload_commitment( pub async fn build_payload_commitment( membership: &Arc::Membership>>, view: TYPES::View, - epoch: TYPES::Epoch, + epoch: Option, ) -> ::Commit { // 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. @@ -324,7 +324,7 @@ pub async fn build_payload_commitment( pub async fn build_vid_proposal( membership: &Arc::Membership>>, view_number: TYPES::View, - epoch_number: TYPES::Epoch, + epoch_number: Option, transactions: Vec, private_key: &::PrivateKey, ) -> VidProposal { @@ -367,7 +367,7 @@ pub async fn build_vid_proposal( pub async fn build_da_certificate( membership: &Arc::Membership>>, view_number: TYPES::View, - epoch_number: TYPES::Epoch, + epoch_number: Option, transactions: Vec, public_key: &TYPES::SignatureKey, private_key: &::PrivateKey, @@ -436,8 +436,9 @@ pub async fn build_fake_view_with_leaf_and_state( _upgrade_lock: &UpgradeLock, epoch_height: u64, ) -> View { - let epoch = - ::Epoch::new(epoch_from_block_number(leaf.height(), epoch_height)); + let epoch = Some(::Epoch::new( + epoch_from_block_number(leaf.height(), epoch_height), + )); View { view_inner: ViewInner::Leaf { leaf: leaf.commit(), diff --git a/crates/testing/src/overall_safety_task.rs b/crates/testing/src/overall_safety_task.rs index 600e4a87a8..dbc360978f 100644 --- a/crates/testing/src/overall_safety_task.rs +++ b/crates/testing/src/overall_safety_task.rs @@ -188,12 +188,23 @@ impl, V: Versions> TestTas }; if let Some(ref key) = key { - if *key.epoch(self.epoch_height) > self.ctx.latest_epoch { - self.ctx.latest_epoch = *key.epoch(self.epoch_height); + match ( + key.epoch(self.epoch_height).map(|x| *x), + self.ctx.latest_epoch, + ) { + (Some(key_epoch), Some(latest_epoch)) => { + if key_epoch > latest_epoch { + self.ctx.latest_epoch = Some(key_epoch); + } + } + (Some(key_epoch), None) => { + self.ctx.latest_epoch = Some(key_epoch); + } + _ => {} } } - let epoch = TYPES::Epoch::new(self.ctx.latest_epoch); + let epoch = self.ctx.latest_epoch.map(TYPES::Epoch::new); let memberships_arc = Arc::clone( &self .handles @@ -376,7 +387,7 @@ impl Default for RoundCtx { round_results: HashMap::default(), failed_views: HashSet::default(), successful_views: HashSet::default(), - latest_epoch: 0u64, + latest_epoch: None, } } } @@ -395,7 +406,7 @@ pub struct RoundCtx { /// successful views pub successful_views: HashSet, /// latest epoch, updated when a leaf with a higher epoch is seen - pub latest_epoch: u64, + pub latest_epoch: Option, } impl RoundCtx { diff --git a/crates/testing/src/spinning_task.rs b/crates/testing/src/spinning_task.rs index 66438d701d..492ac605ab 100644 --- a/crates/testing/src/spinning_task.rs +++ b/crates/testing/src/spinning_task.rs @@ -158,7 +158,7 @@ where TestInstanceState::new(self.async_delay_config.clone()), None, TYPES::View::genesis(), - TYPES::Epoch::genesis(), + None, TYPES::View::genesis(), BTreeMap::new(), self.high_qc.clone(), diff --git a/crates/testing/src/test_runner.rs b/crates/testing/src/test_runner.rs index 600be48823..1ded7f9335 100644 --- a/crates/testing/src/test_runner.rs +++ b/crates/testing/src/test_runner.rs @@ -179,7 +179,7 @@ where late_start, latest_view: None, changes, - last_decided_leaf: Leaf2::genesis( + last_decided_leaf: Leaf2::genesis::( &TestValidatedState::default(), &TestInstanceState::default(), ) @@ -405,7 +405,7 @@ where config.known_nodes_with_stake.clone(), config.known_da_nodes.clone(), ); - let num_nodes = temp_memberships.total_nodes(TYPES::Epoch::new(0)); + let num_nodes = temp_memberships.total_nodes(None); let (mut builder_tasks, builder_urls, fallback_builder_url) = self.init_builders::(num_nodes).await; diff --git a/crates/testing/src/view_generator.rs b/crates/testing/src/view_generator.rs index 266c311429..9fdfad5530 100644 --- a/crates/testing/src/view_generator.rs +++ b/crates/testing/src/view_generator.rs @@ -37,7 +37,7 @@ use hotshot_types::{ }, traits::{ consensus_api::ConsensusApi, - node_implementation::{ConsensusTime, NodeType}, + node_implementation::{ConsensusTime, NodeType, Versions}, BlockPayload, }, }; @@ -54,7 +54,7 @@ pub struct TestView { pub quorum_proposal: Proposal>, pub leaf: Leaf2, pub view_number: ViewNumber, - pub epoch_number: EpochNumber, + pub epoch_number: Option, pub membership: Arc::Membership>>, pub vid_disperse: Proposal>, pub vid_proposal: ( @@ -72,9 +72,11 @@ pub struct TestView { } impl TestView { - pub async fn genesis(membership: &Arc::Membership>>) -> Self { + pub async fn genesis( + membership: &Arc::Membership>>, + ) -> Self { let genesis_view = ViewNumber::new(1); - let genesis_epoch = EpochNumber::new(0); + let genesis_epoch = None; let upgrade_lock = UpgradeLock::new(); let transactions = Vec::new(); @@ -122,7 +124,7 @@ impl TestView { .await; let block_header = TestBlockHeader::new( - &Leaf2::::genesis( + &Leaf2::::genesis::( &TestValidatedState::default(), &TestInstanceState::default(), ) @@ -144,6 +146,7 @@ impl TestView { upgrade_certificate: None, view_change_evidence: None, next_drb_result: None, + with_epoch: true, }; let encoded_transactions = Arc::from(TestTransaction::encode(&transactions)); @@ -376,6 +379,7 @@ impl TestView { upgrade_certificate: upgrade_certificate.clone(), view_change_evidence, next_drb_result: None, + with_epoch: true, }; let mut leaf = Leaf2::from_quorum_proposal(&proposal); @@ -492,16 +496,18 @@ impl TestView { } } -pub struct TestViewGenerator { +pub struct TestViewGenerator { pub current_view: Option, pub membership: Arc::Membership>>, + pub _pd: PhantomData, } -impl TestViewGenerator { +impl TestViewGenerator { pub fn generate(membership: Arc::Membership>>) -> Self { TestViewGenerator { current_view: None, membership, + _pd: PhantomData, } } @@ -574,7 +580,7 @@ impl TestViewGenerator { } } -impl Stream for TestViewGenerator { +impl Stream for TestViewGenerator { type Item = TestView; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { @@ -584,7 +590,7 @@ impl Stream for TestViewGenerator { let mut fut = if let Some(ref view) = curr_view { async move { TestView::next_view(view).await }.boxed() } else { - async move { TestView::genesis(&mem).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 4e594bb4a2..9a7e67299f 100644 --- a/crates/testing/tests/tests_1/da_task.rs +++ b/crates/testing/tests/tests_1/da_task.rs @@ -48,15 +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 - .read() - .await - .total_nodes(EpochNumber::new(0)), + handle.hotshot.memberships.read().await.total_nodes(None), ); - let mut generator = TestViewGenerator::generate(membership.clone()); + let mut generator = TestViewGenerator::::generate(membership.clone()); let mut proposals = Vec::new(); let mut leaders = Vec::new(); @@ -102,17 +97,17 @@ async fn test_da_task() { let inputs = vec![ serial![ - ViewChange(ViewNumber::new(1), EpochNumber::new(0)), - ViewChange(ViewNumber::new(2), EpochNumber::new(0)), + ViewChange(ViewNumber::new(1), None), + ViewChange(ViewNumber::new(2), None), BlockRecv(PackedBundle::new( encoded_transactions.clone(), TestMetadata { num_transactions: transactions.len() as u64 }, ViewNumber::new(2), - EpochNumber::new(0), + None, vec1::vec1![null_block::builder_fee::( - membership.read().await.total_nodes(EpochNumber::new(0)), + membership.read().await.total_nodes(None), ::Base::VERSION, *ViewNumber::new(2), ) @@ -161,15 +156,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 - .read() - .await - .total_nodes(EpochNumber::new(0)), + handle.hotshot.memberships.read().await.total_nodes(None), ); - let mut generator = TestViewGenerator::generate(Arc::clone(&membership)); + let mut generator = TestViewGenerator::::generate(Arc::clone(&membership)); let mut proposals = Vec::new(); let mut leaders = Vec::new(); @@ -215,17 +205,17 @@ async fn test_da_task_storage_failure() { let inputs = vec![ serial![ - ViewChange(ViewNumber::new(1), EpochNumber::new(0)), - ViewChange(ViewNumber::new(2), EpochNumber::new(0)), + ViewChange(ViewNumber::new(1), None), + ViewChange(ViewNumber::new(2), None), BlockRecv(PackedBundle::new( encoded_transactions.clone(), TestMetadata { num_transactions: transactions.len() as u64 }, ViewNumber::new(2), - EpochNumber::new(0), + None, vec1::vec1![null_block::builder_fee::( - membership.read().await.total_nodes(EpochNumber::new(0)), + membership.read().await.total_nodes(None), ::Base::VERSION, *ViewNumber::new(2), ) diff --git a/crates/testing/tests/tests_1/message.rs b/crates/testing/tests/tests_1/message.rs index 456a1321bd..18128726d0 100644 --- a/crates/testing/tests/tests_1/message.rs +++ b/crates/testing/tests/tests_1/message.rs @@ -28,7 +28,7 @@ use vbs::{ fn version_number_at_start_of_serialization() { let sender = BLSPubKey::generated_from_seed_indexed([0u8; 32], 0).0; let view_number = ConsensusTime::new(17); - let epoch = ConsensusTime::new(0); + let epoch = None; // The version we set for the message const MAJOR: u16 = 37; const MINOR: u16 = 17; @@ -81,7 +81,7 @@ async fn test_certificate2_validity() { .0; let membership = Arc::clone(&handle.hotshot.memberships); - let mut generator = TestViewGenerator::generate(Arc::clone(&membership)); + let mut generator = TestViewGenerator::::generate(Arc::clone(&membership)); let mut proposals = Vec::new(); let mut leaders = Vec::new(); @@ -105,8 +105,8 @@ async fn test_certificate2_validity() { let qc = qc2.clone().to_qc(); let membership_reader = membership.read().await; - let membership_stake_table = membership_reader.stake_table(EpochNumber::new(0)); - let membership_success_threshold = membership_reader.success_threshold(EpochNumber::new(0)); + let membership_stake_table = membership_reader.stake_table(None); + let membership_success_threshold = membership_reader.success_threshold(None); drop(membership_reader); assert!( diff --git a/crates/testing/tests/tests_1/network_task.rs b/crates/testing/tests/tests_1/network_task.rs index a23ae64cd5..bf28823ed9 100644 --- a/crates/testing/tests/tests_1/network_task.rs +++ b/crates/testing/tests/tests_1/network_task.rs @@ -66,7 +66,7 @@ async fn test_network_task() { NetworkEventTaskState { network: network.clone(), view: ViewNumber::new(0), - epoch: EpochNumber::new(0), + epoch: None, membership: Arc::clone(&membership), upgrade_lock: upgrade_lock.clone(), storage, @@ -79,7 +79,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); + let mut generator = TestViewGenerator::::generate(membership); let view = generator.next().await.unwrap(); let (out_tx_internal, mut out_rx_internal) = async_broadcast::broadcast(10); @@ -238,7 +238,7 @@ async fn test_network_storage_fail() { NetworkEventTaskState { network: network.clone(), view: ViewNumber::new(0), - epoch: EpochNumber::new(0), + epoch: None, membership: Arc::clone(&membership), upgrade_lock: upgrade_lock.clone(), storage, @@ -251,7 +251,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); + 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 97d39cc9ca..eb053ddb17 100644 --- a/crates/testing/tests/tests_1/quorum_proposal_recv_task.rs +++ b/crates/testing/tests/tests_1/quorum_proposal_recv_task.rs @@ -58,7 +58,7 @@ async fn test_quorum_proposal_recv_task() { let consensus = handle.hotshot.consensus(); let mut consensus_writer = consensus.write().await; - let mut generator = TestViewGenerator::generate(membership); + let mut generator = TestViewGenerator::::generate(membership); let mut proposals = Vec::new(); let mut leaders = Vec::new(); let mut votes = Vec::new(); @@ -96,7 +96,7 @@ async fn test_quorum_proposal_recv_task() { proposals[1].clone(), leaves[0].clone(), )), - exact(ViewChange(ViewNumber::new(2), EpochNumber::new(0))), + exact(ViewChange(ViewNumber::new(2), None)), ])]; let state = @@ -133,7 +133,7 @@ async fn test_quorum_proposal_recv_task_liveness_check() { let consensus = handle.hotshot.consensus(); let mut consensus_writer = consensus.write().await; - let mut generator = TestViewGenerator::generate(membership); + let mut generator = TestViewGenerator::::generate(membership); let mut proposals = Vec::new(); let mut leaders = Vec::new(); let mut votes = Vec::new(); @@ -189,7 +189,7 @@ async fn test_quorum_proposal_recv_task_liveness_check() { let expectations = vec![Expectations::from_outputs(all_predicates![ exact(QuorumProposalPreliminarilyValidated(proposals[2].clone())), - exact(ViewChange(ViewNumber::new(3), EpochNumber::new(0))), + exact(ViewChange(ViewNumber::new(3), None)), exact(QuorumProposalRequestSend(req, signature)), ])]; diff --git a/crates/testing/tests/tests_1/quorum_proposal_task.rs b/crates/testing/tests/tests_1/quorum_proposal_task.rs index 165338945e..a889c515dd 100644 --- a/crates/testing/tests/tests_1/quorum_proposal_task.rs +++ b/crates/testing/tests/tests_1/quorum_proposal_task.rs @@ -56,11 +56,11 @@ async fn test_quorum_proposal_task_quorum_proposal_view_1() { let payload_commitment = build_payload_commitment::( &membership, ViewNumber::new(node_id), - EpochNumber::new(1), + Some(EpochNumber::new(1)), ) .await; - let mut generator = TestViewGenerator::generate(Arc::clone(&membership)); + let mut generator = TestViewGenerator::::generate(Arc::clone(&membership)); let mut proposals = Vec::new(); let mut leaders = Vec::new(); @@ -91,7 +91,10 @@ 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::( - membership.read().await.total_nodes(EpochNumber::new(1)), + membership + .read() + .await + .total_nodes(Some(EpochNumber::new(1))), ::Base::VERSION, *ViewNumber::new(1), ) @@ -148,7 +151,7 @@ async fn test_quorum_proposal_task_quorum_proposal_view_gt_1() { let membership = Arc::clone(&handle.hotshot.memberships); - let mut generator = TestViewGenerator::generate(membership.clone()); + let mut generator = TestViewGenerator::::generate(membership.clone()); let mut proposals = Vec::new(); let mut leaders = Vec::new(); @@ -183,7 +186,10 @@ 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::( - membership.read().await.total_nodes(EpochNumber::new(1)), + membership + .read() + .await + .total_nodes(Some(EpochNumber::new(1))), ::Base::VERSION, *ViewNumber::new(1), ) @@ -196,7 +202,7 @@ async fn test_quorum_proposal_task_quorum_proposal_view_gt_1() { build_payload_commitment::( &membership, ViewNumber::new(1), - EpochNumber::new(1) + Some(EpochNumber::new(1)) ) .await, builder_commitment.clone(), @@ -216,7 +222,7 @@ async fn test_quorum_proposal_task_quorum_proposal_view_gt_1() { build_payload_commitment::( &membership, ViewNumber::new(2), - EpochNumber::new(1) + Some(EpochNumber::new(1)) ) .await, builder_commitment.clone(), @@ -234,7 +240,7 @@ async fn test_quorum_proposal_task_quorum_proposal_view_gt_1() { build_payload_commitment::( &membership, ViewNumber::new(3), - EpochNumber::new(1) + Some(EpochNumber::new(1)) ) .await, builder_commitment.clone(), @@ -252,7 +258,7 @@ async fn test_quorum_proposal_task_quorum_proposal_view_gt_1() { build_payload_commitment::( &membership, ViewNumber::new(4), - EpochNumber::new(1) + Some(EpochNumber::new(1)) ) .await, builder_commitment.clone(), @@ -270,7 +276,7 @@ async fn test_quorum_proposal_task_quorum_proposal_view_gt_1() { build_payload_commitment::( &membership, ViewNumber::new(5), - EpochNumber::new(1) + Some(EpochNumber::new(1)) ) .await, builder_commitment, @@ -319,12 +325,12 @@ async fn test_quorum_proposal_task_qc_timeout() { let payload_commitment = build_payload_commitment::( &membership, ViewNumber::new(node_id), - EpochNumber::new(1), + Some(EpochNumber::new(1)), ) .await; let builder_commitment = BuilderCommitment::from_raw_digest(sha2::Sha256::new().finalize()); - let mut generator = TestViewGenerator::generate(Arc::clone(&membership)); + let mut generator = TestViewGenerator::::generate(Arc::clone(&membership)); let mut proposals = Vec::new(); let mut leaders = Vec::new(); @@ -340,7 +346,7 @@ async fn test_quorum_proposal_task_qc_timeout() { } let timeout_data = TimeoutData2 { view: ViewNumber::new(1), - epoch: EpochNumber::new(0), + epoch: None, }; generator.add_timeout(timeout_data); for view in (&mut generator).take(2).collect::>().await { @@ -367,7 +373,10 @@ async fn test_quorum_proposal_task_qc_timeout() { }, ViewNumber::new(3), vec1![null_block::builder_fee::( - membership.read().await.total_nodes(EpochNumber::new(1)), + membership + .read() + .await + .total_nodes(Some(EpochNumber::new(1))), ::Base::VERSION, *ViewNumber::new(3), ) @@ -409,12 +418,12 @@ async fn test_quorum_proposal_task_view_sync() { let payload_commitment = build_payload_commitment::( &membership, ViewNumber::new(node_id), - EpochNumber::new(1), + Some(EpochNumber::new(1)), ) .await; let builder_commitment = BuilderCommitment::from_raw_digest(sha2::Sha256::new().finalize()); - let mut generator = TestViewGenerator::generate(Arc::clone(&membership)); + let mut generator = TestViewGenerator::::generate(Arc::clone(&membership)); let mut proposals = Vec::new(); let mut leaders = Vec::new(); @@ -432,7 +441,7 @@ async fn test_quorum_proposal_task_view_sync() { let view_sync_finalize_data = ViewSyncFinalizeData2 { relay: 2, round: ViewNumber::new(node_id), - epoch: EpochNumber::new(0), + epoch: None, }; generator.add_view_sync_finalize(view_sync_finalize_data); for view in (&mut generator).take(2).collect::>().await { @@ -459,7 +468,10 @@ async fn test_quorum_proposal_task_view_sync() { }, ViewNumber::new(2), vec1![null_block::builder_fee::( - membership.read().await.total_nodes(EpochNumber::new(1)), + membership + .read() + .await + .total_nodes(Some(EpochNumber::new(1))), ::Base::VERSION, *ViewNumber::new(2), ) @@ -496,7 +508,7 @@ async fn test_quorum_proposal_task_liveness_check() { let membership = Arc::clone(&handle.hotshot.memberships); - let mut generator = TestViewGenerator::generate(Arc::clone(&membership)); + let mut generator = TestViewGenerator::::generate(Arc::clone(&membership)); let mut proposals = Vec::new(); let mut leaders = Vec::new(); @@ -526,7 +538,10 @@ 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::( - membership.read().await.total_nodes(EpochNumber::new(1)), + membership + .read() + .await + .total_nodes(Some(EpochNumber::new(1))), ::Base::VERSION, *ViewNumber::new(1), ) @@ -543,7 +558,7 @@ async fn test_quorum_proposal_task_liveness_check() { build_payload_commitment::( &membership, ViewNumber::new(1), - EpochNumber::new(1) + Some(EpochNumber::new(1)) ) .await, builder_commitment.clone(), @@ -563,7 +578,7 @@ async fn test_quorum_proposal_task_liveness_check() { build_payload_commitment::( &membership, ViewNumber::new(2), - EpochNumber::new(1) + Some(EpochNumber::new(1)) ) .await, builder_commitment.clone(), @@ -581,7 +596,7 @@ async fn test_quorum_proposal_task_liveness_check() { build_payload_commitment::( &membership, ViewNumber::new(3), - EpochNumber::new(1) + Some(EpochNumber::new(1)) ) .await, builder_commitment.clone(), @@ -599,7 +614,7 @@ async fn test_quorum_proposal_task_liveness_check() { build_payload_commitment::( &membership, ViewNumber::new(4), - EpochNumber::new(1) + Some(EpochNumber::new(1)) ) .await, builder_commitment.clone(), @@ -617,7 +632,7 @@ async fn test_quorum_proposal_task_liveness_check() { build_payload_commitment::( &membership, ViewNumber::new(5), - EpochNumber::new(1) + Some(EpochNumber::new(1)) ) .await, builder_commitment, @@ -659,7 +674,7 @@ async fn test_quorum_proposal_task_with_incomplete_events() { .0; let membership = Arc::clone(&handle.hotshot.memberships); - let mut generator = TestViewGenerator::generate(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 4d6c018fbc..2ec77484fb 100644 --- a/crates/testing/tests/tests_1/quorum_vote_task.rs +++ b/crates/testing/tests/tests_1/quorum_vote_task.rs @@ -47,7 +47,7 @@ async fn test_quorum_vote_task_success() { let membership = Arc::clone(&handle.hotshot.memberships); - let mut generator = TestViewGenerator::generate(membership); + let mut generator = TestViewGenerator::::generate(membership); let mut proposals = Vec::new(); let mut leaves = Vec::new(); @@ -83,7 +83,7 @@ async fn test_quorum_vote_task_success() { let expectations = vec![Expectations::from_outputs(all_predicates![ exact(DaCertificateValidated(dacs[1].clone())), exact(VidShareValidated(vids[1].0[0].clone())), - exact(ViewChange(ViewNumber::new(3), EpochNumber::new(0))), + exact(ViewChange(ViewNumber::new(3), None)), quorum_vote_send(), ])]; @@ -114,7 +114,7 @@ async fn test_quorum_vote_task_miss_dependency() { let membership = Arc::clone(&handle.hotshot.memberships); - let mut generator = TestViewGenerator::generate(membership); + let mut generator = TestViewGenerator::::generate(membership); let mut proposals = Vec::new(); let mut leaders = Vec::new(); @@ -198,7 +198,7 @@ async fn test_quorum_vote_task_incorrect_dependency() { let membership = Arc::clone(&handle.hotshot.memberships); - let mut generator = TestViewGenerator::generate(membership); + 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 491400d28d..541cd122ec 100644 --- a/crates/testing/tests/tests_1/transaction_task.rs +++ b/crates/testing/tests/tests_1/transaction_task.rs @@ -33,10 +33,13 @@ async fn test_transaction_task_leader_two_views_in_a_row() { let mut output = Vec::new(); let current_view = ViewNumber::new(4); - input.push(HotShotEvent::ViewChange(current_view, EpochNumber::new(1))); + input.push(HotShotEvent::ViewChange( + current_view, + Some(EpochNumber::new(1)), + )); input.push(HotShotEvent::ViewChange( current_view + 1, - EpochNumber::new(1), + Some(EpochNumber::new(1)), )); input.push(HotShotEvent::Shutdown); @@ -47,7 +50,7 @@ async fn test_transaction_task_leader_two_views_in_a_row() { .memberships .read() .await - .total_nodes(EpochNumber::new(0)), + .total_nodes(Some(EpochNumber::new(0))), ); // current view @@ -57,7 +60,7 @@ async fn test_transaction_task_leader_two_views_in_a_row() { num_transactions: 0, }, current_view, - EpochNumber::new(1), + Some(EpochNumber::new(1)), vec1::vec1![ null_block::builder_fee::( handle @@ -65,7 +68,7 @@ async fn test_transaction_task_leader_two_views_in_a_row() { .memberships .read() .await - .total_nodes(EpochNumber::new(0)), + .total_nodes(Some(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 7dd4324426..b9bf0c28db 100644 --- a/crates/testing/tests/tests_1/upgrade_task_with_proposal.rs +++ b/crates/testing/tests/tests_1/upgrade_task_with_proposal.rs @@ -85,7 +85,7 @@ async fn test_upgrade_task_with_proposal() { let membership = Arc::clone(&handle.hotshot.memberships); - let mut generator = TestViewGenerator::generate(Arc::clone(&membership)); + let mut generator = TestViewGenerator::::generate(Arc::clone(&membership)); for view in (&mut generator).take(1).collect::>().await { proposals.push(view.quorum_proposal.clone()); @@ -126,7 +126,10 @@ 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::( - membership.read().await.total_nodes(EpochNumber::new(1)), + membership + .read() + .await + .total_nodes(Some(EpochNumber::new(1))), ::Base::VERSION, *ViewNumber::new(1), ) @@ -155,7 +158,7 @@ async fn test_upgrade_task_with_proposal() { build_payload_commitment::( &membership, ViewNumber::new(1), - EpochNumber::new(1) + Some(EpochNumber::new(1)) ) .await, builder_commitment.clone(), @@ -175,7 +178,7 @@ async fn test_upgrade_task_with_proposal() { build_payload_commitment::( &membership, ViewNumber::new(2), - EpochNumber::new(1) + Some(EpochNumber::new(1)) ) .await, builder_commitment.clone(), @@ -194,7 +197,7 @@ async fn test_upgrade_task_with_proposal() { build_payload_commitment::( &membership, ViewNumber::new(3), - EpochNumber::new(1) + Some(EpochNumber::new(1)) ) .await, builder_commitment.clone(), 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 7e21efe163..841fd86a86 100644 --- a/crates/testing/tests/tests_1/upgrade_task_with_vote.rs +++ b/crates/testing/tests/tests_1/upgrade_task_with_vote.rs @@ -71,7 +71,7 @@ async fn test_upgrade_task_with_vote() { let mut consensus_writer = consensus.write().await; let membership = Arc::clone(&handle.hotshot.memberships); - let mut generator = TestViewGenerator::generate(membership); + let mut generator = TestViewGenerator::::generate(membership); for view in (&mut generator).take(2).collect::>().await { proposals.push(view.quorum_proposal.clone()); @@ -132,14 +132,14 @@ async fn test_upgrade_task_with_vote() { Expectations::from_outputs(all_predicates![ exact(DaCertificateValidated(dacs[1].clone())), exact(VidShareValidated(vids[1].0[0].clone())), - exact(ViewChange(ViewNumber::new(3), EpochNumber::new(0))), + exact(ViewChange(ViewNumber::new(3), None)), quorum_vote_send(), ]), Expectations::from_outputs_and_task_states( all_predicates![ exact(DaCertificateValidated(dacs[2].clone())), exact(VidShareValidated(vids[2].0[0].clone())), - exact(ViewChange(ViewNumber::new(4), EpochNumber::new(0))), + exact(ViewChange(ViewNumber::new(4), None)), quorum_vote_send(), ], vec![no_decided_upgrade_certificate()], @@ -148,7 +148,7 @@ async fn test_upgrade_task_with_vote() { all_predicates![ exact(DaCertificateValidated(dacs[3].clone())), exact(VidShareValidated(vids[3].0[0].clone())), - exact(ViewChange(ViewNumber::new(5), EpochNumber::new(0))), + exact(ViewChange(ViewNumber::new(5), None)), quorum_vote_send(), ], vec![no_decided_upgrade_certificate()], @@ -157,7 +157,7 @@ async fn test_upgrade_task_with_vote() { all_predicates![ exact(DaCertificateValidated(dacs[4].clone())), exact(VidShareValidated(vids[4].0[0].clone())), - exact(ViewChange(ViewNumber::new(6), EpochNumber::new(0))), + exact(ViewChange(ViewNumber::new(6), None)), quorum_vote_send(), ], vec![no_decided_upgrade_certificate()], diff --git a/crates/testing/tests/tests_1/vid_task.rs b/crates/testing/tests/tests_1/vid_task.rs index 9105b2dd02..9ad620fd2a 100644 --- a/crates/testing/tests/tests_1/vid_task.rs +++ b/crates/testing/tests/tests_1/vid_task.rs @@ -47,12 +47,8 @@ async fn test_vid_task() { let membership = Arc::clone(&handle.hotshot.memberships); - let mut vid = vid_scheme_from_view_number::( - &membership, - ViewNumber::new(0), - EpochNumber::new(0), - ) - .await; + let mut vid = + vid_scheme_from_view_number::(&membership, ViewNumber::new(0), None).await; let transactions = vec![TestTransaction::new(vec![0])]; let (payload, metadata) = >::from_transactions( @@ -91,8 +87,8 @@ async fn test_vid_task() { message.data.view_number, vid_disperse, &membership, - EpochNumber::new(0), - EpochNumber::new(0), + None, + None, None, ) .await; @@ -103,18 +99,18 @@ async fn test_vid_task() { _pd: PhantomData, }; let inputs = vec![ - serial![ViewChange(ViewNumber::new(1), EpochNumber::new(0))], + serial![ViewChange(ViewNumber::new(1), None)], serial![ - ViewChange(ViewNumber::new(2), EpochNumber::new(0)), + ViewChange(ViewNumber::new(2), None), BlockRecv(PackedBundle::new( encoded_transactions.clone(), TestMetadata { num_transactions: transactions.len() as u64 }, ViewNumber::new(2), - EpochNumber::new(0), + None, vec1::vec1![null_block::builder_fee::( - membership.read().await.total_nodes(EpochNumber::new(0)), + membership.read().await.total_nodes(None), ::Base::VERSION, *ViewNumber::new(2), ) @@ -136,7 +132,7 @@ async fn test_vid_task() { }, ViewNumber::new(2), vec1![null_block::builder_fee::( - membership.read().await.total_nodes(EpochNumber::new(0)), + membership.read().await.total_nodes(None), ::Base::VERSION, *ViewNumber::new(2), ) diff --git a/crates/testing/tests/tests_1/view_sync_task.rs b/crates/testing/tests/tests_1/view_sync_task.rs index 255ca4cfa0..c4c2d73900 100644 --- a/crates/testing/tests/tests_1/view_sync_task.rs +++ b/crates/testing/tests/tests_1/view_sync_task.rs @@ -11,8 +11,7 @@ use hotshot_task_impls::{ }; use hotshot_testing::helpers::build_system_handle; use hotshot_types::{ - data::{EpochNumber, ViewNumber}, - simple_vote::ViewSyncPreCommitData2, + data::ViewNumber, simple_vote::ViewSyncPreCommitData2, traits::node_implementation::ConsensusTime, }; @@ -29,7 +28,7 @@ async fn test_view_sync_task() { let vote_data = ViewSyncPreCommitData2 { relay: 0, round: ::View::new(4), - epoch: EpochNumber::new(0), + epoch: None, }; let vote = hotshot_types::simple_vote::ViewSyncPreCommitVote2::::create_signed_vote( vote_data, @@ -46,21 +45,12 @@ async fn test_view_sync_task() { let mut input = Vec::new(); let mut output = Vec::new(); - input.push(HotShotEvent::Timeout( - ViewNumber::new(2), - EpochNumber::new(0), - )); - input.push(HotShotEvent::Timeout( - ViewNumber::new(3), - EpochNumber::new(0), - )); + input.push(HotShotEvent::Timeout(ViewNumber::new(2), None)); + input.push(HotShotEvent::Timeout(ViewNumber::new(3), None)); input.push(HotShotEvent::Shutdown); - output.push(HotShotEvent::ViewChange( - ViewNumber::new(3), - EpochNumber::new(0), - )); + output.push(HotShotEvent::ViewChange(ViewNumber::new(3), None)); output.push(HotShotEvent::ViewSyncPreCommitVoteSend(vote.clone())); let view_sync_state = ViewSyncTaskState::::create_from(&handle).await; diff --git a/crates/testing/tests/tests_1/vote_dependency_handle.rs b/crates/testing/tests/tests_1/vote_dependency_handle.rs index 51c91750fb..b730a8ebdc 100644 --- a/crates/testing/tests/tests_1/vote_dependency_handle.rs +++ b/crates/testing/tests/tests_1/vote_dependency_handle.rs @@ -38,7 +38,7 @@ async fn test_vote_dependency_handle() { .0; let membership = Arc::clone(&handle.hotshot.memberships); - let mut generator = TestViewGenerator::generate(membership); + let mut generator = TestViewGenerator::::generate(membership); // Generate our state for the test let mut proposals = Vec::new(); @@ -76,7 +76,7 @@ async fn test_vote_dependency_handle() { for inputs in all_inputs.into_iter() { // The outputs are static here, but we re-make them since we use `into_iter` below let outputs = vec![ - exact(ViewChange(ViewNumber::new(3), EpochNumber::new(0))), + exact(ViewChange(ViewNumber::new(3), None)), quorum_vote_send(), ]; diff --git a/crates/testing/tests/tests_3/byzantine_tests.rs b/crates/testing/tests/tests_3/byzantine_tests.rs index 2a08d2c871..932259b7ed 100644 --- a/crates/testing/tests/tests_3/byzantine_tests.rs +++ b/crates/testing/tests/tests_3/byzantine_tests.rs @@ -180,7 +180,7 @@ cross_tests!( view_increment: nodes_count as u64, modifier: Arc::new(move |_pk, message_kind, transmit_type: &mut TransmitType, membership: &::Membership| { if let MessageKind::Consensus(SequencingMessage::General(GeneralConsensusMessage::Vote(vote))) = message_kind { - *transmit_type = TransmitType::Direct(membership.leader(vote.view_number() + 1 - nodes_count as u64, EpochNumber::new(0)).unwrap()); + *transmit_type = TransmitType::Direct(membership.leader(vote.view_number() + 1 - nodes_count as u64, None).unwrap()); } else { {} } diff --git a/crates/types/src/consensus.rs b/crates/types/src/consensus.rs index d9978a2749..0142844129 100644 --- a/crates/types/src/consensus.rs +++ b/crates/types/src/consensus.rs @@ -35,8 +35,8 @@ use crate::{ BlockPayload, ValidatedState, }, utils::{ - epoch_from_block_number, is_last_block_in_epoch, BuilderCommitment, LeafCommitment, - StateAndDelta, Terminator, + epoch_from_block_number, is_last_block_in_epoch, option_epoch_from_block_number, + BuilderCommitment, LeafCommitment, StateAndDelta, Terminator, }, vid::VidCommitment, vote::{Certificate, HasViewNumber}, @@ -289,7 +289,7 @@ pub struct Consensus { cur_view: TYPES::View, /// Epoch number that is currently on. - cur_epoch: TYPES::Epoch, + cur_epoch: Option, /// Last proposals we sent out, None if we haven't proposed yet. /// Prevents duplicate proposals, and can be served to those trying to catchup @@ -412,7 +412,7 @@ impl Consensus { pub fn new( validated_state_map: BTreeMap>, cur_view: TYPES::View, - cur_epoch: TYPES::Epoch, + cur_epoch: Option, locked_view: TYPES::View, last_decided_view: TYPES::View, last_actioned_view: TYPES::View, @@ -450,7 +450,7 @@ impl Consensus { } /// Get the current epoch. - pub fn cur_epoch(&self) -> TYPES::Epoch { + pub fn cur_epoch(&self) -> Option { self.cur_epoch } @@ -553,11 +553,15 @@ impl Consensus { /// Can return an error when the new epoch_number is not higher than the existing epoch number. pub fn update_epoch(&mut self, epoch_number: TYPES::Epoch) -> Result<()> { ensure!( - epoch_number > self.cur_epoch, + self.cur_epoch.is_none() || Some(epoch_number) > self.cur_epoch, debug!("New epoch isn't newer than the current epoch.") ); - tracing::trace!("Updating epoch from {} to {}", self.cur_epoch, epoch_number); - self.cur_epoch = epoch_number; + tracing::trace!( + "Updating epoch from {:?} to {}", + self.cur_epoch, + epoch_number + ); + self.cur_epoch = Some(epoch_number); Ok(()) } @@ -648,7 +652,7 @@ impl Consensus { pub fn update_da_view( &mut self, view_number: TYPES::View, - epoch: TYPES::Epoch, + epoch: Option, payload_commitment: VidCommitment, ) -> Result<()> { let view = View { @@ -672,7 +676,11 @@ impl Consensus { delta: Option>::Delta>>, ) -> Result<()> { let view_number = leaf.view_number(); - let epoch = TYPES::Epoch::new(epoch_from_block_number(leaf.height(), self.epoch_height)); + let epoch = option_epoch_from_block_number::( + leaf.with_epoch, + leaf.height(), + self.epoch_height, + ); let view = View { view_inner: ViewInner::Leaf { leaf: leaf.commit(), diff --git a/crates/types/src/data.rs b/crates/types/src/data.rs index fc0ece0b15..739362616f 100644 --- a/crates/types/src/data.rs +++ b/crates/types/src/data.rs @@ -26,6 +26,7 @@ use serde::{Deserialize, Serialize}; use thiserror::Error; use tokio::task::spawn_blocking; use utils::anytrace::*; +use vbs::version::StaticVersionType; use vec1::Vec1; use crate::{ @@ -49,7 +50,7 @@ use crate::{ states::TestableState, BlockPayload, }, - utils::{bincode_opts, epoch_from_block_number}, + utils::{bincode_opts, option_epoch_from_block_number}, vid::{vid_scheme, VidCommitment, VidCommon, VidPrecomputeData, VidSchemeType, VidShare}, vote::{Certificate, HasViewNumber}, }; @@ -159,7 +160,7 @@ pub struct DaProposal2 { /// View this proposal applies to pub view_number: TYPES::View, /// Epoch this proposal applies to - pub epoch: TYPES::Epoch, + pub epoch: Option, } impl From> for DaProposal2 { @@ -168,7 +169,7 @@ impl From> for DaProposal2 { encoded_transactions: da_proposal.encoded_transactions, metadata: da_proposal.metadata, view_number: da_proposal.view_number, - epoch: TYPES::Epoch::new(0), + epoch: None, } } } @@ -206,9 +207,9 @@ pub struct VidDisperse { /// The view number for which this VID data is intended pub view_number: TYPES::View, /// Epoch the data of this proposal belongs to - pub epoch: TYPES::Epoch, + pub epoch: Option, /// Epoch to which the recipients of this VID belong to - pub target_epoch: TYPES::Epoch, + pub target_epoch: Option, /// VidCommitment calculated based on the number of nodes in `target_epoch`. pub payload_commitment: VidCommitment, /// VidCommitment calculated based on the number of nodes in `epoch`. Needed during epoch transition. @@ -227,8 +228,8 @@ impl VidDisperse { view_number: TYPES::View, mut vid_disperse: JfVidDisperse, membership: &Arc>, - target_epoch: TYPES::Epoch, - data_epoch: TYPES::Epoch, + target_epoch: Option, + data_epoch: Option, data_epoch_payload_commitment: Option, ) -> Self { let shares = membership @@ -261,8 +262,8 @@ impl VidDisperse { payload: &TYPES::BlockPayload, membership: &Arc>, view: TYPES::View, - target_epoch: TYPES::Epoch, - data_epoch: TYPES::Epoch, + target_epoch: Option, + data_epoch: Option, ) -> Result { let num_nodes = membership.read().await.total_nodes(target_epoch); @@ -432,8 +433,8 @@ impl VidDisperseShare { ); let mut vid_disperse = VidDisperse { view_number: first_vid_disperse_share.view_number, - epoch: TYPES::Epoch::new(0), - target_epoch: TYPES::Epoch::new(0), + epoch: None, + target_epoch: None, payload_commitment: first_vid_disperse_share.payload_commitment, data_epoch_payload_commitment: None, common: first_vid_disperse_share.common, @@ -477,9 +478,9 @@ pub struct VidDisperseShare2 { /// The view number for which this VID data is intended pub view_number: TYPES::View, /// The epoch number for which this VID data belongs to - pub epoch: TYPES::Epoch, + pub epoch: Option, /// The epoch number to which the recipient of this VID belongs to - pub target_epoch: TYPES::Epoch, + pub target_epoch: Option, /// Block payload commitment pub payload_commitment: VidCommitment, /// VidCommitment calculated based on the number of nodes in `epoch`. Needed during epoch transition. @@ -527,8 +528,8 @@ impl From> for VidDisperseShare2 Self { view_number, - epoch: TYPES::Epoch::new(0), - target_epoch: TYPES::Epoch::new(0), + epoch: None, + target_epoch: None, payload_commitment, data_epoch_payload_commitment: None, share, @@ -683,6 +684,10 @@ pub struct QuorumProposal2 { /// consistent with the result from their computations. #[serde(with = "serde_bytes")] pub next_drb_result: Option, + + /// Indicates whether or not epochs were enabled for this proposal. QuorumProposal2s created from QuorumProposals + /// will have this set to false. + pub with_epoch: bool, } impl From> for QuorumProposal2 { @@ -697,6 +702,7 @@ impl From> for QuorumProposal2 { .proposal_certificate .map(ViewChangeEvidence::to_evidence2), next_drb_result: None, + with_epoch: false, } } } @@ -728,6 +734,7 @@ impl From> for Leaf2 { upgrade_certificate: leaf.upgrade_certificate, block_payload: leaf.block_payload, view_change_evidence: None, + with_epoch: false, } } } @@ -870,6 +877,9 @@ pub struct Leaf2 { /// Possible timeout or view sync certificate. If the `justify_qc` is not for a proposal in the immediately preceding view, then either a timeout or view sync certificate must be attached. pub view_change_evidence: Option>, + + /// Indicates whether or not epochs were enabled. + pub with_epoch: bool, } impl Leaf2 { @@ -880,10 +890,13 @@ impl Leaf2 { /// Panics if the genesis payload (`TYPES::BlockPayload::genesis()`) is malformed (unable to be /// interpreted as bytes). #[must_use] - pub async fn genesis( + pub async fn genesis( validated_state: &TYPES::ValidatedState, instance_state: &TYPES::InstanceState, ) -> Self { + let with_epoch = V::Base::VERSION >= V::Epochs::VERSION; + let epoch = with_epoch.then(TYPES::Epoch::genesis); + let (payload, metadata) = TYPES::BlockPayload::from_transactions([], validated_state, instance_state) .await @@ -902,7 +915,7 @@ impl Leaf2 { let null_quorum_data = QuorumData2 { leaf_commit: Commitment::>::default_commitment_no_preimage(), - epoch: TYPES::Epoch::genesis(), + epoch, }; let justify_qc = QuorumCertificate2::new( @@ -922,6 +935,7 @@ impl Leaf2 { block_header: block_header.clone(), block_payload: Some(payload), view_change_evidence: None, + with_epoch, } } /// Time when this leaf was created. @@ -929,11 +943,12 @@ impl Leaf2 { self.view_number } /// Epoch in which this leaf was created. - pub fn epoch(&self, epoch_height: u64) -> TYPES::Epoch { - TYPES::Epoch::new(epoch_from_block_number( + pub fn epoch(&self, epoch_height: u64) -> Option { + option_epoch_from_block_number::( + self.with_epoch, self.block_header.block_number(), epoch_height, - )) + ) } /// Height of this leaf in the chain. /// @@ -1103,6 +1118,7 @@ impl PartialEq for Leaf2 { upgrade_certificate, block_payload: _, view_change_evidence, + with_epoch, } = self; *view_number == other.view_number @@ -1112,6 +1128,7 @@ impl PartialEq for Leaf2 { && *block_header == other.block_header && *upgrade_certificate == other.upgrade_certificate && *view_change_evidence == other.view_change_evidence + && *with_epoch == other.with_epoch } } @@ -1183,7 +1200,7 @@ impl QuorumCertificate { impl QuorumCertificate2 { #[must_use] - /// Creat the Genesis certificate + /// Create the Genesis certificate pub async fn genesis( validated_state: &TYPES::ValidatedState, instance_state: &TYPES::InstanceState, @@ -1194,10 +1211,10 @@ impl QuorumCertificate2 { let genesis_view = ::genesis(); let data = QuorumData2 { - leaf_commit: Leaf2::genesis(validated_state, instance_state) + leaf_commit: Leaf2::genesis::(validated_state, instance_state) .await .commit(), - epoch: TYPES::Epoch::genesis(), + epoch: None, }; let versioned_data = @@ -1476,6 +1493,7 @@ impl Leaf2 { upgrade_certificate, view_change_evidence, next_drb_result: _, + with_epoch, } = quorum_proposal; Self { @@ -1487,6 +1505,7 @@ impl Leaf2 { upgrade_certificate: upgrade_certificate.clone(), block_payload: None, view_change_evidence: view_change_evidence.clone(), + with_epoch: *with_epoch, } } } @@ -1611,7 +1630,7 @@ pub struct PackedBundle { pub view_number: TYPES::View, /// The view number that this block is associated with. - pub epoch_number: TYPES::Epoch, + pub epoch_number: Option, /// The sequencing fee for submitting bundles. pub sequencing_fees: Vec1>, @@ -1629,7 +1648,7 @@ impl PackedBundle { encoded_transactions: Arc<[u8]>, metadata: >::Metadata, view_number: TYPES::View, - epoch_number: TYPES::Epoch, + epoch_number: Option, sequencing_fees: Vec1>, vid_precompute: Option, auction_result: Option, diff --git a/crates/types/src/message.rs b/crates/types/src/message.rs index 877cdd6282..6379098522 100644 --- a/crates/types/src/message.rs +++ b/crates/types/src/message.rs @@ -47,7 +47,7 @@ use crate::{ node_implementation::{ConsensusTime, NodeType, Versions}, signature_key::SignatureKey, }, - utils::{epoch_from_block_number, mnemonic}, + utils::{mnemonic, option_epoch_from_block_number}, vote::HasViewNumber, }; @@ -433,14 +433,17 @@ where pub async fn validate_signature( &self, membership: &TYPES::Membership, - epoch_height: u64, + _epoch_height: u64, upgrade_lock: &UpgradeLock, ) -> Result<()> { let view_number = self.data.view_number(); - let proposal_epoch = TYPES::Epoch::new(epoch_from_block_number( - self.data.block_header.block_number(), - epoch_height, - )); + // #3967 REVIEW NOTE: This seems like it should be None because QuorumProposal is pre-epoch, is that true? + let proposal_epoch = None; + //let proposal_epoch = option_epoch_from_block_number::(self.data.with_epoch, + //self.data.block_header.block_number(), + //epoch_height, + //); + let view_leader_key = membership.leader(view_number, proposal_epoch)?; let proposed_leaf = Leaf::from_quorum_proposal(&self.data); @@ -469,10 +472,11 @@ where epoch_height: u64, ) -> Result<()> { let view_number = self.data.view_number(); - let proposal_epoch = TYPES::Epoch::new(epoch_from_block_number( + let proposal_epoch = option_epoch_from_block_number::( + self.data.with_epoch, self.data.block_header.block_number(), epoch_height, - )); + ); let view_leader_key = membership.leader(view_number, proposal_epoch)?; let proposed_leaf = Leaf2::from_quorum_proposal(&self.data); diff --git a/crates/types/src/simple_certificate.rs b/crates/types/src/simple_certificate.rs index e7d8f9b437..a6b4a1b142 100644 --- a/crates/types/src/simple_certificate.rs +++ b/crates/types/src/simple_certificate.rs @@ -42,7 +42,7 @@ pub trait Threshold { /// Calculate a threshold based on the membership fn threshold>( membership: &MEMBERSHIP, - epoch: TYPES::Epoch, + epoch: Option, ) -> u64; } @@ -53,7 +53,7 @@ pub struct SuccessThreshold {} impl Threshold for SuccessThreshold { fn threshold>( membership: &MEMBERSHIP, - epoch: TYPES::Epoch, + epoch: Option, ) -> u64 { membership.success_threshold(epoch).into() } @@ -66,7 +66,7 @@ pub struct OneHonestThreshold {} impl Threshold for OneHonestThreshold { fn threshold>( membership: &MEMBERSHIP, - epoch: TYPES::Epoch, + epoch: Option, ) -> u64 { membership.failure_threshold(epoch).into() } @@ -79,7 +79,7 @@ pub struct UpgradeThreshold {} impl Threshold for UpgradeThreshold { fn threshold>( membership: &MEMBERSHIP, - epoch: TYPES::Epoch, + epoch: Option, ) -> u64 { membership.upgrade_threshold(epoch).into() } @@ -190,7 +190,7 @@ impl> Certificate fn stake_table_entry>( membership: &MEMBERSHIP, pub_key: &TYPES::SignatureKey, - epoch: TYPES::Epoch, + epoch: Option, ) -> Option<::StakeTableEntry> { membership.da_stake(pub_key, epoch) } @@ -198,20 +198,20 @@ impl> Certificate /// Proxy's to `Membership.da_stake_table` fn stake_table>( membership: &MEMBERSHIP, - epoch: TYPES::Epoch, + epoch: Option, ) -> Vec<::StakeTableEntry> { membership.da_stake_table(epoch) } /// Proxy's to `Membership.da_total_nodes` fn total_nodes>( membership: &MEMBERSHIP, - epoch: TYPES::Epoch, + epoch: Option, ) -> usize { membership.da_total_nodes(epoch) } fn threshold>( membership: &MEMBERSHIP, - epoch: TYPES::Epoch, + epoch: Option, ) -> u64 { membership.da_success_threshold(epoch).into() } @@ -278,7 +278,7 @@ impl> Certificate>( membership: &MEMBERSHIP, pub_key: &TYPES::SignatureKey, - epoch: TYPES::Epoch, + epoch: Option, ) -> Option<::StakeTableEntry> { membership.da_stake(pub_key, epoch) } @@ -286,20 +286,20 @@ impl> Certificate>( membership: &MEMBERSHIP, - epoch: TYPES::Epoch, + epoch: Option, ) -> Vec<::StakeTableEntry> { membership.da_stake_table(epoch) } /// Proxy's to `Membership.da_total_nodes` fn total_nodes>( membership: &MEMBERSHIP, - epoch: TYPES::Epoch, + epoch: Option, ) -> usize { membership.da_total_nodes(epoch) } fn threshold>( membership: &MEMBERSHIP, - epoch: TYPES::Epoch, + epoch: Option, ) -> u64 { membership.da_success_threshold(epoch).into() } @@ -367,7 +367,7 @@ impl< } fn threshold>( membership: &MEMBERSHIP, - epoch: TYPES::Epoch, + epoch: Option, ) -> u64 { THRESHOLD::threshold(membership, epoch) } @@ -375,14 +375,14 @@ impl< fn stake_table_entry>( membership: &MEMBERSHIP, pub_key: &TYPES::SignatureKey, - epoch: TYPES::Epoch, + epoch: Option, ) -> Option<::StakeTableEntry> { membership.stake(pub_key, epoch) } fn stake_table>( membership: &MEMBERSHIP, - epoch: TYPES::Epoch, + epoch: Option, ) -> Vec<::StakeTableEntry> { membership.stake_table(epoch) } @@ -390,7 +390,7 @@ impl< /// Proxy's to `Membership.total_nodes` fn total_nodes>( membership: &MEMBERSHIP, - epoch: TYPES::Epoch, + epoch: Option, ) -> usize { membership.total_nodes(epoch) } @@ -452,7 +452,7 @@ impl UpgradeCertificate { pub async fn validate( upgrade_certificate: &Option, membership: &RwLock, - epoch: TYPES::Epoch, + epoch: Option, upgrade_lock: &UpgradeLock, ) -> Result<()> { if let Some(ref cert) = upgrade_certificate { @@ -489,7 +489,7 @@ impl QuorumCertificate { let bytes: [u8; 32] = self.data.leaf_commit.into(); let data = QuorumData2 { leaf_commit: Commitment::from_raw(bytes), - epoch: TYPES::Epoch::genesis(), + epoch: None, }; let bytes: [u8; 32] = self.vote_commitment.into(); @@ -531,7 +531,7 @@ impl DaCertificate { pub fn to_dac2(self) -> DaCertificate2 { let data = DaData2 { payload_commit: self.data.payload_commit, - epoch: TYPES::Epoch::new(0), + epoch: None, }; let bytes: [u8; 32] = self.vote_commitment.into(); @@ -573,7 +573,7 @@ impl ViewSyncPreCommitCertificate { let data = ViewSyncPreCommitData2 { relay: self.data.relay, round: self.data.round, - epoch: TYPES::Epoch::new(0), + epoch: None, }; let bytes: [u8; 32] = self.vote_commitment.into(); @@ -616,7 +616,7 @@ impl ViewSyncCommitCertificate { let data = ViewSyncCommitData2 { relay: self.data.relay, round: self.data.round, - epoch: TYPES::Epoch::new(0), + epoch: None, }; let bytes: [u8; 32] = self.vote_commitment.into(); @@ -659,7 +659,7 @@ impl ViewSyncFinalizeCertificate { let data = ViewSyncFinalizeData2 { relay: self.data.relay, round: self.data.round, - epoch: TYPES::Epoch::new(0), + epoch: None, }; let bytes: [u8; 32] = self.vote_commitment.into(); @@ -701,7 +701,7 @@ impl TimeoutCertificate { pub fn to_tc2(self) -> TimeoutCertificate2 { let data = TimeoutData2 { view: self.data.view, - epoch: TYPES::Epoch::new(0), + epoch: None, }; let bytes: [u8; 32] = self.vote_commitment.into(); diff --git a/crates/types/src/simple_vote.rs b/crates/types/src/simple_vote.rs index ad651458d1..0ea17c7195 100644 --- a/crates/types/src/simple_vote.rs +++ b/crates/types/src/simple_vote.rs @@ -22,7 +22,7 @@ use crate::{ data::{Leaf, Leaf2}, message::UpgradeLock, traits::{ - node_implementation::{ConsensusTime, NodeType, Versions}, + node_implementation::{NodeType, Versions}, signature_key::SignatureKey, }, vid::VidCommitment, @@ -46,7 +46,7 @@ pub struct QuorumData2 { /// Commitment to the leaf pub leaf_commit: Commitment>, /// An epoch to which the data belongs to. Relevant for validating against the correct stake table - pub epoch: TYPES::Epoch, + pub epoch: Option, } /// Data used for a yes vote. Used to distinguish votes sent by the next epoch nodes. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)] @@ -64,7 +64,7 @@ pub struct DaData2 { /// Commitment to a block payload pub payload_commit: VidCommitment, /// Epoch number - pub epoch: TYPES::Epoch, + pub epoch: Option, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)] /// Data used for a timeout vote. @@ -78,7 +78,7 @@ pub struct TimeoutData2 { /// View the timeout is for pub view: TYPES::View, /// Epoch number - pub epoch: TYPES::Epoch, + pub epoch: Option, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)] /// Data used for a Pre Commit vote. @@ -96,7 +96,7 @@ pub struct ViewSyncPreCommitData2 { /// The view number we are trying to sync on pub round: TYPES::View, /// Epoch number - pub epoch: TYPES::Epoch, + pub epoch: Option, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)] /// Data used for a Commit vote. @@ -114,7 +114,7 @@ pub struct ViewSyncCommitData2 { /// The view number we are trying to sync on pub round: TYPES::View, /// Epoch number - pub epoch: TYPES::Epoch, + pub epoch: Option, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)] /// Data used for a Finalize vote. @@ -132,7 +132,7 @@ pub struct ViewSyncFinalizeData2 { /// The view number we are trying to sync on pub round: TYPES::View, /// Epoch number - pub epoch: TYPES::Epoch, + pub epoch: Option, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)] /// Data used for a Upgrade vote. @@ -161,7 +161,7 @@ pub struct UpgradeData2 { /// A unique identifier for the specific protocol being voted on pub hash: Vec, /// The first epoch in which the upgrade will be in effect - pub epoch: TYPES::Epoch, + pub epoch: Option, } /// Marker trait for data or commitments that can be voted on. @@ -438,14 +438,18 @@ impl Committable for UpgradeData2 { epoch, } = self; - committable::RawCommitmentBuilder::new("Upgrade data") + let mut cb = committable::RawCommitmentBuilder::new("Upgrade data") .u16(old_version.minor) .u16(old_version.major) .u16(new_version.minor) .u16(new_version.major) - .var_size_bytes(hash.as_slice()) - .u64(**epoch) - .finalize() + .var_size_bytes(hash.as_slice()); + + if let Some(ref epoch) = *epoch { + cb = cb.u64(**epoch); + } + + cb.finalize() } } @@ -516,7 +520,7 @@ impl Committable for ViewSyncCommitData2 { /// A trait for types belonging for specific epoch pub trait HasEpoch { /// Returns `Epoch` - fn epoch(&self) -> TYPES::Epoch; + fn epoch(&self) -> Option; } /// Helper macro for trivial implementation of the `HasEpoch` trait @@ -525,7 +529,7 @@ macro_rules! impl_has_epoch { ($($t:ty),*) => { $( impl HasEpoch for $t { - fn epoch(&self) -> TYPES::Epoch { + fn epoch(&self) -> Option { self.epoch } } @@ -546,7 +550,7 @@ impl_has_epoch!( impl + HasEpoch> HasEpoch for SimpleVote { - fn epoch(&self) -> TYPES::Epoch { + fn epoch(&self) -> Option { self.data.epoch() } } @@ -585,7 +589,7 @@ impl QuorumVote { let signature = self.signature; let data = QuorumData2 { leaf_commit: Commitment::from_raw(bytes), - epoch: TYPES::Epoch::genesis(), + epoch: None, }; let view_number = self.view_number; @@ -622,7 +626,7 @@ impl DaVote { let signature = self.signature; let data = DaData2 { payload_commit: self.data.payload_commit, - epoch: TYPES::Epoch::new(0), + epoch: None, }; let view_number = self.view_number; @@ -657,7 +661,7 @@ impl TimeoutVote { let signature = self.signature; let data = TimeoutData2 { view: self.data.view, - epoch: TYPES::Epoch::new(0), + epoch: None, }; let view_number = self.view_number; @@ -693,7 +697,7 @@ impl ViewSyncPreCommitVote { let data = ViewSyncPreCommitData2 { relay: self.data.relay, round: self.data.round, - epoch: TYPES::Epoch::new(0), + epoch: None, }; let view_number = self.view_number; @@ -730,7 +734,7 @@ impl ViewSyncCommitVote { let data = ViewSyncCommitData2 { relay: self.data.relay, round: self.data.round, - epoch: TYPES::Epoch::new(0), + epoch: None, }; let view_number = self.view_number; @@ -767,7 +771,7 @@ impl ViewSyncFinalizeVote { let data = ViewSyncFinalizeData2 { relay: self.data.relay, round: self.data.round, - epoch: TYPES::Epoch::new(0), + epoch: None, }; let view_number = self.view_number; diff --git a/crates/types/src/traits/election.rs b/crates/types/src/traits/election.rs index c862062190..4e5347f49d 100644 --- a/crates/types/src/traits/election.rs +++ b/crates/types/src/traits/election.rs @@ -30,34 +30,34 @@ pub trait Membership: Debug + Send + Sync { /// Get all participants in the committee (including their stake) for a specific epoch fn stake_table( &self, - epoch: TYPES::Epoch, + epoch: Option, ) -> Vec<::StakeTableEntry>; /// Get all participants in the committee (including their stake) for a specific epoch fn da_stake_table( &self, - epoch: TYPES::Epoch, + epoch: Option, ) -> Vec<::StakeTableEntry>; /// Get all participants in the committee for a specific view for a specific epoch fn committee_members( &self, view_number: TYPES::View, - epoch: TYPES::Epoch, + epoch: Option, ) -> 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, + epoch: Option, ) -> BTreeSet; /// Get all leaders in the committee for a specific view for a specific epoch fn committee_leaders( &self, view_number: TYPES::View, - epoch: TYPES::Epoch, + epoch: Option, ) -> BTreeSet; /// Get the stake table entry for a public key, returns `None` if the @@ -65,7 +65,7 @@ pub trait Membership: Debug + Send + Sync { fn stake( &self, pub_key: &TYPES::SignatureKey, - epoch: TYPES::Epoch, + epoch: Option, ) -> Option<::StakeTableEntry>; /// Get the DA stake table entry for a public key, returns `None` if the @@ -73,14 +73,14 @@ pub trait Membership: Debug + Send + Sync { fn da_stake( &self, pub_key: &TYPES::SignatureKey, - epoch: TYPES::Epoch, + epoch: Option, ) -> 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; + fn has_stake(&self, pub_key: &TYPES::SignatureKey, epoch: Option) -> 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; + fn has_da_stake(&self, pub_key: &TYPES::SignatureKey, epoch: Option) -> bool; /// The leader of the committee for view `view_number` in `epoch`. /// @@ -89,7 +89,11 @@ pub trait Membership: Debug + Send + Sync { /// /// # Errors /// Returns an error if the leader cannot be calculated. - fn leader(&self, view: TYPES::View, epoch: TYPES::Epoch) -> Result { + fn leader( + &self, + view: TYPES::View, + epoch: Option, + ) -> Result { use utils::anytrace::*; self.lookup_leader(view, epoch).wrap().context(info!( @@ -107,31 +111,33 @@ pub trait Membership: Debug + Send + Sync { fn lookup_leader( &self, view: TYPES::View, - epoch: TYPES::Epoch, + epoch: Option, ) -> std::result::Result; /// Returns the number of total nodes in the committee in an epoch `epoch` - fn total_nodes(&self, epoch: TYPES::Epoch) -> usize; + fn total_nodes(&self, epoch: Option) -> usize; /// Returns the number of total DA nodes in the committee in an epoch `epoch` - fn da_total_nodes(&self, epoch: TYPES::Epoch) -> usize; + fn da_total_nodes(&self, epoch: Option) -> usize; /// Returns the threshold for a specific `Membership` implementation - fn success_threshold(&self, epoch: TYPES::Epoch) -> NonZeroU64; + fn success_threshold(&self, epoch: Option) -> NonZeroU64; /// Returns the DA threshold for a specific `Membership` implementation - fn da_success_threshold(&self, epoch: TYPES::Epoch) -> NonZeroU64; + fn da_success_threshold(&self, epoch: Option) -> NonZeroU64; /// Returns the threshold for a specific `Membership` implementation - fn failure_threshold(&self, epoch: TYPES::Epoch) -> NonZeroU64; + fn failure_threshold(&self, epoch: Option) -> NonZeroU64; /// Returns the threshold required to upgrade the network protocol - fn upgrade_threshold(&self, epoch: TYPES::Epoch) -> NonZeroU64; + fn upgrade_threshold(&self, epoch: Option) -> NonZeroU64; #[allow(clippy::type_complexity)] /// Handles notifications that a new epoch root has been created /// Is called under a read lock to the Membership. Return a callback /// with Some to have that callback invoked under a write lock. + /// + /// #3967 REVIEW NOTE: this is only called if epoch is Some. Is there any reason to do otherwise? async fn add_epoch_root( &self, _epoch: TYPES::Epoch, diff --git a/crates/types/src/traits/network.rs b/crates/types/src/traits/network.rs index 2348dc8bc7..4d85ee82dc 100644 --- a/crates/types/src/traits/network.rs +++ b/crates/types/src/traits/network.rs @@ -262,7 +262,7 @@ pub trait ConnectedNetwork: Clone + Send + Sync + 'st async fn update_view<'a, TYPES>( &'a self, _view: u64, - _epoch: u64, + _epoch: Option, _membership: Arc>, ) where TYPES: NodeType + 'a, diff --git a/crates/types/src/utils.rs b/crates/types/src/utils.rs index 6496e182f8..6ea9a31741 100644 --- a/crates/types/src/utils.rs +++ b/crates/types/src/utils.rs @@ -29,7 +29,10 @@ use typenum::Unsigned; use crate::{ data::Leaf2, - traits::{node_implementation::NodeType, ValidatedState}, + traits::{ + node_implementation::{ConsensusTime, NodeType}, + ValidatedState, + }, vid::VidCommitment, }; @@ -46,7 +49,7 @@ pub enum ViewInner { /// Payload commitment to the available block. payload_commitment: VidCommitment, /// An epoch to which the data belongs to. Relevant for validating against the correct stake table - epoch: TYPES::Epoch, + epoch: Option, }, /// Undecided view Leaf { @@ -57,7 +60,7 @@ pub enum ViewInner { /// Optional state delta. delta: Option>::Delta>>, /// An epoch to which the data belongs to. Relevant for validating against the correct stake table - epoch: TYPES::Epoch, + epoch: Option, }, /// Leaf has failed Failed, @@ -151,7 +154,8 @@ impl ViewInner { } /// Returns `Epoch` if possible - pub fn epoch(&self) -> Option { + /// #3967 REVIEW NOTE: This type is kinda ugly, should we Result> instead? + pub fn epoch(&self) -> Option> { match self { Self::Da { epoch, .. } | Self::Leaf { epoch, .. } => Some(*epoch), Self::Failed => None, @@ -252,6 +256,28 @@ pub fn epoch_from_block_number(block_number: u64, epoch_height: u64) -> u64 { } } +/// Returns an Option based on a boolean condition of whether or not epochs are enabled, a block number, +/// and the epoch height. If epochs are disabled or the epoch height is zero, returns None. +#[must_use] +pub fn option_epoch_from_block_number( + with_epoch: bool, + block_number: u64, + epoch_height: u64, +) -> Option { + if with_epoch { + if epoch_height == 0 { + None + } else if block_number % epoch_height == 0 { + Some(block_number / epoch_height) + } else { + Some(block_number / epoch_height + 1) + } + .map(TYPES::Epoch::new) + } else { + None + } +} + /// A function for generating a cute little user mnemonic from a hash #[must_use] pub fn mnemonic(bytes: H) -> String { diff --git a/crates/types/src/vote.rs b/crates/types/src/vote.rs index 20d36e67ee..41a7d72e01 100644 --- a/crates/types/src/vote.rs +++ b/crates/types/src/vote.rs @@ -85,26 +85,26 @@ pub trait Certificate: HasViewNumber { // TODO: Make this a static ratio of the total stake of `Membership` fn threshold>( membership: &MEMBERSHIP, - epoch: TYPES::Epoch, + epoch: Option, ) -> u64; /// Get Stake Table from Membership implementation. fn stake_table>( membership: &MEMBERSHIP, - epoch: TYPES::Epoch, + epoch: Option, ) -> Vec<::StakeTableEntry>; /// Get Total Nodes from Membership implementation. fn total_nodes>( membership: &MEMBERSHIP, - epoch: TYPES::Epoch, + epoch: Option, ) -> usize; /// Get `StakeTableEntry` from Membership implementation. fn stake_table_entry>( membership: &MEMBERSHIP, pub_key: &TYPES::SignatureKey, - epoch: TYPES::Epoch, + epoch: Option, ) -> Option<::StakeTableEntry>; /// Get the commitment which was voted on @@ -165,7 +165,7 @@ impl< &mut self, vote: &VOTE, membership: &Arc>, - epoch: TYPES::Epoch, + epoch: Option, ) -> Either<(), CERT> { let key = vote.signing_key();