From bd6843224613aeffde4971a62e2149025f065028 Mon Sep 17 00:00:00 2001 From: lukaszrzasik Date: Fri, 18 Oct 2024 20:07:01 +0200 Subject: [PATCH] Add epoch parameter in `Membership` trait's methods (#3751) * Add epoch parameter in `Membership` trait's methods * Fixes for dependency-tasks tests * Fix possible deadlock * Add epoch_height to config file * Rename `ViewTime` and `EpochTime` to `View` and `Epoch` --- .../src/auction_results_provider_types.rs | 2 +- crates/example-types/src/node_types.rs | 27 +-- crates/example-types/src/storage_types.rs | 18 +- crates/examples/infra/mod.rs | 4 +- crates/hotshot/src/lib.rs | 36 ++-- crates/hotshot/src/tasks/mod.rs | 3 +- crates/hotshot/src/tasks/task_state.rs | 10 +- .../traits/election/randomized_committee.rs | 22 +- .../src/traits/election/static_committee.rs | 22 +- .../static_committee_leader_two_views.rs | 22 +- .../src/traits/networking/combined_network.rs | 7 +- .../src/traits/networking/libp2p_network.rs | 7 +- crates/hotshot/src/types/handle.rs | 23 ++- crates/orchestrator/run-config.toml | 1 + crates/task-impls/src/consensus/handlers.rs | 32 ++- crates/task-impls/src/consensus/mod.rs | 7 +- crates/task-impls/src/da.rs | 25 ++- crates/task-impls/src/events.rs | 26 +-- crates/task-impls/src/helpers.rs | 51 +++-- crates/task-impls/src/network.rs | 42 ++-- .../src/quorum_proposal/handlers.rs | 4 +- crates/task-impls/src/quorum_proposal/mod.rs | 35 +++- .../src/quorum_proposal_recv/handlers.rs | 1 + .../src/quorum_proposal_recv/mod.rs | 9 +- crates/task-impls/src/quorum_vote/mod.rs | 56 ++++-- crates/task-impls/src/request.rs | 24 ++- crates/task-impls/src/response.rs | 13 +- crates/task-impls/src/transactions.rs | 56 +++--- crates/task-impls/src/upgrade.rs | 31 +-- crates/task-impls/src/vid.rs | 7 +- crates/task-impls/src/view_sync.rs | 88 +++++--- crates/task-impls/src/vote_collection.rs | 53 +++-- .../src/byzantine/byzantine_behaviour.rs | 11 +- crates/testing/src/consistency_task.rs | 10 +- crates/testing/src/helpers.rs | 51 +++-- crates/testing/src/overall_safety_task.rs | 20 +- crates/testing/src/spinning_task.rs | 8 +- crates/testing/src/test_builder.rs | 1 + crates/testing/src/test_runner.rs | 4 +- crates/testing/src/view_generator.rs | 34 +++- crates/testing/tests/tests_1/da_task.rs | 17 +- crates/testing/tests/tests_1/network_task.rs | 4 + .../tests/tests_1/quorum_proposal_task.rs | 92 +++++++-- .../testing/tests/tests_1/transaction_task.rs | 6 +- .../tests_1/upgrade_task_with_proposal.rs | 21 +- crates/testing/tests/tests_1/vid_task.rs | 19 +- .../testing/tests/tests_1/view_sync_task.rs | 4 +- .../tests/tests_1/vote_dependency_handle.rs | 2 + .../testing/tests/tests_3/byzantine_tests.rs | 4 +- .../testing/tests/tests_3/memory_network.rs | 4 +- crates/types/src/consensus.rs | 104 ++++++---- crates/types/src/data.rs | 189 +++++++++++------- crates/types/src/error.rs | 2 +- crates/types/src/event.rs | 8 +- crates/types/src/hotshot_config_file.rs | 4 + crates/types/src/lib.rs | 2 + crates/types/src/message.rs | 19 +- crates/types/src/request_response.rs | 2 +- crates/types/src/simple_certificate.rs | 21 +- crates/types/src/simple_vote.rs | 28 +-- .../src/traits/auction_results_provider.rs | 2 +- crates/types/src/traits/election.rs | 38 ++-- crates/types/src/traits/network.rs | 18 +- .../types/src/traits/node_implementation.rs | 6 +- crates/types/src/traits/storage.rs | 4 +- crates/types/src/utils.rs | 2 +- crates/types/src/vote.rs | 17 +- 67 files changed, 1004 insertions(+), 538 deletions(-) diff --git a/crates/example-types/src/auction_results_provider_types.rs b/crates/example-types/src/auction_results_provider_types.rs index af2f8b7026..01978c1359 100644 --- a/crates/example-types/src/auction_results_provider_types.rs +++ b/crates/example-types/src/auction_results_provider_types.rs @@ -48,7 +48,7 @@ pub struct TestAuctionResultsProvider { impl AuctionResultsProvider for TestAuctionResultsProvider { /// Mock fetching the auction results, with optional error injection to simulate failure cases /// in the solver. - async fn fetch_auction_result(&self, view_number: TYPES::Time) -> Result { + async fn fetch_auction_result(&self, view_number: TYPES::View) -> Result { if let Some(url) = &self.broadcast_url { let resp = reqwest::get(url.join(&format!("/v0/api/auction_results/{}", *view_number))?) diff --git a/crates/example-types/src/node_types.rs b/crates/example-types/src/node_types.rs index a8f06976c3..1ab2446c12 100644 --- a/crates/example-types/src/node_types.rs +++ b/crates/example-types/src/node_types.rs @@ -4,6 +4,12 @@ // You should have received a copy of the MIT License // along with the HotShot repository. If not, see . +use crate::{ + auction_results_provider_types::{TestAuctionResult, TestAuctionResultsProvider}, + block_types::{TestBlockHeader, TestBlockPayload, TestTransaction}, + state_types::{TestInstanceState, TestValidatedState}, + storage_types::TestStorage, +}; use hotshot::traits::{ election::{ randomized_committee::RandomizedCommittee, static_committee::StaticCommittee, @@ -12,6 +18,7 @@ use hotshot::traits::{ implementations::{CombinedNetworks, Libp2pNetwork, MemoryNetwork, PushCdnNetwork}, NodeImplementation, }; +use hotshot_types::data::EpochNumber; use hotshot_types::{ data::ViewNumber, signature_key::{BLSPubKey, BuilderKey}, @@ -20,13 +27,6 @@ use hotshot_types::{ use serde::{Deserialize, Serialize}; use vbs::version::StaticVersion; -use crate::{ - auction_results_provider_types::{TestAuctionResult, TestAuctionResultsProvider}, - block_types::{TestBlockHeader, TestBlockPayload, TestTransaction}, - state_types::{TestInstanceState, TestValidatedState}, - storage_types::TestStorage, -}; - #[derive( Copy, Clone, @@ -45,7 +45,8 @@ use crate::{ pub struct TestTypes; impl NodeType for TestTypes { type AuctionResult = TestAuctionResult; - type Time = ViewNumber; + type View = ViewNumber; + type Epoch = EpochNumber; type BlockHeader = TestBlockHeader; type BlockPayload = TestBlockPayload; type SignatureKey = BLSPubKey; @@ -74,7 +75,8 @@ impl NodeType for TestTypes { pub struct TestTypesRandomizedLeader; impl NodeType for TestTypesRandomizedLeader { type AuctionResult = TestAuctionResult; - type Time = ViewNumber; + type View = ViewNumber; + type Epoch = EpochNumber; type BlockHeader = TestBlockHeader; type BlockPayload = TestBlockPayload; type SignatureKey = BLSPubKey; @@ -103,7 +105,8 @@ impl NodeType for TestTypesRandomizedLeader { pub struct TestConsecutiveLeaderTypes; impl NodeType for TestConsecutiveLeaderTypes { type AuctionResult = TestAuctionResult; - type Time = ViewNumber; + type View = ViewNumber; + type Epoch = EpochNumber; type BlockHeader = TestBlockHeader; type BlockPayload = TestBlockPayload; type SignatureKey = BLSPubKey; @@ -235,8 +238,8 @@ mod tests { let data = TestData { data: 10 }; - let view_0 = ::Time::new(0); - let view_1 = ::Time::new(1); + let view_0 = ::View::new(0); + let view_1 = ::View::new(1); let versioned_data_0 = VersionedVoteData::::new( diff --git a/crates/example-types/src/storage_types.rs b/crates/example-types/src/storage_types.rs index 5c6277f35c..2a093b1f02 100644 --- a/crates/example-types/src/storage_types.rs +++ b/crates/example-types/src/storage_types.rs @@ -29,17 +29,17 @@ use hotshot_types::{ use crate::testable_delay::{DelayConfig, SupportedTraitTypesForAsyncDelay, TestableDelay}; type VidShares = HashMap< - ::Time, + ::View, HashMap<::SignatureKey, Proposal>>, >; #[derive(Clone, Debug)] pub struct TestStorageState { vids: VidShares, - das: HashMap>>, - proposals: BTreeMap>>, + das: HashMap>>, + proposals: BTreeMap>>, high_qc: Option>, - action: TYPES::Time, + action: TYPES::View, } impl Default for TestStorageState { @@ -49,7 +49,7 @@ impl Default for TestStorageState { das: HashMap::new(), proposals: BTreeMap::new(), high_qc: None, - action: TYPES::Time::genesis(), + action: TYPES::View::genesis(), } } } @@ -87,7 +87,7 @@ impl TestableDelay for TestStorage { impl TestStorage { pub async fn proposals_cloned( &self, - ) -> BTreeMap>> { + ) -> BTreeMap>> { self.inner.read().await.proposals.clone() } pub async fn high_qc_cloned(&self) -> Option> { @@ -96,7 +96,7 @@ impl TestStorage { pub async fn decided_upgrade_certificate(&self) -> Option> { self.decided_upgrade_certificate.read().await.clone() } - pub async fn last_actioned_view(&self) -> TYPES::Time { + pub async fn last_actioned_view(&self) -> TYPES::View { self.inner.read().await.action } } @@ -145,7 +145,7 @@ impl Storage for TestStorage { async fn record_action( &self, - view: ::Time, + view: ::View, action: hotshot_types::event::HotShotAction, ) -> Result<()> { if self.should_return_err { @@ -180,7 +180,7 @@ impl Storage for TestStorage { async fn update_undecided_state( &self, _leafs: CommitmentMap>, - _state: BTreeMap>, + _state: BTreeMap>, ) -> Result<()> { if self.should_return_err { bail!("Failed to update high qc to storage"); diff --git a/crates/examples/infra/mod.rs b/crates/examples/infra/mod.rs index 96072cce89..0c7e8db673 100755 --- a/crates/examples/infra/mod.rs +++ b/crates/examples/infra/mod.rs @@ -463,7 +463,7 @@ pub trait RunDa< let start = Instant::now(); let mut event_stream = context.event_stream(); - let mut anchor_view: TYPES::Time = ::genesis(); + let mut anchor_view: TYPES::View = ::genesis(); let mut num_successful_commits = 0; context.hotshot.start_consensus().await; @@ -563,7 +563,7 @@ pub trait RunDa< .hotshot .memberships .quorum_membership - .committee_leaders(TYPES::Time::genesis()) + .committee_leaders(TYPES::View::genesis(), TYPES::Epoch::genesis()) .len(); let total_num_views = usize::try_from(consensus.locked_view().u64()).unwrap(); // `failed_num_views` could include uncommitted views diff --git a/crates/hotshot/src/lib.rs b/crates/hotshot/src/lib.rs index 577318befb..bc2fbf8128 100644 --- a/crates/hotshot/src/lib.rs +++ b/crates/hotshot/src/lib.rs @@ -123,7 +123,7 @@ pub struct SystemContext, V: Versi instance_state: Arc, /// The view to enter when first starting consensus - start_view: TYPES::Time, + start_view: TYPES::View, /// Access to the output event stream. output_event_stream: (Sender>, InactiveReceiver>), @@ -302,9 +302,17 @@ impl, V: Versions> SystemContext, V: Versions> SystemContext, V: Versions> SystemContext, V: Versions> SystemContext Option> { + pub async fn state(&self, view: TYPES::View) -> Option> { self.consensus.read().await.state(view).cloned() } @@ -964,10 +972,10 @@ pub struct HotShotInitializer { state_delta: Option>::Delta>>, /// Starting view number that should be equivelant to the view the node shut down with last. - start_view: TYPES::Time, + start_view: TYPES::View, /// The view we last performed an action in. An action is Proposing or voting for /// Either the quorum or DA. - actioned_view: TYPES::Time, + actioned_view: TYPES::View, /// Highest QC that was seen, for genesis it's the genesis QC. It should be for a view greater /// than `inner`s view number for the non genesis case because we must have seen higher QCs /// to decide on the leaf. @@ -978,9 +986,9 @@ pub struct HotShotInitializer { /// to vote and propose right away if they didn't miss anything while down. undecided_leafs: Vec>, /// Not yet decided state - undecided_state: BTreeMap>, + undecided_state: BTreeMap>, /// Proposals we have sent out to provide to others for catchup - saved_proposals: BTreeMap>>, + saved_proposals: BTreeMap>>, } impl HotShotInitializer { @@ -997,8 +1005,8 @@ impl HotShotInitializer { inner: Leaf::genesis(&validated_state, &instance_state).await, validated_state: Some(Arc::new(validated_state)), state_delta: Some(Arc::new(state_delta)), - start_view: TYPES::Time::new(0), - actioned_view: TYPES::Time::new(0), + start_view: TYPES::View::new(0), + actioned_view: TYPES::View::new(0), saved_proposals: BTreeMap::new(), high_qc, decided_upgrade_certificate: None, @@ -1020,13 +1028,13 @@ impl HotShotInitializer { anchor_leaf: Leaf, instance_state: TYPES::InstanceState, validated_state: Option>, - start_view: TYPES::Time, - actioned_view: TYPES::Time, - saved_proposals: BTreeMap>>, + start_view: TYPES::View, + actioned_view: TYPES::View, + saved_proposals: BTreeMap>>, high_qc: QuorumCertificate, decided_upgrade_certificate: Option>, undecided_leafs: Vec>, - undecided_state: BTreeMap>, + undecided_state: BTreeMap>, ) -> Self { Self { inner: anchor_leaf, diff --git a/crates/hotshot/src/tasks/mod.rs b/crates/hotshot/src/tasks/mod.rs index 8433ed6121..0f89203f45 100644 --- a/crates/hotshot/src/tasks/mod.rs +++ b/crates/hotshot/src/tasks/mod.rs @@ -170,7 +170,8 @@ pub fn add_network_event_task< ) { let network_state: NetworkEventTaskState<_, V, _, _> = NetworkEventTaskState { network, - view: TYPES::Time::genesis(), + view: TYPES::View::genesis(), + epoch: TYPES::Epoch::genesis(), quorum_membership, da_membership, storage: Arc::clone(&handle.storage()), diff --git a/crates/hotshot/src/tasks/task_state.rs b/crates/hotshot/src/tasks/task_state.rs index 50d08221c4..d23799cc59 100644 --- a/crates/hotshot/src/tasks/task_state.rs +++ b/crates/hotshot/src/tasks/task_state.rs @@ -70,6 +70,7 @@ impl, V: Versions> CreateTaskState return Self { output_event_stream: handle.hotshot.external_event_stream.0.clone(), cur_view: handle.cur_view().await, + cur_epoch: handle.cur_epoch().await, quorum_membership: handle.hotshot.memberships.quorum_membership.clone().into(), network: Arc::clone(&handle.hotshot.network), vote_collectors: BTreeMap::default(), @@ -91,6 +92,7 @@ impl, V: Versions> CreateTaskState return Self { output_event_stream: handle.hotshot.external_event_stream.0.clone(), cur_view: handle.cur_view().await, + cur_epoch: handle.cur_epoch().await, quorum_membership: handle.hotshot.memberships.quorum_membership.clone().into(), network: Arc::clone(&handle.hotshot.network), vote_collector: None.into(), @@ -118,6 +120,7 @@ impl, V: Versions> CreateTaskState Self { consensus: OuterConsensus::new(handle.hotshot.consensus()), cur_view: handle.cur_view().await, + cur_epoch: handle.cur_epoch().await, vote_collector: None, network: Arc::clone(&handle.hotshot.network), membership: handle.hotshot.memberships.quorum_membership.clone().into(), @@ -140,6 +143,7 @@ impl, V: Versions> CreateTaskState network: Arc::clone(&handle.hotshot.network), quorum_membership: handle.hotshot.memberships.quorum_membership.clone().into(), cur_view: handle.cur_view().await, + cur_epoch: handle.cur_epoch().await, vote_collectors: BTreeMap::default(), public_key: handle.public_key().clone(), private_key: handle.private_key().clone(), @@ -160,6 +164,7 @@ impl, V: Versions> CreateTaskState Self { current_view: cur_view, next_view: cur_view, + current_epoch: handle.cur_epoch().await, network: Arc::clone(&handle.hotshot.network), membership: handle.hotshot.memberships.quorum_membership.clone().into(), public_key: handle.public_key().clone(), @@ -171,7 +176,7 @@ impl, V: Versions> CreateTaskState finalize_relay_map: HashMap::default().into(), view_sync_timeout: handle.hotshot.config.view_sync_timeout, id: handle.hotshot.id, - last_garbage_collected_view: TYPES::Time::new(0), + last_garbage_collected_view: TYPES::View::new(0), upgrade_lock: handle.hotshot.upgrade_lock.clone(), } } @@ -187,6 +192,7 @@ impl, V: Versions> CreateTaskState output_event_stream: handle.hotshot.external_event_stream.0.clone(), consensus: OuterConsensus::new(handle.hotshot.consensus()), cur_view: handle.cur_view().await, + cur_epoch: handle.cur_epoch().await, network: Arc::clone(&handle.hotshot.network), membership: handle.hotshot.memberships.quorum_membership.clone().into(), public_key: handle.public_key().clone(), @@ -280,6 +286,7 @@ impl, V: Versions> CreateTaskState consensus: OuterConsensus::new(consensus), cur_view: handle.cur_view().await, cur_view_time: Utc::now().timestamp(), + cur_epoch: handle.cur_epoch().await, network: Arc::clone(&handle.hotshot.network), quorum_membership: handle.hotshot.memberships.quorum_membership.clone().into(), timeout_membership: handle.hotshot.memberships.quorum_membership.clone().into(), @@ -317,6 +324,7 @@ impl, V: Versions> CreateTaskState storage: Arc::clone(&handle.storage), cur_view: handle.cur_view().await, cur_view_time: Utc::now().timestamp(), + cur_epoch: handle.cur_epoch().await, output_event_stream: handle.hotshot.external_event_stream.0.clone(), timeout_task: async_spawn(async {}), timeout: handle.hotshot.config.next_view_timeout, diff --git a/crates/hotshot/src/traits/election/randomized_committee.rs b/crates/hotshot/src/traits/election/randomized_committee.rs index b761eb1ee2..4fed098e9c 100644 --- a/crates/hotshot/src/traits/election/randomized_committee.rs +++ b/crates/hotshot/src/traits/election/randomized_committee.rs @@ -82,6 +82,7 @@ impl Membership for RandomizedCommittee { /// Get the stake table for the current view fn stake_table( &self, + _epoch: ::Epoch, ) -> Vec<<::SignatureKey as SignatureKey>::StakeTableEntry> { self.stake_table.clone() } @@ -89,7 +90,8 @@ impl Membership for RandomizedCommittee { /// Get all members of the committee for the current view fn committee_members( &self, - _view_number: ::Time, + _view_number: ::View, + _epoch: ::Epoch, ) -> std::collections::BTreeSet<::SignatureKey> { self.stake_table .iter() @@ -100,7 +102,8 @@ impl Membership for RandomizedCommittee { /// Get all eligible leaders of the committee for the current view fn committee_leaders( &self, - _view_number: ::Time, + _view_number: ::View, + _epoch: ::Epoch, ) -> std::collections::BTreeSet<::SignatureKey> { self.eligible_leaders .iter() @@ -112,13 +115,18 @@ impl Membership for RandomizedCommittee { fn stake( &self, pub_key: &::SignatureKey, + _epoch: ::Epoch, ) -> Option<::StakeTableEntry> { // Only return the stake if it is above zero self.indexed_stake_table.get(pub_key).cloned() } /// Check if a node has stake in the committee - fn has_stake(&self, pub_key: &::SignatureKey) -> bool { + fn has_stake( + &self, + pub_key: &::SignatureKey, + _epoch: ::Epoch, + ) -> bool { self.indexed_stake_table .get(pub_key) .is_some_and(|x| x.stake() > U256::zero()) @@ -130,7 +138,11 @@ impl Membership for RandomizedCommittee { } /// Index the vector of public keys with the current view number - fn leader(&self, view_number: TYPES::Time) -> TYPES::SignatureKey { + fn leader( + &self, + view_number: TYPES::View, + _epoch: ::Epoch, + ) -> TYPES::SignatureKey { let mut rng: StdRng = rand::SeedableRng::seed_from_u64(*view_number); let randomized_view_number: u64 = rng.gen_range(0..=u64::MAX); @@ -143,7 +155,7 @@ impl Membership for RandomizedCommittee { } /// Get the total number of nodes in the committee - fn total_nodes(&self) -> usize { + fn total_nodes(&self, _epoch: ::Epoch) -> usize { self.stake_table.len() } diff --git a/crates/hotshot/src/traits/election/static_committee.rs b/crates/hotshot/src/traits/election/static_committee.rs index 69cc5ce9c2..2ef52a66e2 100644 --- a/crates/hotshot/src/traits/election/static_committee.rs +++ b/crates/hotshot/src/traits/election/static_committee.rs @@ -80,6 +80,7 @@ impl Membership for StaticCommittee { /// Get the stake table for the current view fn stake_table( &self, + _epoch: ::Epoch, ) -> Vec<<::SignatureKey as SignatureKey>::StakeTableEntry> { self.stake_table.clone() } @@ -87,7 +88,8 @@ impl Membership for StaticCommittee { /// Get all members of the committee for the current view fn committee_members( &self, - _view_number: ::Time, + _view_number: ::View, + _epoch: ::Epoch, ) -> std::collections::BTreeSet<::SignatureKey> { self.stake_table .iter() @@ -98,7 +100,8 @@ impl Membership for StaticCommittee { /// Get all eligible leaders of the committee for the current view fn committee_leaders( &self, - _view_number: ::Time, + _view_number: ::View, + _epoch: ::Epoch, ) -> std::collections::BTreeSet<::SignatureKey> { self.eligible_leaders .iter() @@ -110,13 +113,18 @@ impl Membership for StaticCommittee { fn stake( &self, pub_key: &::SignatureKey, + _epoch: ::Epoch, ) -> Option<::StakeTableEntry> { // Only return the stake if it is above zero self.indexed_stake_table.get(pub_key).cloned() } /// Check if a node has stake in the committee - fn has_stake(&self, pub_key: &::SignatureKey) -> bool { + fn has_stake( + &self, + pub_key: &::SignatureKey, + _epoch: ::Epoch, + ) -> bool { self.indexed_stake_table .get(pub_key) .is_some_and(|x| x.stake() > U256::zero()) @@ -128,7 +136,11 @@ impl Membership for StaticCommittee { } /// Index the vector of public keys with the current view number - fn leader(&self, view_number: TYPES::Time) -> TYPES::SignatureKey { + fn leader( + &self, + view_number: TYPES::View, + _epoch: ::Epoch, + ) -> TYPES::SignatureKey { #[allow(clippy::cast_possible_truncation)] let index = *view_number as usize % self.eligible_leaders.len(); let res = self.eligible_leaders[index].clone(); @@ -136,7 +148,7 @@ impl Membership for StaticCommittee { } /// Get the total number of nodes in the committee - fn total_nodes(&self) -> usize { + fn total_nodes(&self, _epoch: ::Epoch) -> usize { self.stake_table.len() } 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 9ce83c14a0..db41aad2ab 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 @@ -80,6 +80,7 @@ impl Membership for StaticCommitteeLeaderForTwoViews::Epoch, ) -> Vec<<::SignatureKey as SignatureKey>::StakeTableEntry> { self.stake_table.clone() } @@ -87,7 +88,8 @@ impl Membership for StaticCommitteeLeaderForTwoViews::Time, + _view_number: ::View, + _epoch: ::Epoch, ) -> std::collections::BTreeSet<::SignatureKey> { self.stake_table .iter() @@ -98,7 +100,8 @@ impl Membership for StaticCommitteeLeaderForTwoViews::Time, + _view_number: ::View, + _epoch: ::Epoch, ) -> std::collections::BTreeSet<::SignatureKey> { self.eligible_leaders .iter() @@ -110,13 +113,18 @@ impl Membership for StaticCommitteeLeaderForTwoViews::SignatureKey, + _epoch: ::Epoch, ) -> Option<::StakeTableEntry> { // Only return the stake if it is above zero self.indexed_stake_table.get(pub_key).cloned() } /// Check if a node has stake in the committee - fn has_stake(&self, pub_key: &::SignatureKey) -> bool { + fn has_stake( + &self, + pub_key: &::SignatureKey, + _epoch: ::Epoch, + ) -> bool { self.indexed_stake_table .get(pub_key) .is_some_and(|x| x.stake() > U256::zero()) @@ -128,7 +136,11 @@ impl Membership for StaticCommitteeLeaderForTwoViews TYPES::SignatureKey { + fn leader( + &self, + view_number: TYPES::View, + _epoch: ::Epoch, + ) -> TYPES::SignatureKey { let index = usize::try_from((*view_number / 2) % self.eligible_leaders.len() as u64).unwrap(); let res = self.eligible_leaders[index].clone(); @@ -136,7 +148,7 @@ impl Membership for StaticCommitteeLeaderForTwoViews usize { + fn total_nodes(&self, _epoch: ::Epoch) -> usize { self.stake_table.len() } diff --git a/crates/hotshot/src/traits/networking/combined_network.rs b/crates/hotshot/src/traits/networking/combined_network.rs index 2bb7eba49c..002f5d543c 100644 --- a/crates/hotshot/src/traits/networking/combined_network.rs +++ b/crates/hotshot/src/traits/networking/combined_network.rs @@ -484,7 +484,7 @@ impl ConnectedNetwork for CombinedNetworks self.secondary().queue_node_lookup(view_number, pk) } - async fn update_view<'a, T>(&'a self, view: u64, membership: &T::Membership) + async fn update_view<'a, T>(&'a self, view: u64, epoch: u64, membership: &T::Membership) where T: NodeType + 'a, { @@ -505,7 +505,10 @@ impl ConnectedNetwork for CombinedNetworks } }); // Run `update_view` logic for the libp2p network - self.networks.1.update_view::(view, membership).await; + self.networks + .1 + .update_view::(view, epoch, membership) + .await; } fn is_primary_down(&self) -> bool { diff --git a/crates/hotshot/src/traits/networking/libp2p_network.rs b/crates/hotshot/src/traits/networking/libp2p_network.rs index 32551fba72..d25fc91762 100644 --- a/crates/hotshot/src/traits/networking/libp2p_network.rs +++ b/crates/hotshot/src/traits/networking/libp2p_network.rs @@ -1019,12 +1019,13 @@ impl ConnectedNetwork for Libp2pNetwork { /// So the logic with libp2p is to prefetch upcomming leaders libp2p address to /// save time when we later need to direct message the leader our vote. Hence the /// use of the future view and leader to queue the lookups. - async fn update_view<'a, TYPES>(&'a self, view: u64, membership: &TYPES::Membership) + async fn update_view<'a, TYPES>(&'a self, view: u64, epoch: u64, membership: &TYPES::Membership) where TYPES: NodeType + 'a, { - let future_view = ::Time::new(view) + LOOK_AHEAD; - let future_leader = membership.leader(future_view); + let future_view = ::View::new(view) + LOOK_AHEAD; + let epoch = ::Epoch::new(epoch); + let future_leader = membership.leader(future_view, epoch); let _ = self .queue_node_lookup(ViewNumber::new(*future_view), future_leader) diff --git a/crates/hotshot/src/types/handle.rs b/crates/hotshot/src/types/handle.rs index 5945e31ceb..cf6c8ffe02 100644 --- a/crates/hotshot/src/types/handle.rs +++ b/crates/hotshot/src/types/handle.rs @@ -136,7 +136,8 @@ impl + 'static, V: Versions> /// Errors if signing the request for proposal fails pub fn request_proposal( &self, - view: TYPES::Time, + view: TYPES::View, + epoch: TYPES::Epoch, leaf_commitment: Commitment>, ) -> Result>>>> { // We need to be able to sign this request before submitting it to the network. Compute the @@ -185,7 +186,7 @@ impl + 'static, V: Versions> { // Make sure that the quorum_proposal is valid if let Err(err) = quorum_proposal - .validate_signature(&mem, &upgrade_lock) + .validate_signature(&mem, epoch, &upgrade_lock) .await { tracing::warn!("Invalid Proposal Received after Request. Err {:?}", err); @@ -242,7 +243,7 @@ impl + 'static, V: Versions> /// return [`None`] if the requested view has already been decided (but see /// [`decided_state`](Self::decided_state)) or if there is no path for the requested /// view to ever be decided. - pub async fn state(&self, view: TYPES::Time) -> Option> { + pub async fn state(&self, view: TYPES::View) -> Option> { self.hotshot.state(view).await } @@ -315,11 +316,15 @@ impl + 'static, V: Versions> /// Wrapper for `HotShotConsensusApi`'s `leader` function #[allow(clippy::unused_async)] // async for API compatibility reasons - pub async fn leader(&self, view_number: TYPES::Time) -> TYPES::SignatureKey { + pub async fn leader( + &self, + view_number: TYPES::View, + epoch_number: TYPES::Epoch, + ) -> TYPES::SignatureKey { self.hotshot .memberships .quorum_membership - .leader(view_number) + .leader(view_number, epoch_number) } // Below is for testing only: @@ -346,10 +351,16 @@ impl + 'static, V: Versions> /// Wrapper to get the view number this node is on. #[instrument(skip_all, target = "SystemContextHandle", fields(id = self.hotshot.id))] - pub async fn cur_view(&self) -> TYPES::Time { + pub async fn cur_view(&self) -> TYPES::View { self.hotshot.consensus.read().await.cur_view() } + /// 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 { + self.hotshot.consensus.read().await.cur_epoch() + } + /// Provides a reference to the underlying storage for this [`SystemContext`], allowing access to /// historical data #[must_use] diff --git a/crates/orchestrator/run-config.toml b/crates/orchestrator/run-config.toml index 909a6043b0..d1fecc10c9 100644 --- a/crates/orchestrator/run-config.toml +++ b/crates/orchestrator/run-config.toml @@ -38,6 +38,7 @@ timeout_ratio = [11, 10] round_start_delay = 1 start_delay = 1 num_bootstrap = 5 +epoch_height = 0 [random_builder] txn_in_block = 100 diff --git a/crates/task-impls/src/consensus/handlers.rs b/crates/task-impls/src/consensus/handlers.rs index cc0fa65a41..fec58d8409 100644 --- a/crates/task-impls/src/consensus/handlers.rs +++ b/crates/task-impls/src/consensus/handlers.rs @@ -42,7 +42,10 @@ pub(crate) async fn handle_quorum_vote_recv< ) -> Result<()> { // Are we the leader for this view? ensure!( - task_state.quorum_membership.leader(vote.view_number() + 1) == task_state.public_key, + task_state + .quorum_membership + .leader(vote.view_number() + 1, task_state.cur_epoch) + == task_state.public_key, format!( "We are not the leader for view {:?}", vote.view_number() + 1 @@ -54,6 +57,7 @@ pub(crate) async fn handle_quorum_vote_recv< vote, task_state.public_key.clone(), &task_state.quorum_membership, + task_state.cur_epoch, task_state.id, &event, sender, @@ -77,7 +81,10 @@ pub(crate) async fn handle_timeout_vote_recv< ) -> Result<()> { // Are we the leader for this view? ensure!( - task_state.timeout_membership.leader(vote.view_number() + 1) == task_state.public_key, + task_state + .timeout_membership + .leader(vote.view_number() + 1, task_state.cur_epoch) + == task_state.public_key, format!( "We are not the leader for view {:?}", vote.view_number() + 1 @@ -89,6 +96,7 @@ pub(crate) async fn handle_timeout_vote_recv< vote, task_state.public_key.clone(), &task_state.quorum_membership, + task_state.cur_epoch, task_state.id, &event, sender, @@ -106,7 +114,7 @@ pub(crate) async fn handle_view_change< I: NodeImplementation, V: Versions, >( - new_view_number: TYPES::Time, + new_view_number: TYPES::View, sender: &Sender>>, task_state: &mut ConsensusTaskState, ) -> Result<()> { @@ -147,7 +155,7 @@ pub(crate) async fn handle_view_change< async move { async_sleep(Duration::from_millis(timeout)).await; broadcast_event( - Arc::new(HotShotEvent::Timeout(TYPES::Time::new(*view_number))), + Arc::new(HotShotEvent::Timeout(TYPES::View::new(*view_number))), &stream, ) .await; @@ -167,7 +175,11 @@ pub(crate) async fn handle_view_change< .current_view .set(usize::try_from(task_state.cur_view.u64()).unwrap()); let cur_view_time = Utc::now().timestamp(); - if task_state.quorum_membership.leader(old_view_number) == task_state.public_key { + if task_state + .quorum_membership + .leader(old_view_number, task_state.cur_epoch) + == task_state.public_key + { #[allow(clippy::cast_precision_loss)] consensus .metrics @@ -203,7 +215,7 @@ pub(crate) async fn handle_view_change< /// Handle a `Timeout` event. #[instrument(skip_all)] pub(crate) async fn handle_timeout, V: Versions>( - view_number: TYPES::Time, + view_number: TYPES::View, sender: &Sender>>, task_state: &mut ConsensusTaskState, ) -> Result<()> { @@ -215,7 +227,7 @@ pub(crate) async fn handle_timeout ensure!( task_state .timeout_membership - .has_stake(&task_state.public_key), + .has_stake(&task_state.public_key, task_state.cur_epoch), format!("We were not chosen for the consensus committee for view {view_number:?}") ); @@ -260,7 +272,11 @@ pub(crate) async fn handle_timeout .metrics .number_of_timeouts .add(1); - if task_state.quorum_membership.leader(view_number) == task_state.public_key { + if task_state + .quorum_membership + .leader(view_number, task_state.cur_epoch) + == task_state.public_key + { task_state .consensus .read() diff --git a/crates/task-impls/src/consensus/mod.rs b/crates/task-impls/src/consensus/mod.rs index 82610978a6..fb1ec86fca 100644 --- a/crates/task-impls/src/consensus/mod.rs +++ b/crates/task-impls/src/consensus/mod.rs @@ -70,11 +70,14 @@ pub struct ConsensusTaskState, V: pub storage: Arc>, /// The view number that this node is currently executing in. - pub cur_view: TYPES::Time, + pub cur_view: TYPES::View, /// Timestamp this view starts at. pub cur_view_time: i64, + /// The epoch number that this node is currently executing in. + pub cur_epoch: TYPES::Epoch, + /// Output events to application pub output_event_stream: async_broadcast::Sender>, @@ -88,7 +91,7 @@ pub struct ConsensusTaskState, V: pub consensus: OuterConsensus, /// The last decided view - pub last_decided_view: TYPES::Time, + pub last_decided_view: TYPES::View, /// The node's id pub id: u64, diff --git a/crates/task-impls/src/da.rs b/crates/task-impls/src/da.rs index 0189e20d41..2e7c1357ff 100644 --- a/crates/task-impls/src/da.rs +++ b/crates/task-impls/src/da.rs @@ -49,7 +49,10 @@ pub struct DaTaskState, V: Version pub output_event_stream: async_broadcast::Sender>, /// View number this view is executing in. - pub cur_view: TYPES::Time, + pub cur_view: TYPES::View, + + /// Epoch number this node is executing in. + pub cur_epoch: TYPES::Epoch, /// Reference to consensus. Leader will require a read lock on this. pub consensus: OuterConsensus, @@ -108,7 +111,7 @@ impl, V: Versions> DaTaskState, V: Versions> DaTaskState, V: Versions> DaTaskState, V: Versions> DaTaskState, V: Versions> DaTaskState, V: Versions> DaTaskState, V: Versions> DaTaskState, V: Versions> DaTaskState TaskEvent for HotShotEvent { #[derive(Debug, Clone)] pub struct ProposalMissing { /// View of missing proposal - pub view: TYPES::Time, + pub view: TYPES::View, /// Channel to send the response back to pub response_chan: Sender>>>, } @@ -93,7 +93,7 @@ pub enum HotShotEvent { /// Send a quorum vote to the next leader; emitted by a replica in the consensus task after seeing a valid quorum proposal QuorumVoteSend(QuorumVote), /// All dependencies for the quorum vote are validated. - QuorumVoteDependenciesValidated(TYPES::Time), + QuorumVoteDependenciesValidated(TYPES::View), /// A quorum proposal with the given parent leaf is validated. /// The full validation checks include: /// 1. The proposal is not for an old view @@ -124,9 +124,9 @@ 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(DaCertificate, 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::Time), + ViewChange(TYPES::View), /// Timeout for the view sync protocol; emitted by a replica in the view sync task - ViewSyncTimeout(TYPES::Time, u64, ViewSyncPhase), + ViewSyncTimeout(TYPES::View, u64, ViewSyncPhase), /// Receive a `ViewSyncPreCommitVote` from the network; received by a relay in the view sync task ViewSyncPreCommitVoteRecv(ViewSyncPreCommitVote), @@ -157,9 +157,9 @@ pub enum HotShotEvent { ViewSyncFinalizeCertificate2Send(ViewSyncFinalizeCertificate2, TYPES::SignatureKey), /// Trigger the start of the view sync protocol; emitted by view sync task; internal trigger only - ViewSyncTrigger(TYPES::Time), + 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::Time), + Timeout(TYPES::View), /// Receive transactions from the network TransactionsRecv(Vec), /// Send transactions to the network @@ -169,14 +169,14 @@ pub enum HotShotEvent { VidCommitment, BuilderCommitment, >::Metadata, - TYPES::Time, + TYPES::View, Vec1>, Option, ), /// Event when the transactions task has sequenced transactions. Contains the encoded transactions, the metadata, and the view number BlockRecv(PackedBundle), /// Event when the transactions task has a block formed - BlockReady(VidDisperse, TYPES::Time), + BlockReady(VidDisperse, TYPES::View), /// Event when consensus decided on a leaf LeafDecided(Vec>), /// Send VID shares to VID storage nodes; emitted by the DA leader @@ -205,13 +205,13 @@ pub enum HotShotEvent { /* Consensus State Update Events */ /// A undecided view has been created and added to the validated state storage. - ValidatedStateUpdated(TYPES::Time, View), + ValidatedStateUpdated(TYPES::View, View), /// A new locked view has been created (2-chain) - LockedViewUpdated(TYPES::Time), + LockedViewUpdated(TYPES::View), /// A new anchor view has been successfully reached by this node (3-chain). - LastDecidedViewUpdated(TYPES::Time), + LastDecidedViewUpdated(TYPES::View), /// A new high_qc has been reached by this node. UpdateHighQc(QuorumCertificate), @@ -260,7 +260,7 @@ pub enum HotShotEvent { impl HotShotEvent { #[allow(clippy::too_many_lines)] /// Return the view number for a hotshot event if present - pub fn view_number(&self) -> Option { + pub fn view_number(&self) -> Option { match self { HotShotEvent::QuorumVoteRecv(v) => Some(v.view_number()), HotShotEvent::TimeoutVoteRecv(v) | HotShotEvent::TimeoutVoteSend(v) => { @@ -512,7 +512,7 @@ impl Display for HotShotEvent { write!(f, "BlockReady(view_number={view_number:?})") } HotShotEvent::LeafDecided(leaves) => { - let view_numbers: Vec<::Time> = + let view_numbers: Vec<::View> = leaves.iter().map(Leaf::view_number).collect(); write!(f, "LeafDecided({view_numbers:?})") } diff --git a/crates/task-impls/src/helpers.rs b/crates/task-impls/src/helpers.rs index 5df8200f16..cd4bf5b3cd 100644 --- a/crates/task-impls/src/helpers.rs +++ b/crates/task-impls/src/helpers.rs @@ -50,7 +50,7 @@ use crate::{ #[instrument(skip_all)] #[allow(clippy::too_many_arguments)] pub(crate) async fn fetch_proposal( - view_number: TYPES::Time, + view_number: TYPES::View, event_sender: Sender>>, event_receiver: Receiver>>, quorum_membership: Arc, @@ -80,6 +80,7 @@ pub(crate) async fn fetch_proposal( .await; let mem = Arc::clone(&quorum_membership); + let current_epoch = consensus.read().await.cur_epoch(); // Make a background task to await the arrival of the event data. let Ok(Some(proposal)) = // We want to explicitly timeout here so we aren't waiting around for the data. @@ -111,7 +112,7 @@ pub(crate) async fn fetch_proposal( hs_event.as_ref() { // Make sure that the quorum_proposal is valid - if quorum_proposal.validate_signature(&mem, upgrade_lock).await.is_ok() { + if quorum_proposal.validate_signature(&mem, current_epoch, upgrade_lock).await.is_ok() { proposal = Some(quorum_proposal.clone()); } @@ -130,7 +131,7 @@ pub(crate) async fn fetch_proposal( let justify_qc = proposal.data.justify_qc.clone(); if !justify_qc - .is_valid_cert(quorum_membership.as_ref(), upgrade_lock) + .is_valid_cert(quorum_membership.as_ref(), current_epoch, upgrade_lock) .await { bail!("Invalid justify_qc in proposal for view {}", *view_number); @@ -167,10 +168,10 @@ pub(crate) async fn fetch_proposal( #[derive(Debug)] pub struct LeafChainTraversalOutcome { /// The new locked view obtained from a 2 chain starting from the proposal's parent. - pub new_locked_view_number: Option, + pub new_locked_view_number: Option, /// The new decided view obtained from a 3 chain starting from the proposal's parent. - pub new_decided_view_number: Option, + pub new_decided_view_number: Option, /// The qc for the decided chain. pub new_decide_qc: Option>, @@ -355,7 +356,7 @@ pub async fn decide_from_proposal( #[instrument(skip_all)] #[allow(clippy::too_many_arguments)] pub(crate) async fn parent_leaf_and_state( - next_proposal_view_number: TYPES::Time, + next_proposal_view_number: TYPES::View, event_sender: &Sender>>, event_receiver: &Receiver>>, quorum_membership: Arc, @@ -364,8 +365,9 @@ pub(crate) async fn parent_leaf_and_state( consensus: OuterConsensus, upgrade_lock: &UpgradeLock, ) -> Result<(Leaf, Arc<::ValidatedState>)> { + let current_epoch = consensus.read().await.cur_epoch(); ensure!( - quorum_membership.leader(next_proposal_view_number) == public_key, + quorum_membership.leader(next_proposal_view_number, current_epoch) == public_key, "Somehow we formed a QC but are not the leader for the next view {next_proposal_view_number:?}", ); let parent_view_number = consensus.read().await.high_qc().view_number(); @@ -485,18 +487,20 @@ pub async fn validate_proposal_safety_and_liveness< if let Err(e) = consensus_write.update_proposed_view(proposal.clone()) { tracing::debug!("Internal proposal update failed; error = {e:#}"); }; - - // Broadcast that we've updated our consensus state so that other tasks know it's safe to grab. - broadcast_event( - Arc::new(HotShotEvent::ValidatedStateUpdated(view_number, view)), - &event_stream, - ) - .await; } + // Broadcast that we've updated our consensus state so that other tasks know it's safe to grab. + broadcast_event( + Arc::new(HotShotEvent::ValidatedStateUpdated(view_number, view)), + &event_stream, + ) + .await; + + let current_epoch = task_state.cur_epoch; UpgradeCertificate::validate( &proposal.data.upgrade_certificate, &task_state.quorum_membership, + current_epoch, &task_state.upgrade_lock, ) .await?; @@ -606,7 +610,11 @@ pub async fn validate_proposal_view_and_certs< // Validate the proposal's signature. This should also catch if the leaf_commitment does not equal our calculated parent commitment proposal - .validate_signature(&task_state.quorum_membership, &task_state.upgrade_lock) + .validate_signature( + &task_state.quorum_membership, + task_state.cur_epoch, + &task_state.upgrade_lock, + ) .await?; // Verify a timeout certificate OR a view sync certificate exists and is valid. @@ -628,6 +636,7 @@ pub async fn validate_proposal_view_and_certs< timeout_cert .is_valid_cert( task_state.timeout_membership.as_ref(), + task_state.cur_epoch, &task_state.upgrade_lock ) .await, @@ -648,6 +657,7 @@ pub async fn validate_proposal_view_and_certs< view_sync_cert .is_valid_cert( task_state.quorum_membership.as_ref(), + task_state.cur_epoch, &task_state.upgrade_lock ) .await, @@ -662,6 +672,7 @@ pub async fn validate_proposal_view_and_certs< UpgradeCertificate::validate( &proposal.data.upgrade_certificate, &task_state.quorum_membership, + task_state.cur_epoch, &task_state.upgrade_lock, ) .await?; @@ -675,7 +686,7 @@ pub async fn validate_proposal_view_and_certs< /// # Errors /// Returns an [`anyhow::Error`] when the new view is not greater than the current view. pub(crate) async fn update_view, V: Versions>( - new_view: TYPES::Time, + new_view: TYPES::View, event_stream: &Sender>>, task_state: &mut QuorumProposalRecvTaskState, ) -> Result<()> { @@ -684,8 +695,10 @@ pub(crate) async fn update_view, V "New view is not greater than our current view" ); - let is_old_view_leader = - task_state.quorum_membership.leader(task_state.cur_view) == task_state.public_key; + let is_old_view_leader = task_state + .quorum_membership + .leader(task_state.cur_view, task_state.cur_epoch) + == task_state.public_key; let old_view = task_state.cur_view; debug!("Updating view from {} to {}", *old_view, *new_view); @@ -722,7 +735,7 @@ pub(crate) async fn update_view, V async move { async_sleep(timeout).await; broadcast_event( - Arc::new(HotShotEvent::Timeout(TYPES::Time::new(*view_number))), + Arc::new(HotShotEvent::Timeout(TYPES::View::new(*view_number))), &stream, ) .await; diff --git a/crates/task-impls/src/network.rs b/crates/task-impls/src/network.rs index ca5f6c7aeb..5b897c7b4f 100644 --- a/crates/task-impls/src/network.rs +++ b/crates/task-impls/src/network.rs @@ -169,7 +169,7 @@ impl NetworkMessageTaskState { // Send the external message to the external event stream so it can be processed broadcast_event( Event { - view_number: TYPES::Time::new(1), + view_number: TYPES::View::new(1), event: EventType::ExternalMessageReceived { sender, data }, }, &self.external_event_stream, @@ -190,7 +190,9 @@ pub struct NetworkEventTaskState< /// comm network pub network: Arc, /// view number - pub view: TYPES::Time, + pub view: TYPES::View, + /// epoch number + pub epoch: TYPES::Epoch, /// quorum for the network pub quorum_membership: TYPES::Membership, /// da for the network @@ -305,7 +307,7 @@ impl< maybe_action: Option, storage: Arc>, state: Arc>>, - view: ::Time, + view: ::View, ) -> Result<(), ()> { if let Some(action) = maybe_action { if !state.write().await.update_action(action, view) { @@ -358,7 +360,10 @@ impl< MessageKind::::from_consensus_message(SequencingMessage::General( GeneralConsensusMessage::Vote(vote.clone()), )), - TransmitType::Direct(self.quorum_membership.leader(vote.view_number() + 1)), + TransmitType::Direct( + self.quorum_membership + .leader(vote.view_number() + 1, self.epoch), + ), )) } HotShotEvent::QuorumProposalRequestSend(req, signature) => Some(( @@ -396,7 +401,10 @@ impl< MessageKind::::from_consensus_message(SequencingMessage::Da( DaConsensusMessage::DaVote(vote.clone()), )), - TransmitType::Direct(self.quorum_membership.leader(vote.view_number())), + TransmitType::Direct( + self.quorum_membership + .leader(vote.view_number(), self.epoch), + ), )) } HotShotEvent::DacSend(certificate, sender) => { @@ -416,7 +424,7 @@ impl< )), TransmitType::Direct( self.quorum_membership - .leader(vote.view_number() + vote.date().relay), + .leader(vote.view_number() + vote.date().relay, self.epoch), ), )), HotShotEvent::ViewSyncCommitVoteSend(vote) => Some(( @@ -426,7 +434,7 @@ impl< )), TransmitType::Direct( self.quorum_membership - .leader(vote.view_number() + vote.date().relay), + .leader(vote.view_number() + vote.date().relay, self.epoch), ), )), HotShotEvent::ViewSyncFinalizeVoteSend(vote) => Some(( @@ -436,7 +444,7 @@ impl< )), TransmitType::Direct( self.quorum_membership - .leader(vote.view_number() + vote.date().relay), + .leader(vote.view_number() + vote.date().relay, self.epoch), ), )), HotShotEvent::ViewSyncPreCommitCertificate2Send(certificate, sender) => Some(( @@ -467,7 +475,10 @@ impl< MessageKind::::from_consensus_message(SequencingMessage::General( GeneralConsensusMessage::TimeoutVote(vote.clone()), )), - TransmitType::Direct(self.quorum_membership.leader(vote.view_number() + 1)), + TransmitType::Direct( + self.quorum_membership + .leader(vote.view_number() + 1, self.epoch), + ), )) } HotShotEvent::UpgradeProposalSend(proposal, sender) => Some(( @@ -484,13 +495,20 @@ impl< MessageKind::::from_consensus_message(SequencingMessage::General( GeneralConsensusMessage::UpgradeVote(vote.clone()), )), - TransmitType::Direct(self.quorum_membership.leader(vote.view_number())), + TransmitType::Direct( + self.quorum_membership + .leader(vote.view_number(), self.epoch), + ), )) } HotShotEvent::ViewChange(view) => { self.view = view; self.network - .update_view::(self.view.u64(), &self.quorum_membership) + .update_view::( + self.view.u64(), + self.epoch.u64(), + &self.quorum_membership, + ) .await; None } @@ -534,7 +552,7 @@ impl< }; let view = message.kind.view_number(); let committee_topic = self.quorum_membership.committee_topic(); - let da_committee = self.da_membership.committee_members(view); + let da_committee = self.da_membership.committee_members(view, self.epoch); let net = Arc::clone(&self.network); let storage = Arc::clone(&self.storage); let state = Arc::clone(&self.consensus); diff --git a/crates/task-impls/src/quorum_proposal/handlers.rs b/crates/task-impls/src/quorum_proposal/handlers.rs index e53a618320..bcdc568e18 100644 --- a/crates/task-impls/src/quorum_proposal/handlers.rs +++ b/crates/task-impls/src/quorum_proposal/handlers.rs @@ -60,10 +60,10 @@ pub(crate) enum ProposalDependency { /// Handler for the proposal dependency pub struct ProposalDependencyHandle { /// Latest view number that has been proposed for (proxy for cur_view). - pub latest_proposed_view: TYPES::Time, + pub latest_proposed_view: TYPES::View, /// The view number to propose for. - pub view_number: TYPES::Time, + pub view_number: TYPES::View, /// The event sender. pub sender: Sender>>, diff --git a/crates/task-impls/src/quorum_proposal/mod.rs b/crates/task-impls/src/quorum_proposal/mod.rs index 2c12b83b28..7390427f09 100644 --- a/crates/task-impls/src/quorum_proposal/mod.rs +++ b/crates/task-impls/src/quorum_proposal/mod.rs @@ -46,10 +46,10 @@ mod handlers; /// The state for the quorum proposal task. pub struct QuorumProposalTaskState, V: Versions> { /// Latest view number that has been proposed for. - pub latest_proposed_view: TYPES::Time, + pub latest_proposed_view: TYPES::View, /// Table for the in-progress proposal dependency tasks. - pub proposal_dependencies: HashMap>, + pub proposal_dependencies: HashMap>, /// The underlying network pub network: Arc, @@ -107,7 +107,7 @@ impl, V: Versions> fn create_event_dependency( &self, dependency_type: ProposalDependency, - view_number: TYPES::Time, + view_number: TYPES::View, event_receiver: Receiver>>, ) -> EventDependency>> { EventDependency::new( @@ -181,7 +181,7 @@ impl, V: Versions> /// Creates the requisite dependencies for the Quorum Proposal task. It also handles any event forwarding. fn create_and_complete_dependencies( &self, - view_number: TYPES::Time, + view_number: TYPES::View, event_receiver: &Receiver>>, event: Arc>, ) -> AndDependency>>>> { @@ -283,13 +283,14 @@ impl, V: Versions> #[instrument(skip_all, fields(id = self.id, latest_proposed_view = *self.latest_proposed_view), name = "Create dependency task", level = "error")] fn create_dependency_task_if_new( &mut self, - view_number: TYPES::Time, + view_number: TYPES::View, + epoch_number: TYPES::Epoch, event_receiver: Receiver>>, event_sender: Sender>>, event: Arc>, ) { // Don't even bother making the task if we are not entitled to propose anyway. - if self.quorum_membership.leader(view_number) != self.public_key { + if self.quorum_membership.leader(view_number, epoch_number) != self.public_key { tracing::trace!("We are not the leader of the next view"); return; } @@ -333,7 +334,7 @@ impl, V: Versions> /// Update the latest proposed view number. #[instrument(skip_all, fields(id = self.id, latest_proposed_view = *self.latest_proposed_view), name = "Update latest proposed view", level = "error")] - async fn update_latest_proposed_view(&mut self, new_view: TYPES::Time) -> bool { + async fn update_latest_proposed_view(&mut self, new_view: TYPES::View) -> bool { if *self.latest_proposed_view < *new_view { debug!( "Updating latest proposed view from {} to {}", @@ -342,7 +343,7 @@ impl, V: Versions> // Cancel the old dependency tasks. for view in (*self.latest_proposed_view + 1)..=(*new_view) { - if let Some(dependency) = self.proposal_dependencies.remove(&TYPES::Time::new(view)) + if let Some(dependency) = self.proposal_dependencies.remove(&TYPES::View::new(view)) { cancel_task(dependency).await; } @@ -380,9 +381,11 @@ impl, V: Versions> HotShotEvent::QcFormed(cert) => match cert.clone() { either::Right(timeout_cert) => { let view_number = timeout_cert.view_number + 1; + let epoch_number = self.consensus.read().await.cur_epoch(); self.create_dependency_task_if_new( view_number, + epoch_number, event_receiver, event_sender, Arc::clone(&event), @@ -411,17 +414,24 @@ impl, V: Versions> _auction_result, ) => { let view_number = *view_number; + let epoch_number = self.consensus.read().await.cur_epoch(); self.create_dependency_task_if_new( view_number, + epoch_number, event_receiver, event_sender, Arc::clone(&event), ); } HotShotEvent::ViewSyncFinalizeCertificate2Recv(certificate) => { + let epoch_number = self.consensus.read().await.cur_epoch(); if !certificate - .is_valid_cert(self.quorum_membership.as_ref(), &self.upgrade_lock) + .is_valid_cert( + self.quorum_membership.as_ref(), + epoch_number, + &self.upgrade_lock, + ) .await { warn!( @@ -435,6 +445,7 @@ impl, V: Versions> self.create_dependency_task_if_new( view_number, + epoch_number, event_receiver, event_sender, event, @@ -447,9 +458,11 @@ impl, V: Versions> if !self.update_latest_proposed_view(view_number).await { tracing::trace!("Failed to update latest proposed view"); } + let epoch_number = self.consensus.read().await.cur_epoch(); self.create_dependency_task_if_new( view_number + 1, + epoch_number, event_receiver, event_sender, Arc::clone(&event), @@ -464,9 +477,11 @@ impl, V: Versions> } HotShotEvent::VidDisperseSend(vid_share, _) => { let view_number = vid_share.data.view_number(); + let epoch_number = self.consensus.read().await.cur_epoch(); self.create_dependency_task_if_new( view_number, + epoch_number, event_receiver, event_sender, Arc::clone(&event), @@ -490,8 +505,10 @@ impl, V: Versions> } HotShotEvent::HighQcUpdated(qc) => { let view_number = qc.view_number() + 1; + let epoch_number = self.consensus.read().await.cur_epoch(); self.create_dependency_task_if_new( view_number, + epoch_number, event_receiver, event_sender, Arc::clone(&event), diff --git a/crates/task-impls/src/quorum_proposal_recv/handlers.rs b/crates/task-impls/src/quorum_proposal_recv/handlers.rs index c268c14b0e..2193bf3dfd 100644 --- a/crates/task-impls/src/quorum_proposal_recv/handlers.rs +++ b/crates/task-impls/src/quorum_proposal_recv/handlers.rs @@ -136,6 +136,7 @@ pub(crate) async fn handle_quorum_proposal_recv< if !justify_qc .is_valid_cert( task_state.quorum_membership.as_ref(), + task_state.cur_epoch, &task_state.upgrade_lock, ) .await diff --git a/crates/task-impls/src/quorum_proposal_recv/mod.rs b/crates/task-impls/src/quorum_proposal_recv/mod.rs index f51a8d5e00..281e472fd0 100644 --- a/crates/task-impls/src/quorum_proposal_recv/mod.rs +++ b/crates/task-impls/src/quorum_proposal_recv/mod.rs @@ -55,11 +55,14 @@ pub struct QuorumProposalRecvTaskState, /// View number this view is executing in. - pub cur_view: TYPES::Time, + pub cur_view: TYPES::View, /// Timestamp this view starts at. pub cur_view_time: i64, + /// Epoch number this node is executing in. + pub cur_epoch: TYPES::Epoch, + /// The underlying network pub network: Arc, @@ -89,7 +92,7 @@ pub struct QuorumProposalRecvTaskState>>, + pub spawned_tasks: BTreeMap>>, /// Immutable instance state pub instance_state: Arc, @@ -105,7 +108,7 @@ impl, V: Versions> QuorumProposalRecvTaskState { /// Cancel all tasks the consensus tasks has spawned before the given view - pub async fn cancel_tasks(&mut self, view: TYPES::Time) { + pub async fn cancel_tasks(&mut self, view: TYPES::View) { let keep = self.spawned_tasks.split_off(&view); let mut cancel = Vec::new(); while let Some((_, tasks)) = self.spawned_tasks.pop_first() { diff --git a/crates/task-impls/src/quorum_vote/mod.rs b/crates/task-impls/src/quorum_vote/mod.rs index ed0d61d4e9..909fcf2db2 100644 --- a/crates/task-impls/src/quorum_vote/mod.rs +++ b/crates/task-impls/src/quorum_vote/mod.rs @@ -75,7 +75,9 @@ pub struct VoteDependencyHandle, V /// Reference to the storage. pub storage: Arc>, /// View number to vote on. - pub view_number: TYPES::Time, + pub view_number: TYPES::View, + /// Epoch number to vote on. + pub epoch_number: TYPES::Epoch, /// Event sender. pub sender: Sender>>, /// Event receiver. @@ -200,7 +202,8 @@ impl + 'static, V: Versions> vid_share: Proposal>, ) -> Result<()> { ensure!( - self.quorum_membership.has_stake(&self.public_key), + self.quorum_membership + .has_stake(&self.public_key, self.epoch_number), format!( "We were not chosen for quorum committee on {:?}", self.view_number @@ -373,10 +376,10 @@ pub struct QuorumVoteTaskState, V: pub instance_state: Arc, /// Latest view number that has been voted for. - pub latest_voted_view: TYPES::Time, + pub latest_voted_view: TYPES::View, /// Table for the in-progress dependency tasks. - pub vote_dependencies: HashMap>, + pub vote_dependencies: HashMap>, /// The underlying network pub network: Arc, @@ -406,7 +409,7 @@ impl, V: Versions> QuorumVoteTaskS fn create_event_dependency( &self, dependency_type: VoteDependency, - view_number: TYPES::Time, + view_number: TYPES::View, event_receiver: Receiver>>, ) -> EventDependency>> { EventDependency::new( @@ -450,7 +453,8 @@ impl, V: Versions> QuorumVoteTaskS #[instrument(skip_all, fields(id = self.id, latest_voted_view = *self.latest_voted_view), name = "Quorum vote crete dependency task if new", level = "error")] fn create_dependency_task_if_new( &mut self, - view_number: TYPES::Time, + view_number: TYPES::View, + epoch_number: TYPES::Epoch, event_receiver: Receiver>>, event_sender: &Sender>>, event: Option>>, @@ -493,6 +497,7 @@ impl, V: Versions> QuorumVoteTaskS quorum_membership: Arc::clone(&self.quorum_membership), storage: Arc::clone(&self.storage), view_number, + epoch_number, sender: event_sender.clone(), receiver: event_receiver.clone(), upgrade_lock: self.upgrade_lock.clone(), @@ -505,7 +510,7 @@ impl, V: Versions> QuorumVoteTaskS /// Update the latest voted view number. #[instrument(skip_all, fields(id = self.id, latest_voted_view = *self.latest_voted_view), name = "Quorum vote update latest voted view", level = "error")] - async fn update_latest_voted_view(&mut self, new_view: TYPES::Time) -> bool { + async fn update_latest_voted_view(&mut self, new_view: TYPES::View) -> bool { if *self.latest_voted_view < *new_view { debug!( "Updating next vote view from {} to {} in the quorum vote task", @@ -514,7 +519,7 @@ impl, V: Versions> QuorumVoteTaskS // Cancel the old dependency tasks. for view in *self.latest_voted_view..(*new_view) { - if let Some(dependency) = self.vote_dependencies.remove(&TYPES::Time::new(view)) { + if let Some(dependency) = self.vote_dependencies.remove(&TYPES::View::new(view)) { cancel_task(dependency).await; debug!("Vote dependency removed for view {:?}", view); } @@ -535,6 +540,7 @@ impl, V: Versions> QuorumVoteTaskS event_receiver: Receiver>>, event_sender: Sender>>, ) { + let current_epoch = self.consensus.read().await.cur_epoch(); match event.as_ref() { HotShotEvent::QuorumProposalValidated(proposal, _leaf) => { trace!("Received Proposal for view {}", *proposal.view_number()); @@ -548,6 +554,7 @@ impl, V: Versions> QuorumVoteTaskS self.create_dependency_task_if_new( proposal.view_number, + current_epoch, event_receiver, &event_sender, Some(Arc::clone(&event)), @@ -560,9 +567,14 @@ impl, V: Versions> QuorumVoteTaskS return; } + let current_epoch = self.consensus.read().await.cur_epoch(); // Validate the DAC. if !cert - .is_valid_cert(self.da_membership.as_ref(), &self.upgrade_lock) + .is_valid_cert( + self.da_membership.as_ref(), + current_epoch, + &self.upgrade_lock, + ) .await { return; @@ -579,7 +591,13 @@ impl, V: Versions> QuorumVoteTaskS &event_sender.clone(), ) .await; - self.create_dependency_task_if_new(view, event_receiver, &event_sender, None); + self.create_dependency_task_if_new( + view, + current_epoch, + event_receiver, + &event_sender, + None, + ); } HotShotEvent::VidShareRecv(sender, disperse) => { let view = disperse.data.view_number(); @@ -590,10 +608,14 @@ impl, V: Versions> QuorumVoteTaskS // Validate the VID share. let payload_commitment = disperse.data.payload_commitment; + let current_epoch = self.consensus.read().await.cur_epoch(); // Check sender of VID disperse share is signed by DA committee member let validate_sender = sender .validate(&disperse.signature, payload_commitment.as_ref()) - && self.da_membership.committee_members(view).contains(sender); + && self + .da_membership + .committee_members(view, current_epoch) + .contains(sender); // Check whether the data satisfies one of the following. // * From the right leader for this view. @@ -603,7 +625,7 @@ impl, V: Versions> QuorumVoteTaskS .validate(&disperse.signature, payload_commitment.as_ref()) || self .quorum_membership - .leader(view) + .leader(view, current_epoch) .validate(&disperse.signature, payload_commitment.as_ref()); if !validate_sender && !validated { warn!("Failed to validated the VID dispersal/share sig."); @@ -613,7 +635,7 @@ impl, V: Versions> QuorumVoteTaskS // NOTE: `verify_share` returns a nested `Result`, so we must check both the inner // and outer results #[allow(clippy::no_effect)] - match vid_scheme(self.quorum_membership.total_nodes()).verify_share( + match vid_scheme(self.quorum_membership.total_nodes(current_epoch)).verify_share( &disperse.data.share, &disperse.data.common, &payload_commitment, @@ -641,7 +663,13 @@ impl, V: Versions> QuorumVoteTaskS &event_sender.clone(), ) .await; - self.create_dependency_task_if_new(view, event_receiver, &event_sender, None); + self.create_dependency_task_if_new( + view, + current_epoch, + event_receiver, + &event_sender, + None, + ); } HotShotEvent::QuorumVoteDependenciesValidated(view_number) => { debug!("All vote dependencies verified for view {:?}", view_number); diff --git a/crates/task-impls/src/request.rs b/crates/task-impls/src/request.rs index 4ea5966fec..8cd336e7b1 100644 --- a/crates/task-impls/src/request.rs +++ b/crates/task-impls/src/request.rs @@ -56,7 +56,7 @@ pub struct NetworkRequestState> { /// before sending a request pub state: OuterConsensus, /// Last seen view, we won't request for proposals before older than this view - pub view: TYPES::Time, + pub view: TYPES::View, /// Delay before requesting peers pub delay: Duration, /// DA Membership @@ -70,7 +70,7 @@ pub struct NetworkRequestState> { /// A flag indicating that `HotShotEvent::Shutdown` has been received pub shutdown_flag: Arc, /// A flag indicating that `HotShotEvent::Shutdown` has been received - pub spawned_tasks: BTreeMap>>, + pub spawned_tasks: BTreeMap>>, } impl> Drop for NetworkRequestState { @@ -97,6 +97,7 @@ impl> TaskState for NetworkRequest match event.as_ref() { HotShotEvent::QuorumProposalValidated(proposal, _) => { let prop_view = proposal.view_number(); + let current_epoch = self.state.read().await.cur_epoch(); // If we already have the VID shares for the next view, do nothing. if prop_view >= self.view @@ -107,7 +108,7 @@ impl> TaskState for NetworkRequest .vid_shares() .contains_key(&prop_view) { - self.spawn_requests(prop_view, sender, receiver); + self.spawn_requests(prop_view, current_epoch, sender, receiver); } Ok(()) } @@ -144,7 +145,8 @@ impl> NetworkRequestState>>, receiver: &Receiver>>, ) { @@ -158,6 +160,7 @@ impl> NetworkRequestState> NetworkRequestState, sender: Sender>>, receiver: Receiver>>, - view: TYPES::Time, + view: TYPES::View, + epoch: TYPES::Epoch, ) { let state = OuterConsensus::new(Arc::clone(&self.state.inner_consensus)); let network = Arc::clone(&self.network); let shutdown_flag = Arc::clone(&self.shutdown_flag); let delay = self.delay; - let da_committee_for_view = self.da_membership.committee_members(view); + let da_committee_for_view = self.da_membership.committee_members(view, epoch); let public_key = self.public_key.clone(); // Get committee members for view let mut recipients: Vec = self .da_membership - .committee_members(view) + .committee_members(view, epoch) .into_iter() .collect(); // Randomize the recipients so all replicas don't overload the same 1 recipients @@ -256,7 +260,7 @@ impl> NetworkRequestState::SignatureKey>, public_key: &::SignatureKey, - view: TYPES::Time, + view: TYPES::View, ) -> bool { // First send request to a random DA member for the view broadcast_event( @@ -299,7 +303,7 @@ impl> NetworkRequestState>>, da_members_for_view: BTreeSet<::SignatureKey>, - view: TYPES::Time, + view: TYPES::View, ) -> Option>> { EventDependency::new( receiver.clone(), @@ -326,7 +330,7 @@ impl> NetworkRequestState, sender: &Sender>>, public_key: &::SignatureKey, - view: &TYPES::Time, + view: &TYPES::View, shutdown_flag: &Arc, ) -> bool { let state = state.read().await; diff --git a/crates/task-impls/src/response.rs b/crates/task-impls/src/response.rs index e8e8483b08..475a843896 100644 --- a/crates/task-impls/src/response.rs +++ b/crates/task-impls/src/response.rs @@ -76,7 +76,7 @@ impl NetworkResponseState { match event.as_ref() { HotShotEvent::VidRequestRecv(request, sender) => { // Verify request is valid - if !self.valid_sender(sender) + if !self.valid_sender(sender, self.consensus.read().await.cur_epoch()) || !valid_signature::(request, sender) { continue; @@ -140,7 +140,7 @@ impl NetworkResponseState { #[instrument(skip_all, target = "NetworkResponseState", fields(id = self.id))] async fn get_or_calc_vid_share( &self, - view: TYPES::Time, + view: TYPES::View, key: &TYPES::SignatureKey, ) -> Option>> { let contained = self @@ -151,11 +151,13 @@ impl NetworkResponseState { .get(&view) .is_some_and(|m| m.contains_key(key)); if !contained { + let current_epoch = self.consensus.read().await.cur_epoch(); if Consensus::calculate_and_update_vid( OuterConsensus::new(Arc::clone(&self.consensus)), view, Arc::clone(&self.quorum), &self.private_key, + current_epoch, ) .await .is_none() @@ -167,6 +169,7 @@ impl NetworkResponseState { view, Arc::clone(&self.quorum), &self.private_key, + current_epoch, ) .await?; } @@ -188,9 +191,9 @@ impl NetworkResponseState { .cloned() } - /// Makes sure the sender is allowed to send a request. - fn valid_sender(&self, sender: &TYPES::SignatureKey) -> bool { - self.quorum.has_stake(sender) + /// Makes sure the sender is allowed to send a request in the given epoch. + fn valid_sender(&self, sender: &TYPES::SignatureKey, epoch: TYPES::Epoch) -> bool { + self.quorum.has_stake(sender, epoch) } } diff --git a/crates/task-impls/src/transactions.rs b/crates/task-impls/src/transactions.rs index bd1aa73307..354d9d1fd6 100644 --- a/crates/task-impls/src/transactions.rs +++ b/crates/task-impls/src/transactions.rs @@ -82,7 +82,10 @@ pub struct TransactionTaskState, V pub output_event_stream: async_broadcast::Sender>, /// View number this view is executing in. - pub cur_view: TYPES::Time, + pub cur_view: TYPES::View, + + /// Epoch number this node is executing in. + pub cur_epoch: TYPES::Epoch, /// Reference to consensus. Leader will require a read lock on this. pub consensus: OuterConsensus, @@ -117,7 +120,7 @@ impl, V: Versions> TransactionTask pub async fn handle_view_change( &mut self, event_stream: &Sender>>, - block_view: TYPES::Time, + block_view: TYPES::View, ) -> Option { let version = match self.upgrade_lock.version(block_view).await { Ok(v) => v, @@ -141,7 +144,7 @@ impl, V: Versions> TransactionTask pub async fn handle_view_change_legacy( &mut self, event_stream: &Sender>>, - block_view: TYPES::Time, + block_view: TYPES::View, ) -> Option { let version = match self.upgrade_lock.version(block_view).await { Ok(v) => v, @@ -201,10 +204,11 @@ impl, V: Versions> TransactionTask .number_of_empty_blocks_proposed .add(1); - let membership_total_nodes = self.membership.total_nodes(); - let Some(null_fee) = - null_block::builder_fee::(self.membership.total_nodes(), version) - else { + let membership_total_nodes = self.membership.total_nodes(self.cur_epoch); + let Some(null_fee) = null_block::builder_fee::( + self.membership.total_nodes(self.cur_epoch), + version, + ) else { error!("Failed to get null fee"); return None; }; @@ -239,7 +243,7 @@ impl, V: Versions> TransactionTask /// Returns an error if the solver cannot be contacted, or if none of the builders respond. async fn produce_block_marketplace( &mut self, - block_view: TYPES::Time, + block_view: TYPES::View, task_start_time: Instant, ) -> Result> { ensure!( @@ -336,13 +340,14 @@ impl, V: Versions> TransactionTask /// Produce a null block pub fn null_block( &self, - block_view: TYPES::Time, + block_view: TYPES::View, version: Version, ) -> Option> { - let membership_total_nodes = self.membership.total_nodes(); - let Some(null_fee) = - null_block::builder_fee::(self.membership.total_nodes(), version) - else { + let membership_total_nodes = self.membership.total_nodes(self.cur_epoch); + let Some(null_fee) = null_block::builder_fee::( + self.membership.total_nodes(self.cur_epoch), + version, + ) else { error!("Failed to calculate null block fee."); return None; }; @@ -367,7 +372,7 @@ impl, V: Versions> TransactionTask pub async fn handle_view_change_marketplace( &mut self, event_stream: &Sender>>, - block_view: TYPES::Time, + block_view: TYPES::View, ) -> Option { let task_start_time = Instant::now(); @@ -446,12 +451,13 @@ impl, V: Versions> TransactionTask let mut make_block = false; if *view - *self.cur_view > 1 { info!("View changed by more than 1 going to view {:?}", view); - make_block = self.membership.leader(view) == self.public_key; + make_block = self.membership.leader(view, self.cur_epoch) == self.public_key; } self.cur_view = view; let next_view = self.cur_view + 1; - let next_leader = self.membership.leader(next_view) == self.public_key; + let next_leader = + self.membership.leader(next_view, self.cur_epoch) == self.public_key; if !make_block && !next_leader { debug!("Not next leader for view {:?}", self.cur_view); return None; @@ -478,9 +484,9 @@ impl, V: Versions> TransactionTask #[instrument(skip_all, target = "TransactionTaskState", fields(id = self.id, cur_view = *self.cur_view, block_view = *block_view))] async fn last_vid_commitment_retry( &self, - block_view: TYPES::Time, + block_view: TYPES::View, task_start_time: Instant, - ) -> Result<(TYPES::Time, VidCommitment)> { + ) -> Result<(TYPES::View, VidCommitment)> { loop { match self.last_vid_commitment(block_view).await { Ok((view, comm)) => break Ok((view, comm)), @@ -499,10 +505,10 @@ impl, V: Versions> TransactionTask #[instrument(skip_all, target = "TransactionTaskState", fields(id = self.id, cur_view = *self.cur_view, block_view = *block_view))] async fn last_vid_commitment( &self, - block_view: TYPES::Time, - ) -> Result<(TYPES::Time, VidCommitment)> { + block_view: TYPES::View, + ) -> Result<(TYPES::View, VidCommitment)> { let consensus = self.consensus.read().await; - let mut target_view = TYPES::Time::new(block_view.saturating_sub(1)); + let mut target_view = TYPES::View::new(block_view.saturating_sub(1)); loop { let view_data = consensus @@ -525,7 +531,7 @@ impl, V: Versions> TransactionTask ViewInner::Failed => { // For failed views, backtrack target_view = - TYPES::Time::new(target_view.checked_sub(1).context("Reached genesis")?); + TYPES::View::new(target_view.checked_sub(1).context("Reached genesis")?); continue; } } @@ -533,7 +539,7 @@ impl, V: Versions> TransactionTask } #[instrument(skip_all, fields(id = self.id, cur_view = *self.cur_view, block_view = *block_view), name = "wait_for_block", level = "error")] - async fn wait_for_block(&self, block_view: TYPES::Time) -> Option> { + async fn wait_for_block(&self, block_view: TYPES::View) -> Option> { let task_start_time = Instant::now(); // Find commitment to the block we want to build upon @@ -597,7 +603,7 @@ impl, V: Versions> TransactionTask async fn get_available_blocks( &self, parent_comm: VidCommitment, - view_number: TYPES::Time, + view_number: TYPES::View, parent_comm_sig: &<::SignatureKey as SignatureKey>::PureAssembledSignatureType, ) -> Vec<(AvailableBlockInfo, usize)> { let tasks = self @@ -666,7 +672,7 @@ impl, V: Versions> TransactionTask async fn block_from_builder( &self, parent_comm: VidCommitment, - view_number: TYPES::Time, + view_number: TYPES::View, parent_comm_sig: &<::SignatureKey as SignatureKey>::PureAssembledSignatureType, ) -> anyhow::Result> { let mut available_blocks = self diff --git a/crates/task-impls/src/upgrade.rs b/crates/task-impls/src/upgrade.rs index d050ef4e54..4992b06887 100644 --- a/crates/task-impls/src/upgrade.rs +++ b/crates/task-impls/src/upgrade.rs @@ -43,7 +43,10 @@ pub struct UpgradeTaskState, V: Ve pub output_event_stream: async_broadcast::Sender>, /// View number this view is executing in. - pub cur_view: TYPES::Time, + pub cur_view: TYPES::View, + + /// Epoch number this node is executing in. + pub cur_epoch: TYPES::Epoch, /// Membership for Quorum Certs/votes pub quorum_membership: Arc, @@ -166,7 +169,7 @@ impl, V: Versions> UpgradeTaskStat // the `UpgradeProposalRecv` event. Otherwise, the view number subtraction below will // cause an overflow error. // TODO Come back to this - we probably don't need this, but we should also never receive a UpgradeCertificate where this fails, investigate block ready so it doesn't make one for the genesis block - if self.cur_view != TYPES::Time::genesis() && view < self.cur_view - 1 { + if self.cur_view != TYPES::View::genesis() && view < self.cur_view - 1 { warn!("Discarding old upgrade proposal; the proposal is for view {:?}, but the current view is {:?}.", view, self.cur_view @@ -175,7 +178,7 @@ impl, V: Versions> UpgradeTaskStat } // We then validate that the proposal was issued by the leader for the view. - let view_leader_key = self.quorum_membership.leader(view); + let view_leader_key = self.quorum_membership.leader(view, self.cur_epoch); if &view_leader_key != sender { error!("Upgrade proposal doesn't have expected leader key for view {} \n Upgrade proposal is: {:?}", *view, proposal.data.clone()); return None; @@ -219,11 +222,12 @@ impl, V: Versions> UpgradeTaskStat // Check if we are the leader. { let view = vote.view_number(); - if self.quorum_membership.leader(view) != self.public_key { + if self.quorum_membership.leader(view, self.cur_epoch) != self.public_key { error!( "We are not the leader for view {} are we leader for next view? {}", *view, - self.quorum_membership.leader(view + 1) == self.public_key + self.quorum_membership.leader(view + 1, self.cur_epoch) + == self.public_key ); return None; } @@ -234,6 +238,7 @@ impl, V: Versions> UpgradeTaskStat vote, self.public_key.clone(), &self.quorum_membership, + self.cur_epoch, self.id, &event, &tx, @@ -260,23 +265,23 @@ impl, V: Versions> UpgradeTaskStat && time >= self.start_proposing_time && time < self.stop_proposing_time && !self.upgraded().await - && self - .quorum_membership - .leader(TYPES::Time::new(view + UPGRADE_PROPOSE_OFFSET)) - == self.public_key + && self.quorum_membership.leader( + TYPES::View::new(view + UPGRADE_PROPOSE_OFFSET), + self.cur_epoch, + ) == self.public_key { let upgrade_proposal_data = UpgradeProposalData { old_version: V::Base::VERSION, new_version: V::Upgrade::VERSION, new_version_hash: V::UPGRADE_HASH.to_vec(), - old_version_last_view: TYPES::Time::new(view + UPGRADE_BEGIN_OFFSET), - new_version_first_view: TYPES::Time::new(view + UPGRADE_FINISH_OFFSET), - decide_by: TYPES::Time::new(view + UPGRADE_DECIDE_BY_OFFSET), + old_version_last_view: TYPES::View::new(view + UPGRADE_BEGIN_OFFSET), + new_version_first_view: TYPES::View::new(view + UPGRADE_FINISH_OFFSET), + decide_by: TYPES::View::new(view + UPGRADE_DECIDE_BY_OFFSET), }; let upgrade_proposal = UpgradeProposal { upgrade_proposal: upgrade_proposal_data.clone(), - view_number: TYPES::Time::new(view + UPGRADE_PROPOSE_OFFSET), + view_number: TYPES::View::new(view + UPGRADE_PROPOSE_OFFSET), }; let signature = TYPES::SignatureKey::sign( diff --git a/crates/task-impls/src/vid.rs b/crates/task-impls/src/vid.rs index 105aad46dc..106203bd07 100644 --- a/crates/task-impls/src/vid.rs +++ b/crates/task-impls/src/vid.rs @@ -30,7 +30,9 @@ use crate::{ /// Tracks state of a VID task pub struct VidTaskState> { /// View number this view is executing in. - pub cur_view: TYPES::Time, + pub cur_view: TYPES::View, + /// Epoch number this node is executing in. + pub cur_epoch: TYPES::Epoch, /// Reference to consensus. Leader will require a read lock on this. pub consensus: OuterConsensus, /// The underlying network @@ -42,7 +44,7 @@ pub struct VidTaskState> { /// Our Private Key pub private_key: ::PrivateKey, /// The view and ID of the current vote collection task, if there is one. - pub vote_collector: Option<(TYPES::Time, usize, usize)>, + pub vote_collector: Option<(TYPES::View, usize, usize)>, /// This state's ID pub id: u64, } @@ -73,6 +75,7 @@ impl> VidTaskState { Arc::clone(encoded_transactions), &Arc::clone(&self.membership), *view_number, + self.cur_epoch, vid_precompute.clone(), ) .await; diff --git a/crates/task-impls/src/view_sync.rs b/crates/task-impls/src/view_sync.rs index cc06ed170d..fb00054f88 100644 --- a/crates/task-impls/src/view_sync.rs +++ b/crates/task-impls/src/view_sync.rs @@ -62,16 +62,18 @@ pub enum ViewSyncPhase { /// Type alias for a map from View Number to Relay to Vote Task type RelayMap = HashMap< - ::Time, + ::View, BTreeMap>, >; /// Main view sync task state pub struct ViewSyncTaskState, V: Versions> { /// View HotShot is currently in - pub current_view: TYPES::Time, + pub current_view: TYPES::View, /// View HotShot wishes to be in - pub next_view: TYPES::Time, + pub next_view: TYPES::View, + /// Epoch HotShot is currently in + pub current_epoch: TYPES::Epoch, /// The underlying network pub network: Arc, /// Membership for the quorum @@ -87,7 +89,7 @@ pub struct ViewSyncTaskState, V: V pub num_timeouts_tracked: u64, /// Map of running replica tasks - pub replica_task_map: RwLock>>, + pub replica_task_map: RwLock>>, /// Map of pre-commit vote accumulates for the relay pub pre_commit_relay_map: RwLock< @@ -105,7 +107,7 @@ pub struct ViewSyncTaskState, V: V pub view_sync_timeout: Duration, /// Last view we garbage collected old tasks - pub last_garbage_collected_view: TYPES::Time, + pub last_garbage_collected_view: TYPES::View, /// Lock for a decided upgrade pub upgrade_lock: UpgradeLock, @@ -136,9 +138,11 @@ pub struct ViewSyncReplicaTaskState, V: Versions> ViewSyncTaskSta pub async fn send_to_or_create_replica( &mut self, event: Arc>, - view: TYPES::Time, + view: TYPES::View, sender: &Sender>>, ) { // This certificate is old, we can throw it away @@ -221,6 +225,7 @@ impl, V: Versions> ViewSyncTaskSta let mut replica_state: ViewSyncReplicaTaskState = ViewSyncReplicaTaskState { current_view: view, next_view: view, + current_epoch: self.current_epoch, relay: 0, finalized: false, sent_view_change_event: false, @@ -299,7 +304,11 @@ impl, V: Versions> ViewSyncTaskSta } // We do not have a relay task already running, so start one - if self.membership.leader(vote_view + relay) != self.public_key { + if self + .membership + .leader(vote_view + relay, self.current_epoch) + != self.public_key + { debug!("View sync vote sent to wrong leader"); return; } @@ -308,6 +317,7 @@ impl, V: Versions> ViewSyncTaskSta public_key: self.public_key.clone(), membership: Arc::clone(&self.membership), view: vote_view, + epoch: self.current_epoch, id: self.id, }; let vote_collector = @@ -337,7 +347,11 @@ impl, V: Versions> ViewSyncTaskSta } // We do not have a relay task already running, so start one - if self.membership.leader(vote_view + relay) != self.public_key { + if self + .membership + .leader(vote_view + relay, self.current_epoch) + != self.public_key + { debug!("View sync vote sent to wrong leader"); return; } @@ -346,6 +360,7 @@ impl, V: Versions> ViewSyncTaskSta public_key: self.public_key.clone(), membership: Arc::clone(&self.membership), view: vote_view, + epoch: self.current_epoch, id: self.id, }; let vote_collector = @@ -375,7 +390,11 @@ impl, V: Versions> ViewSyncTaskSta } // We do not have a relay task already running, so start one - if self.membership.leader(vote_view + relay) != self.public_key { + if self + .membership + .leader(vote_view + relay, self.current_epoch) + != self.public_key + { debug!("View sync vote sent to wrong leader"); return; } @@ -384,6 +403,7 @@ impl, V: Versions> ViewSyncTaskSta public_key: self.public_key.clone(), membership: Arc::clone(&self.membership), view: vote_view, + epoch: self.current_epoch, id: self.id, }; let vote_collector = @@ -395,7 +415,7 @@ impl, V: Versions> ViewSyncTaskSta } &HotShotEvent::ViewChange(new_view) => { - let new_view = TYPES::Time::new(*new_view); + let new_view = TYPES::View::new(*new_view); if self.current_view < new_view { debug!( "Change from view {} to view {} in view sync task", @@ -414,19 +434,19 @@ impl, V: Versions> ViewSyncTaskSta self.replica_task_map .write() .await - .remove_entry(&TYPES::Time::new(i)); + .remove_entry(&TYPES::View::new(i)); self.pre_commit_relay_map .write() .await - .remove_entry(&TYPES::Time::new(i)); + .remove_entry(&TYPES::View::new(i)); self.commit_relay_map .write() .await - .remove_entry(&TYPES::Time::new(i)); + .remove_entry(&TYPES::View::new(i)); self.finalize_relay_map .write() .await - .remove_entry(&TYPES::Time::new(i)); + .remove_entry(&TYPES::View::new(i)); } self.last_garbage_collected_view = self.current_view - 1; @@ -434,12 +454,12 @@ impl, V: Versions> ViewSyncTaskSta } &HotShotEvent::Timeout(view_number) => { // This is an old timeout and we can ignore it - if view_number <= TYPES::Time::new(*self.current_view) { + if view_number <= TYPES::View::new(*self.current_view) { return; } self.num_timeouts_tracked += 1; - let leader = self.membership.leader(view_number); + let leader = self.membership.leader(view_number, self.current_epoch); warn!( %leader, leader_mnemonic = cdn_proto::util::mnemonic(&leader), @@ -465,7 +485,7 @@ impl, V: Versions> ViewSyncTaskSta // If this is the first timeout we've seen advance to the next view self.current_view = view_number; broadcast_event( - Arc::new(HotShotEvent::ViewChange(TYPES::Time::new( + Arc::new(HotShotEvent::ViewChange(TYPES::View::new( *self.current_view, ))), &event_stream, @@ -502,7 +522,11 @@ impl, V: Versions> // If certificate is not valid, return current state if !certificate - .is_valid_cert(self.membership.as_ref(), &self.upgrade_lock) + .is_valid_cert( + self.membership.as_ref(), + self.current_epoch, + &self.upgrade_lock, + ) .await { error!("Not valid view sync cert! {:?}", certificate.data()); @@ -561,7 +585,7 @@ impl, V: Versions> broadcast_event( Arc::new(HotShotEvent::ViewSyncTimeout( - TYPES::Time::new(*next_view), + TYPES::View::new(*next_view), relay, phase, )), @@ -584,7 +608,11 @@ impl, V: Versions> // If certificate is not valid, return current state if !certificate - .is_valid_cert(self.membership.as_ref(), &self.upgrade_lock) + .is_valid_cert( + self.membership.as_ref(), + self.current_epoch, + &self.upgrade_lock, + ) .await { error!("Not valid view sync cert! {:?}", certificate.data()); @@ -655,7 +683,7 @@ impl, V: Versions> ); broadcast_event( Arc::new(HotShotEvent::ViewSyncTimeout( - TYPES::Time::new(*next_view), + TYPES::View::new(*next_view), relay, phase, )), @@ -676,7 +704,11 @@ impl, V: Versions> // If certificate is not valid, return current state if !certificate - .is_valid_cert(self.membership.as_ref(), &self.upgrade_lock) + .is_valid_cert( + self.membership.as_ref(), + self.current_epoch, + &self.upgrade_lock, + ) .await { error!("Not valid view sync cert! {:?}", certificate.data()); @@ -708,7 +740,7 @@ impl, V: Versions> HotShotEvent::ViewSyncTrigger(view_number) => { let view_number = *view_number; - if self.next_view != TYPES::Time::new(*view_number) { + if self.next_view != TYPES::View::new(*view_number) { error!("Unexpected view number to triger view sync"); return None; } @@ -748,7 +780,7 @@ impl, V: Versions> warn!("Vote sending timed out in ViewSyncTrigger"); broadcast_event( Arc::new(HotShotEvent::ViewSyncTimeout( - TYPES::Time::new(*next_view), + TYPES::View::new(*next_view), relay, ViewSyncPhase::None, )), @@ -764,7 +796,7 @@ impl, V: Versions> HotShotEvent::ViewSyncTimeout(round, relay, last_seen_certificate) => { let round = *round; // Shouldn't ever receive a timeout for a relay higher than ours - if TYPES::Time::new(*round) == self.next_view && *relay == self.relay { + if TYPES::View::new(*round) == self.next_view && *relay == self.relay { if let Some(timeout_task) = self.timeout_task.take() { cancel_task(timeout_task).await; } @@ -817,7 +849,7 @@ impl, V: Versions> ); broadcast_event( Arc::new(HotShotEvent::ViewSyncTimeout( - TYPES::Time::new(*next_view), + TYPES::View::new(*next_view), relay, last_cert, )), diff --git a/crates/task-impls/src/vote_collection.rs b/crates/task-impls/src/vote_collection.rs index a7ec9cd7e9..533a96b719 100644 --- a/crates/task-impls/src/vote_collection.rs +++ b/crates/task-impls/src/vote_collection.rs @@ -39,7 +39,7 @@ use crate::{ /// Alias for a map of Vote Collectors pub type VoteCollectorsMap = - BTreeMap<::Time, VoteCollectionTaskState>; + BTreeMap<::View, VoteCollectionTaskState>; /// Task state for collecting votes of one type and emitting a certificate pub struct VoteCollectionTaskState< @@ -58,7 +58,10 @@ pub struct VoteCollectionTaskState< pub accumulator: Option>, /// The view which we are collecting votes for - pub view: TYPES::Time, + pub view: TYPES::View, + + /// The epoch which we are collecting votes for + pub epoch: TYPES::Epoch, /// Node id pub id: u64, @@ -71,8 +74,8 @@ pub trait AggregatableVote< CERT: Certificate, > { - /// return the leader for this votes - fn leader(&self, membership: &TYPES::Membership) -> TYPES::SignatureKey; + /// return the leader for this votes in the given epoch + fn leader(&self, membership: &TYPES::Membership, epoch: TYPES::Epoch) -> TYPES::SignatureKey; /// return the Hotshot event for the completion of this CERT fn make_cert_event(certificate: CERT, key: &TYPES::SignatureKey) -> HotShotEvent; @@ -93,7 +96,7 @@ impl< vote: &VOTE, event_stream: &Sender>>, ) -> Option { - if vote.leader(&self.membership) != self.public_key { + if vote.leader(&self.membership, self.epoch) != self.public_key { error!("Received vote for a view in which we were not the leader."); return None; } @@ -108,7 +111,10 @@ impl< } let accumulator = self.accumulator.as_mut()?; - match accumulator.accumulate(vote, &self.membership).await { + match accumulator + .accumulate(vote, &self.membership, self.epoch) + .await + { Either::Left(()) => None, Either::Right(cert) => { debug!("Certificate Formed! {:?}", cert); @@ -151,7 +157,9 @@ pub struct AccumulatorInfo { /// Membership we are accumulation votes for pub membership: Arc, /// View of the votes we are collecting - pub view: TYPES::Time, + pub view: TYPES::View, + /// Epoch of the votes we are collecting + pub epoch: TYPES::Epoch, /// This nodes id pub id: u64, } @@ -192,6 +200,7 @@ where public_key: info.public_key.clone(), accumulator: Some(new_accumulator), view: info.view, + epoch: info.epoch, id: info.id, }; @@ -217,6 +226,7 @@ pub async fn handle_vote< vote: &VOTE, public_key: TYPES::SignatureKey, membership: &Arc, + epoch: TYPES::Epoch, id: u64, event: &Arc>, event_stream: &Sender>>, @@ -231,6 +241,7 @@ pub async fn handle_vote< public_key, membership: Arc::clone(membership), view: vote.view_number(), + epoch, id, }; if let Some(collector) = create_vote_accumulator( @@ -292,8 +303,8 @@ type ViewSyncFinalizeVoteState = VoteCollectionTaskState< impl AggregatableVote, QuorumCertificate> for QuorumVote { - fn leader(&self, membership: &TYPES::Membership) -> TYPES::SignatureKey { - membership.leader(self.view_number() + 1) + fn leader(&self, membership: &TYPES::Membership, epoch: TYPES::Epoch) -> TYPES::SignatureKey { + membership.leader(self.view_number() + 1, epoch) } fn make_cert_event( certificate: QuorumCertificate, @@ -306,8 +317,8 @@ impl AggregatableVote, QuorumCertifica impl AggregatableVote, UpgradeCertificate> for UpgradeVote { - fn leader(&self, membership: &TYPES::Membership) -> TYPES::SignatureKey { - membership.leader(self.view_number()) + fn leader(&self, membership: &TYPES::Membership, epoch: TYPES::Epoch) -> TYPES::SignatureKey { + membership.leader(self.view_number(), epoch) } fn make_cert_event( certificate: UpgradeCertificate, @@ -320,8 +331,8 @@ impl AggregatableVote, UpgradeCertifi impl AggregatableVote, DaCertificate> for DaVote { - fn leader(&self, membership: &TYPES::Membership) -> TYPES::SignatureKey { - membership.leader(self.view_number()) + fn leader(&self, membership: &TYPES::Membership, epoch: TYPES::Epoch) -> TYPES::SignatureKey { + membership.leader(self.view_number(), epoch) } fn make_cert_event( certificate: DaCertificate, @@ -334,8 +345,8 @@ impl AggregatableVote, DaCertificate AggregatableVote, TimeoutCertificate> for TimeoutVote { - fn leader(&self, membership: &TYPES::Membership) -> TYPES::SignatureKey { - membership.leader(self.view_number() + 1) + fn leader(&self, membership: &TYPES::Membership, epoch: TYPES::Epoch) -> TYPES::SignatureKey { + membership.leader(self.view_number() + 1, epoch) } fn make_cert_event( certificate: TimeoutCertificate, @@ -349,8 +360,8 @@ impl AggregatableVote, ViewSyncCommitCertificate2> for ViewSyncCommitVote { - fn leader(&self, membership: &TYPES::Membership) -> TYPES::SignatureKey { - membership.leader(self.date().round + self.date().relay) + fn leader(&self, membership: &TYPES::Membership, epoch: TYPES::Epoch) -> TYPES::SignatureKey { + membership.leader(self.date().round + self.date().relay, epoch) } fn make_cert_event( certificate: ViewSyncCommitCertificate2, @@ -364,8 +375,8 @@ impl AggregatableVote, ViewSyncPreCommitCertificate2> for ViewSyncPreCommitVote { - fn leader(&self, membership: &TYPES::Membership) -> TYPES::SignatureKey { - membership.leader(self.date().round + self.date().relay) + fn leader(&self, membership: &TYPES::Membership, epoch: TYPES::Epoch) -> TYPES::SignatureKey { + membership.leader(self.date().round + self.date().relay, epoch) } fn make_cert_event( certificate: ViewSyncPreCommitCertificate2, @@ -379,8 +390,8 @@ impl AggregatableVote, ViewSyncFinalizeCertificate2> for ViewSyncFinalizeVote { - fn leader(&self, membership: &TYPES::Membership) -> TYPES::SignatureKey { - membership.leader(self.date().round + self.date().relay) + fn leader(&self, membership: &TYPES::Membership, epoch: TYPES::Epoch) -> TYPES::SignatureKey { + membership.leader(self.date().round + self.date().relay, epoch) } fn make_cert_event( certificate: ViewSyncFinalizeCertificate2, diff --git a/crates/testing/src/byzantine/byzantine_behaviour.rs b/crates/testing/src/byzantine/byzantine_behaviour.rs index beab8b5ae6..825108e48b 100644 --- a/crates/testing/src/byzantine/byzantine_behaviour.rs +++ b/crates/testing/src/byzantine/byzantine_behaviour.rs @@ -116,7 +116,7 @@ pub struct DishonestLeader { /// How far back to look for a QC pub view_look_back: usize, /// Shared state of all view numbers we send bad proposal at - pub dishonest_proposal_view_numbers: Arc>>, + pub dishonest_proposal_view_numbers: Arc>>, } /// Add method that will handle `QuorumProposalSend` events @@ -246,7 +246,7 @@ pub struct ViewDelay { /// How many views the node will be delayed pub number_of_views_to_delay: u64, /// A map that is from view number to vector of events - pub events_for_view: HashMap>>, + pub events_for_view: HashMap>>, /// Specify which view number to stop delaying pub stop_view_delay_at_view_number: u64, } @@ -271,7 +271,7 @@ impl + std::fmt::Debug, V: Version if view_diff > 0 { return match self .events_for_view - .remove(&::Time::new(view_diff)) + .remove(&::View::new(view_diff)) { Some(lookback_events) => lookback_events.clone(), // we have already return all received events for this view @@ -346,7 +346,8 @@ impl + std::fmt::Debug, V: Version ) { let network_state: NetworkEventTaskState<_, V, _, _> = NetworkEventTaskState { network, - view: TYPES::Time::genesis(), + view: TYPES::View::genesis(), + epoch: TYPES::Epoch::genesis(), quorum_membership, da_membership, storage: Arc::clone(&handle.storage()), @@ -375,7 +376,7 @@ pub struct DishonestVoter { /// Collect all votes the node sends pub votes_sent: Vec>, /// Shared state with views numbers that leaders were dishonest at - pub dishonest_proposal_view_numbers: Arc>>, + pub dishonest_proposal_view_numbers: Arc>>, } #[async_trait] diff --git a/crates/testing/src/consistency_task.rs b/crates/testing/src/consistency_task.rs index 3cb04f65c1..0d78c4d12c 100644 --- a/crates/testing/src/consistency_task.rs +++ b/crates/testing/src/consistency_task.rs @@ -24,10 +24,10 @@ use crate::{ }; /// Map from views to leaves for a single node, allowing multiple leaves for each view (because the node may a priori send us multiple leaves for a given view). -pub type NodeMap = BTreeMap<::Time, Vec>>; +pub type NodeMap = BTreeMap<::View, Vec>>; /// A sanitized map from views to leaves for a single node, with only a single leaf per view. -pub type NodeMapSanitized = BTreeMap<::Time, Leaf>; +pub type NodeMapSanitized = BTreeMap<::View, Leaf>; /// Validate that the `NodeMap` only has a single leaf per view. fn sanitize_node_map( @@ -68,7 +68,7 @@ async fn validate_node_map( .map(|((a, b), c)| (a, b, c)); let mut decided_upgrade_certificate = None; - let mut view_decided = TYPES::Time::new(0); + let mut view_decided = TYPES::View::new(0); for (grandparent, _parent, child) in leaf_triples { if let Some(cert) = grandparent.upgrade_certificate() { @@ -144,7 +144,7 @@ fn sanitize_network_map( Ok(result) } -pub type ViewMap = BTreeMap<::Time, BTreeMap>>; +pub type ViewMap = BTreeMap<::View, BTreeMap>>; // Invert the network map by interchanging the roles of the node_id and view number. // @@ -171,7 +171,7 @@ async fn invert_network_map( } /// A view map, sanitized to have exactly one leaf per view. -pub type ViewMapSanitized = BTreeMap<::Time, Leaf>; +pub type ViewMapSanitized = BTreeMap<::View, Leaf>; fn sanitize_view_map( view_map: &ViewMap, diff --git a/crates/testing/src/helpers.rs b/crates/testing/src/helpers.rs index 54eab9d4fa..842ef3e538 100644 --- a/crates/testing/src/helpers.rs +++ b/crates/testing/src/helpers.rs @@ -150,7 +150,8 @@ pub async fn build_cert< >( data: DATAType, membership: &TYPES::Membership, - view: TYPES::Time, + view: TYPES::View, + epoch: TYPES::Epoch, public_key: &TYPES::SignatureKey, private_key: &::PrivateKey, upgrade_lock: &UpgradeLock, @@ -159,6 +160,7 @@ pub async fn build_cert< &data, membership, view, + epoch, upgrade_lock, ) .await; @@ -214,10 +216,11 @@ pub async fn build_assembled_sig< >( data: &DATAType, membership: &TYPES::Membership, - view: TYPES::Time, + view: TYPES::View, + epoch: TYPES::Epoch, upgrade_lock: &UpgradeLock, ) -> ::QcType { - let stake_table = membership.stake_table(); + let stake_table = membership.stake_table(epoch); let real_qc_pp: ::QcParams = ::public_parameter( stake_table.clone(), @@ -272,18 +275,23 @@ pub fn key_pair_for_id( #[must_use] pub fn vid_scheme_from_view_number( membership: &TYPES::Membership, - view_number: TYPES::Time, + view_number: TYPES::View, + epoch_number: TYPES::Epoch, ) -> VidSchemeType { - let num_storage_nodes = membership.committee_members(view_number).len(); + let num_storage_nodes = membership + .committee_members(view_number, epoch_number) + .len(); vid_scheme(num_storage_nodes) } pub fn vid_payload_commitment( quorum_membership: &::Membership, - view_number: TYPES::Time, + view_number: TYPES::View, + epoch_number: TYPES::Epoch, transactions: Vec, ) -> VidCommitment { - let mut vid = vid_scheme_from_view_number::(quorum_membership, view_number); + let mut vid = + vid_scheme_from_view_number::(quorum_membership, view_number, epoch_number); let encoded_transactions = TestTransaction::encode(&transactions); let vid_disperse = vid.disperse(&encoded_transactions).unwrap(); @@ -293,19 +301,24 @@ pub fn vid_payload_commitment( pub fn da_payload_commitment( quorum_membership: &::Membership, transactions: Vec, + epoch_number: TYPES::Epoch, ) -> VidCommitment { let encoded_transactions = TestTransaction::encode(&transactions); - vid_commitment(&encoded_transactions, quorum_membership.total_nodes()) + vid_commitment( + &encoded_transactions, + quorum_membership.total_nodes(epoch_number), + ) } pub fn build_payload_commitment( membership: &::Membership, - view: TYPES::Time, + view: TYPES::View, + epoch: TYPES::Epoch, ) -> ::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. - let mut vid = vid_scheme_from_view_number::(membership, view); + let mut vid = vid_scheme_from_view_number::(membership, view, epoch); let encoded_transactions = Vec::new(); vid.commit_only(&encoded_transactions).unwrap() } @@ -313,17 +326,20 @@ pub fn build_payload_commitment( /// TODO: pub fn build_vid_proposal( quorum_membership: &::Membership, - view_number: TYPES::Time, + view_number: TYPES::View, + epoch_number: TYPES::Epoch, transactions: Vec, private_key: &::PrivateKey, ) -> VidProposal { - let mut vid = vid_scheme_from_view_number::(quorum_membership, view_number); + let mut vid = + vid_scheme_from_view_number::(quorum_membership, view_number, epoch_number); let encoded_transactions = TestTransaction::encode(&transactions); let vid_disperse = VidDisperse::from_membership( view_number, vid.disperse(&encoded_transactions).unwrap(), quorum_membership, + epoch_number, ); let signature = @@ -348,10 +364,12 @@ pub fn build_vid_proposal( ) } +#[allow(clippy::too_many_arguments)] pub async fn build_da_certificate( quorum_membership: &::Membership, da_membership: &::Membership, - view_number: TYPES::Time, + view_number: TYPES::View, + epoch_number: TYPES::Epoch, transactions: Vec, public_key: &TYPES::SignatureKey, private_key: &::PrivateKey, @@ -359,8 +377,10 @@ pub async fn build_da_certificate( ) -> DaCertificate { let encoded_transactions = TestTransaction::encode(&transactions); - let da_payload_commitment = - vid_commitment(&encoded_transactions, quorum_membership.total_nodes()); + let da_payload_commitment = vid_commitment( + &encoded_transactions, + quorum_membership.total_nodes(epoch_number), + ); let da_data = DaData { payload_commit: da_payload_commitment, @@ -370,6 +390,7 @@ pub async fn build_da_certificate( da_data, da_membership, view_number, + epoch_number, public_key, private_key, upgrade_lock, diff --git a/crates/testing/src/overall_safety_task.rs b/crates/testing/src/overall_safety_task.rs index 3cbe364517..979c2c2a04 100644 --- a/crates/testing/src/overall_safety_task.rs +++ b/crates/testing/src/overall_safety_task.rs @@ -61,12 +61,12 @@ pub enum OverallSafetyTaskErr { NotEnoughDecides { got: usize, expected: usize }, #[error("Too many view failures: {0:?}")] - TooManyFailures(HashSet), + TooManyFailures(HashSet), #[error("Inconsistent failed views: expected: {expected_failed_views:?}, actual: {actual_failed_views:?}")] InconsistentFailedViews { - expected_failed_views: Vec, - actual_failed_views: HashSet, + expected_failed_views: Vec, + actual_failed_views: HashSet, }, #[error( "Not enough round results: results_count: {results_count}, views_count: {views_count}" @@ -97,7 +97,7 @@ pub struct OverallSafetyTask, V: Versions> OverallSafetyTask { - async fn handle_view_failure(&mut self, num_failed_views: usize, view_number: TYPES::Time) { + async fn handle_view_failure(&mut self, num_failed_views: usize, view_number: TYPES::View) { let expected_views_to_fail = &mut self.properties.expected_views_to_fail; self.ctx.failed_views.insert(view_number); @@ -155,7 +155,7 @@ impl, V: Versions> TestTas block_size: maybe_block_size, } => { // Skip the genesis leaf. - if leaf_chain.last().unwrap().leaf.view_number() == TYPES::Time::genesis() { + if leaf_chain.last().unwrap().leaf.view_number() == TYPES::View::genesis() { return Ok(()); } let paired_up = (leaf_chain.to_vec(), (*qc).clone()); @@ -364,18 +364,18 @@ impl Default for RoundCtx { pub struct RoundCtx { /// results from previous rounds /// view number -> round result - pub round_results: HashMap>, + pub round_results: HashMap>, /// during the run view refactor - pub failed_views: HashSet, + pub failed_views: HashSet, /// successful views - pub successful_views: HashSet, + pub successful_views: HashSet, } impl RoundCtx { /// inserts an error into the context pub fn insert_error_to_context( &mut self, - view_number: TYPES::Time, + view_number: TYPES::View, idx: usize, error: Arc>, ) { @@ -569,7 +569,7 @@ pub struct OverallSafetyPropertiesDescription { /// required to mark view as successful pub threshold_calculator: Arc usize + Send + Sync>, /// pass in the views that we expect to fail - pub expected_views_to_fail: HashMap, + pub expected_views_to_fail: HashMap, } impl std::fmt::Debug for OverallSafetyPropertiesDescription { diff --git a/crates/testing/src/spinning_task.rs b/crates/testing/src/spinning_task.rs index eafc52fbb2..5593a4336e 100644 --- a/crates/testing/src/spinning_task.rs +++ b/crates/testing/src/spinning_task.rs @@ -58,9 +58,9 @@ pub struct SpinningTask< /// late start nodes pub(crate) late_start: HashMap>, /// time based changes - pub(crate) changes: BTreeMap>, + pub(crate) changes: BTreeMap>, /// most recent view seen by spinning task - pub(crate) latest_view: Option, + pub(crate) latest_view: Option, /// Last decided leaf that can be used as the anchor leaf to initialize the node. pub(crate) last_decided_leaf: Leaf, /// Highest qc seen in the test for restarting nodes @@ -155,8 +155,8 @@ where self.last_decided_leaf.clone(), TestInstanceState::new(self.async_delay_config.clone()), None, - TYPES::Time::genesis(), - TYPES::Time::genesis(), + TYPES::View::genesis(), + TYPES::View::genesis(), BTreeMap::new(), self.high_qc.clone(), None, diff --git a/crates/testing/src/test_builder.rs b/crates/testing/src/test_builder.rs index 2a7414475f..64d16ede22 100644 --- a/crates/testing/src/test_builder.rs +++ b/crates/testing/src/test_builder.rs @@ -529,6 +529,7 @@ where stop_proposing_time: 0, start_voting_time: u64::MAX, stop_voting_time: 0, + epoch_height: 0, }; let TimingData { next_view_timeout, diff --git a/crates/testing/src/test_runner.rs b/crates/testing/src/test_runner.rs index 221f43ced6..cb2c8119ed 100644 --- a/crates/testing/src/test_runner.rs +++ b/crates/testing/src/test_runner.rs @@ -170,10 +170,10 @@ where // add spinning task // map spinning to view - let mut changes: BTreeMap> = BTreeMap::new(); + let mut changes: BTreeMap> = BTreeMap::new(); for (view, mut change) in spinning_changes { changes - .entry(TYPES::Time::new(view)) + .entry(TYPES::View::new(view)) .or_insert_with(Vec::new) .append(&mut change); } diff --git a/crates/testing/src/view_generator.rs b/crates/testing/src/view_generator.rs index 315ac2d5e8..dcaedd5dc1 100644 --- a/crates/testing/src/view_generator.rs +++ b/crates/testing/src/view_generator.rs @@ -12,6 +12,9 @@ use std::{ task::{Context, Poll}, }; +use crate::helpers::{ + build_cert, build_da_certificate, build_vid_proposal, da_payload_commitment, key_pair_for_id, +}; use futures::{FutureExt, Stream}; use hotshot::types::{BLSPubKey, SignatureKey, SystemContextHandle}; use hotshot_example_types::{ @@ -19,6 +22,7 @@ use hotshot_example_types::{ node_types::{MemoryImpl, TestTypes, TestVersions}, state_types::{TestInstanceState, TestValidatedState}, }; +use hotshot_types::data::EpochNumber; use hotshot_types::{ data::{ DaProposal, Leaf, QuorumProposal, VidDisperse, VidDisperseShare, ViewChangeEvidence, @@ -42,16 +46,13 @@ use hotshot_types::{ use rand::{thread_rng, Rng}; use sha2::{Digest, Sha256}; -use crate::helpers::{ - build_cert, build_da_certificate, build_vid_proposal, da_payload_commitment, key_pair_for_id, -}; - #[derive(Clone)] pub struct TestView { pub da_proposal: Proposal>, pub quorum_proposal: Proposal>, pub leaf: Leaf, pub view_number: ViewNumber, + pub epoch_number: EpochNumber, pub quorum_membership: ::Membership, pub da_membership: ::Membership, pub vid_disperse: Proposal>, @@ -75,6 +76,7 @@ impl TestView { da_membership: &::Membership, ) -> Self { let genesis_view = ViewNumber::new(1); + let genesis_epoch = EpochNumber::new(1); let upgrade_lock = UpgradeLock::new(); let transactions = Vec::new(); @@ -97,12 +99,16 @@ impl TestView { let leader_public_key = public_key; - let payload_commitment = - da_payload_commitment::(quorum_membership, transactions.clone()); + let payload_commitment = da_payload_commitment::( + quorum_membership, + transactions.clone(), + genesis_epoch, + ); let (vid_disperse, vid_proposal) = build_vid_proposal( quorum_membership, genesis_view, + genesis_epoch, transactions.clone(), &private_key, ); @@ -111,6 +117,7 @@ impl TestView { quorum_membership, da_membership, genesis_view, + genesis_epoch, transactions.clone(), &public_key, &private_key, @@ -180,6 +187,7 @@ impl TestView { quorum_proposal, leaf, view_number: genesis_view, + epoch_number: genesis_epoch, quorum_membership: quorum_membership.clone(), da_membership: da_membership.clone(), vid_disperse, @@ -237,12 +245,16 @@ impl TestView { &metadata, ); - let payload_commitment = - da_payload_commitment::(quorum_membership, transactions.clone()); + let payload_commitment = da_payload_commitment::( + quorum_membership, + transactions.clone(), + self.epoch_number, + ); let (vid_disperse, vid_proposal) = build_vid_proposal( quorum_membership, next_view, + self.epoch_number, transactions.clone(), &private_key, ); @@ -251,6 +263,7 @@ impl TestView { quorum_membership, da_membership, next_view, + self.epoch_number, transactions.clone(), &public_key, &private_key, @@ -268,6 +281,7 @@ impl TestView { quorum_data, quorum_membership, old_view, + self.epoch_number, &old_public_key, &old_private_key, &self.upgrade_lock, @@ -285,6 +299,7 @@ impl TestView { data.clone(), quorum_membership, next_view, + self.epoch_number, &public_key, &private_key, &self.upgrade_lock, @@ -307,6 +322,7 @@ impl TestView { data.clone(), quorum_membership, next_view, + self.epoch_number, &public_key, &private_key, &self.upgrade_lock, @@ -329,6 +345,7 @@ impl TestView { data.clone(), quorum_membership, next_view, + self.epoch_number, &public_key, &private_key, &self.upgrade_lock, @@ -406,6 +423,7 @@ impl TestView { quorum_proposal, leaf, view_number: next_view, + epoch_number: self.epoch_number, quorum_membership: quorum_membership.clone(), da_membership: self.da_membership.clone(), vid_disperse, diff --git a/crates/testing/tests/tests_1/da_task.rs b/crates/testing/tests/tests_1/da_task.rs index b39f59ff32..1f78de5467 100644 --- a/crates/testing/tests/tests_1/da_task.rs +++ b/crates/testing/tests/tests_1/da_task.rs @@ -21,6 +21,7 @@ use hotshot_testing::{ serial, view_generator::TestViewGenerator, }; +use hotshot_types::data::EpochNumber; use hotshot_types::{ data::{null_block, PackedBundle, ViewNumber}, simple_vote::DaData, @@ -50,7 +51,11 @@ async fn test_da_task() { let encoded_transactions = Arc::from(TestTransaction::encode(&transactions)); let (payload_commit, precompute) = precompute_vid_commitment( &encoded_transactions, - handle.hotshot.memberships.quorum_membership.total_nodes(), + handle + .hotshot + .memberships + .quorum_membership + .total_nodes(EpochNumber::new(0)), ); let mut generator = TestViewGenerator::generate(quorum_membership.clone(), da_membership); @@ -96,7 +101,7 @@ async fn test_da_task() { }, ViewNumber::new(2), vec1::vec1![null_block::builder_fee::( - quorum_membership.total_nodes(), + quorum_membership.total_nodes(EpochNumber::new(0)), ::Base::VERSION ) .unwrap()], @@ -147,7 +152,11 @@ async fn test_da_task_storage_failure() { let encoded_transactions = Arc::from(TestTransaction::encode(&transactions)); let (payload_commit, precompute) = precompute_vid_commitment( &encoded_transactions, - handle.hotshot.memberships.quorum_membership.total_nodes(), + handle + .hotshot + .memberships + .quorum_membership + .total_nodes(EpochNumber::new(0)), ); let mut generator = TestViewGenerator::generate(quorum_membership.clone(), da_membership); @@ -193,7 +202,7 @@ async fn test_da_task_storage_failure() { }, ViewNumber::new(2), vec1::vec1![null_block::builder_fee::( - quorum_membership.total_nodes(), + quorum_membership.total_nodes(EpochNumber::new(0)), ::Base::VERSION ) .unwrap()], diff --git a/crates/testing/tests/tests_1/network_task.rs b/crates/testing/tests/tests_1/network_task.rs index 272d132ae3..6a583c058f 100644 --- a/crates/testing/tests/tests_1/network_task.rs +++ b/crates/testing/tests/tests_1/network_task.rs @@ -17,6 +17,7 @@ use hotshot_testing::{ helpers::build_system_handle, test_builder::TestDescription, test_task::add_network_message_test_task, view_generator::TestViewGenerator, }; +use hotshot_types::data::EpochNumber; use hotshot_types::{ data::ViewNumber, message::UpgradeLock, @@ -25,6 +26,7 @@ use hotshot_types::{ node_implementation::{ConsensusTime, NodeType}, }, }; + // Test that the event task sends a message, and the message task receives it // and emits the proper event #[cfg(test)] @@ -62,6 +64,7 @@ async fn test_network_task() { NetworkEventTaskState { network: network.clone(), view: ViewNumber::new(0), + epoch: EpochNumber::new(0), quorum_membership: membership.clone(), da_membership: membership.clone(), upgrade_lock: upgrade_lock.clone(), @@ -242,6 +245,7 @@ async fn test_network_storage_fail() { NetworkEventTaskState { network: network.clone(), view: ViewNumber::new(0), + epoch: EpochNumber::new(0), quorum_membership: membership.clone(), da_membership: membership.clone(), upgrade_lock: upgrade_lock.clone(), diff --git a/crates/testing/tests/tests_1/quorum_proposal_task.rs b/crates/testing/tests/tests_1/quorum_proposal_task.rs index 455f304114..5be8b11ba1 100644 --- a/crates/testing/tests/tests_1/quorum_proposal_task.rs +++ b/crates/testing/tests/tests_1/quorum_proposal_task.rs @@ -24,6 +24,7 @@ use hotshot_testing::{ serial, view_generator::TestViewGenerator, }; +use hotshot_types::data::EpochNumber; use hotshot_types::{ data::{null_block, Leaf, ViewChangeEvidence, ViewNumber}, simple_vote::{TimeoutData, ViewSyncFinalizeData}, @@ -56,8 +57,11 @@ async fn test_quorum_proposal_task_quorum_proposal_view_1() { let quorum_membership = handle.hotshot.memberships.quorum_membership.clone(); let da_membership = handle.hotshot.memberships.da_membership.clone(); - let payload_commitment = - build_payload_commitment::(&quorum_membership, ViewNumber::new(node_id)); + let payload_commitment = build_payload_commitment::( + &quorum_membership, + ViewNumber::new(node_id), + EpochNumber::new(1), + ); let mut generator = TestViewGenerator::generate(quorum_membership.clone(), da_membership); @@ -89,7 +93,7 @@ async fn test_quorum_proposal_task_quorum_proposal_view_1() { let genesis_cert = proposals[0].data.justify_qc.clone(); let builder_commitment = BuilderCommitment::from_raw_digest(sha2::Sha256::new().finalize()); let builder_fee = null_block::builder_fee::( - quorum_membership.total_nodes(), + quorum_membership.total_nodes(EpochNumber::new(1)), ::Base::VERSION, ) .unwrap(); @@ -201,7 +205,7 @@ async fn test_quorum_proposal_task_quorum_proposal_view_gt_1() { let builder_commitment = BuilderCommitment::from_raw_digest(sha2::Sha256::new().finalize()); let builder_fee = null_block::builder_fee::( - quorum_membership.total_nodes(), + quorum_membership.total_nodes(EpochNumber::new(1)), ::Base::VERSION, ) .unwrap(); @@ -210,7 +214,11 @@ async fn test_quorum_proposal_task_quorum_proposal_view_gt_1() { random![ QcFormed(either::Left(genesis_cert.clone())), SendPayloadCommitmentAndMetadata( - build_payload_commitment::(&quorum_membership, ViewNumber::new(1)), + build_payload_commitment::( + &quorum_membership, + ViewNumber::new(1), + EpochNumber::new(1) + ), builder_commitment.clone(), TestMetadata { num_transactions: 0 @@ -229,7 +237,11 @@ async fn test_quorum_proposal_task_quorum_proposal_view_gt_1() { QuorumProposalPreliminarilyValidated(proposals[0].clone()), QcFormed(either::Left(proposals[1].data.justify_qc.clone())), SendPayloadCommitmentAndMetadata( - build_payload_commitment::(&quorum_membership, ViewNumber::new(2)), + build_payload_commitment::( + &quorum_membership, + ViewNumber::new(2), + EpochNumber::new(1) + ), builder_commitment.clone(), proposals[0].data.block_header.metadata, ViewNumber::new(2), @@ -246,7 +258,11 @@ async fn test_quorum_proposal_task_quorum_proposal_view_gt_1() { QuorumProposalPreliminarilyValidated(proposals[1].clone()), QcFormed(either::Left(proposals[2].data.justify_qc.clone())), SendPayloadCommitmentAndMetadata( - build_payload_commitment::(&quorum_membership, ViewNumber::new(3)), + build_payload_commitment::( + &quorum_membership, + ViewNumber::new(3), + EpochNumber::new(1) + ), builder_commitment.clone(), proposals[1].data.block_header.metadata, ViewNumber::new(3), @@ -263,7 +279,11 @@ async fn test_quorum_proposal_task_quorum_proposal_view_gt_1() { QuorumProposalPreliminarilyValidated(proposals[2].clone()), QcFormed(either::Left(proposals[3].data.justify_qc.clone())), SendPayloadCommitmentAndMetadata( - build_payload_commitment::(&quorum_membership, ViewNumber::new(4)), + build_payload_commitment::( + &quorum_membership, + ViewNumber::new(4), + EpochNumber::new(1) + ), builder_commitment.clone(), proposals[2].data.block_header.metadata, ViewNumber::new(4), @@ -280,7 +300,11 @@ async fn test_quorum_proposal_task_quorum_proposal_view_gt_1() { QuorumProposalPreliminarilyValidated(proposals[3].clone()), QcFormed(either::Left(proposals[4].data.justify_qc.clone())), SendPayloadCommitmentAndMetadata( - build_payload_commitment::(&quorum_membership, ViewNumber::new(5)), + build_payload_commitment::( + &quorum_membership, + ViewNumber::new(5), + EpochNumber::new(1) + ), builder_commitment, proposals[3].data.block_header.metadata, ViewNumber::new(5), @@ -347,8 +371,11 @@ async fn test_quorum_proposal_task_qc_timeout() { let quorum_membership = handle.hotshot.memberships.quorum_membership.clone(); let da_membership = handle.hotshot.memberships.da_membership.clone(); - let payload_commitment = - build_payload_commitment::(&quorum_membership, ViewNumber::new(node_id)); + let payload_commitment = build_payload_commitment::( + &quorum_membership, + ViewNumber::new(node_id), + EpochNumber::new(1), + ); let builder_commitment = BuilderCommitment::from_raw_digest(sha2::Sha256::new().finalize()); let mut generator = TestViewGenerator::generate(quorum_membership.clone(), da_membership); @@ -393,7 +420,7 @@ async fn test_quorum_proposal_task_qc_timeout() { }, ViewNumber::new(3), vec1![null_block::builder_fee::( - quorum_membership.total_nodes(), + quorum_membership.total_nodes(EpochNumber::new(1)), ::Base::VERSION ) .unwrap()], @@ -437,8 +464,11 @@ async fn test_quorum_proposal_task_view_sync() { let quorum_membership = handle.hotshot.memberships.quorum_membership.clone(); let da_membership = handle.hotshot.memberships.da_membership.clone(); - let payload_commitment = - build_payload_commitment::(&quorum_membership, ViewNumber::new(node_id)); + let payload_commitment = build_payload_commitment::( + &quorum_membership, + ViewNumber::new(node_id), + EpochNumber::new(1), + ); let builder_commitment = BuilderCommitment::from_raw_digest(sha2::Sha256::new().finalize()); let mut generator = TestViewGenerator::generate(quorum_membership.clone(), da_membership); @@ -485,7 +515,7 @@ async fn test_quorum_proposal_task_view_sync() { }, ViewNumber::new(2), vec1![null_block::builder_fee::( - quorum_membership.total_nodes(), + quorum_membership.total_nodes(EpochNumber::new(1)), ::Base::VERSION ) .unwrap()], @@ -562,7 +592,7 @@ async fn test_quorum_proposal_task_liveness_check() { let builder_commitment = BuilderCommitment::from_raw_digest(sha2::Sha256::new().finalize()); let builder_fee = null_block::builder_fee::( - quorum_membership.total_nodes(), + quorum_membership.total_nodes(EpochNumber::new(1)), ::Base::VERSION, ) .unwrap(); @@ -580,7 +610,11 @@ async fn test_quorum_proposal_task_liveness_check() { random![ QcFormed(either::Left(genesis_cert.clone())), SendPayloadCommitmentAndMetadata( - build_payload_commitment::(&quorum_membership, ViewNumber::new(1)), + build_payload_commitment::( + &quorum_membership, + ViewNumber::new(1), + EpochNumber::new(1) + ), builder_commitment.clone(), TestMetadata { num_transactions: 0 @@ -599,7 +633,11 @@ async fn test_quorum_proposal_task_liveness_check() { QuorumProposalPreliminarilyValidated(proposals[0].clone()), QcFormed(either::Left(proposals[1].data.justify_qc.clone())), SendPayloadCommitmentAndMetadata( - build_payload_commitment::(&quorum_membership, ViewNumber::new(2)), + build_payload_commitment::( + &quorum_membership, + ViewNumber::new(2), + EpochNumber::new(1) + ), builder_commitment.clone(), proposals[0].data.block_header.metadata, ViewNumber::new(2), @@ -616,7 +654,11 @@ async fn test_quorum_proposal_task_liveness_check() { QuorumProposalPreliminarilyValidated(proposals[1].clone()), QcFormed(either::Left(proposals[2].data.justify_qc.clone())), SendPayloadCommitmentAndMetadata( - build_payload_commitment::(&quorum_membership, ViewNumber::new(3)), + build_payload_commitment::( + &quorum_membership, + ViewNumber::new(3), + EpochNumber::new(1) + ), builder_commitment.clone(), proposals[1].data.block_header.metadata, ViewNumber::new(3), @@ -633,7 +675,11 @@ async fn test_quorum_proposal_task_liveness_check() { QuorumProposalPreliminarilyValidated(proposals[2].clone()), QcFormed(either::Left(proposals[3].data.justify_qc.clone())), SendPayloadCommitmentAndMetadata( - build_payload_commitment::(&quorum_membership, ViewNumber::new(4)), + build_payload_commitment::( + &quorum_membership, + ViewNumber::new(4), + EpochNumber::new(1) + ), builder_commitment.clone(), proposals[2].data.block_header.metadata, ViewNumber::new(4), @@ -650,7 +696,11 @@ async fn test_quorum_proposal_task_liveness_check() { QuorumProposalPreliminarilyValidated(proposals[3].clone()), QcFormed(either::Left(proposals[4].data.justify_qc.clone())), SendPayloadCommitmentAndMetadata( - build_payload_commitment::(&quorum_membership, ViewNumber::new(5)), + build_payload_commitment::( + &quorum_membership, + ViewNumber::new(5), + EpochNumber::new(1) + ), builder_commitment, proposals[3].data.block_header.metadata, ViewNumber::new(5), diff --git a/crates/testing/tests/tests_1/transaction_task.rs b/crates/testing/tests/tests_1/transaction_task.rs index d73cd48ea7..b8c6a194b9 100644 --- a/crates/testing/tests/tests_1/transaction_task.rs +++ b/crates/testing/tests/tests_1/transaction_task.rs @@ -7,6 +7,7 @@ use hotshot_task_impls::{ events::HotShotEvent, harness::run_harness, transactions::TransactionTaskState, }; use hotshot_testing::helpers::build_system_handle; +use hotshot_types::data::EpochNumber; use hotshot_types::{ data::{null_block, PackedBundle, ViewNumber}, traits::{ @@ -39,7 +40,8 @@ async fn test_transaction_task_leader_two_views_in_a_row() { input.push(HotShotEvent::Shutdown); let quorum_membership = handle.hotshot.memberships.quorum_membership.clone(); - let (_, precompute_data) = precompute_vid_commitment(&[], quorum_membership.total_nodes()); + let (_, precompute_data) = + precompute_vid_commitment(&[], quorum_membership.total_nodes(EpochNumber::new(0))); // current view let mut exp_packed_bundle = PackedBundle::new( @@ -50,7 +52,7 @@ async fn test_transaction_task_leader_two_views_in_a_row() { current_view, vec1::vec1![ null_block::builder_fee::( - quorum_membership.total_nodes(), + quorum_membership.total_nodes(EpochNumber::new(0)), ::Base::VERSION ) .unwrap() 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 5bf95997c0..f0493fb5e6 100644 --- a/crates/testing/tests/tests_1/upgrade_task_with_proposal.rs +++ b/crates/testing/tests/tests_1/upgrade_task_with_proposal.rs @@ -29,6 +29,7 @@ use hotshot_testing::{ serial, view_generator::TestViewGenerator, }; +use hotshot_types::data::EpochNumber; use hotshot_types::{ data::{null_block, Leaf, ViewNumber}, simple_vote::UpgradeProposalData, @@ -145,7 +146,7 @@ async fn test_upgrade_task_with_proposal() { let genesis_leaf = Leaf::genesis(&validated_state, &*handle.hotshot.instance_state()).await; let builder_commitment = BuilderCommitment::from_raw_digest(sha2::Sha256::new().finalize()); let builder_fee = null_block::builder_fee::( - quorum_membership.total_nodes(), + quorum_membership.total_nodes(EpochNumber::new(1)), ::Base::VERSION, ) .unwrap(); @@ -171,7 +172,11 @@ async fn test_upgrade_task_with_proposal() { random![ QcFormed(either::Left(genesis_cert.clone())), SendPayloadCommitmentAndMetadata( - build_payload_commitment::(&quorum_membership, ViewNumber::new(1)), + build_payload_commitment::( + &quorum_membership, + ViewNumber::new(1), + EpochNumber::new(1) + ), builder_commitment.clone(), TestMetadata { num_transactions: 0 @@ -190,7 +195,11 @@ async fn test_upgrade_task_with_proposal() { QuorumProposalPreliminarilyValidated(proposals[0].clone()), QcFormed(either::Left(proposals[1].data.justify_qc.clone())), SendPayloadCommitmentAndMetadata( - build_payload_commitment::(&quorum_membership, ViewNumber::new(2)), + build_payload_commitment::( + &quorum_membership, + ViewNumber::new(2), + EpochNumber::new(1) + ), builder_commitment.clone(), proposals[0].data.block_header.metadata, ViewNumber::new(2), @@ -208,7 +217,11 @@ async fn test_upgrade_task_with_proposal() { QuorumProposalPreliminarilyValidated(proposals[1].clone()), QcFormed(either::Left(proposals[2].data.justify_qc.clone())), SendPayloadCommitmentAndMetadata( - build_payload_commitment::(&quorum_membership, ViewNumber::new(3)), + build_payload_commitment::( + &quorum_membership, + ViewNumber::new(3), + EpochNumber::new(1) + ), builder_commitment.clone(), proposals[1].data.block_header.metadata, ViewNumber::new(3), diff --git a/crates/testing/tests/tests_1/vid_task.rs b/crates/testing/tests/tests_1/vid_task.rs index 17922d42cb..714a12a3b5 100644 --- a/crates/testing/tests/tests_1/vid_task.rs +++ b/crates/testing/tests/tests_1/vid_task.rs @@ -20,6 +20,7 @@ use hotshot_testing::{ script::{Expectations, InputOrder, TaskScript}, serial, }; +use hotshot_types::data::EpochNumber; use hotshot_types::{ data::{null_block, DaProposal, PackedBundle, VidDisperse, ViewNumber}, traits::{ @@ -50,7 +51,11 @@ async fn test_vid_task() { // quorum membership for VID share distribution let quorum_membership = handle.hotshot.memberships.quorum_membership.clone(); - let mut vid = vid_scheme_from_view_number::(&quorum_membership, ViewNumber::new(0)); + let mut vid = vid_scheme_from_view_number::( + &quorum_membership, + ViewNumber::new(0), + EpochNumber::new(0), + ); let transactions = vec![TestTransaction::new(vec![0])]; let (payload, metadata) = >::from_transactions( @@ -85,8 +90,12 @@ async fn test_vid_task() { _pd: PhantomData, }; - let vid_disperse = - VidDisperse::from_membership(message.data.view_number, vid_disperse, &quorum_membership); + let vid_disperse = VidDisperse::from_membership( + message.data.view_number, + vid_disperse, + &quorum_membership, + EpochNumber::new(0), + ); let vid_proposal = Proposal { data: vid_disperse.clone(), @@ -104,7 +113,7 @@ async fn test_vid_task() { }, ViewNumber::new(2), vec1::vec1![null_block::builder_fee::( - quorum_membership.total_nodes(), + quorum_membership.total_nodes(EpochNumber::new(0)), ::Base::VERSION ) .unwrap()], @@ -125,7 +134,7 @@ async fn test_vid_task() { }, ViewNumber::new(2), vec1![null_block::builder_fee::( - quorum_membership.total_nodes(), + quorum_membership.total_nodes(EpochNumber::new(0)), ::Base::VERSION ) .unwrap()], diff --git a/crates/testing/tests/tests_1/view_sync_task.rs b/crates/testing/tests/tests_1/view_sync_task.rs index c0c4913981..5b2a9bf2d3 100644 --- a/crates/testing/tests/tests_1/view_sync_task.rs +++ b/crates/testing/tests/tests_1/view_sync_task.rs @@ -29,11 +29,11 @@ async fn test_view_sync_task() { let vote_data = ViewSyncPreCommitData { relay: 0, - round: ::Time::new(4), + round: ::View::new(4), }; let vote = hotshot_types::simple_vote::ViewSyncPreCommitVote::::create_signed_vote( vote_data, - ::Time::new(4), + ::View::new(4), hotshot_types::traits::consensus_api::ConsensusApi::public_key(&handle), hotshot_types::traits::consensus_api::ConsensusApi::private_key(&handle), &handle.hotshot.upgrade_lock, diff --git a/crates/testing/tests/tests_1/vote_dependency_handle.rs b/crates/testing/tests/tests_1/vote_dependency_handle.rs index 48fcffe5e7..6fe2979420 100644 --- a/crates/testing/tests/tests_1/vote_dependency_handle.rs +++ b/crates/testing/tests/tests_1/vote_dependency_handle.rs @@ -11,6 +11,7 @@ use hotshot_testing::{ predicates::{event::*, Predicate, PredicateResult}, view_generator::TestViewGenerator, }; +use hotshot_types::data::EpochNumber; use hotshot_types::{ consensus::OuterConsensus, data::ViewNumber, @@ -96,6 +97,7 @@ async fn test_vote_dependency_handle() { quorum_membership: handle.hotshot.memberships.quorum_membership.clone().into(), storage: Arc::clone(&handle.storage()), view_number, + epoch_number: EpochNumber::new(1), sender: event_sender.clone(), receiver: event_receiver.clone(), upgrade_lock: handle.hotshot.upgrade_lock.clone(), diff --git a/crates/testing/tests/tests_3/byzantine_tests.rs b/crates/testing/tests/tests_3/byzantine_tests.rs index 4687d3ff66..e5ac199aaf 100644 --- a/crates/testing/tests/tests_3/byzantine_tests.rs +++ b/crates/testing/tests/tests_3/byzantine_tests.rs @@ -21,7 +21,7 @@ use hotshot_testing::{ test_builder::{Behaviour, TestDescription}, }; use hotshot_types::{ - data::ViewNumber, + data::{EpochNumber, ViewNumber}, message::{GeneralConsensusMessage, MessageKind, SequencingMessage}, traits::{ election::Membership, @@ -176,7 +176,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)); + *transmit_type = TransmitType::Direct(membership.leader(vote.view_number() + 1 - nodes_count as u64, EpochNumber::new(0))); } else { {} } diff --git a/crates/testing/tests/tests_3/memory_network.rs b/crates/testing/tests/tests_3/memory_network.rs index 38b2da02e1..3050cd7d32 100644 --- a/crates/testing/tests/tests_3/memory_network.rs +++ b/crates/testing/tests/tests_3/memory_network.rs @@ -23,6 +23,7 @@ use hotshot_example_types::{ state_types::{TestInstanceState, TestValidatedState}, storage_types::TestStorage, }; +use hotshot_types::data::EpochNumber; use hotshot_types::{ data::ViewNumber, message::{DataMessage, Message, MessageKind, UpgradeLock}, @@ -53,7 +54,8 @@ pub struct Test; impl NodeType for Test { type AuctionResult = TestAuctionResult; - type Time = ViewNumber; + type View = ViewNumber; + type Epoch = EpochNumber; type BlockHeader = TestBlockHeader; type BlockPayload = TestBlockPayload; type SignatureKey = BLSPubKey; diff --git a/crates/types/src/consensus.rs b/crates/types/src/consensus.rs index 759127fef5..a351a09b96 100644 --- a/crates/types/src/consensus.rs +++ b/crates/types/src/consensus.rs @@ -43,7 +43,7 @@ pub type CommitmentMap = HashMap, T>; /// A type alias for `BTreeMap>>>` pub type VidShares = BTreeMap< - ::Time, + ::View, HashMap<::SignatureKey, Proposal>>, >; @@ -272,27 +272,30 @@ impl HotShotActionViews { #[derive(custom_debug::Debug, Clone)] pub struct Consensus { /// The validated states that are currently loaded in memory. - validated_state_map: BTreeMap>, + validated_state_map: BTreeMap>, /// All the VID shares we've received for current and future views. vid_shares: VidShares, /// All the DA certs we've received for current and future views. /// view -> DA cert - saved_da_certs: HashMap>, + saved_da_certs: HashMap>, /// View number that is currently on. - cur_view: TYPES::Time, + cur_view: TYPES::View, + + /// Epoch number that is currently on. + cur_epoch: TYPES::Epoch, /// Last proposals we sent out, None if we haven't proposed yet. /// Prevents duplicate proposals, and can be served to those trying to catchup - last_proposals: BTreeMap>>, + last_proposals: BTreeMap>>, /// last view had a successful decide event - last_decided_view: TYPES::Time, + last_decided_view: TYPES::View, /// The `locked_qc` view number - locked_view: TYPES::Time, + locked_view: TYPES::View, /// Map of leaf hash -> leaf /// - contains undecided leaves @@ -302,12 +305,12 @@ pub struct Consensus { /// Bundle of views which we performed the most recent action /// visibible to the network. Actions are votes and proposals /// for DA and Quorum - last_actions: HotShotActionViews, + last_actions: HotShotActionViews, /// Saved payloads. /// /// Encoded transactions for every view if we got a payload for that view. - saved_payloads: BTreeMap>, + saved_payloads: BTreeMap>, /// the highqc per spec high_qc: QuorumCertificate, @@ -387,14 +390,15 @@ impl Consensus { /// Constructor. #[allow(clippy::too_many_arguments)] pub fn new( - validated_state_map: BTreeMap>, - cur_view: TYPES::Time, - locked_view: TYPES::Time, - last_decided_view: TYPES::Time, - last_actioned_view: TYPES::Time, - last_proposals: BTreeMap>>, + validated_state_map: BTreeMap>, + cur_view: TYPES::View, + cur_epoch: TYPES::Epoch, + locked_view: TYPES::View, + last_decided_view: TYPES::View, + last_actioned_view: TYPES::View, + last_proposals: BTreeMap>>, saved_leaves: CommitmentMap>, - saved_payloads: BTreeMap>, + saved_payloads: BTreeMap>, high_qc: QuorumCertificate, metrics: Arc, ) -> Self { @@ -403,6 +407,7 @@ impl Consensus { vid_shares: BTreeMap::new(), saved_da_certs: HashMap::new(), cur_view, + cur_epoch, last_decided_view, last_proposals, last_actions: HotShotActionViews::from_view(last_actioned_view), @@ -415,17 +420,22 @@ impl Consensus { } /// Get the current view. - pub fn cur_view(&self) -> TYPES::Time { + pub fn cur_view(&self) -> TYPES::View { self.cur_view } + /// Get the current epoch. + pub fn cur_epoch(&self) -> TYPES::Epoch { + self.cur_epoch + } + /// Get the last decided view. - pub fn last_decided_view(&self) -> TYPES::Time { + pub fn last_decided_view(&self) -> TYPES::View { self.last_decided_view } /// Get the locked view. - pub fn locked_view(&self) -> TYPES::Time { + pub fn locked_view(&self) -> TYPES::View { self.locked_view } @@ -435,7 +445,7 @@ impl Consensus { } /// Get the validated state map. - pub fn validated_state_map(&self) -> &BTreeMap> { + pub fn validated_state_map(&self) -> &BTreeMap> { &self.validated_state_map } @@ -445,7 +455,7 @@ impl Consensus { } /// Get the saved payloads. - pub fn saved_payloads(&self) -> &BTreeMap> { + pub fn saved_payloads(&self) -> &BTreeMap> { &self.saved_payloads } @@ -455,19 +465,19 @@ impl Consensus { } /// Get the saved DA certs. - pub fn saved_da_certs(&self) -> &HashMap> { + pub fn saved_da_certs(&self) -> &HashMap> { &self.saved_da_certs } /// Get the map of our recent proposals - pub fn last_proposals(&self) -> &BTreeMap>> { + pub fn last_proposals(&self) -> &BTreeMap>> { &self.last_proposals } /// Update the current view. /// # Errors /// Can return an error when the new view_number is not higher than the existing view number. - pub fn update_view(&mut self, view_number: TYPES::Time) -> Result<()> { + pub fn update_view(&mut self, view_number: TYPES::View) -> Result<()> { ensure!( view_number > self.cur_view, "New view isn't newer than the current view." @@ -476,10 +486,22 @@ impl Consensus { Ok(()) } + /// Update the current epoch. + /// # Errors + /// 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, + "New epoch isn't newer than the current epoch." + ); + self.cur_epoch = epoch_number; + Ok(()) + } + /// Update the last actioned view internally for votes and proposals /// /// Returns true if the action is for a newer view than the last action of that type - pub fn update_action(&mut self, action: HotShotAction, view: TYPES::Time) -> bool { + pub fn update_action(&mut self, action: HotShotAction, view: TYPES::View) -> bool { let old_view = match action { HotShotAction::Vote => &mut self.last_actions.voted, HotShotAction::Propose => &mut self.last_actions.proposed, @@ -521,7 +543,7 @@ impl Consensus { > self .last_proposals .last_key_value() - .map_or(TYPES::Time::genesis(), |(k, _)| { *k }), + .map_or(TYPES::View::genesis(), |(k, _)| { *k }), "New view isn't newer than the previously proposed view." ); self.last_proposals @@ -533,7 +555,7 @@ impl Consensus { /// /// # Errors /// Can return an error when the new view_number is not higher than the existing decided view number. - pub fn update_last_decided_view(&mut self, view_number: TYPES::Time) -> Result<()> { + pub fn update_last_decided_view(&mut self, view_number: TYPES::View) -> Result<()> { ensure!( view_number > self.last_decided_view, "New view isn't newer than the previously decided view." @@ -546,7 +568,7 @@ impl Consensus { /// /// # Errors /// Can return an error when the new view_number is not higher than the existing locked view number. - pub fn update_locked_view(&mut self, view_number: TYPES::Time) -> Result<()> { + pub fn update_locked_view(&mut self, view_number: TYPES::View) -> Result<()> { ensure!( view_number > self.locked_view, "New view isn't newer than the previously locked view." @@ -562,7 +584,7 @@ impl Consensus { /// with the same view number. pub fn update_validated_state_map( &mut self, - view_number: TYPES::Time, + view_number: TYPES::View, new_view: View, ) -> Result<()> { if let Some(existing_view) = self.validated_state_map().get(&view_number) { @@ -607,7 +629,7 @@ impl Consensus { /// Can return an error when there's an existing payload corresponding to the same view number. pub fn update_saved_payloads( &mut self, - view_number: TYPES::Time, + view_number: TYPES::View, encoded_transaction: Arc<[u8]>, ) -> Result<()> { ensure!( @@ -635,7 +657,7 @@ impl Consensus { /// Add a new entry to the vid_shares map. pub fn update_vid_shares( &mut self, - view_number: TYPES::Time, + view_number: TYPES::View, disperse: Proposal>, ) { self.vid_shares @@ -645,7 +667,7 @@ impl Consensus { } /// Add a new entry to the da_certs map. - pub fn update_saved_da_certs(&mut self, view_number: TYPES::Time, cert: DaCertificate) { + pub fn update_saved_da_certs(&mut self, view_number: TYPES::View, cert: DaCertificate) { self.saved_da_certs.insert(view_number, cert); } @@ -654,8 +676,8 @@ impl Consensus { /// If the leaf or its ancestors are not found in storage pub fn visit_leaf_ancestors( &self, - start_from: TYPES::Time, - terminator: Terminator, + start_from: TYPES::View, + terminator: Terminator, ok_when_finished: bool, mut f: F, ) -> Result<(), HotShotError> @@ -714,7 +736,7 @@ impl Consensus { /// `saved_payloads` and `validated_state_map` fields of `Consensus`. /// # Panics /// On inconsistent stored entries - pub fn collect_garbage(&mut self, old_anchor_view: TYPES::Time, new_anchor_view: TYPES::Time) { + pub fn collect_garbage(&mut self, old_anchor_view: TYPES::View, new_anchor_view: TYPES::View) { // state check let anchor_entry = self .validated_state_map @@ -758,7 +780,7 @@ impl Consensus { /// Gets the validated state with the given view number, if in the state map. #[must_use] - pub fn state(&self, view_number: TYPES::Time) -> Option<&Arc> { + pub fn state(&self, view_number: TYPES::View) -> Option<&Arc> { match self.validated_state_map.get(&view_number) { Some(view) => view.state(), None => None, @@ -767,7 +789,7 @@ impl Consensus { /// Gets the validated state and state delta with the given view number, if in the state map. #[must_use] - pub fn state_and_delta(&self, view_number: TYPES::Time) -> StateAndDelta { + pub fn state_and_delta(&self, view_number: TYPES::View) -> StateAndDelta { match self.validated_state_map.get(&view_number) { Some(view) => view.state_and_delta(), None => (None, None), @@ -795,14 +817,16 @@ impl Consensus { #[instrument(skip_all, target = "Consensus", fields(view = *view))] pub async fn calculate_and_update_vid( consensus: OuterConsensus, - view: ::Time, + view: ::View, membership: Arc, private_key: &::PrivateKey, + epoch: TYPES::Epoch, ) -> Option<()> { let consensus = consensus.upgradable_read().await; let txns = consensus.saved_payloads().get(&view)?; let vid = - VidDisperse::calculate_vid_disperse(Arc::clone(txns), &membership, view, None).await; + VidDisperse::calculate_vid_disperse(Arc::clone(txns), &membership, view, epoch, None) + .await; let shares = VidDisperseShare::from_vid_disperse(vid); let mut consensus = ConsensusUpgradableReadLockGuard::upgrade(consensus).await; for share in shares { @@ -827,7 +851,7 @@ pub struct CommitmentAndMetadata { /// Builder fee data pub fees: Vec1>, /// View number this block is for - pub block_view: TYPES::Time, + pub block_view: TYPES::View, /// auction result that the block was produced from, if any pub auction_result: Option, } diff --git a/crates/types/src/data.rs b/crates/types/src/data.rs index e70af8c294..3518f2e8bd 100644 --- a/crates/types/src/data.rs +++ b/crates/types/src/data.rs @@ -9,14 +9,6 @@ //! This module provides types for representing consensus internal state, such as leaves, //! `HotShot`'s version of a block, and proposals, messages upon which to reach the consensus. -use std::{ - collections::BTreeMap, - fmt::{Debug, Display}, - hash::Hash, - marker::PhantomData, - sync::Arc, -}; - use anyhow::{ensure, Result}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use async_lock::RwLock; @@ -28,6 +20,13 @@ use derivative::Derivative; use jf_vid::{precomputable::Precomputable, VidDisperse as JfVidDisperse, VidScheme}; use rand::Rng; use serde::{Deserialize, Serialize}; +use std::{ + collections::BTreeMap, + fmt::{Debug, Display}, + hash::Hash, + marker::PhantomData, + sync::Arc, +}; use thiserror::Error; #[cfg(async_executor_impl = "tokio")] use tokio::task::spawn_blocking; @@ -56,6 +55,62 @@ use crate::{ vote::{Certificate, HasViewNumber}, }; +/// Implements `ConsensusTime`, `Display`, `Add`, `AddAssign`, `Deref` and `Sub` +/// for the given thing wrapper type around u64. +macro_rules! impl_u64_wrapper { + ($t:ty) => { + impl ConsensusTime for $t { + /// Create a genesis number (0) + fn genesis() -> Self { + Self(0) + } + /// Create a new number with the given value. + fn new(n: u64) -> Self { + Self(n) + } + /// Return the u64 format + fn u64(&self) -> u64 { + self.0 + } + } + + impl Display for $t { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } + } + + impl std::ops::Add for $t { + type Output = $t; + + fn add(self, rhs: u64) -> Self::Output { + Self(self.0 + rhs) + } + } + + impl std::ops::AddAssign for $t { + fn add_assign(&mut self, rhs: u64) { + self.0 += rhs; + } + } + + impl std::ops::Deref for $t { + type Target = u64; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl std::ops::Sub for $t { + type Output = $t; + fn sub(self, rhs: u64) -> Self::Output { + Self(self.0 - rhs) + } + } + }; +} + /// Type-safe wrapper around `u64` so we know the thing we're talking about is a view number. #[derive( Copy, @@ -73,27 +128,6 @@ use crate::{ )] pub struct ViewNumber(u64); -impl ConsensusTime for ViewNumber { - /// Create a genesis view number (0) - fn genesis() -> Self { - Self(0) - } - /// Create a new `ViewNumber` with the given value. - fn new(n: u64) -> Self { - Self(n) - } - /// Returen the u64 format - fn u64(&self) -> u64 { - self.0 - } -} - -impl Display for ViewNumber { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} - impl Committable for ViewNumber { fn commit(&self) -> Commitment { let builder = RawCommitmentBuilder::new("View Number Commitment"); @@ -101,34 +135,33 @@ impl Committable for ViewNumber { } } -impl std::ops::Add for ViewNumber { - type Output = ViewNumber; - - fn add(self, rhs: u64) -> Self::Output { - Self(self.0 + rhs) - } -} - -impl std::ops::AddAssign for ViewNumber { - fn add_assign(&mut self, rhs: u64) { - self.0 += rhs; - } -} +impl_u64_wrapper!(ViewNumber); -impl std::ops::Deref for ViewNumber { - type Target = u64; +/// Type-safe wrapper around `u64` so we know the thing we're talking about is a epoch number. +#[derive( + Copy, + Clone, + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Serialize, + Deserialize, + CanonicalSerialize, + CanonicalDeserialize, +)] +pub struct EpochNumber(u64); - fn deref(&self) -> &Self::Target { - &self.0 +impl Committable for EpochNumber { + fn commit(&self) -> Commitment { + let builder = RawCommitmentBuilder::new("Epoch Number Commitment"); + builder.u64(self.0).finalize() } } -impl std::ops::Sub for ViewNumber { - type Output = ViewNumber; - fn sub(self, rhs: u64) -> Self::Output { - Self(self.0 - rhs) - } -} +impl_u64_wrapper!(EpochNumber); /// A proposal to start providing data availability for a block. #[derive(custom_debug::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)] @@ -139,7 +172,7 @@ pub struct DaProposal { /// Metadata of the block to be applied. pub metadata: >::Metadata, /// View this proposal applies to - pub view_number: TYPES::Time, + pub view_number: TYPES::View, } /// A proposal to upgrade the network @@ -152,7 +185,7 @@ where /// The information about which version we are upgrading to. pub upgrade_proposal: UpgradeProposalData, /// View this proposal applies to - pub view_number: TYPES::Time, + pub view_number: TYPES::View, } /// VID dispersal data @@ -163,7 +196,7 @@ where #[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)] pub struct VidDisperse { /// The view number for which this VID data is intended - pub view_number: TYPES::Time, + pub view_number: TYPES::View, /// Block payload commitment pub payload_commitment: VidCommitment, /// A storage node's key and its corresponding VID share @@ -173,16 +206,17 @@ pub struct VidDisperse { } impl VidDisperse { - /// Create VID dispersal from a specified membership + /// Create VID dispersal from a specified membership for a given epoch. /// Uses the specified function to calculate share dispersal /// Allows for more complex stake table functionality pub fn from_membership( - view_number: TYPES::Time, + view_number: TYPES::View, mut vid_disperse: JfVidDisperse, membership: &TYPES::Membership, + epoch: TYPES::Epoch, ) -> Self { let shares = membership - .committee_members(view_number) + .committee_members(view_number, epoch) .iter() .map(|node| (node.clone(), vid_disperse.shares.remove(0))) .collect(); @@ -195,7 +229,7 @@ impl VidDisperse { } } - /// Calculate the vid disperse information from the payload given a view and membership, + /// Calculate the vid disperse information from the payload given a view, epoch and membership, /// optionally using precompute data from builder /// /// # Panics @@ -204,10 +238,11 @@ impl VidDisperse { pub async fn calculate_vid_disperse( txns: Arc<[u8]>, membership: &Arc, - view: TYPES::Time, + view: TYPES::View, + epoch: TYPES::Epoch, precompute_data: Option, ) -> Self { - let num_nodes = membership.total_nodes(); + let num_nodes = membership.total_nodes(epoch); let vid_disperse = spawn_blocking(move || { precompute_data @@ -222,7 +257,7 @@ impl VidDisperse { // Unwrap here will just propagate any panic from the spawned task, it's not a new place we can panic. let vid_disperse = vid_disperse.unwrap(); - Self::from_membership(view, vid_disperse, membership.as_ref()) + Self::from_membership(view, vid_disperse, membership.as_ref(), epoch) } } @@ -239,7 +274,7 @@ pub enum ViewChangeEvidence { impl ViewChangeEvidence { /// Check that the given ViewChangeEvidence is relevant to the current view. - pub fn is_valid_for_view(&self, view: &TYPES::Time) -> bool { + pub fn is_valid_for_view(&self, view: &TYPES::View) -> bool { match self { ViewChangeEvidence::Timeout(timeout_cert) => timeout_cert.data().view == *view - 1, ViewChangeEvidence::ViewSync(view_sync_cert) => view_sync_cert.view_number == *view, @@ -251,7 +286,7 @@ impl ViewChangeEvidence { /// VID share and associated metadata for a single node pub struct VidDisperseShare { /// The view number for which this VID data is intended - pub view_number: TYPES::Time, + pub view_number: TYPES::View, /// Block payload commitment pub payload_commitment: VidCommitment, /// A storage node's key and its corresponding VID share @@ -353,7 +388,7 @@ pub struct QuorumProposal { pub block_header: TYPES::BlockHeader, /// CurView from leader when proposing leaf - pub view_number: TYPES::Time, + pub view_number: TYPES::View, /// Per spec, justification pub justify_qc: QuorumCertificate, @@ -369,31 +404,31 @@ pub struct QuorumProposal { } impl HasViewNumber for DaProposal { - fn view_number(&self) -> TYPES::Time { + fn view_number(&self) -> TYPES::View { self.view_number } } impl HasViewNumber for VidDisperse { - fn view_number(&self) -> TYPES::Time { + fn view_number(&self) -> TYPES::View { self.view_number } } impl HasViewNumber for VidDisperseShare { - fn view_number(&self) -> TYPES::Time { + fn view_number(&self) -> TYPES::View { self.view_number } } impl HasViewNumber for QuorumProposal { - fn view_number(&self) -> TYPES::Time { + fn view_number(&self) -> TYPES::View { self.view_number } } impl HasViewNumber for UpgradeProposal { - fn view_number(&self) -> TYPES::Time { + fn view_number(&self) -> TYPES::View { self.view_number } } @@ -430,7 +465,7 @@ pub trait TestableLeaf { #[serde(bound(deserialize = ""))] pub struct Leaf { /// CurView from leader when proposing leaf - view_number: TYPES::Time, + view_number: TYPES::View, /// Per spec, justification justify_qc: QuorumCertificate, @@ -503,7 +538,7 @@ impl QuorumCertificate { // since this is genesis, we should never have a decided upgrade certificate. let upgrade_lock = UpgradeLock::::new(); - let genesis_view = ::genesis(); + let genesis_view = ::genesis(); let data = QuorumData { leaf_commit: Leaf::genesis(validated_state, instance_state) @@ -563,13 +598,13 @@ impl Leaf { let justify_qc = QuorumCertificate::new( null_quorum_data.clone(), null_quorum_data.commit(), - ::genesis(), + ::genesis(), None, PhantomData, ); Self { - view_number: TYPES::Time::genesis(), + view_number: TYPES::View::genesis(), justify_qc, parent_commitment: null_quorum_data.leaf_commit, upgrade_certificate: None, @@ -579,7 +614,7 @@ impl Leaf { } /// Time when this leaf was created. - pub fn view_number(&self) -> TYPES::Time { + pub fn view_number(&self) -> TYPES::View { self.view_number } /// Height of this leaf in the chain. @@ -866,7 +901,7 @@ pub struct PackedBundle { pub metadata: >::Metadata, /// The view number that this block is associated with. - pub view_number: TYPES::Time, + pub view_number: TYPES::View, /// The sequencing fee for submitting bundles. pub sequencing_fees: Vec1>, @@ -883,7 +918,7 @@ impl PackedBundle { pub fn new( encoded_transactions: Arc<[u8]>, metadata: >::Metadata, - view_number: TYPES::Time, + view_number: TYPES::View, sequencing_fees: Vec1>, vid_precompute: Option, auction_result: Option, diff --git a/crates/types/src/error.rs b/crates/types/src/error.rs index f76c46906f..80c1baae8d 100644 --- a/crates/types/src/error.rs +++ b/crates/types/src/error.rs @@ -41,7 +41,7 @@ pub enum HotShotError { #[error("View {view_number} timed out: {state:?}")] ViewTimedOut { /// The view number that timed out - view_number: TYPES::Time, + view_number: TYPES::View, /// The state that the round was in when it timed out state: RoundTimedoutState, }, diff --git a/crates/types/src/event.rs b/crates/types/src/event.rs index e71d0c8196..ca833c9f5d 100644 --- a/crates/types/src/event.rs +++ b/crates/types/src/event.rs @@ -25,7 +25,7 @@ use crate::{ #[serde(bound(deserialize = "TYPES: NodeType"))] pub struct Event { /// The view number that this event originates from - pub view_number: TYPES::Time, + pub view_number: TYPES::View, /// The underlying event pub event: EventType, } @@ -128,17 +128,17 @@ pub enum EventType { /// A replica task was canceled by a timeout interrupt ReplicaViewTimeout { /// The view that timed out - view_number: TYPES::Time, + view_number: TYPES::View, }, /// The view has finished. If values were decided on, a `Decide` event will also be emitted. ViewFinished { /// The view number that has just finished - view_number: TYPES::Time, + view_number: TYPES::View, }, /// The view timed out ViewTimeout { /// The view that timed out - view_number: TYPES::Time, + view_number: TYPES::View, }, /// New transactions were received from the network /// or submitted to the network by us diff --git a/crates/types/src/hotshot_config_file.rs b/crates/types/src/hotshot_config_file.rs index 37457b8d28..08b81af3e4 100644 --- a/crates/types/src/hotshot_config_file.rs +++ b/crates/types/src/hotshot_config_file.rs @@ -65,6 +65,8 @@ pub struct HotShotConfigFile { pub builder_urls: Vec1, /// Upgrade config pub upgrade: UpgradeConfig, + /// Number of blocks in an epoch, zero means there are no epochs + pub epoch_height: u64, } impl From> for HotShotConfig { @@ -98,6 +100,7 @@ impl From> for HotShotConfig { stop_proposing_time: val.upgrade.stop_proposing_time, start_voting_time: val.upgrade.start_voting_time, stop_voting_time: val.upgrade.stop_voting_time, + epoch_height: val.epoch_height, } } } @@ -147,6 +150,7 @@ impl HotShotConfigFile { data_request_delay: Some(Duration::from_millis(REQUEST_DATA_DELAY)), builder_urls: default_builder_urls(), upgrade: UpgradeConfig::default(), + epoch_height: 0, } } } diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index 806667bf9b..897fef5c73 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -232,6 +232,8 @@ pub struct HotShotConfig { pub start_voting_time: u64, /// Unix time in seconds at which we stop voting on an upgrade. To prevent voting on an upgrade, set stop_voting_time <= start_voting_time. pub stop_voting_time: u64, + /// Number of blocks in an epoch, zero means there are no epochs + pub epoch_height: u64, } impl HotShotConfig { diff --git a/crates/types/src/message.rs b/crates/types/src/message.rs index ab72986652..42ae087b9a 100644 --- a/crates/types/src/message.rs +++ b/crates/types/src/message.rs @@ -67,7 +67,7 @@ impl fmt::Debug for Message { impl HasViewNumber for Message { /// get the view number out of a message - fn view_number(&self) -> TYPES::Time { + fn view_number(&self) -> TYPES::View { self.kind.view_number() } } @@ -147,16 +147,16 @@ impl From> for MessageKind { } impl ViewMessage for MessageKind { - fn view_number(&self) -> TYPES::Time { + fn view_number(&self) -> TYPES::View { match &self { MessageKind::Consensus(message) => message.view_number(), MessageKind::Data(DataMessage::SubmitTransaction(_, v)) => *v, MessageKind::Data(DataMessage::RequestData(msg)) => msg.view, MessageKind::Data(DataMessage::DataResponse(msg)) => match msg { ResponseMessage::Found(m) => m.view_number(), - ResponseMessage::NotFound | ResponseMessage::Denied => TYPES::Time::new(1), + ResponseMessage::NotFound | ResponseMessage::Denied => TYPES::View::new(1), }, - MessageKind::External(_) => TYPES::Time::new(1), + MessageKind::External(_) => TYPES::View::new(1), } } @@ -248,7 +248,7 @@ pub enum SequencingMessage { impl SequencingMessage { /// Get the view number this message relates to - fn view_number(&self) -> TYPES::Time { + fn view_number(&self) -> TYPES::View { match &self { SequencingMessage::General(general_message) => { match general_message { @@ -342,7 +342,7 @@ pub enum DataMessage { /// Contains a transaction to be submitted /// TODO rethink this when we start to send these messages /// we only need the view number for broadcast - SubmitTransaction(TYPES::Transaction, TYPES::Time), + SubmitTransaction(TYPES::Transaction, TYPES::View), /// A request for data RequestData(DataRequest), /// A response to a data request @@ -373,10 +373,11 @@ where pub async fn validate_signature( &self, quorum_membership: &TYPES::Membership, + epoch: TYPES::Epoch, upgrade_lock: &UpgradeLock, ) -> Result<()> { let view_number = self.data.view_number(); - let view_leader_key = quorum_membership.leader(view_number); + let view_leader_key = quorum_membership.leader(view_number, epoch); let proposed_leaf = Leaf::from_quorum_proposal(&self.data); ensure!( @@ -424,7 +425,7 @@ impl UpgradeLock { /// /// # Errors /// Returns an error if we do not support the version required by the decided upgrade certificate. - pub async fn version(&self, view: TYPES::Time) -> Result { + pub async fn version(&self, view: TYPES::View) -> Result { let upgrade_certificate = self.decided_upgrade_certificate.read().await; let version = match *upgrade_certificate { @@ -448,7 +449,7 @@ impl UpgradeLock { /// Calculate the version applied in a view, based on the provided upgrade lock. /// /// This function does not fail, since it does not check that the version is supported. - pub async fn version_infallible(&self, view: TYPES::Time) -> Version { + pub async fn version_infallible(&self, view: TYPES::View) -> Version { let upgrade_certificate = self.decided_upgrade_certificate.read().await; match *upgrade_certificate { diff --git a/crates/types/src/request_response.rs b/crates/types/src/request_response.rs index d27cc27313..6829d19743 100644 --- a/crates/types/src/request_response.rs +++ b/crates/types/src/request_response.rs @@ -16,7 +16,7 @@ use crate::traits::{node_implementation::NodeType, signature_key::SignatureKey}; /// A signed request for a proposal. pub struct ProposalRequestPayload { /// The view number that we're requesting a proposal for. - pub view_number: TYPES::Time, + pub view_number: TYPES::View, /// Our public key. The ensures that the receipient can reply to /// us directly. diff --git a/crates/types/src/simple_certificate.rs b/crates/types/src/simple_certificate.rs index 6e9e7bbbfb..bbdc88eb05 100644 --- a/crates/types/src/simple_certificate.rs +++ b/crates/types/src/simple_certificate.rs @@ -78,7 +78,7 @@ pub struct SimpleCertificate, /// Which view this QC relates to - pub view_number: TYPES::Time, + pub view_number: TYPES::View, /// assembled signature for certificate aggregation pub signatures: Option<::QcType>, /// phantom data for `THRESHOLD` and `TYPES` @@ -92,7 +92,7 @@ impl> pub fn new( data: VOTEABLE, vote_commitment: Commitment, - view_number: TYPES::Time, + view_number: TYPES::View, signatures: Option<::QcType>, pd: PhantomData<(TYPES, THRESHOLD)>, ) -> Self { @@ -133,7 +133,7 @@ impl> vote_commitment: Commitment>, data: Self::Voteable, sig: ::QcType, - view: TYPES::Time, + view: TYPES::View, ) -> Self { let vote_commitment_bytes: [u8; 32] = vote_commitment.into(); @@ -148,13 +148,14 @@ impl> async fn is_valid_cert, V: Versions>( &self, membership: &MEMBERSHIP, + epoch: TYPES::Epoch, upgrade_lock: &UpgradeLock, ) -> bool { - if self.view_number == TYPES::Time::genesis() { + if self.view_number == TYPES::View::genesis() { return true; } let real_qc_pp = ::public_parameter( - membership.stake_table(), + membership.stake_table(epoch), U256::from(Self::threshold(membership)), ); let Ok(commit) = self.data_commitment(upgrade_lock).await else { @@ -187,7 +188,7 @@ impl> impl> HasViewNumber for SimpleCertificate { - fn view_number(&self) -> TYPES::Time { + fn view_number(&self) -> TYPES::View { self.view_number } } @@ -205,7 +206,7 @@ impl UpgradeCertificate { /// Returns an error when the certificate is no longer relevant pub async fn is_relevant( &self, - view_number: TYPES::Time, + view_number: TYPES::View, decided_upgrade_certificate: Arc>>, ) -> Result<()> { let decided_upgrade_certificate_read = decided_upgrade_certificate.read().await; @@ -226,11 +227,13 @@ impl UpgradeCertificate { pub async fn validate( upgrade_certificate: &Option, quorum_membership: &TYPES::Membership, + epoch: TYPES::Epoch, upgrade_lock: &UpgradeLock, ) -> Result<()> { if let Some(ref cert) = upgrade_certificate { ensure!( - cert.is_valid_cert(quorum_membership, upgrade_lock).await, + cert.is_valid_cert(quorum_membership, epoch, upgrade_lock) + .await, "Invalid upgrade certificate." ); Ok(()) @@ -241,7 +244,7 @@ impl UpgradeCertificate { /// Given an upgrade certificate and a view, tests whether the view is in the period /// where we are upgrading, which requires that we propose with null blocks. - pub fn upgrading_in(&self, view: TYPES::Time) -> bool { + pub fn upgrading_in(&self, view: TYPES::View) -> bool { view > self.data.old_version_last_view && view < self.data.new_version_first_view } } diff --git a/crates/types/src/simple_vote.rs b/crates/types/src/simple_vote.rs index 45e131f756..1c1fcba31b 100644 --- a/crates/types/src/simple_vote.rs +++ b/crates/types/src/simple_vote.rs @@ -41,7 +41,7 @@ pub struct DaData { /// Data used for a timeout vote. pub struct TimeoutData { /// View the timeout is for - pub view: TYPES::Time, + pub view: TYPES::View, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)] /// Data used for a VID vote. @@ -55,7 +55,7 @@ pub struct ViewSyncPreCommitData { /// The relay this vote is intended for pub relay: u64, /// The view number we are trying to sync on - pub round: TYPES::Time, + pub round: TYPES::View, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)] /// Data used for a Commit vote. @@ -63,7 +63,7 @@ pub struct ViewSyncCommitData { /// The relay this vote is intended for pub relay: u64, /// The view number we are trying to sync on - pub round: TYPES::Time, + pub round: TYPES::View, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)] /// Data used for a Finalize vote. @@ -71,7 +71,7 @@ pub struct ViewSyncFinalizeData { /// The relay this vote is intended for pub relay: u64, /// The view number we are trying to sync on - pub round: TYPES::Time, + pub round: TYPES::View, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)] /// Data used for a Upgrade vote. @@ -82,13 +82,13 @@ pub struct UpgradeProposalData { pub new_version: Version, /// The last view in which we are allowed to reach a decide on this upgrade. /// If it is not decided by that view, we discard it. - pub decide_by: TYPES::Time, + pub decide_by: TYPES::View, /// A unique identifier for the specific protocol being voted on. pub new_version_hash: Vec, /// The last block for which the old version will be in effect. - pub old_version_last_view: TYPES::Time, + pub old_version_last_view: TYPES::View, /// The first block for which the new version will be in effect. - pub new_version_first_view: TYPES::Time, + pub new_version_first_view: TYPES::View, } /// Marker trait for data or commitments that can be voted on. @@ -123,11 +123,11 @@ pub struct SimpleVote { /// The leaf commitment being voted on. pub data: DATA, /// The view this vote was cast for - pub view_number: TYPES::Time, + pub view_number: TYPES::View, } impl HasViewNumber for SimpleVote { - fn view_number(&self) -> ::Time { + fn view_number(&self) -> ::View { self.view_number } } @@ -158,7 +158,7 @@ impl SimpleVote { /// If we are unable to sign the data pub async fn create_signed_vote( data: DATA, - view: TYPES::Time, + view: TYPES::View, pub_key: &TYPES::SignatureKey, private_key: &::PrivateKey, upgrade_lock: &UpgradeLock, @@ -187,7 +187,7 @@ pub struct VersionedVoteData { data: DATA, /// view number - view: TYPES::Time, + view: TYPES::View, /// version applied to the view number version: Version, @@ -204,7 +204,7 @@ impl VersionedVoteData, ) -> Result { let version = upgrade_lock.version(view).await?; @@ -222,7 +222,7 @@ impl VersionedVoteData, ) -> Self { let version = upgrade_lock.version_infallible(view).await; @@ -303,7 +303,7 @@ impl Committable for UpgradeProposalData { /// This implements commit for all the types which contain a view and relay public key. fn view_and_relay_commit( - view: TYPES::Time, + view: TYPES::View, relay: u64, tag: &str, ) -> Commitment { diff --git a/crates/types/src/traits/auction_results_provider.rs b/crates/types/src/traits/auction_results_provider.rs index 283ada9d68..7fcd8498e4 100644 --- a/crates/types/src/traits/auction_results_provider.rs +++ b/crates/types/src/traits/auction_results_provider.rs @@ -20,5 +20,5 @@ use super::node_implementation::NodeType; pub trait AuctionResultsProvider: Send + Sync + Clone { /// Fetches the auction result for a view. Does not cache the result, /// subsequent calls will invoke additional wasted calls. - async fn fetch_auction_result(&self, view_number: TYPES::Time) -> Result; + async fn fetch_auction_result(&self, view_number: TYPES::View) -> Result; } diff --git a/crates/types/src/traits/election.rs b/crates/types/src/traits/election.rs index b03bf8a843..43d0ebf12f 100644 --- a/crates/types/src/traits/election.rs +++ b/crates/types/src/traits/election.rs @@ -23,33 +23,45 @@ pub trait Membership: committee_topic: Topic, ) -> Self; - /// Get all participants in the committee (including their stake) - fn stake_table(&self) -> Vec<::StakeTableEntry>; + /// Get all participants in the committee (including their stake) for a specific epoch + fn stake_table( + &self, + epoch: TYPES::Epoch, + ) -> Vec<::StakeTableEntry>; - /// Get all participants in the committee for a specific view - fn committee_members(&self, view_number: TYPES::Time) -> BTreeSet; + /// 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, + ) -> BTreeSet; - /// Get all leaders in the committee for a specific view - fn committee_leaders(&self, view_number: TYPES::Time) -> 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, + ) -> BTreeSet; /// Get the stake table entry for a public key, returns `None` if the - /// key is not in the table + /// key is not in the table for a specific epoch fn stake( &self, pub_key: &TYPES::SignatureKey, + epoch: TYPES::Epoch, ) -> Option<::StakeTableEntry>; - /// See if a node has stake in the committee - fn has_stake(&self, pub_key: &TYPES::SignatureKey) -> bool; + /// 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; - /// The leader of the committee for view `view_number`. - fn leader(&self, view_number: TYPES::Time) -> TYPES::SignatureKey; + /// The leader of the committee for view `view_number` in an epoch `epoch`. + fn leader(&self, view_number: TYPES::View, epoch: TYPES::Epoch) -> TYPES::SignatureKey; /// Get the network topic for the committee fn committee_topic(&self) -> Topic; - /// Returns the number of total nodes in the committee - fn total_nodes(&self) -> usize; + /// Returns the number of total nodes in the committee in an epoch `epoch` + fn total_nodes(&self, epoch: TYPES::Epoch) -> usize; /// Returns the threshold for a specific `Membership` implementation fn success_threshold(&self) -> NonZeroU64; diff --git a/crates/types/src/traits/network.rs b/crates/types/src/traits/network.rs index 97851e6963..27fe0ec3c9 100644 --- a/crates/types/src/traits/network.rs +++ b/crates/types/src/traits/network.rs @@ -126,7 +126,7 @@ pub trait Id: Eq + PartialEq + Hash {} /// a message pub trait ViewMessage { /// get the view out of the message - fn view_number(&self) -> TYPES::Time; + fn view_number(&self) -> TYPES::View; // TODO move out of this trait. /// get the purpose of the message fn purpose(&self) -> MessagePurpose; @@ -139,7 +139,7 @@ pub struct DataRequest { /// Request pub request: RequestKind, /// View this message is for - pub view: TYPES::Time, + pub view: TYPES::View, /// signature of the Sha256 hash of the data so outsiders can't use know /// public keys with stake. pub signature: ::PureAssembledSignatureType, @@ -149,11 +149,11 @@ pub struct DataRequest { #[derive(Serialize, Deserialize, Derivative, Clone, Debug, PartialEq, Eq, Hash)] pub enum RequestKind { /// Request VID data by our key and the VID commitment - Vid(TYPES::Time, TYPES::SignatureKey), + Vid(TYPES::View, TYPES::SignatureKey), /// Request a DA proposal for a certain view - DaProposal(TYPES::Time), + DaProposal(TYPES::View), /// Request for quorum proposal for a view - Proposal(TYPES::Time), + Proposal(TYPES::View), } /// A response for a request. `SequencingMessage` is the same as other network messages @@ -271,8 +271,12 @@ pub trait ConnectedNetwork: Clone + Send + Sync + 'st /// Update view can be used for any reason, but mostly it's for canceling tasks, /// and looking up the address of the leader of a future view. - async fn update_view<'a, TYPES>(&'a self, _view: u64, _membership: &TYPES::Membership) - where + async fn update_view<'a, TYPES>( + &'a self, + _view: u64, + _epoch: u64, + _membership: &TYPES::Membership, + ) where TYPES: NodeType + 'a, { } diff --git a/crates/types/src/traits/node_implementation.rs b/crates/types/src/traits/node_implementation.rs index 060df7adf3..c84031218c 100644 --- a/crates/types/src/traits/node_implementation.rs +++ b/crates/types/src/traits/node_implementation.rs @@ -210,7 +210,9 @@ pub trait NodeType: /// The time type that this hotshot setup is using. /// /// This should be the same `Time` that `ValidatedState::Time` is using. - type Time: ConsensusTime + Display; + type View: ConsensusTime + Display; + /// Same as above but for epoch. + type Epoch: ConsensusTime + Display; /// The AuctionSolverResult is a type that holds the data associated with a particular solver /// run, for a particular view. type AuctionResult: Debug @@ -244,7 +246,7 @@ pub trait NodeType: type InstanceState: InstanceState; /// The validated state type that this hotshot setup is using. - type ValidatedState: ValidatedState; + type ValidatedState: ValidatedState; /// Membership used for this implementation type Membership: Membership; diff --git a/crates/types/src/traits/storage.rs b/crates/types/src/traits/storage.rs index c11ba771b3..d400ce455b 100644 --- a/crates/types/src/traits/storage.rs +++ b/crates/types/src/traits/storage.rs @@ -36,7 +36,7 @@ pub trait Storage: Send + Sync + Clone { proposal: &Proposal>, ) -> Result<()>; /// Record a HotShotAction taken. - async fn record_action(&self, view: TYPES::Time, action: HotShotAction) -> Result<()>; + async fn record_action(&self, view: TYPES::View, action: HotShotAction) -> Result<()>; /// Update the current high QC in storage. async fn update_high_qc(&self, high_qc: QuorumCertificate) -> Result<()>; /// Update the currently undecided state of consensus. This includes the undecided leaf chain, @@ -44,7 +44,7 @@ pub trait Storage: Send + Sync + Clone { async fn update_undecided_state( &self, leafs: CommitmentMap>, - state: BTreeMap>, + state: BTreeMap>, ) -> Result<()>; /// Upgrade the current decided upgrade certificate in storage. async fn update_decided_upgrade_certificate( diff --git a/crates/types/src/utils.rs b/crates/types/src/utils.rs index 8795ab028a..e3d19a8286 100644 --- a/crates/types/src/utils.rs +++ b/crates/types/src/utils.rs @@ -150,7 +150,7 @@ pub struct View { #[derive(Debug, Clone)] pub struct RoundFinishedEvent { /// The round that finished - pub view_number: TYPES::Time, + pub view_number: TYPES::View, } /// Whether or not to stop inclusively or exclusively when walking diff --git a/crates/types/src/vote.rs b/crates/types/src/vote.rs index 1376aa0d03..882512eae9 100644 --- a/crates/types/src/vote.rs +++ b/crates/types/src/vote.rs @@ -48,7 +48,7 @@ pub trait Vote: HasViewNumber { /// Any type that is associated with a view pub trait HasViewNumber { /// Returns the view number the type refers to. - fn view_number(&self) -> TYPES::Time; + fn view_number(&self) -> TYPES::View; } /** @@ -68,13 +68,14 @@ pub trait Certificate: HasViewNumber { vote_commitment: Commitment>, data: Self::Voteable, sig: ::QcType, - view: TYPES::Time, + view: TYPES::View, ) -> Self; - /// Checks if the cert is valid + /// Checks if the cert is valid in the given epoch fn is_valid_cert, V: Versions>( &self, membership: &MEMBERSHIP, + epoch: TYPES::Epoch, upgrade_lock: &UpgradeLock, ) -> impl std::future::Future; /// Returns the amount of stake needed to create this certificate @@ -130,12 +131,14 @@ impl< V: Versions, > VoteAccumulator { - /// Add a vote to the total accumulated votes. Returns the accumulator or the certificate if we + /// Add a vote to the total accumulated votes for the given epoch. + /// Returns the accumulator or the certificate if we /// have accumulated enough votes to exceed the threshold for creating a certificate. pub async fn accumulate( &mut self, vote: &VOTE, membership: &TYPES::Membership, + epoch: TYPES::Epoch, ) -> Either<(), CERT> { let key = vote.signing_key(); @@ -158,10 +161,10 @@ impl< return Either::Left(()); } - let Some(stake_table_entry) = membership.stake(&key) else { + let Some(stake_table_entry) = membership.stake(&key, epoch) else { return Either::Left(()); }; - let stake_table = membership.stake_table(); + let stake_table = membership.stake_table(epoch); let Some(vote_node_id) = stake_table .iter() .position(|x| *x == stake_table_entry.clone()) @@ -184,7 +187,7 @@ impl< let (signers, sig_list) = self .signers .entry(vote_commitment) - .or_insert((bitvec![0; membership.total_nodes()], Vec::new())); + .or_insert((bitvec![0; membership.total_nodes(epoch)], Vec::new())); if signers.get(vote_node_id).as_deref() == Some(&true) { error!("Node id is already in signers list"); return Either::Left(());