From edebf50fa3b7e0d5bda9b679e633c3a0fc24af35 Mon Sep 17 00:00:00 2001 From: ss-es <155648797+ss-es@users.noreply.github.com> Date: Thu, 22 Aug 2024 11:16:32 -0400 Subject: [PATCH] Add versioning to vote commitment calculations (#3584) --- crates/example-types/src/node_types.rs | 129 ++++++++++++++++ crates/hotshot/src/tasks/mod.rs | 43 +++++- crates/hotshot/src/tasks/task_state.rs | 6 +- crates/task-impls/src/consensus/handlers.rs | 25 +++- crates/task-impls/src/consensus/mod.rs | 41 +++-- crates/task-impls/src/consensus2/handlers.rs | 14 +- crates/task-impls/src/consensus2/mod.rs | 7 +- crates/task-impls/src/da.rs | 38 +++-- crates/task-impls/src/helpers.rs | 45 ++++-- .../src/quorum_proposal/handlers.rs | 3 + crates/task-impls/src/quorum_proposal/mod.rs | 5 +- .../src/quorum_proposal_recv/handlers.rs | 12 +- crates/task-impls/src/quorum_vote/mod.rs | 8 +- crates/task-impls/src/upgrade.rs | 18 +-- crates/task-impls/src/view_sync.rs | 101 +++++++++---- crates/task-impls/src/vote_collection.rs | 80 +++++----- crates/testing/src/helpers.rs | 140 +++++++++++------- crates/testing/src/view_generator.rs | 62 ++++++-- .../testing/tests/tests_1/consensus_task.rs | 24 +-- crates/testing/tests/tests_1/da_task.rs | 24 ++- crates/testing/tests/tests_1/message.rs | 9 +- .../tests_1/quorum_proposal_recv_task.rs | 4 +- .../tests/tests_1/quorum_proposal_task.rs | 29 ++-- .../testing/tests/tests_1/quorum_vote_task.rs | 2 +- .../tests/tests_1/test_with_failures_2.rs | 41 ++++- .../tests_1/upgrade_task_with_consensus.rs | 32 ++-- .../tests_1/upgrade_task_with_proposal.rs | 25 ++-- .../tests/tests_1/upgrade_task_with_vote.rs | 4 +- .../testing/tests/tests_1/view_sync_task.rs | 5 +- crates/testing/tests/tests_5/fake_solver.rs | 4 +- crates/types/src/data.rs | 26 ++-- crates/types/src/simple_certificate.rs | 65 ++++++-- crates/types/src/simple_vote.rs | 91 ++++++++++-- crates/types/src/vote.rs | 70 +++++++-- 34 files changed, 908 insertions(+), 324 deletions(-) diff --git a/crates/example-types/src/node_types.rs b/crates/example-types/src/node_types.rs index 390b5472d2..a3ff669ab3 100644 --- a/crates/example-types/src/node_types.rs +++ b/crates/example-types/src/node_types.rs @@ -160,3 +160,132 @@ impl Versions for MarketplaceUpgradeTestVersions { type Marketplace = StaticVersion<0, 3>; } + +#[derive(Clone, Debug, Copy)] +pub struct MarketplaceTestVersions {} + +impl Versions for MarketplaceTestVersions { + type Base = StaticVersion<0, 3>; + type Upgrade = StaticVersion<0, 3>; + const UPGRADE_HASH: [u8; 32] = [ + 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, + ]; + + type Marketplace = StaticVersion<0, 3>; +} + +#[cfg(test)] +mod tests { + use committable::{Commitment, Committable}; + use hotshot_types::{ + message::UpgradeLock, simple_vote::VersionedVoteData, + traits::node_implementation::ConsensusTime, + }; + use serde::{Deserialize, Serialize}; + + use crate::node_types::{MarketplaceTestVersions, NodeType, TestTypes, TestVersions}; + #[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Hash, Eq)] + /// Dummy data used for test + struct TestData { + data: u64, + } + + impl Committable for TestData { + fn commit(&self) -> Commitment { + committable::RawCommitmentBuilder::new("Test data") + .u64(self.data) + .finalize() + } + } + + #[cfg_attr(async_executor_impl = "tokio", tokio::test(flavor = "multi_thread"))] + #[cfg_attr(async_executor_impl = "async-std", async_std::test)] + async fn test_versioned_commitment() { + let view = ::Time::new(0); + let upgrade_lock = UpgradeLock::new(); + + let data = TestData { data: 10 }; + let data_commitment: [u8; 32] = data.commit().into(); + + let versioned_data = + VersionedVoteData::::new(data, view, &upgrade_lock) + .await + .unwrap(); + let versioned_data_commitment: [u8; 32] = versioned_data.commit().into(); + + assert_eq!(versioned_data_commitment, data_commitment); + } + + #[cfg_attr(async_executor_impl = "tokio", tokio::test(flavor = "multi_thread"))] + #[cfg_attr(async_executor_impl = "async-std", async_std::test)] + /// Test that the view number affects the commitment post-marketplace + async fn test_versioned_commitment_includes_view() { + let upgrade_lock = UpgradeLock::new(); + + let data = TestData { data: 10 }; + + let view_0 = ::Time::new(0); + let view_1 = ::Time::new(1); + + let versioned_data_0 = + VersionedVoteData::::new( + data, + view_0, + &upgrade_lock, + ) + .await + .unwrap(); + let versioned_data_1 = + VersionedVoteData::::new( + data, + view_1, + &upgrade_lock, + ) + .await + .unwrap(); + + let versioned_data_commitment_0: [u8; 32] = versioned_data_0.commit().into(); + let versioned_data_commitment_1: [u8; 32] = versioned_data_1.commit().into(); + + assert!( + versioned_data_commitment_0 != versioned_data_commitment_1, + "left: {versioned_data_commitment_0:?}, right: {versioned_data_commitment_1:?}" + ); + } + + #[cfg_attr(async_executor_impl = "tokio", tokio::test(flavor = "multi_thread"))] + #[cfg_attr(async_executor_impl = "async-std", async_std::test)] + /// Test that the view number does not affect the commitment pre-marketplace + async fn test_versioned_commitment_excludes_view() { + let upgrade_lock = UpgradeLock::new(); + + let data = TestData { data: 10 }; + + let view_0 = ::Time::new(0); + let view_1 = ::Time::new(1); + + let versioned_data_0 = VersionedVoteData::::new( + data, + view_0, + &upgrade_lock, + ) + .await + .unwrap(); + let versioned_data_1 = VersionedVoteData::::new( + data, + view_1, + &upgrade_lock, + ) + .await + .unwrap(); + + let versioned_data_commitment_0: [u8; 32] = versioned_data_0.commit().into(); + let versioned_data_commitment_1: [u8; 32] = versioned_data_1.commit().into(); + + assert!( + versioned_data_commitment_0 == versioned_data_commitment_1, + "left: {versioned_data_commitment_0:?}, right: {versioned_data_commitment_1:?}" + ); + } +} diff --git a/crates/hotshot/src/tasks/mod.rs b/crates/hotshot/src/tasks/mod.rs index 0d8428469c..47e99c15db 100644 --- a/crates/hotshot/src/tasks/mod.rs +++ b/crates/hotshot/src/tasks/mod.rs @@ -206,9 +206,9 @@ pub fn add_network_event_task< pub async fn add_consensus_tasks, V: Versions>( handle: &mut SystemContextHandle, ) { - handle.add_task(ViewSyncTaskState::::create_from(handle).await); + handle.add_task(ViewSyncTaskState::::create_from(handle).await); handle.add_task(VidTaskState::::create_from(handle).await); - handle.add_task(DaTaskState::::create_from(handle).await); + handle.add_task(DaTaskState::::create_from(handle).await); handle.add_task(TransactionTaskState::::create_from(handle).await); // only spawn the upgrade task if we are actually configured to perform an upgrade. @@ -619,6 +619,45 @@ impl + std::fmt::Debug, V: Version } } +#[derive(Debug)] +/// An `EventHandlerState` that modifies view number on the certificate of `DacSend` event to that of a future view +pub struct DishonestDa { + /// How many times current node has been elected leader and sent Da Cert + pub total_da_certs_sent_from_node: u64, + /// Which proposals to be dishonest at + pub dishonest_at_da_cert_sent_numbers: HashSet, + /// When leader how many times we will send DacSend and increment view number + pub total_views_add_to_cert: u64, +} + +#[async_trait] +impl + std::fmt::Debug, V: Versions> + EventTransformerState for DishonestDa +{ + async fn recv_handler(&mut self, event: &HotShotEvent) -> Vec> { + vec![event.clone()] + } + + async fn send_handler(&mut self, event: &HotShotEvent) -> Vec> { + if let HotShotEvent::DacSend(cert, sender) = event { + self.total_da_certs_sent_from_node += 1; + if self + .dishonest_at_da_cert_sent_numbers + .contains(&self.total_da_certs_sent_from_node) + { + let mut result = vec![HotShotEvent::DacSend(cert.clone(), sender.clone())]; + for i in 1..=self.total_views_add_to_cert { + let mut bad_cert = cert.clone(); + bad_cert.view_number = cert.view_number + i; + result.push(HotShotEvent::DacSend(bad_cert, sender.clone())); + } + return result; + } + } + vec![event.clone()] + } +} + /// adds tasks for sending/receiving messages to/from the network. pub async fn add_network_tasks, V: Versions>( handle: &mut SystemContextHandle, diff --git a/crates/hotshot/src/tasks/task_state.rs b/crates/hotshot/src/tasks/task_state.rs index 5391283afb..7754822716 100644 --- a/crates/hotshot/src/tasks/task_state.rs +++ b/crates/hotshot/src/tasks/task_state.rs @@ -130,7 +130,7 @@ impl, V: Versions> CreateTaskState #[async_trait] impl, V: Versions> CreateTaskState - for DaTaskState + for DaTaskState { async fn create_from(handle: &SystemContextHandle) -> Self { Self { @@ -145,13 +145,14 @@ impl, V: Versions> CreateTaskState private_key: handle.private_key().clone(), id: handle.hotshot.id, storage: Arc::clone(&handle.storage), + upgrade_lock: handle.hotshot.upgrade_lock.clone(), } } } #[async_trait] impl, V: Versions> CreateTaskState - for ViewSyncTaskState + for ViewSyncTaskState { async fn create_from(handle: &SystemContextHandle) -> Self { let cur_view = handle.cur_view().await; @@ -176,6 +177,7 @@ impl, V: Versions> CreateTaskState view_sync_timeout: handle.hotshot.config.view_sync_timeout, id: handle.hotshot.id, last_garbage_collected_view: TYPES::Time::new(0), + upgrade_lock: handle.hotshot.upgrade_lock.clone(), } } } diff --git a/crates/task-impls/src/consensus/handlers.rs b/crates/task-impls/src/consensus/handlers.rs index 340741b5f5..9716ca6998 100644 --- a/crates/task-impls/src/consensus/handlers.rs +++ b/crates/task-impls/src/consensus/handlers.rs @@ -183,6 +183,7 @@ pub async fn publish_proposal_from_commitment_and_metadata, vote_info: VoteInfo, id: u64, + upgrade_lock: &UpgradeLock, ) -> bool { use hotshot_types::simple_vote::QuorumVote; @@ -754,6 +766,7 @@ pub async fn update_state_and_vote_if_able< Arc::clone(&quorum_membership), OuterConsensus::new(Arc::clone(&consensus.inner_consensus)), public_key.clone(), + upgrade_lock, ) .await .ok(), @@ -807,7 +820,10 @@ pub async fn update_state_and_vote_if_able< } // Validate the DAC. - let message = if cert.is_valid_cert(vote_info.da_membership.as_ref()) { + let message = if cert + .is_valid_cert(vote_info.da_membership.as_ref(), upgrade_lock) + .await + { // Validate the block payload commitment for non-genesis DAC. if cert.date().payload_commit != proposal.block_header.payload_commitment() { warn!( @@ -823,7 +839,10 @@ pub async fn update_state_and_vote_if_able< view, &public_key, &vote_info.private_key, - ) { + &vote_info.upgrade_lock, + ) + .await + { GeneralConsensusMessage::::Vote(vote) } else { error!("Unable to sign quorum vote!"); diff --git a/crates/task-impls/src/consensus/mod.rs b/crates/task-impls/src/consensus/mod.rs index a9cf49aa73..445c81fc1f 100644 --- a/crates/task-impls/src/consensus/mod.rs +++ b/crates/task-impls/src/consensus/mod.rs @@ -52,7 +52,8 @@ use crate::{ pub(crate) mod handlers; /// Alias for Optional type for Vote Collectors -type VoteCollectorOption = Option>; +type VoteCollectorOption = + Option>; /// The state for the consensus task. Contains all of the information for the implementation /// of consensus @@ -92,11 +93,11 @@ pub struct ConsensusTaskState, V: /// Current Vote collection task, with it's view. pub vote_collector: - RwLock, QuorumCertificate>>, + RwLock, QuorumCertificate, V>>, /// Current timeout vote collection task with its view pub timeout_vote_collector: - RwLock, TimeoutCertificate>>, + RwLock, TimeoutCertificate, V>>, /// timeout task handle pub timeout_task: JoinHandle<()>, @@ -248,6 +249,7 @@ impl, V: Versions> ConsensusTaskSt let instance_state = Arc::clone(&self.instance_state); let id = self.id; let handle = async_spawn(async move { + let upgrade_lock = upgrade.clone(); update_state_and_vote_if_able::( view, proposal, @@ -264,6 +266,7 @@ impl, V: Versions> ConsensusTaskSt event_receiver, }, id, + &upgrade_lock, ) .await; }); @@ -333,11 +336,12 @@ impl, V: Versions> ConsensusTaskSt view: vote.view_number(), id: self.id, }; - *collector = create_vote_accumulator::< - TYPES, - QuorumVote, - QuorumCertificate, - >(&info, event, &event_sender) + *collector = create_vote_accumulator( + &info, + event, + &event_sender, + self.upgrade_lock.clone(), + ) .await; } else { let result = collector @@ -372,11 +376,12 @@ impl, V: Versions> ConsensusTaskSt view: vote.view_number(), id: self.id, }; - *collector = create_vote_accumulator::< - TYPES, - TimeoutVote, - TimeoutCertificate, - >(&info, event, &event_sender) + *collector = create_vote_accumulator( + &info, + event, + &event_sender, + self.upgrade_lock.clone(), + ) .await; } else { let result = collector @@ -569,7 +574,10 @@ impl, V: Versions> ConsensusTaskSt view, &self.public_key, &self.private_key, - ) else { + &self.upgrade_lock, + ) + .await + else { error!("Failed to sign TimeoutData!"); return; }; @@ -667,7 +675,10 @@ impl, V: Versions> ConsensusTaskSt } } HotShotEvent::ViewSyncFinalizeCertificate2Recv(certificate) => { - if !certificate.is_valid_cert(self.quorum_membership.as_ref()) { + if !certificate + .is_valid_cert(self.quorum_membership.as_ref(), &self.upgrade_lock) + .await + { error!( "View Sync Finalize certificate {:?} was invalid", certificate.date() diff --git a/crates/task-impls/src/consensus2/handlers.rs b/crates/task-impls/src/consensus2/handlers.rs index 69d10e9788..1c615636e0 100644 --- a/crates/task-impls/src/consensus2/handlers.rs +++ b/crates/task-impls/src/consensus2/handlers.rs @@ -12,7 +12,6 @@ use async_compatibility_layer::art::{async_sleep, async_spawn}; use chrono::Utc; use hotshot_types::{ event::{Event, EventType}, - simple_certificate::{QuorumCertificate, TimeoutCertificate}, simple_vote::{QuorumVote, TimeoutData, TimeoutVote}, traits::{ election::Membership, @@ -59,10 +58,8 @@ pub(crate) async fn handle_quorum_vote_recv< view: vote.view_number(), id: task_state.id, }; - *collector = create_vote_accumulator::, QuorumCertificate>( - &info, event, sender, - ) - .await; + *collector = + create_vote_accumulator(&info, event, sender, task_state.upgrade_lock.clone()).await; } else { let result = collector .as_mut() @@ -107,10 +104,7 @@ pub(crate) async fn handle_timeout_vote_recv< id: task_state.id, }; *collector = - create_vote_accumulator::, TimeoutCertificate>( - &info, event, sender, - ) - .await; + create_vote_accumulator(&info, event, sender, task_state.upgrade_lock.clone()).await; } else { let result = collector .as_mut() @@ -250,7 +244,9 @@ pub(crate) async fn handle_timeout view_number, &task_state.public_key, &task_state.private_key, + &task_state.upgrade_lock, ) + .await .context("Failed to sign TimeoutData")?; broadcast_event(Arc::new(HotShotEvent::TimeoutVoteSend(vote)), sender).await; diff --git a/crates/task-impls/src/consensus2/mod.rs b/crates/task-impls/src/consensus2/mod.rs index 1e74508278..4b503f46fc 100644 --- a/crates/task-impls/src/consensus2/mod.rs +++ b/crates/task-impls/src/consensus2/mod.rs @@ -34,7 +34,8 @@ use self::handlers::{ use crate::{events::HotShotEvent, vote_collection::VoteCollectionTaskState}; /// Alias for Optional type for Vote Collectors -type VoteCollectorOption = Option>; +type VoteCollectorOption = + Option>; /// Event handlers for use in the `handle` method. mod handlers; @@ -64,11 +65,11 @@ pub struct Consensus2TaskState, V: /// Current Vote collection task, with it's view. pub vote_collector: - RwLock, QuorumCertificate>>, + RwLock, QuorumCertificate, V>>, /// Current timeout vote collection task with its view pub timeout_vote_collector: - RwLock, TimeoutCertificate>>, + RwLock, TimeoutCertificate, V>>, /// This node's storage ref pub storage: Arc>, diff --git a/crates/task-impls/src/da.rs b/crates/task-impls/src/da.rs index bc38aff941..111dc0d43a 100644 --- a/crates/task-impls/src/da.rs +++ b/crates/task-impls/src/da.rs @@ -18,14 +18,14 @@ use hotshot_types::{ consensus::{Consensus, OuterConsensus, View}, data::{DaProposal, PackedBundle}, event::{Event, EventType}, - message::Proposal, + message::{Proposal, UpgradeLock}, simple_certificate::DaCertificate, simple_vote::{DaData, DaVote}, traits::{ block_contents::vid_commitment, election::Membership, network::ConnectedNetwork, - node_implementation::{ConsensusTime, NodeImplementation, NodeType}, + node_implementation::{ConsensusTime, NodeImplementation, NodeType, Versions}, signature_key::SignatureKey, storage::Storage, }, @@ -46,10 +46,11 @@ use crate::{ }; /// Alias for Optional type for Vote Collectors -type VoteCollectorOption = Option>; +type VoteCollectorOption = + Option>; /// Tracks state of a DA task -pub struct DaTaskState> { +pub struct DaTaskState, V: Versions> { /// Output events to application pub output_event_stream: async_broadcast::Sender>, @@ -71,7 +72,7 @@ pub struct DaTaskState> { pub network: Arc, /// The current vote collection task, if there is one. - pub vote_collector: RwLock, DaCertificate>>, + pub vote_collector: RwLock, DaCertificate, V>>, /// This Nodes public key pub public_key: TYPES::SignatureKey, @@ -84,9 +85,12 @@ pub struct DaTaskState> { /// This node's storage ref pub storage: Arc>, + + /// Lock for a decided upgrade + pub upgrade_lock: UpgradeLock, } -impl> DaTaskState { +impl, V: Versions> DaTaskState { /// main task event handler #[instrument(skip_all, fields(id = self.id, view = *self.cur_view), name = "DA Main Task", level = "error", target = "DaTaskState")] pub async fn handle( @@ -195,7 +199,10 @@ impl> DaTaskState { view_number, &self.public_key, &self.private_key, - ) else { + &self.upgrade_lock, + ) + .await + else { error!("Failed to sign DA Vote!"); return None; }; @@ -270,11 +277,12 @@ impl> DaTaskState { view: vote.view_number(), id: self.id, }; - *collector = create_vote_accumulator::< - TYPES, - DaVote, - DaCertificate, - >(&info, event, &event_stream) + *collector = create_vote_accumulator( + &info, + event, + &event_stream, + self.upgrade_lock.clone(), + ) .await; } else { let result = collector @@ -364,7 +372,9 @@ impl> DaTaskState { #[async_trait] /// task state implementation for DA Task -impl> TaskState for DaTaskState { +impl, V: Versions> TaskState + for DaTaskState +{ type Event = HotShotEvent; async fn handle_event( @@ -381,6 +391,6 @@ impl> TaskState for DaTaskState &'static str { - std::any::type_name::>() + std::any::type_name::>() } } diff --git a/crates/task-impls/src/helpers.rs b/crates/task-impls/src/helpers.rs index 4a7903b847..f8af2e37df 100644 --- a/crates/task-impls/src/helpers.rs +++ b/crates/task-impls/src/helpers.rs @@ -10,6 +10,7 @@ use std::{ sync::Arc, }; +use crate::{events::HotShotEvent, request::REQUEST_TIMEOUT}; use anyhow::{bail, ensure, Context, Result}; use async_broadcast::{Receiver, SendError, Sender}; use async_compatibility_layer::art::{async_sleep, async_spawn, async_timeout}; @@ -19,6 +20,8 @@ use async_std::task::JoinHandle; use chrono::Utc; use committable::{Commitment, Committable}; use hotshot_task::dependency::{Dependency, EventDependency}; +use hotshot_types::message::UpgradeLock; +use hotshot_types::traits::node_implementation::Versions; use hotshot_types::{ consensus::{ConsensusUpgradableReadLockGuard, OuterConsensus}, data::{Leaf, QuorumProposal, ViewChangeEvidence}, @@ -38,17 +41,16 @@ use hotshot_types::{ use tokio::task::JoinHandle; use tracing::{debug, error, info, instrument, warn}; -use crate::{events::HotShotEvent, request::REQUEST_TIMEOUT}; - /// Trigger a request to the network for a proposal for a view and wait for the response or timeout. #[instrument(skip_all)] -pub(crate) async fn fetch_proposal( +pub(crate) async fn fetch_proposal( view_number: TYPES::Time, event_sender: Sender>>, event_receiver: Receiver>>, quorum_membership: Arc, consensus: OuterConsensus, sender_key: TYPES::SignatureKey, + upgrade_lock: &UpgradeLock, ) -> Result> { // First, broadcast that we need a proposal to the current leader broadcast_event( @@ -99,7 +101,10 @@ pub(crate) async fn fetch_proposal( let view_number = proposal.data.view_number(); let justify_qc = proposal.data.justify_qc.clone(); - if !justify_qc.is_valid_cert(quorum_membership.as_ref()) { + if !justify_qc + .is_valid_cert(quorum_membership.as_ref(), upgrade_lock) + .await + { bail!("Invalid justify_qc in proposal for view {}", *view_number); } let mut consensus_write = consensus.write().await; @@ -318,13 +323,14 @@ pub async fn decide_from_proposal( /// Gets the parent leaf and state from the parent of a proposal, returning an [`anyhow::Error`] if not. #[instrument(skip_all)] -pub(crate) async fn parent_leaf_and_state( +pub(crate) async fn parent_leaf_and_state( next_proposal_view_number: TYPES::Time, event_sender: &Sender>>, event_receiver: &Receiver>>, quorum_membership: Arc, public_key: TYPES::SignatureKey, consensus: OuterConsensus, + upgrade_lock: &UpgradeLock, ) -> Result<(Leaf, Arc<::ValidatedState>)> { ensure!( quorum_membership.leader(next_proposal_view_number) == public_key, @@ -344,6 +350,7 @@ pub(crate) async fn parent_leaf_and_state( quorum_membership, consensus.clone(), public_key.clone(), + upgrade_lock, ) .await .context("Failed to fetch proposal")?; @@ -404,7 +411,7 @@ pub(crate) async fn parent_leaf_and_state( #[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_lines)] #[instrument(skip_all, fields(id = id, view = *proposal.data.view_number()))] -pub async fn validate_proposal_safety_and_liveness( +pub async fn validate_proposal_safety_and_liveness( proposal: Proposal>, parent_leaf: Leaf, consensus: OuterConsensus, @@ -414,6 +421,7 @@ pub async fn validate_proposal_safety_and_liveness( sender: TYPES::SignatureKey, event_sender: Sender>, id: u64, + upgrade_lock: UpgradeLock, ) -> Result<()> { let view_number = proposal.data.view_number(); @@ -453,7 +461,12 @@ pub async fn validate_proposal_safety_and_liveness( ) .await; - UpgradeCertificate::validate(&proposal.data.upgrade_certificate, &quorum_membership)?; + UpgradeCertificate::validate( + &proposal.data.upgrade_certificate, + &quorum_membership, + &upgrade_lock, + ) + .await?; // Validate that the upgrade certificate is re-attached, if we saw one on the parent proposed_leaf @@ -529,11 +542,12 @@ pub async fn validate_proposal_safety_and_liveness( /// /// # Errors /// If any validation or view number check fails. -pub fn validate_proposal_view_and_certs( +pub async fn validate_proposal_view_and_certs( proposal: &Proposal>, cur_view: TYPES::Time, quorum_membership: &Arc, timeout_membership: &Arc, + upgrade_lock: &UpgradeLock, ) -> Result<()> { let view = proposal.data.view_number(); ensure!( @@ -561,7 +575,9 @@ pub fn validate_proposal_view_and_certs( *view ); ensure!( - timeout_cert.is_valid_cert(timeout_membership.as_ref()), + timeout_cert + .is_valid_cert(timeout_membership.as_ref(), upgrade_lock) + .await, "Timeout certificate for view {} was invalid", *view ); @@ -576,7 +592,9 @@ pub fn validate_proposal_view_and_certs( // View sync certs must also be valid. ensure!( - view_sync_cert.is_valid_cert(quorum_membership.as_ref()), + view_sync_cert + .is_valid_cert(quorum_membership.as_ref(), upgrade_lock) + .await, "Invalid view sync finalize cert provided" ); } @@ -585,7 +603,12 @@ pub fn validate_proposal_view_and_certs( // Validate the upgrade certificate -- this is just a signature validation. // Note that we don't do anything with the certificate directly if this passes; it eventually gets stored as part of the leaf if nothing goes wrong. - UpgradeCertificate::validate(&proposal.data.upgrade_certificate, quorum_membership)?; + UpgradeCertificate::validate( + &proposal.data.upgrade_certificate, + quorum_membership, + upgrade_lock, + ) + .await?; Ok(()) } diff --git a/crates/task-impls/src/quorum_proposal/handlers.rs b/crates/task-impls/src/quorum_proposal/handlers.rs index cafb29b597..a3f9ec5e1b 100644 --- a/crates/task-impls/src/quorum_proposal/handlers.rs +++ b/crates/task-impls/src/quorum_proposal/handlers.rs @@ -125,6 +125,7 @@ impl ProposalDependencyHandle { Arc::clone(&self.quorum_membership), self.public_key.clone(), OuterConsensus::new(Arc::clone(&self.consensus.inner_consensus)), + &self.upgrade_lock, ) .await?; @@ -263,6 +264,7 @@ impl HandleDepOutput for ProposalDependencyHandle< let event_receiver = self.receiver.clone(); let sender_key = self.public_key.clone(); let consensus = OuterConsensus::new(Arc::clone(&self.consensus.inner_consensus)); + let upgrade_lock = self.upgrade_lock.clone(); async_spawn(async move { fetch_proposal( high_qc_view_number, @@ -271,6 +273,7 @@ impl HandleDepOutput for ProposalDependencyHandle< membership, consensus, sender_key, + &upgrade_lock, ) .await }); diff --git a/crates/task-impls/src/quorum_proposal/mod.rs b/crates/task-impls/src/quorum_proposal/mod.rs index 8fc4c3141e..fb146eb058 100644 --- a/crates/task-impls/src/quorum_proposal/mod.rs +++ b/crates/task-impls/src/quorum_proposal/mod.rs @@ -423,7 +423,10 @@ impl, V: Versions> ); } HotShotEvent::ViewSyncFinalizeCertificate2Recv(certificate) => { - if !certificate.is_valid_cert(self.quorum_membership.as_ref()) { + if !certificate + .is_valid_cert(self.quorum_membership.as_ref(), &self.upgrade_lock) + .await + { warn!( "View Sync Finalize certificate {:?} was invalid", certificate.date() diff --git a/crates/task-impls/src/quorum_proposal_recv/handlers.rs b/crates/task-impls/src/quorum_proposal_recv/handlers.rs index c1774e11ee..dcc4eb405c 100644 --- a/crates/task-impls/src/quorum_proposal_recv/handlers.rs +++ b/crates/task-impls/src/quorum_proposal_recv/handlers.rs @@ -144,13 +144,21 @@ pub(crate) async fn handle_quorum_proposal_recv< task_state.cur_view, &task_state.quorum_membership, &task_state.timeout_membership, + &task_state.upgrade_lock, ) + .await .context("Failed to validate proposal view or attached certs")?; let view_number = proposal.data.view_number(); let justify_qc = proposal.data.justify_qc.clone(); - if !justify_qc.is_valid_cert(task_state.quorum_membership.as_ref()) { + if !justify_qc + .is_valid_cert( + task_state.quorum_membership.as_ref(), + &task_state.upgrade_lock, + ) + .await + { let consensus = task_state.consensus.read().await; consensus.metrics.invalid_qc.update(1); bail!("Invalid justify_qc in proposal for view {}", *view_number); @@ -185,6 +193,7 @@ pub(crate) async fn handle_quorum_proposal_recv< // This is because the key that we receive is for the prior leader, so the payload would be routed // incorrectly. task_state.public_key.clone(), + &task_state.upgrade_lock, ) .await .ok(), @@ -246,6 +255,7 @@ pub(crate) async fn handle_quorum_proposal_recv< quorum_proposal_sender_key, task_state.output_event_stream.clone(), task_state.id, + task_state.upgrade_lock.clone(), ) .await?; diff --git a/crates/task-impls/src/quorum_vote/mod.rs b/crates/task-impls/src/quorum_vote/mod.rs index 969d99ba26..18545172ec 100644 --- a/crates/task-impls/src/quorum_vote/mod.rs +++ b/crates/task-impls/src/quorum_vote/mod.rs @@ -116,6 +116,7 @@ impl + 'static, V: Versions> Arc::clone(&self.quorum_membership), OuterConsensus::new(Arc::clone(&self.consensus.inner_consensus)), self.public_key.clone(), + &self.upgrade_lock, ) .await .ok(), @@ -212,7 +213,9 @@ impl + 'static, V: Versions> self.view_number, &self.public_key, &self.private_key, + &self.upgrade_lock, ) + .await .context("Failed to sign vote")?; debug!( "sending vote to next quorum leader {:?}", @@ -556,7 +559,10 @@ impl, V: Versions> QuorumVoteTaskS } // Validate the DAC. - if !cert.is_valid_cert(self.da_membership.as_ref()) { + if !cert + .is_valid_cert(self.da_membership.as_ref(), &self.upgrade_lock) + .await + { return; } diff --git a/crates/task-impls/src/upgrade.rs b/crates/task-impls/src/upgrade.rs index 1a05629879..9b39344cb8 100644 --- a/crates/task-impls/src/upgrade.rs +++ b/crates/task-impls/src/upgrade.rs @@ -41,7 +41,8 @@ use crate::{ }; /// Alias for Optional type for Vote Collectors -type VoteCollectorOption = Option>; +type VoteCollectorOption = + Option>; /// Tracks state of a DA task pub struct UpgradeTaskState, V: Versions> { @@ -58,7 +59,7 @@ pub struct UpgradeTaskState, V: Ve /// The current vote collection task, if there is one. pub vote_collector: - RwLock, UpgradeCertificate>>, + RwLock, UpgradeCertificate, V>>, /// This Nodes public key pub public_key: TYPES::SignatureKey, @@ -210,7 +211,10 @@ impl, V: Versions> UpgradeTaskStat view, &self.public_key, &self.private_key, - ) else { + &self.upgrade_lock, + ) + .await + else { error!("Failed to sign UpgradeVote!"); return None; }; @@ -243,12 +247,8 @@ impl, V: Versions> UpgradeTaskStat view: vote.view_number(), id: self.id, }; - *collector = create_vote_accumulator::< - TYPES, - UpgradeVote, - UpgradeCertificate, - >(&info, event, &tx) - .await; + *collector = + create_vote_accumulator(&info, event, &tx, self.upgrade_lock.clone()).await; } else { let result = collector .as_mut() diff --git a/crates/task-impls/src/view_sync.rs b/crates/task-impls/src/view_sync.rs index 2e9544a5c6..d6568c48b7 100644 --- a/crates/task-impls/src/view_sync.rs +++ b/crates/task-impls/src/view_sync.rs @@ -21,7 +21,7 @@ use async_std::task::JoinHandle; use async_trait::async_trait; use hotshot_task::task::TaskState; use hotshot_types::{ - message::GeneralConsensusMessage, + message::{GeneralConsensusMessage, UpgradeLock}, simple_certificate::{ ViewSyncCommitCertificate2, ViewSyncFinalizeCertificate2, ViewSyncPreCommitCertificate2, }, @@ -31,7 +31,7 @@ use hotshot_types::{ }, traits::{ election::Membership, - node_implementation::{ConsensusTime, NodeImplementation, NodeType}, + node_implementation::{ConsensusTime, NodeImplementation, NodeType, Versions}, signature_key::SignatureKey, }, vote::{Certificate, HasViewNumber, Vote}, @@ -61,11 +61,13 @@ pub enum ViewSyncPhase { } /// Type alias for a map from View Number to Relay to Vote Task -type RelayMap = - HashMap<::Time, BTreeMap>>; +type RelayMap = HashMap< + ::Time, + BTreeMap>, +>; /// Main view sync task state -pub struct ViewSyncTaskState> { +pub struct ViewSyncTaskState, V: Versions> { /// View HotShot is currently in pub current_view: TYPES::Time, /// View HotShot wishes to be in @@ -85,27 +87,34 @@ pub struct ViewSyncTaskState> { 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, ViewSyncPreCommitCertificate2>>, + pub pre_commit_relay_map: RwLock< + RelayMap, ViewSyncPreCommitCertificate2, V>, + >, /// Map of commit vote accumulates for the relay pub commit_relay_map: - RwLock, ViewSyncCommitCertificate2>>, + RwLock, ViewSyncCommitCertificate2, V>>, /// Map of finalize vote accumulates for the relay - pub finalize_relay_map: - RwLock, ViewSyncFinalizeCertificate2>>, + pub finalize_relay_map: RwLock< + RelayMap, ViewSyncFinalizeCertificate2, V>, + >, /// Timeout duration for view sync rounds pub view_sync_timeout: Duration, /// Last view we garbage collected old tasks pub last_garbage_collected_view: TYPES::Time, + + /// Lock for a decided upgrade + pub upgrade_lock: UpgradeLock, } #[async_trait] -impl> TaskState for ViewSyncTaskState { +impl, V: Versions> TaskState + for ViewSyncTaskState +{ type Event = HotShotEvent; async fn handle_event( @@ -122,12 +131,12 @@ impl> TaskState for ViewSyncTaskSt async fn cancel_subtasks(&mut self) {} fn get_task_name(&self) -> &'static str { - std::any::type_name::>() + std::any::type_name::>() } } /// State of a view sync replica task -pub struct ViewSyncReplicaTaskState> { +pub struct ViewSyncReplicaTaskState, V: Versions> { /// Timeout for view sync rounds pub view_sync_timeout: Duration, /// Current round HotShot is in @@ -153,11 +162,13 @@ pub struct ViewSyncReplicaTaskState::PrivateKey, + /// Lock for a decided upgrade + pub upgrade_lock: UpgradeLock, } #[async_trait] -impl> TaskState - for ViewSyncReplicaTaskState +impl, V: Versions> TaskState + for ViewSyncReplicaTaskState { type Event = HotShotEvent; @@ -175,11 +186,11 @@ impl> TaskState async fn cancel_subtasks(&mut self) {} fn get_task_name(&self) -> &'static str { - std::any::type_name::>() + std::any::type_name::>() } } -impl> ViewSyncTaskState { +impl, V: Versions> ViewSyncTaskState { #[instrument(skip_all, fields(id = self.id, view = *self.current_view), name = "View Sync Main Task", level = "error")] #[allow(clippy::type_complexity)] /// Handles incoming events for the main view sync task @@ -215,7 +226,7 @@ impl> ViewSyncTaskState } // We do not have a replica task already running, so start one - let mut replica_state: ViewSyncReplicaTaskState = ViewSyncReplicaTaskState { + let mut replica_state: ViewSyncReplicaTaskState = ViewSyncReplicaTaskState { current_view: view, next_view: view, relay: 0, @@ -228,6 +239,7 @@ impl> ViewSyncTaskState private_key: self.private_key.clone(), view_sync_timeout: self.view_sync_timeout, id: self.id, + upgrade_lock: self.upgrade_lock.clone(), }; let result = replica_state @@ -307,7 +319,9 @@ impl> ViewSyncTaskState view: vote_view, id: self.id, }; - let vote_collector = create_vote_accumulator(&info, event, &event_stream).await; + let vote_collector = + create_vote_accumulator(&info, event, &event_stream, self.upgrade_lock.clone()) + .await; if let Some(vote_task) = vote_collector { relay_map.insert(relay, vote_task); } @@ -344,7 +358,9 @@ impl> ViewSyncTaskState view: vote_view, id: self.id, }; - let vote_collector = create_vote_accumulator(&info, event, &event_stream).await; + let vote_collector = + create_vote_accumulator(&info, event, &event_stream, self.upgrade_lock.clone()) + .await; if let Some(vote_task) = vote_collector { relay_map.insert(relay, vote_task); } @@ -381,7 +397,9 @@ impl> ViewSyncTaskState view: vote_view, id: self.id, }; - let vote_collector = create_vote_accumulator(&info, event, &event_stream).await; + let vote_collector = + create_vote_accumulator(&info, event, &event_stream, self.upgrade_lock.clone()) + .await; if let Some(vote_task) = vote_collector { relay_map.insert(relay, vote_task); } @@ -472,7 +490,9 @@ impl> ViewSyncTaskState } } -impl> ViewSyncReplicaTaskState { +impl, V: Versions> + ViewSyncReplicaTaskState +{ #[instrument(skip_all, fields(id = self.id, view = *self.current_view), name = "View Sync Replica Task", level = "error")] /// Handle incoming events for the view sync replica task pub async fn handle( @@ -492,7 +512,10 @@ impl> ViewSyncReplicaTaskState> ViewSyncReplicaTaskState> ViewSyncReplicaTaskState> ViewSyncReplicaTaskState> ViewSyncReplicaTaskState> ViewSyncReplicaTaskState> ViewSyncReplicaTaskState, CERT: Certificate + Debug, + V: Versions, > { /// Public key for this node. pub public_key: TYPES::SignatureKey, @@ -41,7 +46,7 @@ pub struct VoteCollectionTaskState< pub membership: Arc, /// accumulator handles aggregating the votes - pub accumulator: Option>, + pub accumulator: Option>, /// The view which we are collecting votes for pub view: TYPES::Time, @@ -68,7 +73,8 @@ impl< TYPES: NodeType, VOTE: Vote + AggregatableVote, CERT: Certificate + Debug, - > VoteCollectionTaskState + V: Versions, + > VoteCollectionTaskState { /// Take one vote and accumulate it. Returns either the cert or the updated state /// after the vote is accumulated @@ -93,7 +99,7 @@ impl< } let accumulator = self.accumulator.as_mut()?; - match accumulator.accumulate(vote, &self.membership) { + match accumulator.accumulate(vote, &self.membership).await { Either::Left(()) => None, Either::Right(cert) => { debug!("Certificate Formed! {:?}", cert); @@ -144,11 +150,12 @@ pub struct AccumulatorInfo { /// Generic function for spawning a vote task. Returns the event stream id of the spawned task if created /// # Panics /// Calls unwrap but should never panic. -pub async fn create_vote_accumulator( +pub async fn create_vote_accumulator( info: &AccumulatorInfo, event: Arc>, sender: &Sender>>, -) -> Option> + upgrade_lock: UpgradeLock, +) -> Option> where TYPES: NodeType, VOTE: Vote @@ -161,15 +168,17 @@ where + std::marker::Send + std::marker::Sync + 'static, - VoteCollectionTaskState: HandleVoteEvent, + V: Versions, + VoteCollectionTaskState: HandleVoteEvent, { let new_accumulator = VoteAccumulator { vote_outcomes: HashMap::new(), signers: HashMap::new(), phantom: PhantomData, + upgrade_lock, }; - let mut state = VoteCollectionTaskState:: { + let mut state = VoteCollectionTaskState:: { membership: Arc::clone(&info.membership), public_key: info.public_key.clone(), accumulator: Some(new_accumulator), @@ -188,30 +197,32 @@ where } /// Alias for Quorum vote accumulator -type QuorumVoteState = - VoteCollectionTaskState, QuorumCertificate>; +type QuorumVoteState = + VoteCollectionTaskState, QuorumCertificate, V>; /// Alias for DA vote accumulator -type DaVoteState = VoteCollectionTaskState, DaCertificate>; +type DaVoteState = VoteCollectionTaskState, DaCertificate, V>; /// Alias for Timeout vote accumulator -type TimeoutVoteState = - VoteCollectionTaskState, TimeoutCertificate>; +type TimeoutVoteState = + VoteCollectionTaskState, TimeoutCertificate, V>; /// Alias for upgrade vote accumulator -type UpgradeVoteState = - VoteCollectionTaskState, UpgradeCertificate>; +type UpgradeVoteState = + VoteCollectionTaskState, UpgradeCertificate, V>; /// Alias for View Sync Pre Commit vote accumulator -type ViewSyncPreCommitState = VoteCollectionTaskState< +type ViewSyncPreCommitState = VoteCollectionTaskState< TYPES, ViewSyncPreCommitVote, ViewSyncPreCommitCertificate2, + V, >; /// Alias for View Sync Commit vote accumulator -type ViewSyncCommitVoteState = - VoteCollectionTaskState, ViewSyncCommitCertificate2>; +type ViewSyncCommitVoteState = + VoteCollectionTaskState, ViewSyncCommitCertificate2, V>; /// Alias for View Sync Finalize vote accumulator -type ViewSyncFinalizeVoteState = VoteCollectionTaskState< +type ViewSyncFinalizeVoteState = VoteCollectionTaskState< TYPES, ViewSyncFinalizeVote, ViewSyncFinalizeCertificate2, + V, >; impl AggregatableVote, QuorumCertificate> @@ -317,8 +328,9 @@ impl // Handlers for all vote accumulators #[async_trait] -impl HandleVoteEvent, QuorumCertificate> - for QuorumVoteState +impl + HandleVoteEvent, QuorumCertificate> + for QuorumVoteState { async fn handle_vote_event( &mut self, @@ -337,8 +349,9 @@ impl HandleVoteEvent, QuorumCertificat // Handlers for all vote accumulators #[async_trait] -impl HandleVoteEvent, UpgradeCertificate> - for UpgradeVoteState +impl + HandleVoteEvent, UpgradeCertificate> + for UpgradeVoteState { async fn handle_vote_event( &mut self, @@ -356,8 +369,8 @@ impl HandleVoteEvent, UpgradeCertific } #[async_trait] -impl HandleVoteEvent, DaCertificate> - for DaVoteState +impl HandleVoteEvent, DaCertificate> + for DaVoteState { async fn handle_vote_event( &mut self, @@ -375,8 +388,9 @@ impl HandleVoteEvent, DaCertificate } #[async_trait] -impl HandleVoteEvent, TimeoutCertificate> - for TimeoutVoteState +impl + HandleVoteEvent, TimeoutCertificate> + for TimeoutVoteState { async fn handle_vote_event( &mut self, @@ -394,9 +408,9 @@ impl HandleVoteEvent, TimeoutCertific } #[async_trait] -impl +impl HandleVoteEvent, ViewSyncPreCommitCertificate2> - for ViewSyncPreCommitState + for ViewSyncPreCommitState { async fn handle_vote_event( &mut self, @@ -416,9 +430,9 @@ impl } #[async_trait] -impl +impl HandleVoteEvent, ViewSyncCommitCertificate2> - for ViewSyncCommitVoteState + for ViewSyncCommitVoteState { async fn handle_vote_event( &mut self, @@ -436,9 +450,9 @@ impl } #[async_trait] -impl +impl HandleVoteEvent, ViewSyncFinalizeCertificate2> - for ViewSyncFinalizeVoteState + for ViewSyncFinalizeVoteState { async fn handle_vote_event( &mut self, diff --git a/crates/testing/src/helpers.rs b/crates/testing/src/helpers.rs index 09c3b6e87e..8ffe9fafe7 100644 --- a/crates/testing/src/helpers.rs +++ b/crates/testing/src/helpers.rs @@ -13,29 +13,29 @@ use committable::Committable; use ethereum_types::U256; use hotshot::{ traits::{NodeImplementation, TestableNodeImplementation}, - types::{BLSPubKey, SignatureKey, SystemContextHandle}, + types::{SignatureKey, SystemContextHandle}, HotShotInitializer, Memberships, SystemContext, }; use hotshot_example_types::{ auction_results_provider_types::TestAuctionResultsProvider, block_types::TestTransaction, - node_types::{MemoryImpl, TestTypes, TestVersions}, + node_types::TestTypes, state_types::{TestInstanceState, TestValidatedState}, storage_types::TestStorage, }; use hotshot_task_impls::events::HotShotEvent; use hotshot_types::{ consensus::ConsensusMetricsValue, - data::{Leaf, QuorumProposal, VidDisperse, VidDisperseShare, ViewNumber}, - message::{GeneralConsensusMessage, Proposal}, + data::{Leaf, QuorumProposal, VidDisperse, VidDisperseShare}, + message::{GeneralConsensusMessage, Proposal, UpgradeLock}, simple_certificate::DaCertificate, - simple_vote::{DaData, DaVote, QuorumData, QuorumVote, SimpleVote}, + simple_vote::{DaData, DaVote, QuorumData, QuorumVote, SimpleVote, VersionedVoteData}, traits::{ block_contents::vid_commitment, consensus_api::ConsensusApi, election::Membership, network::Topic, - node_implementation::{ConsensusTime, NodeType, Versions}, + node_implementation::{NodeType, Versions}, }, utils::{View, ViewInner}, vid::{vid_scheme, VidCommitment, VidSchemeType}, @@ -131,8 +131,9 @@ pub async fn build_system_handle< /// create certificate /// # Panics /// if we fail to sign the data -pub fn build_cert< - TYPES: NodeType, +pub async fn build_cert< + TYPES: NodeType, + V: Versions, DATAType: Committable + Clone + Eq + Hash + Serialize + Debug + 'static, VOTE: Vote, CERT: Certificate, @@ -142,14 +143,34 @@ pub fn build_cert< view: TYPES::Time, public_key: &TYPES::SignatureKey, private_key: &::PrivateKey, + upgrade_lock: &UpgradeLock, ) -> CERT { - let real_qc_sig = build_assembled_sig::(&data, membership, view); + let real_qc_sig = build_assembled_sig::( + &data, + membership, + view, + upgrade_lock, + ) + .await; + + let vote = SimpleVote::::create_signed_vote( + data, + view, + public_key, + private_key, + upgrade_lock, + ) + .await + .expect("Failed to sign data!"); + + let vote_commitment = + VersionedVoteData::new(vote.date().clone(), vote.view_number(), upgrade_lock) + .await + .expect("Failed to create VersionedVoteData!") + .commit(); - let vote = - SimpleVote::::create_signed_vote(data, view, public_key, private_key) - .expect("Failed to sign data!"); let cert = CERT::create_signed_certificate( - vote.date_commitment(), + vote_commitment, vote.date().clone(), real_qc_sig, vote.view_number(), @@ -174,8 +195,9 @@ pub fn vid_share( /// create signature /// # Panics /// if fails to convert node id into keypair -pub fn build_assembled_sig< - TYPES: NodeType, +pub async fn build_assembled_sig< + TYPES: NodeType, + V: Versions, VOTE: Vote, CERT: Certificate, DATAType: Committable + Clone + Eq + Hash + Serialize + Debug + 'static, @@ -183,6 +205,7 @@ pub fn build_assembled_sig< data: &DATAType, membership: &TYPES::Membership, view: TYPES::Time, + upgrade_lock: &UpgradeLock, ) -> ::QcType { let stake_table = membership.committee_qc_stake_table(); let real_qc_pp: ::QcParams = @@ -196,13 +219,15 @@ pub fn build_assembled_sig< // assemble the vote for node_id in 0..total_nodes { - let (private_key_i, public_key_i) = key_pair_for_id(node_id.try_into().unwrap()); + let (private_key_i, public_key_i) = key_pair_for_id::(node_id.try_into().unwrap()); let vote: SimpleVote = SimpleVote::::create_signed_vote( data.clone(), view, &public_key_i, &private_key_i, + upgrade_lock, ) + .await .expect("Failed to sign data!"); let original_signature: ::PureAssembledSignatureType = vote.signature(); @@ -220,10 +245,14 @@ pub fn build_assembled_sig< /// get the keypair for a node id #[must_use] -pub fn key_pair_for_id(node_id: u64) -> (::PrivateKey, BLSPubKey) { - let private_key = - ::generated_from_seed_indexed([0u8; 32], node_id).1; - let public_key = ::SignatureKey::from_private(&private_key); +pub fn key_pair_for_id( + node_id: u64, +) -> ( + ::PrivateKey, + TYPES::SignatureKey, +) { + let private_key = TYPES::SignatureKey::generated_from_seed_indexed([0u8; 32], node_id).1; + let public_key = ::SignatureKey::from_private(&private_key); (private_key, public_key) } @@ -239,20 +268,20 @@ pub fn vid_scheme_from_view_number( vid_scheme(num_storage_nodes) } -pub fn vid_payload_commitment( - quorum_membership: &::Membership, - view_number: ViewNumber, +pub fn vid_payload_commitment( + quorum_membership: &::Membership, + view_number: TYPES::Time, 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); let encoded_transactions = TestTransaction::encode(&transactions); let vid_disperse = vid.disperse(&encoded_transactions).unwrap(); vid_disperse.commit } -pub fn da_payload_commitment( - quorum_membership: &::Membership, +pub fn da_payload_commitment( + quorum_membership: &::Membership, transactions: Vec, ) -> VidCommitment { let encoded_transactions = TestTransaction::encode(&transactions); @@ -260,29 +289,29 @@ pub fn da_payload_commitment( vid_commitment(&encoded_transactions, quorum_membership.total_nodes()) } -pub fn build_payload_commitment( - membership: &::Membership, - view: ViewNumber, +pub fn build_payload_commitment( + membership: &::Membership, + view: TYPES::Time, ) -> ::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); let encoded_transactions = Vec::new(); vid.commit_only(&encoded_transactions).unwrap() } /// TODO: #[allow(clippy::type_complexity)] -pub fn build_vid_proposal( - quorum_membership: &::Membership, - view_number: ViewNumber, +pub fn build_vid_proposal( + quorum_membership: &::Membership, + view_number: TYPES::Time, transactions: Vec, - private_key: &::PrivateKey, + private_key: &::PrivateKey, ) -> ( - Proposal>, - Vec>>, + Proposal>, + Vec>>, ) { - let mut vid = vid_scheme_from_view_number::(quorum_membership, view_number); + let mut vid = vid_scheme_from_view_number::(quorum_membership, view_number); let encoded_transactions = TestTransaction::encode(&transactions); let vid_disperse = VidDisperse::from_membership( @@ -292,7 +321,7 @@ pub fn build_vid_proposal( ); let signature = - ::sign(private_key, vid_disperse.payload_commitment.as_ref()) + TYPES::SignatureKey::sign(private_key, vid_disperse.payload_commitment.as_ref()) .expect("Failed to sign VID commitment"); let vid_disperse_proposal = Proposal { data: vid_disperse.clone(), @@ -313,14 +342,15 @@ pub fn build_vid_proposal( ) } -pub fn build_da_certificate( - quorum_membership: &::Membership, - da_membership: &::Membership, - view_number: ViewNumber, +pub async fn build_da_certificate( + quorum_membership: &::Membership, + da_membership: &::Membership, + view_number: TYPES::Time, transactions: Vec, - public_key: &::SignatureKey, - private_key: &::PrivateKey, -) -> DaCertificate { + public_key: &TYPES::SignatureKey, + private_key: &::PrivateKey, + upgrade_lock: &UpgradeLock, +) -> DaCertificate { let encoded_transactions = TestTransaction::encode(&transactions); let da_payload_commitment = @@ -330,32 +360,36 @@ pub fn build_da_certificate( payload_commit: da_payload_commitment, }; - build_cert::, DaCertificate>( + build_cert::, DaCertificate>( da_data, da_membership, view_number, public_key, private_key, + upgrade_lock, ) + .await } -pub async fn build_vote( - handle: &SystemContextHandle, - proposal: QuorumProposal, -) -> GeneralConsensusMessage { - let view = ViewNumber::new(*proposal.view_number); +pub async fn build_vote, V: Versions>( + handle: &SystemContextHandle, + proposal: QuorumProposal, +) -> GeneralConsensusMessage { + let view = proposal.view_number; let leaf: Leaf<_> = Leaf::from_quorum_proposal(&proposal); - let vote = QuorumVote::::create_signed_vote( + let vote = QuorumVote::::create_signed_vote( QuorumData { leaf_commit: leaf.commit(), }, view, &handle.public_key(), handle.private_key(), + &handle.hotshot.upgrade_lock, ) + .await .expect("Failed to create quorum vote"); - GeneralConsensusMessage::::Vote(vote) + GeneralConsensusMessage::::Vote(vote) } /// This function permutes the provided input vector `inputs`, given some order provided within the diff --git a/crates/testing/src/view_generator.rs b/crates/testing/src/view_generator.rs index 0c335bbc14..7c6c8af2ee 100644 --- a/crates/testing/src/view_generator.rs +++ b/crates/testing/src/view_generator.rs @@ -25,7 +25,7 @@ use hotshot_types::{ DaProposal, Leaf, QuorumProposal, VidDisperse, VidDisperseShare, ViewChangeEvidence, ViewNumber, }, - message::Proposal, + message::{Proposal, UpgradeLock}, simple_certificate::{ DaCertificate, QuorumCertificate, TimeoutCertificate, UpgradeCertificate, ViewSyncFinalizeCertificate2, @@ -66,6 +66,7 @@ pub struct TestView { formed_upgrade_certificate: Option>, view_sync_finalize_data: Option>, timeout_cert_data: Option>, + upgrade_lock: UpgradeLock, } impl TestView { @@ -74,6 +75,7 @@ impl TestView { da_membership: &::Membership, ) -> Self { let genesis_view = ViewNumber::new(1); + let upgrade_lock = UpgradeLock::new(); let transactions = Vec::new(); @@ -91,11 +93,12 @@ impl TestView { &metadata, ); - let (private_key, public_key) = key_pair_for_id(*genesis_view); + let (private_key, public_key) = key_pair_for_id::(*genesis_view); 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()); let (vid_disperse, vid_proposal) = build_vid_proposal( quorum_membership, @@ -111,7 +114,9 @@ impl TestView { transactions.clone(), &public_key, &private_key, - ); + &upgrade_lock, + ) + .await; let block_header = TestBlockHeader { block_number: 1, @@ -180,6 +185,7 @@ impl TestView { view_sync_finalize_data: None, timeout_cert_data: None, da_proposal, + upgrade_lock, } } @@ -205,9 +211,9 @@ impl TestView { leaf_commit: old.leaf.commit(), }; - let (old_private_key, old_public_key) = key_pair_for_id(*old_view); + let (old_private_key, old_public_key) = key_pair_for_id::(*old_view); - let (private_key, public_key) = key_pair_for_id(*next_view); + let (private_key, public_key) = key_pair_for_id::(*next_view); let leader_public_key = public_key; @@ -224,7 +230,8 @@ impl TestView { &metadata, ); - let payload_commitment = da_payload_commitment(quorum_membership, transactions.clone()); + let payload_commitment = + da_payload_commitment::(quorum_membership, transactions.clone()); let (vid_disperse, vid_proposal) = build_vid_proposal( quorum_membership, @@ -233,17 +240,20 @@ impl TestView { &private_key, ); - let da_certificate = build_da_certificate( + let da_certificate = build_da_certificate::( quorum_membership, da_membership, next_view, transactions.clone(), &public_key, &private_key, - ); + &self.upgrade_lock, + ) + .await; let quorum_certificate = build_cert::< TestTypes, + TestVersions, QuorumData, QuorumVote, QuorumCertificate, @@ -253,11 +263,14 @@ impl TestView { old_view, &old_public_key, &old_private_key, - ); + &self.upgrade_lock, + ) + .await; let upgrade_certificate = if let Some(ref data) = self.upgrade_data { let cert = build_cert::< TestTypes, + TestVersions, UpgradeProposalData, UpgradeVote, UpgradeCertificate, @@ -267,7 +280,9 @@ impl TestView { next_view, &public_key, &private_key, - ); + &self.upgrade_lock, + ) + .await; Some(cert) } else { @@ -277,6 +292,7 @@ impl TestView { let view_sync_certificate = if let Some(ref data) = self.view_sync_finalize_data { let cert = build_cert::< TestTypes, + TestVersions, ViewSyncFinalizeData, ViewSyncFinalizeVote, ViewSyncFinalizeCertificate2, @@ -286,7 +302,9 @@ impl TestView { next_view, &public_key, &private_key, - ); + &self.upgrade_lock, + ) + .await; Some(cert) } else { @@ -296,6 +314,7 @@ impl TestView { let timeout_certificate = if let Some(ref data) = self.timeout_cert_data { let cert = build_cert::< TestTypes, + TestVersions, TimeoutData, TimeoutVote, TimeoutCertificate, @@ -305,7 +324,9 @@ impl TestView { next_view, &public_key, &private_key, - ); + &self.upgrade_lock, + ) + .await; Some(cert) } else { @@ -365,6 +386,8 @@ impl TestView { _pd: PhantomData, }; + let upgrade_lock = UpgradeLock::new(); + TestView { quorum_proposal, leaf, @@ -385,6 +408,7 @@ impl TestView { view_sync_finalize_data: None, timeout_cert_data: None, da_proposal, + upgrade_lock, } } @@ -392,7 +416,7 @@ impl TestView { self.next_view_from_ancestor(self.clone()).await } - pub fn create_quorum_vote( + pub async fn create_quorum_vote( &self, handle: &SystemContextHandle, ) -> QuorumVote { @@ -403,11 +427,13 @@ impl TestView { self.view_number, &handle.public_key(), handle.private_key(), + &handle.hotshot.upgrade_lock, ) + .await .expect("Failed to generate a signature on QuorumVote") } - pub fn create_upgrade_vote( + pub async fn create_upgrade_vote( &self, data: UpgradeProposalData, handle: &SystemContextHandle, @@ -417,11 +443,13 @@ impl TestView { self.view_number, &handle.public_key(), handle.private_key(), + &handle.hotshot.upgrade_lock, ) + .await .expect("Failed to generate a signature on UpgradVote") } - pub fn create_da_vote( + pub async fn create_da_vote( &self, data: DaData, handle: &SystemContextHandle, @@ -431,7 +459,9 @@ impl TestView { self.view_number, &handle.public_key(), handle.private_key(), + &handle.hotshot.upgrade_lock, ) + .await .expect("Failed to sign DaData") } } diff --git a/crates/testing/tests/tests_1/consensus_task.rs b/crates/testing/tests/tests_1/consensus_task.rs index 1de54635a8..0c2506af47 100644 --- a/crates/testing/tests/tests_1/consensus_task.rs +++ b/crates/testing/tests/tests_1/consensus_task.rs @@ -85,7 +85,7 @@ async fn test_consensus_task() { proposals.push(view.quorum_proposal.clone()); leaders.push(view.leader_public_key); leaves.push(view.leaf.clone()); - votes.push(view.create_quorum_vote(&handle)); + votes.push(view.create_quorum_vote(&handle).await); dacs.push(view.da_certificate.clone()); vids.push(view.vid_proposal.clone()); } @@ -170,7 +170,7 @@ async fn test_consensus_vote() { proposals.push(view.quorum_proposal.clone()); leaders.push(view.leader_public_key); leaves.push(view.leaf.clone()); - votes.push(view.create_quorum_vote(&handle)); + votes.push(view.create_quorum_vote(&handle).await); dacs.push(view.da_certificate.clone()); vids.push(view.vid_proposal.clone()); } @@ -215,7 +215,7 @@ async fn test_view_sync_finalize_propose() { let handle = build_system_handle::(4) .await .0; - let (priv_key, pub_key) = key_pair_for_id(4); + let (priv_key, pub_key) = key_pair_for_id::(4); let quorum_membership = handle.hotshot.memberships.quorum_membership.clone(); let da_membership = handle.hotshot.memberships.da_membership.clone(); @@ -243,7 +243,7 @@ async fn test_view_sync_finalize_propose() { let view = generator.current_view.clone().unwrap(); proposals.push(view.quorum_proposal.clone()); leaders.push(view.leader_public_key); - votes.push(view.create_quorum_vote(&handle)); + votes.push(view.create_quorum_vote(&handle).await); vids.push(view.vid_proposal.clone()); dacs.push(view.da_certificate.clone()); @@ -258,7 +258,7 @@ async fn test_view_sync_finalize_propose() { let view = generator.current_view.unwrap(); proposals.push(view.quorum_proposal.clone()); leaders.push(view.leader_public_key); - votes.push(view.create_quorum_vote(&handle)); + votes.push(view.create_quorum_vote(&handle).await); vids.push(view.vid_proposal); // Handle the view sync finalize cert, get the requisite data, propose. @@ -275,7 +275,9 @@ async fn test_view_sync_finalize_propose() { ViewNumber::new(2), &pub_key, &priv_key, + &handle.hotshot.upgrade_lock, ) + .await .unwrap(); let timeout_vote_view_3 = TimeoutVote::create_signed_vote( @@ -285,7 +287,9 @@ async fn test_view_sync_finalize_propose() { ViewNumber::new(3), &pub_key, &priv_key, + &handle.hotshot.upgrade_lock, ) + .await .unwrap(); let inputs = vec![ @@ -375,7 +379,7 @@ async fn test_view_sync_finalize_vote() { for view in (&mut generator).take(3).collect::>().await { proposals.push(view.quorum_proposal.clone()); leaders.push(view.leader_public_key); - votes.push(view.create_quorum_vote(&handle)); + votes.push(view.create_quorum_vote(&handle).await); vids.push(view.vid_proposal.clone()); dacs.push(view.da_certificate.clone()); } @@ -386,7 +390,7 @@ async fn test_view_sync_finalize_vote() { for view in (&mut generator).take(1).collect::>().await { proposals.push(view.quorum_proposal.clone()); leaders.push(view.leader_public_key); - votes.push(view.create_quorum_vote(&handle)); + votes.push(view.create_quorum_vote(&handle).await); vids.push(view.vid_proposal.clone()); dacs.push(view.da_certificate.clone()); } @@ -473,7 +477,7 @@ async fn test_view_sync_finalize_vote_fail_view_number() { for view in (&mut generator).take(3).collect::>().await { proposals.push(view.quorum_proposal.clone()); leaders.push(view.leader_public_key); - votes.push(view.create_quorum_vote(&handle)); + votes.push(view.create_quorum_vote(&handle).await); vids.push(view.vid_proposal.clone()); dacs.push(view.da_certificate.clone()); } @@ -484,7 +488,7 @@ async fn test_view_sync_finalize_vote_fail_view_number() { for view in (&mut generator).take(1).collect::>().await { proposals.push(view.quorum_proposal.clone()); leaders.push(view.leader_public_key); - votes.push(view.create_quorum_vote(&handle)); + votes.push(view.create_quorum_vote(&handle).await); vids.push(view.vid_proposal.clone()); dacs.push(view.da_certificate.clone()); } @@ -575,7 +579,7 @@ async fn test_vid_disperse_storage_failure() { for view in (&mut generator).take(1).collect::>().await { proposals.push(view.quorum_proposal.clone()); leaders.push(view.leader_public_key); - votes.push(view.create_quorum_vote(&handle)); + votes.push(view.create_quorum_vote(&handle).await); dacs.push(view.da_certificate.clone()); vids.push(view.vid_proposal.clone()); } diff --git a/crates/testing/tests/tests_1/da_task.rs b/crates/testing/tests/tests_1/da_task.rs index 34dfc406b6..9f8fb0b744 100644 --- a/crates/testing/tests/tests_1/da_task.rs +++ b/crates/testing/tests/tests_1/da_task.rs @@ -64,7 +64,10 @@ async fn test_da_task() { for view in (&mut generator).take(1).collect::>().await { proposals.push(view.da_proposal.clone()); leaders.push(view.leader_public_key); - votes.push(view.create_da_vote(DaData { payload_commit }, &handle)); + votes.push( + view.create_da_vote(DaData { payload_commit }, &handle) + .await, + ); dacs.push(view.da_certificate.clone()); vids.push(view.vid_proposal.clone()); } @@ -74,7 +77,10 @@ async fn test_da_task() { for view in (&mut generator).take(1).collect::>().await { proposals.push(view.da_proposal.clone()); leaders.push(view.leader_public_key); - votes.push(view.create_da_vote(DaData { payload_commit }, &handle)); + votes.push( + view.create_da_vote(DaData { payload_commit }, &handle) + .await, + ); dacs.push(view.da_certificate.clone()); vids.push(view.vid_proposal.clone()); } @@ -99,7 +105,7 @@ async fn test_da_task() { serial![DaProposalRecv(proposals[1].clone(), leaders[1])], ]; - let da_state = DaTaskState::::create_from(&handle).await; + let da_state = DaTaskState::::create_from(&handle).await; let mut da_script = TaskScript { timeout: Duration::from_millis(35), state: da_state, @@ -153,7 +159,10 @@ async fn test_da_task_storage_failure() { for view in (&mut generator).take(1).collect::>().await { proposals.push(view.da_proposal.clone()); leaders.push(view.leader_public_key); - votes.push(view.create_da_vote(DaData { payload_commit }, &handle)); + votes.push( + view.create_da_vote(DaData { payload_commit }, &handle) + .await, + ); dacs.push(view.da_certificate.clone()); vids.push(view.vid_proposal.clone()); } @@ -163,7 +172,10 @@ async fn test_da_task_storage_failure() { for view in (&mut generator).take(1).collect::>().await { proposals.push(view.da_proposal.clone()); leaders.push(view.leader_public_key); - votes.push(view.create_da_vote(DaData { payload_commit }, &handle)); + votes.push( + view.create_da_vote(DaData { payload_commit }, &handle) + .await, + ); dacs.push(view.da_certificate.clone()); vids.push(view.vid_proposal.clone()); } @@ -200,7 +212,7 @@ async fn test_da_task_storage_failure() { Expectations::from_outputs(vec![]), ]; - let da_state = DaTaskState::::create_from(&handle).await; + let da_state = DaTaskState::::create_from(&handle).await; let mut da_script = TaskScript { timeout: Duration::from_millis(35), state: da_state, diff --git a/crates/testing/tests/tests_1/message.rs b/crates/testing/tests/tests_1/message.rs index 541e427251..92c8cb1da7 100644 --- a/crates/testing/tests/tests_1/message.rs +++ b/crates/testing/tests/tests_1/message.rs @@ -41,13 +41,8 @@ fn version_number_at_start_of_serialization() { relay: 37, round: view_number, }; - let simple_certificate = SimpleCertificate { - data: data.clone(), - vote_commitment: data.commit(), - view_number, - signatures: None, - _pd: PhantomData, - }; + let simple_certificate = + SimpleCertificate::new(data.clone(), data.commit(), view_number, None, PhantomData); let message = Message { sender, kind: MessageKind::Consensus(SequencingMessage::General( diff --git a/crates/testing/tests/tests_1/quorum_proposal_recv_task.rs b/crates/testing/tests/tests_1/quorum_proposal_recv_task.rs index 7479536487..007d16185d 100644 --- a/crates/testing/tests/tests_1/quorum_proposal_recv_task.rs +++ b/crates/testing/tests/tests_1/quorum_proposal_recv_task.rs @@ -64,7 +64,7 @@ async fn test_quorum_proposal_recv_task() { for view in (&mut generator).take(2).collect::>().await { proposals.push(view.quorum_proposal.clone()); leaders.push(view.leader_public_key); - votes.push(view.create_quorum_vote(&handle)); + votes.push(view.create_quorum_vote(&handle).await); dacs.push(view.da_certificate.clone()); vids.push(view.vid_proposal.clone()); leaves.push(view.leaf.clone()); @@ -154,7 +154,7 @@ async fn test_quorum_proposal_recv_task_liveness_check() { for view in (&mut generator).take(4).collect::>().await { proposals.push(view.quorum_proposal.clone()); leaders.push(view.leader_public_key); - votes.push(view.create_quorum_vote(&handle)); + votes.push(view.create_quorum_vote(&handle).await); dacs.push(view.da_certificate.clone()); vids.push(view.vid_proposal.clone()); leaves.push(view.leaf.clone()); diff --git a/crates/testing/tests/tests_1/quorum_proposal_task.rs b/crates/testing/tests/tests_1/quorum_proposal_task.rs index 61372110a8..b984c60889 100644 --- a/crates/testing/tests/tests_1/quorum_proposal_task.rs +++ b/crates/testing/tests/tests_1/quorum_proposal_task.rs @@ -58,7 +58,8 @@ 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)); let mut generator = TestViewGenerator::generate(quorum_membership.clone(), da_membership); @@ -201,7 +202,7 @@ 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)), builder_commitment.clone(), TestMetadata, ViewNumber::new(1), @@ -218,7 +219,7 @@ 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)), builder_commitment.clone(), TestMetadata, ViewNumber::new(2), @@ -235,7 +236,7 @@ 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)), builder_commitment.clone(), TestMetadata, ViewNumber::new(3), @@ -252,7 +253,7 @@ 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)), builder_commitment.clone(), TestMetadata, ViewNumber::new(4), @@ -269,7 +270,7 @@ 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)), builder_commitment, TestMetadata, ViewNumber::new(5), @@ -336,7 +337,8 @@ 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)); let builder_commitment = BuilderCommitment::from_raw_digest(sha2::Sha256::new().finalize()); let mut generator = TestViewGenerator::generate(quorum_membership.clone(), da_membership); @@ -423,7 +425,8 @@ 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)); let builder_commitment = BuilderCommitment::from_raw_digest(sha2::Sha256::new().finalize()); let mut generator = TestViewGenerator::generate(quorum_membership.clone(), da_membership); @@ -559,7 +562,7 @@ 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)), builder_commitment.clone(), TestMetadata, ViewNumber::new(1), @@ -576,7 +579,7 @@ 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)), builder_commitment.clone(), TestMetadata, ViewNumber::new(2), @@ -593,7 +596,7 @@ 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)), builder_commitment.clone(), TestMetadata, ViewNumber::new(3), @@ -610,7 +613,7 @@ 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)), builder_commitment.clone(), TestMetadata, ViewNumber::new(4), @@ -627,7 +630,7 @@ 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)), builder_commitment, TestMetadata, ViewNumber::new(5), diff --git a/crates/testing/tests/tests_1/quorum_vote_task.rs b/crates/testing/tests/tests_1/quorum_vote_task.rs index 83019caad4..265910168d 100644 --- a/crates/testing/tests/tests_1/quorum_vote_task.rs +++ b/crates/testing/tests/tests_1/quorum_vote_task.rs @@ -127,7 +127,7 @@ async fn test_quorum_vote_task_miss_dependency() { for view in (&mut generator).take(5).collect::>().await { proposals.push(view.quorum_proposal.clone()); leaders.push(view.leader_public_key); - votes.push(view.create_quorum_vote(&handle)); + votes.push(view.create_quorum_vote(&handle).await); dacs.push(view.da_certificate.clone()); vids.push(view.vid_proposal.clone()); leaves.push(view.leaf.clone()); diff --git a/crates/testing/tests/tests_1/test_with_failures_2.rs b/crates/testing/tests/tests_1/test_with_failures_2.rs index 9d551629b8..eedb2e55c2 100644 --- a/crates/testing/tests/tests_1/test_with_failures_2.rs +++ b/crates/testing/tests/tests_1/test_with_failures_2.rs @@ -11,8 +11,12 @@ use std::{ time::Duration, }; +use hotshot::tasks::DishonestDa; use hotshot_example_types::{ - node_types::{Libp2pImpl, MemoryImpl, PushCdnImpl, TestConsecutiveLeaderTypes, TestVersions}, + node_types::{ + Libp2pImpl, MarketplaceTestVersions, MemoryImpl, PushCdnImpl, TestConsecutiveLeaderTypes, + TestVersions, + }, state_types::TestTypes, }; use hotshot_macros::cross_tests; @@ -109,6 +113,41 @@ cross_tests!( }, ); +cross_tests!( + TestName: dishonest_da, + Impls: [MemoryImpl, Libp2pImpl, PushCdnImpl], + Types: [TestTypes], + Versions: [MarketplaceTestVersions], + Ignore: false, + Metadata: { + let behaviour = Rc::new(|node_id| { + let dishonest_da = DishonestDa { + dishonest_at_da_cert_sent_numbers: HashSet::from([2]), + total_da_certs_sent_from_node: 0, + total_views_add_to_cert: 4 + }; + match node_id { + 2 => Behaviour::Byzantine(Box::new(dishonest_da)), + _ => Behaviour::Standard, + } + }); + + let mut metadata = TestDescription { + // allow more time to pass in CI + completion_task_description: CompletionTaskDescription::TimeBasedCompletionTaskBuilder( + TimeBasedCompletionTaskDescription { + duration: Duration::from_secs(60), + }, + ), + behaviour, + ..TestDescription::default() + }; + + metadata.num_nodes_with_stake = 10; + metadata + }, +); + cross_tests!( TestName: test_with_double_leader_failures, Impls: [MemoryImpl, Libp2pImpl, PushCdnImpl], diff --git a/crates/testing/tests/tests_1/upgrade_task_with_consensus.rs b/crates/testing/tests/tests_1/upgrade_task_with_consensus.rs index 9c4ada9ae4..bae16c1267 100644 --- a/crates/testing/tests/tests_1/upgrade_task_with_consensus.rs +++ b/crates/testing/tests/tests_1/upgrade_task_with_consensus.rs @@ -78,7 +78,7 @@ async fn test_upgrade_task_vote() { for view in (&mut generator).take(2).collect::>().await { proposals.push(view.quorum_proposal.clone()); - votes.push(view.create_quorum_vote(&handle)); + votes.push(view.create_quorum_vote(&handle).await); dacs.push(view.da_certificate.clone()); vids.push(view.vid_proposal.clone()); leaders.push(view.leader_public_key); @@ -89,7 +89,7 @@ async fn test_upgrade_task_vote() { for view in generator.take(4).collect::>().await { proposals.push(view.quorum_proposal.clone()); - votes.push(view.create_quorum_vote(&handle)); + votes.push(view.create_quorum_vote(&handle).await); dacs.push(view.da_certificate.clone()); vids.push(view.vid_proposal.clone()); leaders.push(view.leader_public_key); @@ -239,7 +239,7 @@ async fn test_upgrade_task_propose() { for view in (&mut generator).take(1).collect::>().await { proposals.push(view.quorum_proposal.clone()); - votes.push(view.create_quorum_vote(&handle)); + votes.push(view.create_quorum_vote(&handle).await); dacs.push(view.da_certificate.clone()); vids.push(view.vid_proposal.clone()); leaders.push(view.leader_public_key); @@ -250,23 +250,29 @@ async fn test_upgrade_task_propose() { for view in generator.take(4).collect::>().await { proposals.push(view.quorum_proposal.clone()); - votes.push(view.create_quorum_vote(&handle)); + votes.push(view.create_quorum_vote(&handle).await); dacs.push(view.da_certificate.clone()); vids.push(view.vid_proposal.clone()); leaders.push(view.leader_public_key); views.push(view.clone()); } - let upgrade_votes = other_handles - .iter() - .map(|h| views[2].create_upgrade_vote(upgrade_data.clone(), &h.0)); + let mut upgrade_votes = Vec::new(); + + for handle in other_handles { + upgrade_votes.push( + views[2] + .create_upgrade_vote(upgrade_data.clone(), &handle.0) + .await, + ); + } let consensus_state = ConsensusTaskState::::create_from(&handle).await; let upgrade_state = UpgradeTaskState::::create_from(&handle).await; - let upgrade_vote_recvs: Vec<_> = upgrade_votes.map(UpgradeVoteRecv).collect(); + let upgrade_vote_recvs: Vec<_> = upgrade_votes.into_iter().map(UpgradeVoteRecv).collect(); let inputs = vec![ vec![ @@ -409,7 +415,7 @@ async fn test_upgrade_task_blank_blocks() { for view in (&mut generator).take(1).collect::>().await { proposals.push(view.quorum_proposal.clone()); - votes.push(view.create_quorum_vote(&handle)); + votes.push(view.create_quorum_vote(&handle).await); dacs.push(view.da_certificate.clone()); vids.push(view.vid_proposal.clone()); leaders.push(view.leader_public_key); @@ -420,7 +426,7 @@ async fn test_upgrade_task_blank_blocks() { for view in (&mut generator).take(3).collect::>().await { proposals.push(view.quorum_proposal.clone()); - votes.push(view.create_quorum_vote(&handle)); + votes.push(view.create_quorum_vote(&handle).await); dacs.push(view.da_certificate.clone()); vids.push(view.vid_proposal.clone()); leaders.push(view.leader_public_key); @@ -433,7 +439,7 @@ async fn test_upgrade_task_blank_blocks() { for view in (&mut generator).take(1).collect::>().await { proposals.push(view.quorum_proposal.clone()); - votes.push(view.create_quorum_vote(&handle)); + votes.push(view.create_quorum_vote(&handle).await); dacs.push(view.da_certificate.clone()); vids.push(view.vid_proposal.clone()); leaders.push(view.leader_public_key); @@ -446,7 +452,7 @@ async fn test_upgrade_task_blank_blocks() { for view in (&mut generator).take(1).collect::>().await { proposals.push(view.quorum_proposal.clone()); - votes.push(view.create_quorum_vote(&handle)); + votes.push(view.create_quorum_vote(&handle).await); dacs.push(view.da_certificate.clone()); vids.push(view.vid_proposal.clone()); leaders.push(view.leader_public_key); @@ -458,7 +464,7 @@ async fn test_upgrade_task_blank_blocks() { for view in generator.take(1).collect::>().await { proposals.push(view.quorum_proposal.clone()); - votes.push(view.create_quorum_vote(&handle)); + votes.push(view.create_quorum_vote(&handle).await); dacs.push(view.da_certificate.clone()); vids.push(view.vid_proposal.clone()); leaders.push(view.leader_public_key); 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 ce30a6f1fd..fcb25aaf23 100644 --- a/crates/testing/tests/tests_1/upgrade_task_with_proposal.rs +++ b/crates/testing/tests/tests_1/upgrade_task_with_proposal.rs @@ -96,7 +96,7 @@ async fn test_upgrade_task_with_proposal() { for view in (&mut generator).take(1).collect::>().await { proposals.push(view.quorum_proposal.clone()); - votes.push(view.create_quorum_vote(&handle)); + votes.push(view.create_quorum_vote(&handle).await); dacs.push(view.da_certificate.clone()); vid_dispersals.push(view.vid_disperse.clone()); leaders.push(view.leader_public_key); @@ -115,7 +115,7 @@ async fn test_upgrade_task_with_proposal() { for view in generator.take(4).collect::>().await { proposals.push(view.quorum_proposal.clone()); - votes.push(view.create_quorum_vote(&handle)); + votes.push(view.create_quorum_vote(&handle).await); dacs.push(view.da_certificate.clone()); vid_dispersals.push(view.vid_disperse.clone()); leaders.push(view.leader_public_key); @@ -143,22 +143,29 @@ async fn test_upgrade_task_with_proposal() { ::Base::VERSION, ) .unwrap(); - let upgrade_votes = other_handles - .iter() - .map(|h| views[2].create_upgrade_vote(upgrade_data.clone(), &h.0)); + + let mut upgrade_votes = Vec::new(); + + for handle in other_handles { + upgrade_votes.push( + views[2] + .create_upgrade_vote(upgrade_data.clone(), &handle.0) + .await, + ); + } let proposal_state = QuorumProposalTaskState::::create_from(&handle).await; let upgrade_state = UpgradeTaskState::::create_from(&handle).await; - let upgrade_vote_recvs: Vec<_> = upgrade_votes.map(UpgradeVoteRecv).collect(); + let upgrade_vote_recvs: Vec<_> = upgrade_votes.into_iter().map(UpgradeVoteRecv).collect(); let inputs = vec![ random![ QcFormed(either::Left(genesis_cert.clone())), SendPayloadCommitmentAndMetadata( - build_payload_commitment(&quorum_membership, ViewNumber::new(1)), + build_payload_commitment::(&quorum_membership, ViewNumber::new(1)), builder_commitment.clone(), TestMetadata, ViewNumber::new(1), @@ -175,7 +182,7 @@ 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)), builder_commitment.clone(), TestMetadata, ViewNumber::new(2), @@ -193,7 +200,7 @@ 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)), builder_commitment.clone(), TestMetadata, ViewNumber::new(3), diff --git a/crates/testing/tests/tests_1/upgrade_task_with_vote.rs b/crates/testing/tests/tests_1/upgrade_task_with_vote.rs index 7fa5d3bf40..37e3134bf1 100644 --- a/crates/testing/tests/tests_1/upgrade_task_with_vote.rs +++ b/crates/testing/tests/tests_1/upgrade_task_with_vote.rs @@ -81,7 +81,7 @@ async fn test_upgrade_task_with_vote() { let mut generator = TestViewGenerator::generate(quorum_membership.clone(), da_membership); for view in (&mut generator).take(2).collect::>().await { proposals.push(view.quorum_proposal.clone()); - votes.push(view.create_quorum_vote(&handle)); + votes.push(view.create_quorum_vote(&handle).await); dacs.push(view.da_certificate.clone()); vids.push(view.vid_proposal.clone()); leaders.push(view.leader_public_key); @@ -100,7 +100,7 @@ async fn test_upgrade_task_with_vote() { for view in generator.take(4).collect::>().await { proposals.push(view.quorum_proposal.clone()); - votes.push(view.create_quorum_vote(&handle)); + votes.push(view.create_quorum_vote(&handle).await); dacs.push(view.da_certificate.clone()); vids.push(view.vid_proposal.clone()); leaders.push(view.leader_public_key); diff --git a/crates/testing/tests/tests_1/view_sync_task.rs b/crates/testing/tests/tests_1/view_sync_task.rs index eba1c2ebdf..c0c4913981 100644 --- a/crates/testing/tests/tests_1/view_sync_task.rs +++ b/crates/testing/tests/tests_1/view_sync_task.rs @@ -36,7 +36,9 @@ async fn test_view_sync_task() { ::Time::new(4), hotshot_types::traits::consensus_api::ConsensusApi::public_key(&handle), hotshot_types::traits::consensus_api::ConsensusApi::private_key(&handle), + &handle.hotshot.upgrade_lock, ) + .await .expect("Failed to create a ViewSyncPreCommitVote!"); tracing::error!("Vote in test is {:?}", vote.clone()); @@ -52,6 +54,7 @@ async fn test_view_sync_task() { output.push(HotShotEvent::ViewChange(ViewNumber::new(2))); output.push(HotShotEvent::ViewSyncPreCommitVoteSend(vote.clone())); - let view_sync_state = ViewSyncTaskState::::create_from(&handle).await; + let view_sync_state = + ViewSyncTaskState::::create_from(&handle).await; run_harness(input, output, view_sync_state, false).await; } diff --git a/crates/testing/tests/tests_5/fake_solver.rs b/crates/testing/tests/tests_5/fake_solver.rs index 4e90656a60..27f1c91bb8 100644 --- a/crates/testing/tests/tests_5/fake_solver.rs +++ b/crates/testing/tests/tests_5/fake_solver.rs @@ -164,7 +164,7 @@ async fn test_fake_solver_fetch_permissioned_no_error() { ); // We need a private key - let (private_key, _) = key_pair_for_id(0); + let (private_key, _) = key_pair_for_id::(0); // Fire up the solver. let solver_url: Url = format!( @@ -220,7 +220,7 @@ async fn test_fake_solver_fetch_permissioned_with_errors() { FakeSolverState::new(Some(0.5), vec!["http://localhost:1111".parse().unwrap()]); // We need a private key - let (private_key, _) = key_pair_for_id(0); + let (private_key, _) = key_pair_for_id::(0); // Fire up the solver. let solver_url: Url = format!( diff --git a/crates/types/src/data.rs b/crates/types/src/data.rs index 15eb3599e6..168ca0669b 100644 --- a/crates/types/src/data.rs +++ b/crates/types/src/data.rs @@ -487,13 +487,13 @@ impl QuorumCertificate { .commit(), }; let commit = data.commit(); - Self { + Self::new( data, - vote_commitment: commit, - view_number: ::genesis(), - signatures: None, - _pd: PhantomData, - } + commit, + ::genesis(), + None, + PhantomData, + ) } } @@ -529,13 +529,13 @@ impl Leaf { leaf_commit: Commitment::>::default_commitment_no_preimage(), }; - let justify_qc = QuorumCertificate { - data: null_quorum_data.clone(), - vote_commitment: null_quorum_data.commit(), - view_number: ::genesis(), - signatures: None, - _pd: PhantomData, - }; + let justify_qc = QuorumCertificate::new( + null_quorum_data.clone(), + null_quorum_data.commit(), + ::genesis(), + None, + PhantomData, + ); Self { view_number: TYPES::Time::genesis(), diff --git a/crates/types/src/simple_certificate.rs b/crates/types/src/simple_certificate.rs index 4069cb2d98..c3b2b93e1e 100644 --- a/crates/types/src/simple_certificate.rs +++ b/crates/types/src/simple_certificate.rs @@ -19,15 +19,16 @@ use committable::{Commitment, Committable}; use ethereum_types::U256; use serde::{Deserialize, Serialize}; +use crate::message::UpgradeLock; use crate::{ data::serialize_signature2, simple_vote::{ - DaData, QuorumData, TimeoutData, UpgradeProposalData, ViewSyncCommitData, - ViewSyncFinalizeData, ViewSyncPreCommitData, Voteable, + DaData, QuorumData, TimeoutData, UpgradeProposalData, VersionedVoteData, + ViewSyncCommitData, ViewSyncFinalizeData, ViewSyncPreCommitData, Voteable, }, traits::{ election::Membership, - node_implementation::{ConsensusTime, NodeType}, + node_implementation::{ConsensusTime, NodeType, Versions}, signature_key::SignatureKey, }, vote::{Certificate, HasViewNumber}, @@ -75,7 +76,7 @@ pub struct SimpleCertificate, + vote_commitment: Commitment, /// Which view this QC relates to pub view_number: TYPES::Time, /// assembled signature for certificate aggregation @@ -84,6 +85,27 @@ pub struct SimpleCertificate, } +impl> + SimpleCertificate +{ + /// Creates a new instance of `SimpleCertificate` + pub fn new( + data: VOTEABLE, + vote_commitment: Commitment, + view_number: TYPES::Time, + signatures: Option<::QcType>, + pd: PhantomData<(TYPES, THRESHOLD)>, + ) -> Self { + Self { + data, + vote_commitment, + view_number, + signatures, + _pd: pd, + } + } +} + impl> Committable for SimpleCertificate { @@ -107,21 +129,27 @@ impl> type Voteable = VOTEABLE; type Threshold = THRESHOLD; - fn create_signed_certificate( - vote_commitment: Commitment, + fn create_signed_certificate( + vote_commitment: Commitment>, data: Self::Voteable, sig: ::QcType, view: TYPES::Time, ) -> Self { + let vote_commitment_bytes: [u8; 32] = vote_commitment.into(); + SimpleCertificate { data, - vote_commitment, + vote_commitment: Commitment::from_raw(vote_commitment_bytes), view_number: view, signatures: Some(sig), _pd: PhantomData, } } - fn is_valid_cert>(&self, membership: &MEMBERSHIP) -> bool { + async fn is_valid_cert, V: Versions>( + &self, + membership: &MEMBERSHIP, + upgrade_lock: &UpgradeLock, + ) -> bool { if self.view_number == TYPES::Time::genesis() { return true; } @@ -129,9 +157,12 @@ impl> membership.committee_qc_stake_table(), U256::from(Self::threshold(membership)), ); + let Ok(commit) = self.date_commitment(upgrade_lock).await else { + return false; + }; ::check( &real_qc_pp, - self.vote_commitment.as_ref(), + commit.as_ref(), self.signatures.as_ref().unwrap(), ) } @@ -141,8 +172,15 @@ impl> fn date(&self) -> &Self::Voteable { &self.data } - fn date_commitment(&self) -> Commitment { - self.vote_commitment + async fn date_commitment( + &self, + upgrade_lock: &UpgradeLock, + ) -> Result>> { + Ok( + VersionedVoteData::new(self.data.clone(), self.view_number, upgrade_lock) + .await? + .commit(), + ) } } @@ -206,13 +244,14 @@ impl UpgradeCertificate { /// Validate an upgrade certificate. /// # Errors /// Returns an error when the upgrade certificate is invalid. - pub fn validate( + pub async fn validate( upgrade_certificate: &Option, quorum_membership: &TYPES::Membership, + upgrade_lock: &UpgradeLock, ) -> Result<()> { if let Some(ref cert) = upgrade_certificate { ensure!( - cert.is_valid_cert(quorum_membership), + cert.is_valid_cert(quorum_membership, upgrade_lock).await, "Invalid upgrade certificate." ); Ok(()) diff --git a/crates/types/src/simple_vote.rs b/crates/types/src/simple_vote.rs index c91aac557c..fd1500c04c 100644 --- a/crates/types/src/simple_vote.rs +++ b/crates/types/src/simple_vote.rs @@ -6,15 +6,20 @@ //! Implementations of the simple vote types. -use std::{fmt::Debug, hash::Hash}; +use std::{fmt::Debug, hash::Hash, marker::PhantomData}; +use anyhow::Result; use committable::{Commitment, Committable}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use vbs::version::Version; +use vbs::version::{StaticVersionType, Version}; use crate::{ data::Leaf, - traits::{node_implementation::NodeType, signature_key::SignatureKey}, + message::UpgradeLock, + traits::{ + node_implementation::{NodeType, Versions}, + signature_key::SignatureKey, + }, vid::VidCommitment, vote::{HasViewNumber, Vote}, }; @@ -151,19 +156,81 @@ impl SimpleVote { /// Creates and signs a simple vote /// # Errors /// If we are unable to sign the data - pub fn create_signed_vote( + pub async fn create_signed_vote( data: DATA, view: TYPES::Time, pub_key: &TYPES::SignatureKey, private_key: &::PrivateKey, - ) -> Result::SignError> { - match TYPES::SignatureKey::sign(private_key, data.commit().as_ref()) { - Ok(signature) => Ok(Self { - signature: (pub_key.clone(), signature), - data, - view_number: view, - }), - Err(e) => Err(e), + upgrade_lock: &UpgradeLock, + ) -> Result { + let commit = VersionedVoteData::new(data.clone(), view, upgrade_lock) + .await? + .commit(); + + let signature = ( + pub_key.clone(), + TYPES::SignatureKey::sign(private_key, commit.as_ref())?, + ); + + Ok(Self { + signature, + data, + view_number: view, + }) + } +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)] +/// A wrapper for vote data that carries a view number and an `upgrade_lock`, allowing switching the commitment calculation dynamically depending on the version +pub struct VersionedVoteData { + /// underlying vote data + data: DATA, + + /// view number + view: TYPES::Time, + + /// version applied to the view number + version: Version, + + /// phantom data + _pd: PhantomData, +} + +impl VersionedVoteData { + /// Create a new `VersionedVoteData` struct + /// + /// # Errors + /// + /// Returns an error if `upgrade_lock.version(view)` is unable to return a version we support + pub async fn new( + data: DATA, + view: TYPES::Time, + upgrade_lock: &UpgradeLock, + ) -> Result { + let version = upgrade_lock.version(view).await?; + + Ok(Self { + data, + view, + version, + _pd: PhantomData, + }) + } +} + +impl Committable + for VersionedVoteData +{ + fn commit(&self) -> Commitment { + if self.version < V::Marketplace::VERSION { + let bytes: [u8; 32] = self.data.commit().into(); + + Commitment::::from_raw(bytes) + } else { + committable::RawCommitmentBuilder::new("Vote") + .var_size_bytes(self.data.commit().as_ref()) + .u64(*self.view) + .finalize() } } } diff --git a/crates/types/src/vote.rs b/crates/types/src/vote.rs index 14b868d624..83b0898c24 100644 --- a/crates/types/src/vote.rs +++ b/crates/types/src/vote.rs @@ -11,18 +11,20 @@ use std::{ marker::PhantomData, }; +use anyhow::Result; use bitvec::{bitvec, vec::BitVec}; -use committable::Commitment; +use committable::{Commitment, Committable}; use either::Either; use ethereum_types::U256; use tracing::error; use crate::{ + message::UpgradeLock, simple_certificate::Threshold, - simple_vote::Voteable, + simple_vote::{VersionedVoteData, Voteable}, traits::{ election::Membership, - node_implementation::NodeType, + node_implementation::{NodeType, Versions}, signature_key::{SignatureKey, StakeTableEntryType}, }, }; @@ -62,22 +64,29 @@ pub trait Certificate: HasViewNumber { type Threshold: Threshold; /// Build a certificate from the data commitment and the quorum of signers - fn create_signed_certificate( - vote_commitment: Commitment, + fn create_signed_certificate( + vote_commitment: Commitment>, data: Self::Voteable, sig: ::QcType, view: TYPES::Time, ) -> Self; /// Checks if the cert is valid - fn is_valid_cert>(&self, membership: &MEMBERSHIP) -> bool; + fn is_valid_cert, V: Versions>( + &self, + membership: &MEMBERSHIP, + upgrade_lock: &UpgradeLock, + ) -> impl std::future::Future; /// Returns the amount of stake needed to create this certificate // TODO: Make this a static ratio of the total stake of `Membership` fn threshold>(membership: &MEMBERSHIP) -> u64; /// Get the commitment which was voted on fn date(&self) -> &Self::Voteable; /// Get the vote commitment which the votes commit to - fn date_commitment(&self) -> Commitment; + fn date_commitment( + &self, + upgrade_lock: &UpgradeLock, + ) -> impl std::future::Future>>>; } /// Mapping of vote commitment to signatures and bitvec type SignersMap = HashMap< @@ -87,34 +96,63 @@ type SignersMap = HashMap< Vec<::PureAssembledSignatureType>, ), >; + +#[allow(clippy::type_complexity)] /// Accumulates votes until a certificate is formed. This implementation works for all simple vote and certificate pairs pub struct VoteAccumulator< TYPES: NodeType, VOTE: Vote, CERT: Certificate, + V: Versions, > { /// Map of all signatures accumulated so far pub vote_outcomes: VoteMap2< - Commitment, + Commitment>::Commitment, V>>, TYPES::SignatureKey, ::PureAssembledSignatureType, >, /// A bitvec to indicate which node is active and send out a valid signature for certificate aggregation, this automatically do uniqueness check /// And a list of valid signatures for certificate aggregation - pub signers: SignersMap, TYPES::SignatureKey>, + pub signers: SignersMap< + Commitment>::Commitment, V>>, + TYPES::SignatureKey, + >, /// Phantom data to specify the types this accumulator is for pub phantom: PhantomData<(TYPES, VOTE, CERT)>, + /// version information + pub upgrade_lock: UpgradeLock, } -impl, CERT: Certificate> - VoteAccumulator +impl< + TYPES: NodeType, + VOTE: Vote, + CERT: Certificate, + V: Versions, + > VoteAccumulator { /// Add a vote to the total accumulated votes. Returns the accumulator or the certificate if we /// have accumulated enough votes to exceed the threshold for creating a certificate. - pub fn accumulate(&mut self, vote: &VOTE, membership: &TYPES::Membership) -> Either<(), CERT> { + pub async fn accumulate( + &mut self, + vote: &VOTE, + membership: &TYPES::Membership, + ) -> Either<(), CERT> { let key = vote.signing_key(); - let vote_commitment = vote.date_commitment(); + let vote_commitment = match VersionedVoteData::new( + vote.date().clone(), + vote.view_number(), + &self.upgrade_lock, + ) + .await + { + Ok(data) => data.commit(), + Err(e) => { + tracing::warn!("Failed to generate versioned vote data: {e}"); + return Either::Left(()); + } + }; + if !key.validate(&vote.signature(), vote_commitment.as_ref()) { error!("Invalid vote! Vote Data {:?}", vote.date()); return Either::Left(()); @@ -156,7 +194,7 @@ impl, CERT: Certificate= CERT::threshold(membership).into() { // Assemble QC @@ -172,8 +210,8 @@ impl, CERT: Certificate( + vote_commitment, vote.date().clone(), real_qc_sig, vote.view_number(),