From 56b6663410bd95dbf30ab0ea6be9bf7057e9beeb Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Mon, 18 Sep 2023 14:17:15 -0400 Subject: [PATCH 01/56] TimeoutCert trait and TimeoutVote2 trait done --- crates/types/src/certificate.rs | 41 +++++++++++++++++++++++++++++++++ crates/types/src/vote.rs | 34 +++++++++++++++++++++++++++ flake.nix | 2 +- 3 files changed, 76 insertions(+), 1 deletion(-) diff --git a/crates/types/src/certificate.rs b/crates/types/src/certificate.rs index 9792026dcf..b86e96cce8 100644 --- a/crates/types/src/certificate.rs +++ b/crates/types/src/certificate.rs @@ -1,8 +1,10 @@ //! Provides two types of cerrtificates and their accumulators. +use crate::vote::AccumulatorPlaceholder; use crate::vote::DAVoteAccumulator; use crate::vote::QuorumVote; use crate::vote::QuorumVoteAccumulator; +use crate::vote::TimeoutVote2; use crate::vote::ViewSyncVoteAccumulator; use crate::vote::VoteType; use crate::{ @@ -83,6 +85,45 @@ pub struct TimeoutCertificate { pub signatures: AssembledSignature, } +impl SignedCertificate + for TimeoutCertificate +{ + type Vote = TimeoutVote2; + + type VoteAccumulator = AccumulatorPlaceholder; + + fn from_signatures_and_commitment( + signatures: AssembledSignature, + vote: Self::Vote, + ) -> Self { + todo!() + } + + fn view_number(&self) -> TYPES::Time { + todo!() + } + + fn signatures(&self) -> AssembledSignature { + todo!() + } + + fn leaf_commitment(&self) -> Commitment { + todo!() + } + + fn set_leaf_commitment(&mut self, commitment: Commitment) { + todo!() + } + + fn is_genesis(&self) -> bool { + todo!() + } + + fn genesis() -> Self { + todo!() + } +} + /// Certificate for view sync. #[derive(custom_debug::Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Hash)] #[serde(bound(deserialize = ""))] diff --git a/crates/types/src/vote.rs b/crates/types/src/vote.rs index d5dda8d825..e92d6175d0 100644 --- a/crates/types/src/vote.rs +++ b/crates/types/src/vote.rs @@ -95,6 +95,40 @@ pub struct TimeoutVote> { pub vote_data: VoteData, } +/// A timeout vote +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)] +#[serde(bound(deserialize = ""))] +pub struct TimeoutVote2 { + /// The signature share associated with this vote + pub signature: (EncodedPublicKey, EncodedSignature), + /// The view this vote was cast for + pub current_view: TYPES::Time, + /// The vote token generated by this replica + pub vote_token: TYPES::VoteTokenType, +} + +impl VoteType for TimeoutVote2 { + fn get_view(&self) -> ::Time { + todo!() + } + + fn get_key(&self) -> ::SignatureKey { + todo!() + } + + fn get_signature(&self) -> EncodedSignature { + todo!() + } + + fn get_data(&self) -> VoteData { + todo!() + } + + fn get_vote_token(&self) -> ::VoteTokenType { + todo!() + } +} + /// The internals of a view sync vote #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)] #[serde(bound(deserialize = ""))] diff --git a/flake.nix b/flake.nix index 2ff009c372..04cbf3c782 100644 --- a/flake.nix +++ b/flake.nix @@ -173,7 +173,7 @@ zlib.out fenix.packages.${system}.rust-analyzer just - pkgconfig + pkg-config openssl.dev openssl.out ] ++ lib.optionals stdenv.isDarwin [ From 9aa591be91afd63ec78ff56bfe80a090c01c0e86 Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Mon, 18 Sep 2023 14:22:04 -0400 Subject: [PATCH 02/56] TimeoutExchange wireframe --- crates/types/src/traits/election.rs | 100 +++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 2 deletions(-) diff --git a/crates/types/src/traits/election.rs b/crates/types/src/traits/election.rs index 81dfd46d57..832d9fe4b0 100644 --- a/crates/types/src/traits/election.rs +++ b/crates/types/src/traits/election.rs @@ -9,9 +9,9 @@ use super::{ }; use crate::{ certificate::{ - AssembledSignature, DACertificate, QuorumCertificate, ViewSyncCertificate, VoteMetaData, + AssembledSignature, DACertificate, QuorumCertificate, ViewSyncCertificate, VoteMetaData, TimeoutCertificate, }, - data::{DAProposal, ProposalType}, + data::{DAProposal, ProposalType}, vote::TimeoutVote2, }; use crate::{ @@ -1448,6 +1448,102 @@ impl< } } +// TODO ED All the exchange structs are the same. We could just considate them into one struct +/// Standard implementation of a Timeout Exchange based on Hot Stuff consensus. +#[derive(Derivative)] +#[derivative(Clone, Debug)] +pub struct TimeoutExchange< + TYPES: NodeType, + PROPOSAL: ProposalType, + MEMBERSHIP: Membership, + NETWORK: CommunicationChannel, + M: NetworkMsg, +> { + /// The network being used by this exchange. + network: NETWORK, + /// The committee which votes on proposals. + membership: MEMBERSHIP, + /// This participant's public key. + public_key: TYPES::SignatureKey, + /// Entry with public key and staking value for certificate aggregation in the stake table. + entry: ::StakeTableEntry, + /// This participant's private key. + #[derivative(Debug = "ignore")] + private_key: ::PrivateKey, + #[doc(hidden)] + _pd: PhantomData<(PROPOSAL, MEMBERSHIP, M)>, +} + +// TODO ED Get rid of ProposalType as generic, is debt left over from Validating Consensus +impl< + TYPES: NodeType, + PROPOSAL: ProposalType, + MEMBERSHIP: Membership, + NETWORK: CommunicationChannel, + M: NetworkMsg, + > ConsensusExchange for TimeoutExchange +{ + type Proposal = PROPOSAL; + type Vote = TimeoutVote2; + type Certificate = TimeoutCertificate; + type Membership = MEMBERSHIP; + type Networking = NETWORK; + type Commitment = TYPES::Time; + + fn create( + entries: Vec<::StakeTableEntry>, + config: TYPES::ElectionConfigType, + network: Self::Networking, + pk: TYPES::SignatureKey, + entry: ::StakeTableEntry, + sk: ::PrivateKey, + ) -> Self { + let membership = + >::Membership::create_election(entries, config); + Self { + network, + membership, + public_key: pk, + entry, + private_key: sk, + _pd: PhantomData, + } + } + + fn network(&self) -> &NETWORK { + &self.network + } + + fn vote_data(&self, _commit: Commitment) -> VoteData { + unimplemented!() + } + + + fn membership(&self) -> &Self::Membership { + &self.membership + } + fn public_key(&self) -> &TYPES::SignatureKey { + &self.public_key + } + fn private_key(&self) -> &<::SignatureKey as SignatureKey>::PrivateKey { + &self.private_key + } + + fn accumulate_vote( + &self, + encoded_key: &EncodedPublicKey, + encoded_signature: &EncodedSignature, + leaf_commitment: Commitment, + vote_data: VoteData, + vote_token: ::VoteTokenType, + view_number: ::Time, + accumlator: VoteAccumulator<::VoteTokenType, Self::Commitment, TYPES>, + relay: Option, + ) -> Either::VoteTokenType, Self::Commitment, TYPES>, Self::Certificate> { + todo!() + } +} + /// Testable implementation of a [`Membership`]. Will expose a method to generate a vote token used for testing. pub trait TestableElection: Membership { /// Generate a vote token used for testing. From 95f1aacb07cedbb6c8743ce6a8b4321f0812243f Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Mon, 18 Sep 2023 15:08:58 -0400 Subject: [PATCH 03/56] Add TimeoutExchangeType trait --- crates/types/src/traits/election.rs | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/crates/types/src/traits/election.rs b/crates/types/src/traits/election.rs index 832d9fe4b0..ce58bdd62b 100644 --- a/crates/types/src/traits/election.rs +++ b/crates/types/src/traits/election.rs @@ -9,9 +9,11 @@ use super::{ }; use crate::{ certificate::{ - AssembledSignature, DACertificate, QuorumCertificate, ViewSyncCertificate, VoteMetaData, TimeoutCertificate, + AssembledSignature, DACertificate, QuorumCertificate, TimeoutCertificate, + ViewSyncCertificate, VoteMetaData, }, - data::{DAProposal, ProposalType}, vote::TimeoutVote2, + data::{DAProposal, ProposalType}, + vote::TimeoutVote2, }; use crate::{ @@ -1474,6 +1476,18 @@ pub struct TimeoutExchange< _pd: PhantomData<(PROPOSAL, MEMBERSHIP, M)>, } +pub trait TimeoutExchangeType: ConsensusExchange {} + +impl< + TYPES: NodeType, + PROPOSAL: ProposalType, + MEMBERSHIP: Membership, + NETWORK: CommunicationChannel, + M: NetworkMsg, + > TimeoutExchangeType for TimeoutExchange +{ +} + // TODO ED Get rid of ProposalType as generic, is debt left over from Validating Consensus impl< TYPES: NodeType, @@ -1518,7 +1532,6 @@ impl< unimplemented!() } - fn membership(&self) -> &Self::Membership { &self.membership } @@ -1539,7 +1552,10 @@ impl< view_number: ::Time, accumlator: VoteAccumulator<::VoteTokenType, Self::Commitment, TYPES>, relay: Option, - ) -> Either::VoteTokenType, Self::Commitment, TYPES>, Self::Certificate> { + ) -> Either< + VoteAccumulator<::VoteTokenType, Self::Commitment, TYPES>, + Self::Certificate, + > { todo!() } } From 1acb8a2c1b0c25b563d844cad09242e5bd378ca5 Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Mon, 18 Sep 2023 15:58:26 -0400 Subject: [PATCH 04/56] Compiles with timeout_exchange added to consensus task --- crates/hotshot/src/lib.rs | 1 + crates/hotshot/src/tasks/mod.rs | 1 + crates/task-impls/src/consensus.rs | 3 ++ .../types/src/traits/node_implementation.rs | 37 +++++++++++++++++-- 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/crates/hotshot/src/lib.rs b/crates/hotshot/src/lib.rs index b09ea6235d..e2582b85b8 100644 --- a/crates/hotshot/src/lib.rs +++ b/crates/hotshot/src/lib.rs @@ -182,6 +182,7 @@ impl> SystemContext { initializer: HotShotInitializer, metrics: Box, ) -> Result> { + debug!("Creating a new hotshot"); let consensus_metrics = Arc::new(ConsensusMetrics::new( diff --git a/crates/hotshot/src/tasks/mod.rs b/crates/hotshot/src/tasks/mod.rs index 619fdb6d59..87c776563a 100644 --- a/crates/hotshot/src/tasks/mod.rs +++ b/crates/hotshot/src/tasks/mod.rs @@ -286,6 +286,7 @@ where cur_view: TYPES::Time::new(0), block: TYPES::BlockType::new(), quorum_exchange: c_api.inner.exchanges.quorum_exchange().clone().into(), + timeout_exchange: c_api.inner.exchanges.timeout_exchange().clone().into(), api: c_api.clone(), committee_exchange: c_api.inner.exchanges.committee_exchange().clone().into(), _pd: PhantomData, diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index dccf7919c3..defa7a6475 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -34,6 +34,7 @@ use hotshot_types::{ utils::{Terminator, ViewInner}, vote::{QuorumVote, VoteType}, }; +use hotshot_types::traits::node_implementation::SequencingTimeoutEx; use snafu::Snafu; use std::{ @@ -89,6 +90,8 @@ pub struct SequencingConsensusTaskState< /// the quorum exchange pub quorum_exchange: Arc>, + pub timeout_exchange: Arc>, + /// Consensus api pub api: A, diff --git a/crates/types/src/traits/node_implementation.rs b/crates/types/src/traits/node_implementation.rs index 49d9184bc4..7219743536 100644 --- a/crates/types/src/traits/node_implementation.rs +++ b/crates/types/src/traits/node_implementation.rs @@ -7,7 +7,7 @@ use super::{ block_contents::Transaction, election::{ CommitteeExchangeType, ConsensusExchange, ElectionConfig, QuorumExchangeType, - ViewSyncExchangeType, VoteToken, + TimeoutExchange, TimeoutExchangeType, ViewSyncExchangeType, VoteToken, }, network::{CommunicationChannel, NetworkMsg, TestableNetworkingImplementation}, state::{ConsensusTime, TestableBlock, TestableState}, @@ -154,12 +154,16 @@ pub trait ExchangesType, MESSA /// Get the committee exchange. fn committee_exchange(&self) -> &Self::CommitteeExchange; + fn timeout_exchange(&self) -> &Self::TimeoutExchange; + /// Protocol for exchanging quorum proposals and votes. type QuorumExchange: QuorumExchangeType + Clone + Debug; /// Protocol for exchanging view sync proposals and votes. type ViewSyncExchange: ViewSyncExchangeType + Clone + Debug; + type TimeoutExchange: TimeoutExchangeType + Clone + Debug; + /// Election configurations for exchanges type ElectionConfigs; @@ -217,9 +221,9 @@ pub trait TestableExchange, ME pub struct SequencingExchanges< TYPES: NodeType, MESSAGE: NetworkMsg, - QUORUMEXCHANGE: QuorumExchangeType, MESSAGE>, - COMMITTEEEXCHANGE: CommitteeExchangeType, - VIEWSYNCEXCHANGE: ViewSyncExchangeType, + QUORUMEXCHANGE: QuorumExchangeType, MESSAGE> + Clone + Debug, + COMMITTEEEXCHANGE: CommitteeExchangeType + Clone + Debug, + VIEWSYNCEXCHANGE: ViewSyncExchangeType + Clone + Debug, > { /// Quorum exchange. quorum_exchange: QUORUMEXCHANGE, @@ -230,6 +234,9 @@ pub struct SequencingExchanges< /// Committee exchange. committee_exchange: COMMITTEEEXCHANGE, + // TODO ED Make this not public + pub timeout_exchange: TimeoutExchange as ExchangesType, MESSAGE>>::QuorumExchange as ConsensusExchange>::Proposal, < as ExchangesType, MESSAGE>>::QuorumExchange as ConsensusExchange>::Membership, >::Networking, MESSAGE>, + /// Phantom data. _phantom: PhantomData<(TYPES, MESSAGE)>, } @@ -248,12 +255,17 @@ where type CommitteeExchange = COMMITTEEEXCHANGE; type QuorumExchange = QUORUMEXCHANGE; type ViewSyncExchange = VIEWSYNCEXCHANGE; + type TimeoutExchange = TimeoutExchange as ExchangesType, MESSAGE>>::QuorumExchange as ConsensusExchange>::Proposal, < as ExchangesType, MESSAGE>>::QuorumExchange as ConsensusExchange>::Membership, >::Networking, MESSAGE>; type ElectionConfigs = (TYPES::ElectionConfigType, TYPES::ElectionConfigType); fn committee_exchange(&self) -> &COMMITTEEEXCHANGE { &self.committee_exchange } + fn timeout_exchange(&self) -> &Self::TimeoutExchange { + &self.timeout_exchange + } + fn create( entries: Vec<::StakeTableEntry>, configs: Self::ElectionConfigs, @@ -267,6 +279,14 @@ where sk: ::PrivateKey, ) -> Self { let quorum_exchange = QUORUMEXCHANGE::create( + entries.clone(), + configs.0.clone(), + networks.0.clone(), + pk.clone(), + entry.clone(), + sk.clone(), + ); + let timeout_exchange: TimeoutExchange as ExchangesType, MESSAGE>>::QuorumExchange as ConsensusExchange>::Proposal, < as ExchangesType, MESSAGE>>::QuorumExchange as ConsensusExchange>::Membership, >::Networking, MESSAGE> = TimeoutExchange::create( entries.clone(), configs.0.clone(), networks.0, @@ -274,6 +294,7 @@ where entry.clone(), sk.clone(), ); + let view_sync_exchange = VIEWSYNCEXCHANGE::create( entries.clone(), configs.0, @@ -289,6 +310,7 @@ where quorum_exchange, committee_exchange, view_sync_exchange, + timeout_exchange, _phantom: PhantomData, } } @@ -327,6 +349,13 @@ pub type SequencingQuorumEx = Message, >>::QuorumExchange; + pub type SequencingTimeoutEx = + <>::Exchanges as ExchangesType< + TYPES, + >::Leaf, + Message, + >>::TimeoutExchange; + /// Alias for the [`CommitteeExchange`] type. pub type CommitteeEx = <>::Exchanges as ExchangesType< TYPES, From 2d55199fa71aaedca9dfc42c39a59e16bf87db68 Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Mon, 18 Sep 2023 16:27:04 -0400 Subject: [PATCH 05/56] Rough addition of create_timeout_message on timeout_exchange --- crates/task-impls/src/consensus.rs | 10 ++++-- crates/types/src/traits/election.rs | 35 ++++++++++++++++++- .../types/src/traits/node_implementation.rs | 2 +- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index defa7a6475..90ed192393 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -15,6 +15,8 @@ use hotshot_task::{ task::{FilterEvent, HandleEvent, HotShotTaskCompleted, HotShotTaskTypes, TS}, task_impls::{HSTWithEvent, TaskBuilder}, }; +use hotshot_types::traits::election::TimeoutExchangeType; +use hotshot_types::traits::node_implementation::SequencingTimeoutEx; use hotshot_types::vote::QuorumVoteAccumulator; use hotshot_types::{ certificate::{DACertificate, QuorumCertificate}, @@ -34,7 +36,6 @@ use hotshot_types::{ utils::{Terminator, ViewInner}, vote::{QuorumVote, VoteType}, }; -use hotshot_types::traits::node_implementation::SequencingTimeoutEx; use snafu::Snafu; use std::{ @@ -90,6 +91,7 @@ pub struct SequencingConsensusTaskState< /// the quorum exchange pub quorum_exchange: Arc>, + /// The timeout exchange pub timeout_exchange: Arc>, /// Consensus api @@ -1068,8 +1070,10 @@ where } } SequencingHotShotEvent::Timeout(view) => { - // The view sync module will handle updating views in the case of timeout - // TODO ED In the future send a timeout vote + + // TODO ED Why I here and not in quorum exchange? + self.timeout_exchange.create_timeout_message::(view); + self.quorum_exchange .network() .inject_consensus_info(ConsensusIntentEvent::CancelPollForVotes(*view)) diff --git a/crates/types/src/traits/election.rs b/crates/types/src/traits/election.rs index ce58bdd62b..c0da8fef1d 100644 --- a/crates/types/src/traits/election.rs +++ b/crates/types/src/traits/election.rs @@ -1476,7 +1476,40 @@ pub struct TimeoutExchange< _pd: PhantomData<(PROPOSAL, MEMBERSHIP, M)>, } -pub trait TimeoutExchangeType: ConsensusExchange {} +impl< + TYPES: NodeType, + PROPOSAL: ProposalType, + MEMBERSHIP: Membership, + NETWORK: CommunicationChannel, + M: NetworkMsg, + > TimeoutExchange +{ +} + +pub trait TimeoutExchangeType: ConsensusExchange { + // TODO ED Clean this function up + fn create_timeout_message>( + &self, + view: TYPES::Time, + ) -> GeneralConsensusMessage + where + I::Exchanges: ExchangesType>, + { + let signature = TYPES::SignatureKey::sign( + &self.private_key(), + VoteData::::Timeout(view.commit()) + .commit() + .as_ref(), + ); + + // GeneralConsensusMessage::::TODO { + // signature: (self.public_key.to_bytes(), signature), + // current_view: view, + // vote_data: VoteData::Timeout(view.commit()), + // }) + todo!() + } +} impl< TYPES: NodeType, diff --git a/crates/types/src/traits/node_implementation.rs b/crates/types/src/traits/node_implementation.rs index 7219743536..f7aafe8615 100644 --- a/crates/types/src/traits/node_implementation.rs +++ b/crates/types/src/traits/node_implementation.rs @@ -349,7 +349,7 @@ pub type SequencingQuorumEx = Message, >>::QuorumExchange; - pub type SequencingTimeoutEx = +pub type SequencingTimeoutEx = <>::Exchanges as ExchangesType< TYPES, >::Leaf, From 809a27a743edc3fd2417a63a5d67c7dfbe646aa1 Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Mon, 18 Sep 2023 18:17:36 -0400 Subject: [PATCH 06/56] Add Timeout events to networking task --- crates/task-impls/src/consensus.rs | 26 +++++++++++++++++++++++-- crates/task-impls/src/events.rs | 4 +++- crates/task-impls/src/network.rs | 15 +++++++++++++++ crates/types/src/message.rs | 11 ++++++++++- crates/types/src/traits/election.rs | 30 +++++++++++++++-------------- 5 files changed, 68 insertions(+), 18 deletions(-) diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index 90ed192393..36dced718b 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -1069,10 +1069,32 @@ where ); } } + SequencingHotShotEvent::TimeoutVoteRecv(vote) => { + panic!() + } SequencingHotShotEvent::Timeout(view) => { + let vote_token = self.timeout_exchange.make_vote_token(view); - // TODO ED Why I here and not in quorum exchange? - self.timeout_exchange.create_timeout_message::(view); + match vote_token { + Err(e) => { + error!("Failed to generate vote token for {:?} {:?}", view, e); + } + Ok(None) => { + debug!("We were not chosen for consensus committee on {:?}", view); + } + Ok(Some(vote_token)) => { + // TODO ED Why I here and not in quorum exchange? + let message = self + .timeout_exchange + .create_timeout_message::(view, vote_token); + + if let GeneralConsensusMessage::TimeoutVote(vote) = message { + self.event_stream + .publish(SequencingHotShotEvent::TimeoutVoteSend(vote)) + .await; + } + } + } self.quorum_exchange .network() diff --git a/crates/task-impls/src/events.rs b/crates/task-impls/src/events.rs index de8e31fd03..6c3d978bdb 100644 --- a/crates/task-impls/src/events.rs +++ b/crates/task-impls/src/events.rs @@ -5,7 +5,7 @@ use hotshot_types::{ traits::node_implementation::{ NodeImplementation, NodeType, QuorumProposalType, ViewSyncProposalType, }, - vote::{DAVote, QuorumVote, ViewSyncVote}, + vote::{DAVote, QuorumVote, ViewSyncVote, TimeoutVote2}, }; use crate::view_sync::ViewSyncPhase; @@ -19,6 +19,8 @@ pub enum SequencingHotShotEvent> { QuorumProposalRecv(Proposal>, TYPES::SignatureKey), /// A quorum vote has been received from the network; handled by the consensus task QuorumVoteRecv(QuorumVote), + TimeoutVoteRecv(TimeoutVote2), + TimeoutVoteSend(TimeoutVote2), /// A DA proposal has been received from the network; handled by the DA task DAProposalRecv(Proposal>, TYPES::SignatureKey), /// A DA vote has been received by the network; handled by the DA task diff --git a/crates/task-impls/src/network.rs b/crates/task-impls/src/network.rs index 5fbada8539..2070eda63f 100644 --- a/crates/task-impls/src/network.rs +++ b/crates/task-impls/src/network.rs @@ -89,6 +89,9 @@ impl< GeneralConsensusMessage::ViewSyncCertificate(view_sync_message) => { SequencingHotShotEvent::ViewSyncCertificateRecv(view_sync_message) } + GeneralConsensusMessage::TimeoutVote(message) => { + SequencingHotShotEvent::TimeoutVoteRecv(message) + } GeneralConsensusMessage::InternalTrigger(_) => { error!("Got unexpected message type in network task!"); return; @@ -282,6 +285,16 @@ impl< Some(membership.get_leader(vote.round() + vote.relay())), ) } + SequencingHotShotEvent::TimeoutVoteSend(vote) => { + ( + vote.get_key(), + MessageKind::::from_consensus_message(SequencingMessage(Left( + GeneralConsensusMessage::TimeoutVote(vote.clone()), + ))), + TransmitType::Direct, + Some(membership.get_leader(vote.get_view())), + ) + } SequencingHotShotEvent::ViewChange(view) => { self.view = view; return None; @@ -336,6 +349,8 @@ impl< | SequencingHotShotEvent::DACSend(_, _) | SequencingHotShotEvent::VidCertSend(_, _) | SequencingHotShotEvent::ViewChange(_) + | SequencingHotShotEvent::TimeoutVoteSend(_) + ) } diff --git a/crates/types/src/message.rs b/crates/types/src/message.rs index d5897c0ef8..28daf64b43 100644 --- a/crates/types/src/message.rs +++ b/crates/types/src/message.rs @@ -13,7 +13,7 @@ use crate::{ }, signature_key::EncodedSignature, }, - vote::{DAVote, QuorumVote, ViewSyncVote, VoteType}, + vote::{DAVote, QuorumVote, TimeoutVote, TimeoutVote2, ViewSyncVote, VoteType}, }; use derivative::Derivative; use either::Either::{self, Left, Right}; @@ -201,6 +201,8 @@ where } GeneralConsensusMessage::ViewSyncVote(_) | GeneralConsensusMessage::ViewSyncCertificate(_) => todo!(), + | GeneralConsensusMessage::TimeoutVote(_) => todo!(), + } } } @@ -322,6 +324,9 @@ where /// Message with a view sync certificate. ViewSyncCertificate(Proposal>), + /// Message with a Timeout vote + TimeoutVote(TimeoutVote2), + /// Internal ONLY message indicating a view interrupt. #[serde(skip)] InternalTrigger(InternalTrigger), @@ -416,6 +421,9 @@ impl< GeneralConsensusMessage::ViewSyncCertificate(message) => { message.data.get_view_number() } + GeneralConsensusMessage::TimeoutVote(message) => { + message.get_view() + } } } Right(committee_message) => { @@ -447,6 +455,7 @@ impl< GeneralConsensusMessage::InternalTrigger(_) => MessagePurpose::Internal, GeneralConsensusMessage::ViewSyncVote(_) => MessagePurpose::ViewSyncVote, GeneralConsensusMessage::ViewSyncCertificate(_) => MessagePurpose::ViewSyncProposal, + GeneralConsensusMessage::TimeoutVote(_) => todo!(), }, Right(committee_message) => match committee_message { CommitteeConsensusMessage::DAProposal(_) => MessagePurpose::Proposal, diff --git a/crates/types/src/traits/election.rs b/crates/types/src/traits/election.rs index c0da8fef1d..7448423c80 100644 --- a/crates/types/src/traits/election.rs +++ b/crates/types/src/traits/election.rs @@ -1491,6 +1491,7 @@ pub trait TimeoutExchangeType: ConsensusExchange fn create_timeout_message>( &self, view: TYPES::Time, + vote_token: TYPES::VoteTokenType, ) -> GeneralConsensusMessage where I::Exchanges: ExchangesType>, @@ -1502,12 +1503,13 @@ pub trait TimeoutExchangeType: ConsensusExchange .as_ref(), ); - // GeneralConsensusMessage::::TODO { - // signature: (self.public_key.to_bytes(), signature), - // current_view: view, - // vote_data: VoteData::Timeout(view.commit()), - // }) - todo!() + GeneralConsensusMessage::::TimeoutVote(TimeoutVote2 { + signature: (self.public_key().to_bytes(), signature), + current_view: view, + vote_token + }) + + } } @@ -1577,14 +1579,14 @@ impl< fn accumulate_vote( &self, - encoded_key: &EncodedPublicKey, - encoded_signature: &EncodedSignature, - leaf_commitment: Commitment, - vote_data: VoteData, - vote_token: ::VoteTokenType, - view_number: ::Time, - accumlator: VoteAccumulator<::VoteTokenType, Self::Commitment, TYPES>, - relay: Option, + _encoded_key: &EncodedPublicKey, + _encoded_signature: &EncodedSignature, + _leaf_commitment: Commitment, + _vote_data: VoteData, + _vote_token: ::VoteTokenType, + _view_number: ::Time, + _accumlator: VoteAccumulator<::VoteTokenType, Self::Commitment, TYPES>, + _relay: Option, ) -> Either< VoteAccumulator<::VoteTokenType, Self::Commitment, TYPES>, Self::Certificate, From 14e53364bbd5788ba6f956b03c28c6ed99932992 Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Mon, 18 Sep 2023 18:24:39 -0400 Subject: [PATCH 07/56] Consensus task properly recieves TimeoutVotes --- crates/task-impls/src/consensus.rs | 1 + crates/task-impls/src/network.rs | 2 +- crates/types/src/vote.rs | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index 36dced718b..04c28681eb 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -1323,6 +1323,7 @@ pub fn consensus_event_filter>( | SequencingHotShotEvent::ViewChange(_) | SequencingHotShotEvent::SendDABlockData(_) | SequencingHotShotEvent::Timeout(_) + | SequencingHotShotEvent::TimeoutVoteRecv(_) | SequencingHotShotEvent::Shutdown, ) } diff --git a/crates/task-impls/src/network.rs b/crates/task-impls/src/network.rs index 2070eda63f..bfba4a413d 100644 --- a/crates/task-impls/src/network.rs +++ b/crates/task-impls/src/network.rs @@ -292,7 +292,7 @@ impl< GeneralConsensusMessage::TimeoutVote(vote.clone()), ))), TransmitType::Direct, - Some(membership.get_leader(vote.get_view())), + Some(membership.get_leader(vote.get_view() + 1)), ) } SequencingHotShotEvent::ViewChange(view) => { diff --git a/crates/types/src/vote.rs b/crates/types/src/vote.rs index e92d6175d0..6513044394 100644 --- a/crates/types/src/vote.rs +++ b/crates/types/src/vote.rs @@ -109,15 +109,15 @@ pub struct TimeoutVote2 { impl VoteType for TimeoutVote2 { fn get_view(&self) -> ::Time { - todo!() + self.current_view } fn get_key(&self) -> ::SignatureKey { - todo!() + ::from_bytes(&self.signature.0).unwrap() } fn get_signature(&self) -> EncodedSignature { - todo!() + self.signature.1.clone() } fn get_data(&self) -> VoteData { From d42855724c4d40d5beefa6cd8c80b683b73c190f Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Mon, 18 Sep 2023 21:25:02 -0400 Subject: [PATCH 08/56] Add timeout exchange and accumulator to consensus task, compiles --- crates/hotshot/src/lib.rs | 12 +- crates/hotshot/src/tasks/mod.rs | 9 ++ crates/task-impls/src/consensus.rs | 189 ++++++++++++++++++++++++++++- crates/types/src/certificate.rs | 4 +- crates/types/src/vote.rs | 1 + 5 files changed, 209 insertions(+), 6 deletions(-) diff --git a/crates/hotshot/src/lib.rs b/crates/hotshot/src/lib.rs index e2582b85b8..3617952591 100644 --- a/crates/hotshot/src/lib.rs +++ b/crates/hotshot/src/lib.rs @@ -55,6 +55,8 @@ use hotshot_task::{ task_launcher::TaskRunner, }; use hotshot_task_impls::{events::SequencingHotShotEvent, network::NetworkTaskKind}; +use hotshot_types::certificate::TimeoutCertificate; +use hotshot_types::traits::node_implementation::SequencingTimeoutEx; use hotshot_types::{ certificate::{DACertificate, ViewSyncCertificate}, @@ -182,7 +184,6 @@ impl> SystemContext { initializer: HotShotInitializer, metrics: Box, ) -> Result> { - debug!("Creating a new hotshot"); let consensus_metrics = Arc::new(ConsensusMetrics::new( @@ -684,6 +685,14 @@ where Commitment = ViewSyncData, Membership = MEMBERSHIP, > + 'static, + SequencingTimeoutEx: ConsensusExchange< + TYPES, + Message, + Proposal = QuorumProposal>, + Certificate = TimeoutCertificate, + Commitment = TYPES::Time, + Membership = MEMBERSHIP, + > + 'static, { fn transactions( &self, @@ -706,6 +715,7 @@ where let quorum_exchange = self.inner.exchanges.quorum_exchange().clone(); let committee_exchange = self.inner.exchanges.committee_exchange().clone(); let view_sync_exchange = self.inner.exchanges.view_sync_exchange().clone(); + let timeout_exchange = self.inner.exchanges.timeout_exchange().clone(); let handle = SystemContextHandle { registry, diff --git a/crates/hotshot/src/tasks/mod.rs b/crates/hotshot/src/tasks/mod.rs index 87c776563a..4dc66b5539 100644 --- a/crates/hotshot/src/tasks/mod.rs +++ b/crates/hotshot/src/tasks/mod.rs @@ -26,6 +26,8 @@ use hotshot_task_impls::{ transactions::{TransactionTaskState, TransactionsTaskTypes}, view_sync::{ViewSyncTaskState, ViewSyncTaskStateTypes}, }; +use hotshot_types::certificate::TimeoutCertificate; +use hotshot_types::traits::node_implementation::SequencingTimeoutEx; use hotshot_types::{ certificate::ViewSyncCertificate, data::{ProposalType, QuorumProposal, SequencingLeaf}, @@ -272,6 +274,13 @@ where Certificate = DACertificate, Commitment = TYPES::BlockType, >, + SequencingTimeoutEx: ConsensusExchange< + TYPES, + Message, + Proposal = QuorumProposal>, + Certificate = TimeoutCertificate, + Commitment = TYPES::Time, + >, { let consensus = handle.hotshot.get_consensus(); let c_api: HotShotSequencingConsensusApi = HotShotSequencingConsensusApi { diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index 04c28681eb..d5310e8156 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -15,8 +15,10 @@ use hotshot_task::{ task::{FilterEvent, HandleEvent, HotShotTaskCompleted, HotShotTaskTypes, TS}, task_impls::{HSTWithEvent, TaskBuilder}, }; +use hotshot_types::certificate::TimeoutCertificate; use hotshot_types::traits::election::TimeoutExchangeType; use hotshot_types::traits::node_implementation::SequencingTimeoutEx; +use hotshot_types::vote::DAVoteAccumulator; use hotshot_types::vote::QuorumVoteAccumulator; use hotshot_types::{ certificate::{DACertificate, QuorumCertificate}, @@ -75,6 +77,13 @@ pub struct SequencingConsensusTaskState< Certificate = DACertificate, Commitment = TYPES::BlockType, >, + SequencingTimeoutEx: ConsensusExchange< + TYPES, + Message, + Proposal = QuorumProposal>, + Certificate = TimeoutCertificate, + Commitment = TYPES::Time, + >, { /// The global task registry pub registry: GlobalRegistry, @@ -146,9 +155,19 @@ pub struct VoteCollectionTaskState< Certificate = QuorumCertificate>, Commitment = SequencingLeaf, >, + SequencingTimeoutEx: ConsensusExchange< + TYPES, + Message, + Proposal = QuorumProposal>, + Certificate = TimeoutCertificate, + Commitment = TYPES::Time, + >, { /// the quorum exchange pub quorum_exchange: Arc>, + + pub timeout_exchange: Arc>, + #[allow(clippy::type_complexity)] /// Accumulator for votes pub accumulator: Either< @@ -160,6 +179,17 @@ pub struct VoteCollectionTaskState< >>::VoteAccumulator, QuorumCertificate>, >, + + /// Accumulator for votes + pub timeout_accumulator: Either< + as SignedCertificate< + TYPES, + TYPES::Time, + TYPES::VoteTokenType, + TYPES::Time, + >>::VoteAccumulator, + TimeoutCertificate, + >, /// View which this vote collection task is collecting votes in pub cur_view: TYPES::Time, /// The event stream shared by all tasks @@ -178,6 +208,13 @@ where Certificate = QuorumCertificate>, Commitment = SequencingLeaf, >, + SequencingTimeoutEx: ConsensusExchange< + TYPES, + Message, + Proposal = QuorumProposal>, + Certificate = TimeoutCertificate, + Commitment = TYPES::Time, + >, { } @@ -198,6 +235,13 @@ where Certificate = QuorumCertificate>, Commitment = SequencingLeaf, >, + SequencingTimeoutEx: ConsensusExchange< + TYPES, + Message, + Proposal = QuorumProposal>, + Certificate = TimeoutCertificate, + Commitment = TYPES::Time, + >, { match event { SequencingHotShotEvent::QuorumVoteRecv(vote) => match vote.clone() { @@ -247,6 +291,7 @@ where } } } + QuorumVote::Timeout(_vote) => { error!("The next leader has received an unexpected vote!"); return (None, state); @@ -255,6 +300,9 @@ where error!("The next leader has received an unexpected vote!"); } }, + SequencingHotShotEvent::TimeoutVoteRecv(vote) => { + panic!() + } SequencingHotShotEvent::Shutdown => { return (Some(HotShotTaskCompleted::ShutDown), state); } @@ -286,6 +334,13 @@ where Certificate = DACertificate, Commitment = TYPES::BlockType, >, + SequencingTimeoutEx: ConsensusExchange< + TYPES, + Message, + Proposal = QuorumProposal>, + Certificate = TimeoutCertificate, + Commitment = TYPES::Time, + >, { #[instrument(skip_all, fields(id = self.id, view = *self.cur_view), name = "Consensus genesis leaf", level = "error")] @@ -883,6 +938,8 @@ where return; } + // TODO ED Insert TimeoutVote accumulator stuff here + match vote.clone() { QuorumVote::Yes(vote_internal) => { let handle_event = HandleEvent(Arc::new(move |event, state| { @@ -920,10 +977,23 @@ where &vote_internal.clone().leaf_commitment, ); + let timeout_accumulator = DAVoteAccumulator { + da_vote_outcomes: HashMap::new(), + + // TODO ED Don't use quorum exchange here + success_threshold: self.quorum_exchange.success_threshold(), + + sig_lists: Vec::new(), + signers: bitvec![0; self.quorum_exchange.total_nodes()], + phantom: PhantomData, + }; + if vote_internal.current_view > collection_view { let state = VoteCollectionTaskState { quorum_exchange: self.quorum_exchange.clone(), + timeout_exchange: self.timeout_exchange.clone(), accumulator, + timeout_accumulator: either::Left(timeout_accumulator), cur_view: vote_internal.current_view, event_stream: self.event_stream.clone(), id: self.id, @@ -969,6 +1039,108 @@ where } } } + SequencingHotShotEvent::TimeoutVoteRecv(vote) => { + // debug!("Received quroum vote: {:?}", vote.get_view()); + + if !self.timeout_exchange.is_leader(vote.get_view() + 1) { + error!( + "We are not the leader for view {} are we the leader for view + 1? {}", + *vote.get_view() + 1, + self.timeout_exchange.is_leader(vote.get_view() + 2) + ); + return; + } + + // // TODO ED Insert TimeoutVote accumulator stuff here + + // match vote.clone() { + // QuorumVote::Yes(vote_internal)=> { + let handle_event = HandleEvent(Arc::new(move |event, state| { + async move { vote_handle(state, event).await }.boxed() + })); + let collection_view = + if let Some((collection_view, collection_task, _)) = &self.vote_collector { + if vote.get_view() > *collection_view { + // ED I think we'd want to let that task timeout to avoid a griefing vector + self.registry.shutdown_task(*collection_task).await; + } + *collection_view + } else { + TYPES::Time::new(0) + }; + + // // Todo check if we are the leader + // TODO ED Make this a default accum + let new_accumulator = DAVoteAccumulator { + da_vote_outcomes: HashMap::new(), + + // TODO ED Don't use quorum exchange here + success_threshold: self.quorum_exchange.success_threshold(), + + sig_lists: Vec::new(), + signers: bitvec![0; self.quorum_exchange.total_nodes()], + phantom: PhantomData, + }; + + let timeout_accumulator = self.timeout_exchange.accumulate_vote_2( + new_accumulator, + &vote, + &vote.get_view().commit(), + ); + + let quorum_accumulator = QuorumVoteAccumulator { + total_vote_outcomes: HashMap::new(), + yes_vote_outcomes: HashMap::new(), + no_vote_outcomes: HashMap::new(), + + success_threshold: self.quorum_exchange.success_threshold(), + failure_threshold: self.quorum_exchange.failure_threshold(), + + sig_lists: Vec::new(), + signers: bitvec![0; self.quorum_exchange.total_nodes()], + phantom: PhantomData, + }; + + // self.timeout_accumulator = accumulator; + + if vote.get_view() > collection_view { + let state = VoteCollectionTaskState { + quorum_exchange: self.quorum_exchange.clone(), + timeout_exchange: self.timeout_exchange.clone(), + accumulator: either::Left(quorum_accumulator), + timeout_accumulator, + cur_view: vote.get_view(), + event_stream: self.event_stream.clone(), + id: self.id, + }; + let name = "Quorum Vote Collection"; + let filter = FilterEvent(Arc::new(|event| { + matches!(event, SequencingHotShotEvent::QuorumVoteRecv(_)) + })); + + let builder = + TaskBuilder::>::new(name.to_string()) + .register_event_stream(self.event_stream.clone(), filter) + .await + .register_registry(&mut self.registry.clone()) + .await + .register_state(state) + .register_event_handler(handle_event); + let id = builder.get_task_id().unwrap(); + let stream_id = builder.get_stream_id().unwrap(); + + self.vote_collector = Some((vote.get_view(), id, stream_id)); + + let _task = async_spawn(async move { + VoteCollectionTypes::build(builder).launch().await; + }); + debug!("Starting vote handle for view {:?}", vote.get_view()); + } else if let Some((_, _, stream_id)) = self.vote_collector { + self.event_stream + .direct_message(stream_id, SequencingHotShotEvent::TimeoutVoteRecv(vote)) + .await; + } + } SequencingHotShotEvent::QCFormed(qc) => { debug!("QC Formed event happened!"); @@ -1069,9 +1241,6 @@ where ); } } - SequencingHotShotEvent::TimeoutVoteRecv(vote) => { - panic!() - } SequencingHotShotEvent::Timeout(view) => { let vote_token = self.timeout_exchange.make_vote_token(view); @@ -1251,6 +1420,13 @@ where Certificate = DACertificate, Commitment = TYPES::BlockType, >, + SequencingTimeoutEx: ConsensusExchange< + TYPES, + Message, + Proposal = QuorumProposal>, + Certificate = TimeoutCertificate, + Commitment = TYPES::Time, + >, { } @@ -1300,6 +1476,13 @@ where Certificate = DACertificate, Commitment = TYPES::BlockType, >, + SequencingTimeoutEx: ConsensusExchange< + TYPES, + Message, + Proposal = QuorumProposal>, + Certificate = TimeoutCertificate, + Commitment = TYPES::Time, + >, { if let SequencingHotShotEvent::Shutdown = event { (Some(HotShotTaskCompleted::ShutDown), state) diff --git a/crates/types/src/certificate.rs b/crates/types/src/certificate.rs index b86e96cce8..3457f8523d 100644 --- a/crates/types/src/certificate.rs +++ b/crates/types/src/certificate.rs @@ -90,8 +90,8 @@ impl SignedCertificate; - type VoteAccumulator = AccumulatorPlaceholder; - + type VoteAccumulator = DAVoteAccumulator; + fn from_signatures_and_commitment( signatures: AssembledSignature, vote: Self::Vote, diff --git a/crates/types/src/vote.rs b/crates/types/src/vote.rs index 6513044394..e593f6aab7 100644 --- a/crates/types/src/vote.rs +++ b/crates/types/src/vote.rs @@ -593,6 +593,7 @@ impl< /// Accumulates view sync votes pub struct ViewSyncVoteAccumulator< TYPES: NodeType, + // TODO ED : Doesn't need to be generic over vote or committable, we can infer that from the accumulator type (unless it is a generic type) COMMITTABLE: Committable + Serialize + Clone, VOTE: VoteType, > { From 81dd0190b75b6bffc1eba7449c0f96434c210ad7 Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Tue, 19 Sep 2023 08:55:55 -0400 Subject: [PATCH 09/56] Comments --- crates/types/src/traits/election.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/types/src/traits/election.rs b/crates/types/src/traits/election.rs index 7448423c80..ac449c2b8a 100644 --- a/crates/types/src/traits/election.rs +++ b/crates/types/src/traits/election.rs @@ -300,6 +300,7 @@ pub trait ConsensusExchange: Send + Sync { /// Network used by [`Membership`](Self::Membership) to communicate. type Networking: CommunicationChannel; /// Commitments to items which are the subject of proposals and decisions. + // TODO ED The above isn't true for all proposals (Timeout, ViewSync) type Commitment: Committable + Serialize + Clone; /// Join a [`ConsensusExchange`] with the given identity (`pk` and `sk`). @@ -786,6 +787,7 @@ pub trait QuorumExchangeType, ConsensusExchange { /// Create a message with a positive vote on validating or commitment proposal. + // TODO ED This returns just a general message type, it's not even bound to a proposal, and this is just a function on the QC. Make proprosal doesn't really apply to all cert types. fn create_yes_message>( &self, justify_qc_commitment: Commitment, From 112a404acb1548266b4c891a7033a6b753fffd38 Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Tue, 19 Sep 2023 09:38:55 -0400 Subject: [PATCH 10/56] Vote collection is receiving timeout votes correctly --- crates/task-impls/src/consensus.rs | 15 ++-- crates/task-impls/src/network.rs | 1 + crates/types/src/certificate.rs | 5 +- crates/types/src/data.rs | 4 ++ crates/types/src/traits/election.rs | 8 +++ crates/types/src/vote.rs | 106 +++++++++++++++++++++++++++- 6 files changed, 130 insertions(+), 9 deletions(-) diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index d5310e8156..0572db796d 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -20,6 +20,7 @@ use hotshot_types::traits::election::TimeoutExchangeType; use hotshot_types::traits::node_implementation::SequencingTimeoutEx; use hotshot_types::vote::DAVoteAccumulator; use hotshot_types::vote::QuorumVoteAccumulator; +use hotshot_types::vote::TimeoutVoteAccumulator; use hotshot_types::{ certificate::{DACertificate, QuorumCertificate}, consensus::{Consensus, View}, @@ -306,7 +307,7 @@ where SequencingHotShotEvent::Shutdown => { return (Some(HotShotTaskCompleted::ShutDown), state); } - _ => {} + _ => {error!("Unexpected event")} } (None, state) } @@ -977,7 +978,7 @@ where &vote_internal.clone().leaf_commitment, ); - let timeout_accumulator = DAVoteAccumulator { + let timeout_accumulator = TimeoutVoteAccumulator { da_vote_outcomes: HashMap::new(), // TODO ED Don't use quorum exchange here @@ -1000,7 +1001,7 @@ where }; let name = "Quorum Vote Collection"; let filter = FilterEvent(Arc::new(|event| { - matches!(event, SequencingHotShotEvent::QuorumVoteRecv(_)) + matches!(event, SequencingHotShotEvent::QuorumVoteRecv(_) | SequencingHotShotEvent::TimeoutVoteRecv(_)) })); let builder = @@ -1041,6 +1042,7 @@ where } SequencingHotShotEvent::TimeoutVoteRecv(vote) => { // debug!("Received quroum vote: {:?}", vote.get_view()); + if !self.timeout_exchange.is_leader(vote.get_view() + 1) { error!( @@ -1051,6 +1053,8 @@ where return; } + + // // TODO ED Insert TimeoutVote accumulator stuff here // match vote.clone() { @@ -1071,7 +1075,7 @@ where // // Todo check if we are the leader // TODO ED Make this a default accum - let new_accumulator = DAVoteAccumulator { + let new_accumulator = TimeoutVoteAccumulator { da_vote_outcomes: HashMap::new(), // TODO ED Don't use quorum exchange here @@ -1115,7 +1119,7 @@ where }; let name = "Quorum Vote Collection"; let filter = FilterEvent(Arc::new(|event| { - matches!(event, SequencingHotShotEvent::QuorumVoteRecv(_)) + matches!(event, SequencingHotShotEvent::QuorumVoteRecv(_) | SequencingHotShotEvent::TimeoutVoteRecv(_)) })); let builder = @@ -1257,6 +1261,7 @@ where .timeout_exchange .create_timeout_message::(view, vote_token); + // error!("Sending timeout vote for view {}", *view); if let GeneralConsensusMessage::TimeoutVote(vote) = message { self.event_stream .publish(SequencingHotShotEvent::TimeoutVoteSend(vote)) diff --git a/crates/task-impls/src/network.rs b/crates/task-impls/src/network.rs index bfba4a413d..c7df58d90a 100644 --- a/crates/task-impls/src/network.rs +++ b/crates/task-impls/src/network.rs @@ -90,6 +90,7 @@ impl< SequencingHotShotEvent::ViewSyncCertificateRecv(view_sync_message) } GeneralConsensusMessage::TimeoutVote(message) => { + // error!("Recv timeout vote in network task for view {:?}", message.get_view()); SequencingHotShotEvent::TimeoutVoteRecv(message) } GeneralConsensusMessage::InternalTrigger(_) => { diff --git a/crates/types/src/certificate.rs b/crates/types/src/certificate.rs index 3457f8523d..c4e180bb48 100644 --- a/crates/types/src/certificate.rs +++ b/crates/types/src/certificate.rs @@ -5,6 +5,7 @@ use crate::vote::DAVoteAccumulator; use crate::vote::QuorumVote; use crate::vote::QuorumVoteAccumulator; use crate::vote::TimeoutVote2; +use crate::vote::TimeoutVoteAccumulator; use crate::vote::ViewSyncVoteAccumulator; use crate::vote::VoteType; use crate::{ @@ -90,7 +91,7 @@ impl SignedCertificate; - type VoteAccumulator = DAVoteAccumulator; + type VoteAccumulator = TimeoutVoteAccumulator; fn from_signatures_and_commitment( signatures: AssembledSignature, @@ -168,6 +169,8 @@ pub enum AssembledSignature { No(::QCType), /// These signatures are for a 'DA' certificate DA(::QCType), + + Timeout(::QCType), /// These signatures are for genesis certificate Genesis(), /// These signatures are for ViewSyncPreCommit diff --git a/crates/types/src/data.rs b/crates/types/src/data.rs index 8275c8d9b8..e749c39251 100644 --- a/crates/types/src/data.rs +++ b/crates/types/src/data.rs @@ -885,6 +885,10 @@ pub fn serialize_signature(signature: &AssembledSignature { + signatures_bytes.extend("Timeout".as_bytes()); + Some(signatures.clone()) + } AssembledSignature::ViewSyncPreCommit(signatures) => { signatures_bytes.extend("ViewSyncPreCommit".as_bytes()); Some(signatures.clone()) diff --git a/crates/types/src/traits/election.rs b/crates/types/src/traits/election.rs index ac449c2b8a..b4196ce666 100644 --- a/crates/types/src/traits/election.rs +++ b/crates/types/src/traits/election.rs @@ -393,6 +393,14 @@ pub trait ConsensusExchange: Send + Sync { ); ::check(&real_qc_pp, real_commit.as_ref(), &qc) } + AssembledSignature::Timeout(qc) => { + let real_commit = VoteData::Timeout(leaf_commitment).commit(); + let real_qc_pp = ::get_public_parameter( + self.membership().get_committee_qc_stake_table(), + U256::from(self.membership().success_threshold().get()), + ); + ::check(&real_qc_pp, real_commit.as_ref(), &qc) + } AssembledSignature::Genesis() => true, AssembledSignature::ViewSyncPreCommit(_) | AssembledSignature::ViewSyncCommit(_) diff --git a/crates/types/src/vote.rs b/crates/types/src/vote.rs index e593f6aab7..e19464e141 100644 --- a/crates/types/src/vote.rs +++ b/crates/types/src/vote.rs @@ -25,7 +25,7 @@ use std::{ marker::PhantomData, num::NonZeroU64, }; -use tracing::error; +use tracing::{error, warn}; /// The vote sent by consensus messages. pub trait VoteType: @@ -121,11 +121,11 @@ impl VoteType for TimeoutVote2 { } fn get_data(&self) -> VoteData { - todo!() + VoteData::Timeout(self.get_view().commit()) } fn get_vote_token(&self) -> ::VoteTokenType { - todo!() + self.vote_token.clone() } } @@ -368,6 +368,98 @@ pub trait Accumulator2< ) -> Either>; } +// TODO ED Make a default accumulator +pub struct TimeoutVoteAccumulator< + TYPES: NodeType, + COMMITTABLE: Committable + Serialize + Clone, + VOTE: VoteType, +> { + /// Map of all da signatures accumlated so far + pub da_vote_outcomes: VoteMap, + /// A quorum's worth of stake, generally 2f + 1 + pub success_threshold: NonZeroU64, + /// A list of valid signatures for certificate aggregation + pub sig_lists: Vec<::PureAssembledSignatureType>, + /// A bitvec to indicate which node is active and send out a valid signature for certificate aggregation, this automatically do uniqueness check + pub signers: BitVec, + /// Phantom data to specify the vote this accumulator is for + pub phantom: PhantomData, +} + +impl< + TYPES: NodeType, + COMMITTABLE: Committable + Serialize + Clone, + VOTE: VoteType, + > Accumulator2 for TimeoutVoteAccumulator +{ + fn append( + mut self, + vote: VOTE, + vote_node_id: usize, + stake_table_entries: Vec<::StakeTableEntry>, + ) -> Either> { + + let VoteData::Timeout(vote_commitment) = vote.get_data() else { + return Either::Left(self); + }; + + let encoded_key = vote.get_key().to_bytes(); + + // Deserialize the signature so that it can be assembeld into a QC + // TODO ED Update this once we've gotten rid of EncodedSignature + let original_signature: ::PureAssembledSignatureType = + bincode_opts() + .deserialize(&vote.get_signature().0) + .expect("Deserialization on the signature shouldn't be able to fail."); + + let (da_stake_casted, da_vote_map) = self + .da_vote_outcomes + .entry(vote_commitment) + .or_insert_with(|| (0, BTreeMap::new())); + + // Check for duplicate vote + // TODO ED Re-encoding signature key to bytes until we get rid of EncodedKey + // Have to do this because SignatureKey is not hashable + if da_vote_map.contains_key(&encoded_key) { + return Either::Left(self); + } + + if self.signers.get(vote_node_id).as_deref() == Some(&true) { + error!("Node id is already in signers list"); + return Either::Left(self); + } + self.signers.set(vote_node_id, true); + self.sig_lists.push(original_signature); + + // Already checked that vote data was for a DA vote above + *da_stake_casted += u64::from(vote.get_vote_token().vote_count()); + da_vote_map.insert( + encoded_key, + (vote.get_signature(), vote.get_data(), vote.get_vote_token()), + ); + + if *da_stake_casted >= u64::from(self.success_threshold) { + // Assemble QC + let real_qc_pp = ::get_public_parameter( + // TODO ED Something about stake table entries. Might be easier to just pass in membership? + stake_table_entries.clone(), + U256::from(self.success_threshold.get()), + ); + + let real_qc_sig = ::assemble( + &real_qc_pp, + self.signers.as_bitslice(), + &self.sig_lists[..], + ); + + self.da_vote_outcomes.remove(&vote_commitment); + + return Either::Right(AssembledSignature::Timeout(real_qc_sig)); + } + Either::Left(self) + } +} + /// Accumulates DA votes pub struct DAVoteAccumulator< TYPES: NodeType, @@ -398,6 +490,14 @@ impl< vote_node_id: usize, stake_table_entries: Vec<::StakeTableEntry>, ) -> Either> { + + match vote.get_data() { + VoteData::DA(_) => warn!("DA vote data"), + VoteData::Timeout(_) => panic!(), + _ => error!("Wrong vote data") + + } + let VoteData::DA(vote_commitment) = vote.get_data() else { return Either::Left(self); }; From f6968ce03c2d6feb6eb060c34160117535fcbdd8 Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Tue, 19 Sep 2023 10:29:08 -0400 Subject: [PATCH 11/56] Bug - proposals after timeout don't have timeout cert and have too low of qc --- crates/task-impls/src/consensus.rs | 171 +++++++++++++++++++++-------- crates/task-impls/src/events.rs | 5 +- crates/types/src/certificate.rs | 16 ++- 3 files changed, 139 insertions(+), 53 deletions(-) diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index 0572db796d..9cbae0ff6c 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -275,7 +275,7 @@ where debug!("QCFormed! {:?}", qc.view_number); state .event_stream - .publish(SequencingHotShotEvent::QCFormed(qc.clone())) + .publish(SequencingHotShotEvent::QCFormed(either::Left(qc.clone()))) .await; state.accumulator = Either::Right(qc.clone()); @@ -302,12 +302,58 @@ where } }, SequencingHotShotEvent::TimeoutVoteRecv(vote) => { - panic!() + if state.timeout_accumulator.is_right() { + return (None, state); + } + + if vote.get_view() != state.cur_view { + error!( + "Vote view does not match! vote view is {} current view is {}", + *vote.get_view(), + *state.cur_view + ); + return (None, state); + } + + let accumulator = state.timeout_accumulator.left().unwrap(); + + match state.timeout_exchange.accumulate_vote_2( + accumulator, + &vote, + &vote.get_view().commit(), + ) { + Either::Left(acc) => { + state.timeout_accumulator = Either::Left(acc); + return (None, state); + } + Either::Right(qc) => { + debug!("QCFormed! {:?}", qc.view_number); + // TODO ED Make Timeout QC Formed Event + state + .event_stream + .publish(SequencingHotShotEvent::QCFormed(either::Right(qc.clone()))) + .await; + state.timeout_accumulator = Either::Right(qc.clone()); + + // No longer need to poll for votes + state + .quorum_exchange + .network() + .inject_consensus_info(ConsensusIntentEvent::CancelPollForVotes( + *qc.view_number, + )) + .await; + + return (Some(HotShotTaskCompleted::ShutDown), state); + } + } } SequencingHotShotEvent::Shutdown => { return (Some(HotShotTaskCompleted::ShutDown), state); } - _ => {error!("Unexpected event")} + _ => { + error!("Unexpected event") + } } (None, state) } @@ -626,6 +672,7 @@ where return; } + // TODO ED How does this play in with the timeout cert? self.current_proposal = Some(proposal.data.clone()); let vote_token = self.quorum_exchange.make_vote_token(view); @@ -638,11 +685,20 @@ where debug!("We were not chosen for consensus committee on {:?}", view); } Ok(Some(vote_token)) => { + if proposal.data.justify_qc.view_number() != proposal.data.view_number - 1 { + // TODO ED Add timeout cert logic + if proposal.data.timeout_certificate.is_none() { + error!("Proposal needed a timeout cert but didn't have one {:?}", proposal.data.clone()); + return + } + // TODO ED Check timeout cert validity + } + debug!("We were chosen for consensus committee on {:?}", view); let consensus = self.consensus.upgradable_read().await; let message; - // TODO ED Insert TC logic here + // Construct the leaf. let justify_qc = proposal.data.justify_qc; @@ -904,7 +960,7 @@ where "Attempting to publish proposal after voting; now in view: {}", *new_view ); - self.publish_proposal_if_able(qc.clone(), qc.view_number + 1) + self.publish_proposal_if_able(qc.clone(), qc.view_number + 1, None) .await; } if !self.vote_if_able().await { @@ -1001,7 +1057,11 @@ where }; let name = "Quorum Vote Collection"; let filter = FilterEvent(Arc::new(|event| { - matches!(event, SequencingHotShotEvent::QuorumVoteRecv(_) | SequencingHotShotEvent::TimeoutVoteRecv(_)) + matches!( + event, + SequencingHotShotEvent::QuorumVoteRecv(_) + | SequencingHotShotEvent::TimeoutVoteRecv(_) + ) })); let builder = @@ -1042,7 +1102,6 @@ where } SequencingHotShotEvent::TimeoutVoteRecv(vote) => { // debug!("Received quroum vote: {:?}", vote.get_view()); - if !self.timeout_exchange.is_leader(vote.get_view() + 1) { error!( @@ -1053,8 +1112,6 @@ where return; } - - // // TODO ED Insert TimeoutVote accumulator stuff here // match vote.clone() { @@ -1119,7 +1176,11 @@ where }; let name = "Quorum Vote Collection"; let filter = FilterEvent(Arc::new(|event| { - matches!(event, SequencingHotShotEvent::QuorumVoteRecv(_) | SequencingHotShotEvent::TimeoutVoteRecv(_)) + matches!( + event, + SequencingHotShotEvent::QuorumVoteRecv(_) + | SequencingHotShotEvent::TimeoutVoteRecv(_) + ) })); let builder = @@ -1145,42 +1206,62 @@ where .await; } } - SequencingHotShotEvent::QCFormed(qc) => { + SequencingHotShotEvent::QCFormed(cert) => { debug!("QC Formed event happened!"); - let mut consensus = self.consensus.write().await; - consensus.high_qc = qc.clone(); - - drop(consensus); - - // View may have already been updated by replica if they voted for this QC - // TODO ED We should separate leader state from replica state, they shouldn't share the same view - // Leader task should only run for a specific view, and never update its current view, but instead spawn another task - // let _res = self.update_view(qc.view_number + 1).await; - - // Start polling for votes for the next view - // if _res { - // if self.quorum_exchange.is_leader(qc.view_number + 2) { - // self.quorum_exchange - // .network() - // .inject_consensus_info( - // (ConsensusIntentEvent::PollForVotes(*qc.view_number + 1)), - // ) - // .await; - // } - // } + if let either::Right(qc) = cert.clone() { + // So we don't create a QC on the first view unless we are the leader + debug!( + "Attempting to publish proposal after forming a QC for view {}", + *qc.view_number + ); - // So we don't create a QC on the first view unless we are the leader - debug!( - "Attempting to publish proposal after forming a QC for view {}", - *qc.view_number - ); + // TODO ED Clean this up, get rid of clones + if self + .publish_proposal_if_able(self.consensus.read().await.high_qc.clone(), qc.clone().view_number + 1, Some(qc.clone())) + .await + { + self.update_view(qc.view_number + 1).await; + } + else { + error!("Wasn't able to publish proposal"); + } + } + if let either::Left(qc) = cert { + let mut consensus = self.consensus.write().await; + consensus.high_qc = qc.clone(); + + drop(consensus); + + // View may have already been updated by replica if they voted for this QC + // TODO ED We should separate leader state from replica state, they shouldn't share the same view + // Leader task should only run for a specific view, and never update its current view, but instead spawn another task + // let _res = self.update_view(qc.view_number + 1).await; + + // Start polling for votes for the next view + // if _res { + // if self.quorum_exchange.is_leader(qc.view_number + 2) { + // self.quorum_exchange + // .network() + // .inject_consensus_info( + // (ConsensusIntentEvent::PollForVotes(*qc.view_number + 1)), + // ) + // .await; + // } + // } + + // So we don't create a QC on the first view unless we are the leader + debug!( + "Attempting to publish proposal after forming a QC for view {}", + *qc.view_number + ); - if self - .publish_proposal_if_able(qc.clone(), qc.view_number + 1) - .await - { - self.update_view(qc.view_number + 1).await; + if self + .publish_proposal_if_able(qc.clone(), qc.view_number + 1, None) + .await + { + self.update_view(qc.view_number + 1).await; + } } } SequencingHotShotEvent::DACRecv(cert) => { @@ -1238,7 +1319,7 @@ where let consensus = self.consensus.read().await; let qc = consensus.high_qc.clone(); drop(consensus); - if !self.publish_proposal_if_able(qc, self.cur_view).await { + if !self.publish_proposal_if_able(qc, self.cur_view, None).await { error!( "Failed to publish proposal on view change. View = {:?}", self.cur_view @@ -1292,6 +1373,7 @@ where &self, _qc: QuorumCertificate, view: TYPES::Time, + timeout_certificate: Option> ) -> bool { if !self.quorum_exchange.is_leader(view) { error!( @@ -1380,8 +1462,7 @@ where view_number: leaf.view_number, height: leaf.height, justify_qc: consensus.high_qc.clone(), - // TODO ED Update this to be the actual TC if there is one - timeout_certificate: None, + timeout_certificate: timeout_certificate.or_else(|| { None }), proposer_id: leaf.proposer_id, dac: None, }; diff --git a/crates/task-impls/src/events.rs b/crates/task-impls/src/events.rs index 6c3d978bdb..ded16bac6e 100644 --- a/crates/task-impls/src/events.rs +++ b/crates/task-impls/src/events.rs @@ -1,5 +1,6 @@ +use either::Either; use hotshot_types::{ - certificate::{DACertificate, QuorumCertificate}, + certificate::{DACertificate, QuorumCertificate, TimeoutCertificate}, data::{DAProposal, VidDisperse}, message::Proposal, traits::node_implementation::{ @@ -36,7 +37,7 @@ pub enum SequencingHotShotEvent> { /// Send a DA vote to the DA leader; emitted by DA committee members in the DA task after seeing a valid DA proposal DAVoteSend(DAVote), /// The next leader has collected enough votes to form a QC; emitted by the next leader in the consensus task; an internal event only - QCFormed(QuorumCertificate), + QCFormed(Either, TimeoutCertificate>), /// The DA leader has collected enough votes to form a DAC; emitted by the DA leader in the DA task; sent to the entire network via the networking task DACSend(DACertificate, TYPES::SignatureKey), /// The current view has changed; emitted by the replica in the consensus task or replica in the view sync task; received by almost all other tasks diff --git a/crates/types/src/certificate.rs b/crates/types/src/certificate.rs index c4e180bb48..3cc7cb7265 100644 --- a/crates/types/src/certificate.rs +++ b/crates/types/src/certificate.rs @@ -91,25 +91,29 @@ impl SignedCertificate; - type VoteAccumulator = TimeoutVoteAccumulator; - + type VoteAccumulator = TimeoutVoteAccumulator; + fn from_signatures_and_commitment( signatures: AssembledSignature, vote: Self::Vote, ) -> Self { - todo!() + let qc = TimeoutCertificate { + view_number: vote.get_view(), + signatures, + }; + qc } fn view_number(&self) -> TYPES::Time { - todo!() + self.view_number } fn signatures(&self) -> AssembledSignature { - todo!() + self.signatures.clone() } fn leaf_commitment(&self) -> Commitment { - todo!() + self.view_number.commit() } fn set_leaf_commitment(&mut self, commitment: Commitment) { From a519b8ea474d66b23a60aa36d70eb59c67aef803 Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Tue, 19 Sep 2023 10:34:53 -0400 Subject: [PATCH 12/56] Fix previous bug --- crates/task-impls/src/consensus.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index 9cbae0ff6c..29cbaa1f14 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -1319,12 +1319,13 @@ where let consensus = self.consensus.read().await; let qc = consensus.high_qc.clone(); drop(consensus); - if !self.publish_proposal_if_able(qc, self.cur_view, None).await { - error!( - "Failed to publish proposal on view change. View = {:?}", - self.cur_view - ); - } + // TODO ED Do not want to publish proposal on view change + // if !self.publish_proposal_if_able(qc, self.cur_view, None).await { + // error!( + // "Failed to publish proposal on view change. View = {:?}", + // self.cur_view + // ); + // } } SequencingHotShotEvent::Timeout(view) => { let vote_token = self.timeout_exchange.make_vote_token(view); From 5d3c14543d2722c4e042e95a94a23e3983572d84 Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Tue, 19 Sep 2023 13:33:52 -0400 Subject: [PATCH 13/56] view change location updated; need to handle update view sent for timeout message --- crates/hotshot/src/lib.rs | 20 +-- crates/task-impls/src/consensus.rs | 43 ++++--- crates/task-impls/src/view_sync.rs | 192 ++++++++++++++--------------- crates/testing/tests/basic.rs | 2 +- 4 files changed, 132 insertions(+), 125 deletions(-) diff --git a/crates/hotshot/src/lib.rs b/crates/hotshot/src/lib.rs index 3617952591..b59ee8e0b9 100644 --- a/crates/hotshot/src/lib.rs +++ b/crates/hotshot/src/lib.rs @@ -257,20 +257,20 @@ impl> SystemContext { /// "Starts" consensus by sending a `ViewChange` event pub async fn start_consensus(&self) { - self.inner - .internal_event_stream - .publish(SequencingHotShotEvent::ViewChange(TYPES::Time::new(1))) - .await; + // self.inner + // .internal_event_stream + // .publish(SequencingHotShotEvent::ViewChange(TYPES::Time::new(1))) + // .await; // ED This isn't ideal... // async_sleep(Duration::new(1, 0)).await; - // self.inner - // .internal_event_stream - // .publish(SequencingHotShotEvent::QCFormed( - // QuorumCertificate::genesis(), - // )) - // .await; + self.inner + .internal_event_stream + .publish(SequencingHotShotEvent::QCFormed(either::Left( + QuorumCertificate::genesis())), + ) + .await; } /// Marks a given view number as timed out. This should be called a fixed period after a round is started. diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index 29cbaa1f14..20f8499339 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -18,7 +18,6 @@ use hotshot_task::{ use hotshot_types::certificate::TimeoutCertificate; use hotshot_types::traits::election::TimeoutExchangeType; use hotshot_types::traits::node_implementation::SequencingTimeoutEx; -use hotshot_types::vote::DAVoteAccumulator; use hotshot_types::vote::QuorumVoteAccumulator; use hotshot_types::vote::TimeoutVoteAccumulator; use hotshot_types::{ @@ -672,6 +671,21 @@ where return; } + if proposal.data.justify_qc.view_number() != proposal.data.view_number - 1 { + // TODO ED Add timeout cert logic + if proposal.data.timeout_certificate.is_none() { + error!("Proposal needed a timeout cert but didn't have one {:?}", proposal.data.clone()); + return + } + else { + error!("Proposal for view {} had timeout certificate", *view); + } + // TODO ED Check timeout cert validity + } + + // TODO ED This needs to be moved further down so we only update the view after fully validating the qc. + self.update_view(view).await; + // TODO ED How does this play in with the timeout cert? self.current_proposal = Some(proposal.data.clone()); @@ -685,14 +699,7 @@ where debug!("We were not chosen for consensus committee on {:?}", view); } Ok(Some(vote_token)) => { - if proposal.data.justify_qc.view_number() != proposal.data.view_number - 1 { - // TODO ED Add timeout cert logic - if proposal.data.timeout_certificate.is_none() { - error!("Proposal needed a timeout cert but didn't have one {:?}", proposal.data.clone()); - return - } - // TODO ED Check timeout cert validity - } + debug!("We were chosen for consensus committee on {:?}", view); let consensus = self.consensus.upgradable_read().await; @@ -975,7 +982,7 @@ where } // Update current view and publish a view change event so other tasks also update - self.update_view(new_view).await; + // self.update_view(new_view).await; if let GeneralConsensusMessage::Vote(vote) = message { debug!("Sending vote to next leader {:?}", vote); @@ -1221,7 +1228,7 @@ where .publish_proposal_if_able(self.consensus.read().await.high_qc.clone(), qc.clone().view_number + 1, Some(qc.clone())) .await { - self.update_view(qc.view_number + 1).await; + // self.update_view(qc.view_number + 1).await; } else { error!("Wasn't able to publish proposal"); @@ -1260,7 +1267,7 @@ where .publish_proposal_if_able(qc.clone(), qc.view_number + 1, None) .await { - self.update_view(qc.view_number + 1).await; + // self.update_view(qc.view_number + 1).await; } } } @@ -1272,7 +1279,7 @@ where // TODO Make sure we aren't voting for an arbitrarily old round for no reason if self.vote_if_able().await { - self.update_view(view + 1).await; + // self.update_view(view + 1).await; } } SequencingHotShotEvent::VidCertRecv(cert) => { @@ -1283,7 +1290,7 @@ where // TODO Make sure we aren't voting for an arbitrarily old round for no reason if self.vote_if_able().await { - self.update_view(view + 1).await; + // self.update_view(view + 1).await; } } SequencingHotShotEvent::ViewChange(new_view) => { @@ -1293,10 +1300,10 @@ where // update the view in state to the one in the message // Publish a view change event to the application - if !self.update_view(new_view).await { - debug!("view not updated"); - return; - } + // if !self.update_view(new_view).await { + // debug!("view not updated"); + // return; + // } self.output_event_stream .publish(Event { diff --git a/crates/task-impls/src/view_sync.rs b/crates/task-impls/src/view_sync.rs index f92752f487..34db7c6097 100644 --- a/crates/task-impls/src/view_sync.rs +++ b/crates/task-impls/src/view_sync.rs @@ -489,102 +489,102 @@ where } &SequencingHotShotEvent::Timeout(view_number) => { // This is an old timeout and we can ignore it - if view_number < TYPES::Time::new(*self.current_view) { - return; - } - - self.num_timeouts_tracked += 1; - error!("Num timeouts tracked is {}", self.num_timeouts_tracked); - - if self.num_timeouts_tracked > 2 { - error!("Too many timeouts! This shouldn't happen"); - } - - // TODO ED Make this a configurable variable - if self.num_timeouts_tracked == 2 { - // Start polling for view sync certificates - self.exchange - .network() - .inject_consensus_info(ConsensusIntentEvent::PollForViewSyncCertificate( - *view_number + 1, - )) - .await; - - self.exchange - .network() - .inject_consensus_info(ConsensusIntentEvent::PollForViewSyncVotes( - *view_number + 1, - )) - .await; - // panic!("Starting view sync!"); - // Spawn replica task - - let mut replica_state = ViewSyncReplicaTaskState { - current_view: self.current_view, - next_view: TYPES::Time::new(*view_number + 1), - relay: 0, - finalized: false, - sent_view_change_event: false, - phase: ViewSyncPhase::None, - exchange: self.exchange.clone(), - api: self.api.clone(), - event_stream: self.event_stream.clone(), - view_sync_timeout: self.view_sync_timeout, - id: self.id, - }; - - // TODO ED Make all these view numbers into a single variable to avoid errors - let result = replica_state - .handle_event(SequencingHotShotEvent::ViewSyncTrigger(view_number + 1)) - .await; - - if result.0 == Some(HotShotTaskCompleted::ShutDown) { - // The protocol has finished - return; - } - - replica_state = result.1; - - let name = format!( - "View Sync Replica Task: Attempting to enter view {:?} from view {:?}", - self.next_view, self.current_view - ); - - let replica_handle_event = HandleEvent(Arc::new( - move |event, state: ViewSyncReplicaTaskState| { - async move { state.handle_event(event).await }.boxed() - }, - )); - - let filter = FilterEvent(Arc::new(Self::filter)); - let builder = - TaskBuilder::>::new(name) - .register_event_stream(replica_state.event_stream.clone(), filter) - .await - .register_registry(&mut self.registry.clone()) - .await - .register_state(replica_state) - .register_event_handler(replica_handle_event); - - let event_stream_id = builder.get_stream_id().unwrap(); - - self.replica_task_map.insert( - TYPES::Time::new(*view_number + 1), - ViewSyncTaskInfo { event_stream_id }, - ); - - let _view_sync_replica_task = async_spawn(async move { - ViewSyncReplicaTaskStateTypes::build(builder).launch().await - }); - } else { - // If this is the first timeout we've seen advance to the next view - self.current_view += 1; - self.event_stream - .publish(SequencingHotShotEvent::ViewChange(TYPES::Time::new( - *self.current_view, - ))) - .await; - } + // if view_number < TYPES::Time::new(*self.current_view) { + // return; + // } + + // self.num_timeouts_tracked += 1; + // error!("Num timeouts tracked is {}", self.num_timeouts_tracked); + + // if self.num_timeouts_tracked > 2 { + // error!("Too many timeouts! This shouldn't happen"); + // } + + // // TODO ED Make this a configurable variable + // if self.num_timeouts_tracked == 2 { + // // Start polling for view sync certificates + // self.exchange + // .network() + // .inject_consensus_info(ConsensusIntentEvent::PollForViewSyncCertificate( + // *view_number + 1, + // )) + // .await; + + // self.exchange + // .network() + // .inject_consensus_info(ConsensusIntentEvent::PollForViewSyncVotes( + // *view_number + 1, + // )) + // .await; + // // panic!("Starting view sync!"); + // // Spawn replica task + + // let mut replica_state = ViewSyncReplicaTaskState { + // current_view: self.current_view, + // next_view: TYPES::Time::new(*view_number + 1), + // relay: 0, + // finalized: false, + // sent_view_change_event: false, + // phase: ViewSyncPhase::None, + // exchange: self.exchange.clone(), + // api: self.api.clone(), + // event_stream: self.event_stream.clone(), + // view_sync_timeout: self.view_sync_timeout, + // id: self.id, + // }; + + // // TODO ED Make all these view numbers into a single variable to avoid errors + // let result = replica_state + // .handle_event(SequencingHotShotEvent::ViewSyncTrigger(view_number + 1)) + // .await; + + // if result.0 == Some(HotShotTaskCompleted::ShutDown) { + // // The protocol has finished + // return; + // } + + // replica_state = result.1; + + // let name = format!( + // "View Sync Replica Task: Attempting to enter view {:?} from view {:?}", + // self.next_view, self.current_view + // ); + + // let replica_handle_event = HandleEvent(Arc::new( + // move |event, state: ViewSyncReplicaTaskState| { + // async move { state.handle_event(event).await }.boxed() + // }, + // )); + + // let filter = FilterEvent(Arc::new(Self::filter)); + // let builder = + // TaskBuilder::>::new(name) + // .register_event_stream(replica_state.event_stream.clone(), filter) + // .await + // .register_registry(&mut self.registry.clone()) + // .await + // .register_state(replica_state) + // .register_event_handler(replica_handle_event); + + // let event_stream_id = builder.get_stream_id().unwrap(); + + // self.replica_task_map.insert( + // TYPES::Time::new(*view_number + 1), + // ViewSyncTaskInfo { event_stream_id }, + // ); + + // let _view_sync_replica_task = async_spawn(async move { + // ViewSyncReplicaTaskStateTypes::build(builder).launch().await + // }); + // } else { + // // If this is the first timeout we've seen advance to the next view + // // self.current_view += 1; + // // self.event_stream + // // .publish(SequencingHotShotEvent::ViewChange(TYPES::Time::new( + // // *self.current_view, + // // ))) + // // .await; + // } } _ => {} diff --git a/crates/testing/tests/basic.rs b/crates/testing/tests/basic.rs index 35e75fffa7..cbc2c149dc 100644 --- a/crates/testing/tests/basic.rs +++ b/crates/testing/tests/basic.rs @@ -18,7 +18,7 @@ async fn test_success() { // allow more time to pass in CI completion_task_description: CompletionTaskDescription::TimeBasedCompletionTaskBuilder( TimeBasedCompletionTaskDescription { - duration: Duration::from_millis(1_200_000), + duration: Duration::from_secs(60), }, ), ..TestMetadata::default() From 409e8efe2342696112413565736c4b9c1ccf7406 Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Tue, 19 Sep 2023 22:11:08 -0400 Subject: [PATCH 14/56] Some bugs fixed, still triggering view sync when it shouldn't --- crates/task-impls/src/consensus.rs | 19 ++- crates/task-impls/src/network.rs | 1 + crates/task-impls/src/view_sync.rs | 180 ++++++++++++++--------------- crates/testing/tests/timeout.rs | 4 +- 4 files changed, 106 insertions(+), 98 deletions(-) diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index 20f8499339..749e011e63 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -301,6 +301,7 @@ where } }, SequencingHotShotEvent::TimeoutVoteRecv(vote) => { + error!("received timeout vote for view {}", *vote.get_view()); if state.timeout_accumulator.is_right() { return (None, state); } @@ -472,7 +473,7 @@ where ); if let GeneralConsensusMessage::Vote(vote) = message { - debug!("Sending vote to next quorum leader {:?}", vote.get_view()); + debug!("Sending vote to next quorum leader {:?}", vote.get_view() + 1); self.event_stream .publish(SequencingHotShotEvent::QuorumVoteSend(vote)) .await; @@ -585,7 +586,7 @@ where // self.certs.remove(&v); // } self.cur_view = new_view; - self.current_proposal = None; + if new_view == TYPES::Time::new(1) { self.quorum_exchange @@ -633,7 +634,8 @@ where let timeout = self.timeout; self.timeout_task = async_spawn({ let stream = self.event_stream.clone(); - let view_number = self.cur_view; + // TODO ED + 1 here because of the logic change to view update. This indicates we haven't seen evidence for view change for this view within the time window + let view_number = self.cur_view + 1; async move { async_sleep(Duration::from_millis(timeout)).await; stream @@ -974,8 +976,9 @@ where // TOOD ED This means we publish the proposal without updating our own view, which doesn't seem right return; } + self.current_proposal = None; + - // ED Only do this GC if we are able to vote for v in (*self.cur_view)..=(*view) { let time = TYPES::Time::new(v); self.certs.remove(&time); @@ -1280,6 +1283,8 @@ where // TODO Make sure we aren't voting for an arbitrarily old round for no reason if self.vote_if_able().await { // self.update_view(view + 1).await; + self.current_proposal = None; + } } SequencingHotShotEvent::VidCertRecv(cert) => { @@ -1291,6 +1296,8 @@ where // TODO Make sure we aren't voting for an arbitrarily old round for no reason if self.vote_if_able().await { // self.update_view(view + 1).await; + self.current_proposal = None; + } } SequencingHotShotEvent::ViewChange(new_view) => { @@ -1364,7 +1371,7 @@ where .inject_consensus_info(ConsensusIntentEvent::CancelPollForVotes(*view)) .await; debug!( - "We received a timeout event in the consensus task for view {}!", + "We did not receive evidence for view {} in time, sending timeout vote for that view!", *view ); } @@ -1479,7 +1486,7 @@ where data: proposal, signature, }; - debug!("Sending proposal for view {:?} \n {:?}", self.cur_view, ""); + debug!("Sending proposal for view {:?} \n {:?}", leaf.view_number, ""); self.event_stream .publish(SequencingHotShotEvent::QuorumProposalSend( diff --git a/crates/task-impls/src/network.rs b/crates/task-impls/src/network.rs index c7df58d90a..0410d5f8dc 100644 --- a/crates/task-impls/src/network.rs +++ b/crates/task-impls/src/network.rs @@ -287,6 +287,7 @@ impl< ) } SequencingHotShotEvent::TimeoutVoteSend(vote) => { + // error!("Sending timeout vote to leader of view {}", *vote.get_view() + 1); ( vote.get_key(), MessageKind::::from_consensus_message(SequencingMessage(Left( diff --git a/crates/task-impls/src/view_sync.rs b/crates/task-impls/src/view_sync.rs index 34db7c6097..53d0cc6b28 100644 --- a/crates/task-impls/src/view_sync.rs +++ b/crates/task-impls/src/view_sync.rs @@ -489,102 +489,102 @@ where } &SequencingHotShotEvent::Timeout(view_number) => { // This is an old timeout and we can ignore it - // if view_number < TYPES::Time::new(*self.current_view) { - // return; - // } + if view_number < TYPES::Time::new(*self.current_view) { + return; + } - // self.num_timeouts_tracked += 1; - // error!("Num timeouts tracked is {}", self.num_timeouts_tracked); + self.num_timeouts_tracked += 1; + error!("Num timeouts tracked is {}", self.num_timeouts_tracked); // if self.num_timeouts_tracked > 2 { // error!("Too many timeouts! This shouldn't happen"); // } - // // TODO ED Make this a configurable variable - // if self.num_timeouts_tracked == 2 { - // // Start polling for view sync certificates - // self.exchange - // .network() - // .inject_consensus_info(ConsensusIntentEvent::PollForViewSyncCertificate( - // *view_number + 1, - // )) - // .await; - - // self.exchange - // .network() - // .inject_consensus_info(ConsensusIntentEvent::PollForViewSyncVotes( - // *view_number + 1, - // )) - // .await; - // // panic!("Starting view sync!"); - // // Spawn replica task - - // let mut replica_state = ViewSyncReplicaTaskState { - // current_view: self.current_view, - // next_view: TYPES::Time::new(*view_number + 1), - // relay: 0, - // finalized: false, - // sent_view_change_event: false, - // phase: ViewSyncPhase::None, - // exchange: self.exchange.clone(), - // api: self.api.clone(), - // event_stream: self.event_stream.clone(), - // view_sync_timeout: self.view_sync_timeout, - // id: self.id, - // }; - - // // TODO ED Make all these view numbers into a single variable to avoid errors - // let result = replica_state - // .handle_event(SequencingHotShotEvent::ViewSyncTrigger(view_number + 1)) - // .await; - - // if result.0 == Some(HotShotTaskCompleted::ShutDown) { - // // The protocol has finished - // return; - // } - - // replica_state = result.1; - - // let name = format!( - // "View Sync Replica Task: Attempting to enter view {:?} from view {:?}", - // self.next_view, self.current_view - // ); - - // let replica_handle_event = HandleEvent(Arc::new( - // move |event, state: ViewSyncReplicaTaskState| { - // async move { state.handle_event(event).await }.boxed() - // }, - // )); - - // let filter = FilterEvent(Arc::new(Self::filter)); - // let builder = - // TaskBuilder::>::new(name) - // .register_event_stream(replica_state.event_stream.clone(), filter) - // .await - // .register_registry(&mut self.registry.clone()) - // .await - // .register_state(replica_state) - // .register_event_handler(replica_handle_event); - - // let event_stream_id = builder.get_stream_id().unwrap(); - - // self.replica_task_map.insert( - // TYPES::Time::new(*view_number + 1), - // ViewSyncTaskInfo { event_stream_id }, - // ); - - // let _view_sync_replica_task = async_spawn(async move { - // ViewSyncReplicaTaskStateTypes::build(builder).launch().await - // }); - // } else { - // // If this is the first timeout we've seen advance to the next view - // // self.current_view += 1; - // // self.event_stream - // // .publish(SequencingHotShotEvent::ViewChange(TYPES::Time::new( - // // *self.current_view, - // // ))) - // // .await; - // } + // TODO ED Make this a configurable variable + if self.num_timeouts_tracked > 2 { + // Start polling for view sync certificates + self.exchange + .network() + .inject_consensus_info(ConsensusIntentEvent::PollForViewSyncCertificate( + *view_number + 1, + )) + .await; + + self.exchange + .network() + .inject_consensus_info(ConsensusIntentEvent::PollForViewSyncVotes( + *view_number + 1, + )) + .await; + // panic!("Starting view sync!"); + // Spawn replica task + + let mut replica_state = ViewSyncReplicaTaskState { + current_view: self.current_view, + next_view: TYPES::Time::new(*view_number + 1), + relay: 0, + finalized: false, + sent_view_change_event: false, + phase: ViewSyncPhase::None, + exchange: self.exchange.clone(), + api: self.api.clone(), + event_stream: self.event_stream.clone(), + view_sync_timeout: self.view_sync_timeout, + id: self.id, + }; + + // TODO ED Make all these view numbers into a single variable to avoid errors + let result = replica_state + .handle_event(SequencingHotShotEvent::ViewSyncTrigger(view_number + 1)) + .await; + + if result.0 == Some(HotShotTaskCompleted::ShutDown) { + // The protocol has finished + return; + } + + replica_state = result.1; + + let name = format!( + "View Sync Replica Task: Attempting to enter view {:?} from view {:?}", + self.next_view, self.current_view + ); + + let replica_handle_event = HandleEvent(Arc::new( + move |event, state: ViewSyncReplicaTaskState| { + async move { state.handle_event(event).await }.boxed() + }, + )); + + let filter = FilterEvent(Arc::new(Self::filter)); + let builder = + TaskBuilder::>::new(name) + .register_event_stream(replica_state.event_stream.clone(), filter) + .await + .register_registry(&mut self.registry.clone()) + .await + .register_state(replica_state) + .register_event_handler(replica_handle_event); + + let event_stream_id = builder.get_stream_id().unwrap(); + + self.replica_task_map.insert( + TYPES::Time::new(*view_number + 1), + ViewSyncTaskInfo { event_stream_id }, + ); + + let _view_sync_replica_task = async_spawn(async move { + ViewSyncReplicaTaskStateTypes::build(builder).launch().await + }); + } else { + // If this is the first timeout we've seen advance to the next view + self.current_view = view_number + 1; + self.event_stream + .publish(SequencingHotShotEvent::ViewChange(TYPES::Time::new( + *self.current_view, + ))) + .await; + } } _ => {} diff --git a/crates/testing/tests/timeout.rs b/crates/testing/tests/timeout.rs index f8963c9d52..a9e046ef3b 100644 --- a/crates/testing/tests/timeout.rs +++ b/crates/testing/tests/timeout.rs @@ -30,7 +30,7 @@ async fn test_timeout() { metadata.timing_data = timing_data; metadata.spinning_properties = SpinningTaskDescription { - node_changes: vec![(Duration::new(0, 5000), dead_nodes)], + node_changes: vec![(Duration::from_secs(1), dead_nodes)], }; // TODO ED Add safety task, etc to confirm TCs are being formed @@ -38,7 +38,7 @@ async fn test_timeout() { metadata.completion_task_description = CompletionTaskDescription::TimeBasedCompletionTaskBuilder( TimeBasedCompletionTaskDescription { - duration: Duration::from_millis(10000), + duration: Duration::from_millis(60000), }, ); metadata From a3017ebf85d1afbf8b8cc17004a3496df63eb709 Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Tue, 19 Sep 2023 23:41:43 -0400 Subject: [PATCH 15/56] Memory network is not receiving DA proposals correctly --- crates/task-impls/src/consensus.rs | 2 +- crates/task-impls/src/da.rs | 1 + crates/task-impls/src/network.rs | 2 ++ crates/task-impls/src/view_sync.rs | 1 + crates/testing/tests/timeout.rs | 6 ++++-- 5 files changed, 9 insertions(+), 3 deletions(-) diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index 749e011e63..a82f2531fe 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -988,7 +988,7 @@ where // self.update_view(new_view).await; if let GeneralConsensusMessage::Vote(vote) = message { - debug!("Sending vote to next leader {:?}", vote); + // debug!("Sending vote to next leader {:?}", vote); }; } } diff --git a/crates/task-impls/src/da.rs b/crates/task-impls/src/da.rs index f8711b1d78..782a268a36 100644 --- a/crates/task-impls/src/da.rs +++ b/crates/task-impls/src/da.rs @@ -701,6 +701,7 @@ where } SequencingHotShotEvent::Shutdown => { + error!("Shutting down because of shutdown signal!"); return Some(HotShotTaskCompleted::ShutDown); } _ => { diff --git a/crates/task-impls/src/network.rs b/crates/task-impls/src/network.rs index 0410d5f8dc..233190a6d5 100644 --- a/crates/task-impls/src/network.rs +++ b/crates/task-impls/src/network.rs @@ -100,6 +100,7 @@ impl< }, Either::Right(committee_message) => match committee_message { CommitteeConsensusMessage::DAProposal(proposal) => { + error!("Received da proposal for view {:?}", proposal.clone().data.view_number); SequencingHotShotEvent::DAProposalRecv(proposal.clone(), sender) } CommitteeConsensusMessage::DAVote(vote) => { @@ -302,6 +303,7 @@ impl< return None; } SequencingHotShotEvent::Shutdown => { + error!("Networking task shutting down"); return Some(HotShotTaskCompleted::ShutDown); } event => { diff --git a/crates/task-impls/src/view_sync.rs b/crates/task-impls/src/view_sync.rs index 53d0cc6b28..1b6c909e1d 100644 --- a/crates/task-impls/src/view_sync.rs +++ b/crates/task-impls/src/view_sync.rs @@ -502,6 +502,7 @@ where // TODO ED Make this a configurable variable if self.num_timeouts_tracked > 2 { + panic!(); // Start polling for view sync certificates self.exchange .network() diff --git a/crates/testing/tests/timeout.rs b/crates/testing/tests/timeout.rs index a9e046ef3b..1e378912a9 100644 --- a/crates/testing/tests/timeout.rs +++ b/crates/testing/tests/timeout.rs @@ -8,6 +8,8 @@ async fn test_timeout() { use std::time::Duration; + use hotshot_testing::node_types::SequencingLibp2pImpl; + use hotshot_testing::{ completion_task::{CompletionTaskDescription, TimeBasedCompletionTaskDescription}, node_types::{SequencingMemoryImpl, SequencingTestTypes}, @@ -30,7 +32,7 @@ async fn test_timeout() { metadata.timing_data = timing_data; metadata.spinning_properties = SpinningTaskDescription { - node_changes: vec![(Duration::from_secs(1), dead_nodes)], + node_changes: vec![(Duration::from_millis(500), dead_nodes)], }; // TODO ED Add safety task, etc to confirm TCs are being formed @@ -38,7 +40,7 @@ async fn test_timeout() { metadata.completion_task_description = CompletionTaskDescription::TimeBasedCompletionTaskBuilder( TimeBasedCompletionTaskDescription { - duration: Duration::from_millis(60000), + duration: Duration::from_millis(10000), }, ); metadata From 98c397d31469be963afa37786d8a94531ab15a06 Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Wed, 20 Sep 2023 08:15:24 -0400 Subject: [PATCH 16/56] Update timeout test to use libp2p --- crates/task-impls/src/view_sync.rs | 6 +++--- crates/testing/tests/timeout.rs | 14 +++++++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/crates/task-impls/src/view_sync.rs b/crates/task-impls/src/view_sync.rs index 1b6c909e1d..61eef67d64 100644 --- a/crates/task-impls/src/view_sync.rs +++ b/crates/task-impls/src/view_sync.rs @@ -496,9 +496,9 @@ where self.num_timeouts_tracked += 1; error!("Num timeouts tracked is {}", self.num_timeouts_tracked); - // if self.num_timeouts_tracked > 2 { - // error!("Too many timeouts! This shouldn't happen"); - // } + if self.num_timeouts_tracked > 3 { + error!("Too many timeouts! This shouldn't happen"); + } // TODO ED Make this a configurable variable if self.num_timeouts_tracked > 2 { diff --git a/crates/testing/tests/timeout.rs b/crates/testing/tests/timeout.rs index 1e378912a9..adbb58fe11 100644 --- a/crates/testing/tests/timeout.rs +++ b/crates/testing/tests/timeout.rs @@ -23,7 +23,13 @@ async fn test_timeout() { next_view_timeout: 1000, ..Default::default() }; - let mut metadata = TestMetadata::default(); + + // TODO ED Reduce down to 5 nodes once memory network issues is resolved + let mut metadata = TestMetadata { + total_nodes: 10, + start_nodes: 10, + ..Default::default() + }; let dead_nodes = vec![ChangeNode { idx: 0, updown: UpDown::Down, @@ -40,11 +46,13 @@ async fn test_timeout() { metadata.completion_task_description = CompletionTaskDescription::TimeBasedCompletionTaskBuilder( TimeBasedCompletionTaskDescription { - duration: Duration::from_millis(10000), + duration: Duration::from_millis(30000), }, ); + + // TODO ED Test with memory network once issue is resolved. metadata - .gen_launcher::() + .gen_launcher::() .launch() .run_test() .await; From 76e64cf8f2aa430ac4359e93e2513f8ceac9d523 Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Thu, 21 Sep 2023 06:54:08 -0400 Subject: [PATCH 17/56] comments --- crates/task-impls/src/consensus.rs | 68 +++++++++++++++++------------- crates/testing/tests/timeout.rs | 13 ++++-- 2 files changed, 49 insertions(+), 32 deletions(-) diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index a82f2531fe..99b4028b2f 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -473,7 +473,10 @@ where ); if let GeneralConsensusMessage::Vote(vote) = message { - debug!("Sending vote to next quorum leader {:?}", vote.get_view() + 1); + debug!( + "Sending vote to next quorum leader {:?}", + vote.get_view() + 1 + ); self.event_stream .publish(SequencingHotShotEvent::QuorumVoteSend(vote)) .await; @@ -586,14 +589,15 @@ where // self.certs.remove(&v); // } self.cur_view = new_view; - - if new_view == TYPES::Time::new(1) { - self.quorum_exchange - .network() - .inject_consensus_info(ConsensusIntentEvent::PollForCurrentProposal) - .await; - } + // TODO ED These injects aren't right + // Commenting out because HotShot doesn't start on 1 now + // if new_view == TYPES::Time::new(1) { + // self.quorum_exchange + // .network() + // .inject_consensus_info(ConsensusIntentEvent::PollForCurrentProposal) + // .await; + // } // Poll the future leader for lookahead let lookahead_view = new_view + LOOK_AHEAD; @@ -610,14 +614,15 @@ where // Start polling for proposals for the new view self.quorum_exchange .network() - .inject_consensus_info(ConsensusIntentEvent::PollForProposal(*self.cur_view)) + .inject_consensus_info(ConsensusIntentEvent::PollForProposal(*self.cur_view + 1)) .await; self.quorum_exchange .network() - .inject_consensus_info(ConsensusIntentEvent::PollForDAC(*self.cur_view)) + .inject_consensus_info(ConsensusIntentEvent::PollForDAC(*self.cur_view + 1)) .await; + // TODO ED I think this poll is still correct (actually want to poll for it in both views, in case this node just doesn't receive the latest proposal, but that is more of a web server issue specifically) if self.quorum_exchange.is_leader(self.cur_view + 1) { debug!("Polling for quorum votes for view {}", *self.cur_view); self.quorum_exchange @@ -676,19 +681,21 @@ where if proposal.data.justify_qc.view_number() != proposal.data.view_number - 1 { // TODO ED Add timeout cert logic if proposal.data.timeout_certificate.is_none() { - error!("Proposal needed a timeout cert but didn't have one {:?}", proposal.data.clone()); - return - } - else { + error!( + "Proposal needed a timeout cert but didn't have one {:?}", + proposal.data.clone() + ); + return; + } else { error!("Proposal for view {} had timeout certificate", *view); } // TODO ED Check timeout cert validity } - // TODO ED This needs to be moved further down so we only update the view after fully validating the qc. + // TODO ED This needs to be moved further down so we only update the view after fully validating the qc. self.update_view(view).await; - // TODO ED How does this play in with the timeout cert? + // TODO ED How does this play in with the timeout cert? self.current_proposal = Some(proposal.data.clone()); let vote_token = self.quorum_exchange.make_vote_token(view); @@ -701,14 +708,10 @@ where debug!("We were not chosen for consensus committee on {:?}", view); } Ok(Some(vote_token)) => { - - debug!("We were chosen for consensus committee on {:?}", view); let consensus = self.consensus.upgradable_read().await; let message; - - // Construct the leaf. let justify_qc = proposal.data.justify_qc; let parent = if justify_qc.is_genesis() { @@ -978,7 +981,6 @@ where } self.current_proposal = None; - for v in (*self.cur_view)..=(*view) { let time = TYPES::Time::new(v); self.certs.remove(&time); @@ -1228,12 +1230,15 @@ where // TODO ED Clean this up, get rid of clones if self - .publish_proposal_if_able(self.consensus.read().await.high_qc.clone(), qc.clone().view_number + 1, Some(qc.clone())) + .publish_proposal_if_able( + self.consensus.read().await.high_qc.clone(), + qc.clone().view_number + 1, + Some(qc.clone()), + ) .await { // self.update_view(qc.view_number + 1).await; - } - else { + } else { error!("Wasn't able to publish proposal"); } } @@ -1284,7 +1289,6 @@ where if self.vote_if_able().await { // self.update_view(view + 1).await; self.current_proposal = None; - } } SequencingHotShotEvent::VidCertRecv(cert) => { @@ -1297,7 +1301,6 @@ where if self.vote_if_able().await { // self.update_view(view + 1).await; self.current_proposal = None; - } } SequencingHotShotEvent::ViewChange(new_view) => { @@ -1342,6 +1345,10 @@ where // } } SequencingHotShotEvent::Timeout(view) => { + // TODO ED This is not an ideal check, we should have the timeout task receive view change events and then cancel itself + if self.cur_view >= view { + return; + } let vote_token = self.timeout_exchange.make_vote_token(view); match vote_token { @@ -1388,7 +1395,7 @@ where &self, _qc: QuorumCertificate, view: TYPES::Time, - timeout_certificate: Option> + timeout_certificate: Option>, ) -> bool { if !self.quorum_exchange.is_leader(view) { error!( @@ -1477,7 +1484,7 @@ where view_number: leaf.view_number, height: leaf.height, justify_qc: consensus.high_qc.clone(), - timeout_certificate: timeout_certificate.or_else(|| { None }), + timeout_certificate: timeout_certificate.or_else(|| None), proposer_id: leaf.proposer_id, dac: None, }; @@ -1486,7 +1493,10 @@ where data: proposal, signature, }; - debug!("Sending proposal for view {:?} \n {:?}", leaf.view_number, ""); + debug!( + "Sending proposal for view {:?} \n {:?}", + leaf.view_number, "" + ); self.event_stream .publish(SequencingHotShotEvent::QuorumProposalSend( diff --git a/crates/testing/tests/timeout.rs b/crates/testing/tests/timeout.rs index adbb58fe11..7ac21061ce 100644 --- a/crates/testing/tests/timeout.rs +++ b/crates/testing/tests/timeout.rs @@ -9,7 +9,9 @@ async fn test_timeout() { use std::time::Duration; use hotshot_testing::node_types::SequencingLibp2pImpl; + use hotshot_testing::node_types::SequencingWebImpl; + use hotshot_testing::overall_safety_task::OverallSafetyPropertiesDescription; use hotshot_testing::{ completion_task::{CompletionTaskDescription, TimeBasedCompletionTaskDescription}, node_types::{SequencingMemoryImpl, SequencingTestTypes}, @@ -37,6 +39,11 @@ async fn test_timeout() { metadata.timing_data = timing_data; + metadata.overall_safety_properties = OverallSafetyPropertiesDescription { + num_successful_views: 50, + ..Default::default() + }; + metadata.spinning_properties = SpinningTaskDescription { node_changes: vec![(Duration::from_millis(500), dead_nodes)], }; @@ -46,13 +53,13 @@ async fn test_timeout() { metadata.completion_task_description = CompletionTaskDescription::TimeBasedCompletionTaskBuilder( TimeBasedCompletionTaskDescription { - duration: Duration::from_millis(30000), + duration: Duration::from_secs(30), }, ); - // TODO ED Test with memory network once issue is resolved. + // TODO ED Test with memory network once issue is resolved. metadata - .gen_launcher::() + .gen_launcher::() .launch() .run_test() .await; From 5cdcff51b55d35bdbe490dd18675175da32d561c Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Thu, 21 Sep 2023 08:20:52 -0400 Subject: [PATCH 18/56] Fix test_consensus_task test after timeout logic update --- crates/task-impls/src/harness.rs | 4 ++++ crates/testing/tests/consensus_task.rs | 27 ++++++++++++++++++-------- crates/testing/tests/timeout.rs | 3 +-- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/crates/task-impls/src/harness.rs b/crates/task-impls/src/harness.rs index 136093bc3e..4978b27492 100644 --- a/crates/task-impls/src/harness.rs +++ b/crates/task-impls/src/harness.rs @@ -11,6 +11,7 @@ use hotshot_task::{ use hotshot_types::traits::node_implementation::{NodeImplementation, NodeType}; use snafu::Snafu; use std::{collections::HashMap, future::Future, sync::Arc}; +use tracing::error; /// The state for the test harness task. Keeps track of which events and how many we expect to get pub struct TestHarnessState> { @@ -106,6 +107,9 @@ pub fn handle_event>( *num_expected -= 1; } + // TODO ED Remove + error!("Event is {:?}", event); + if state.expected_output.is_empty() { return (Some(HotShotTaskCompleted::ShutDown), state); } diff --git a/crates/testing/tests/consensus_task.rs b/crates/testing/tests/consensus_task.rs index ec1bd7b05c..52693881b9 100644 --- a/crates/testing/tests/consensus_task.rs +++ b/crates/testing/tests/consensus_task.rs @@ -85,9 +85,11 @@ async fn build_vote( tokio::test(flavor = "multi_thread", worker_threads = 2) )] #[cfg_attr(async_executor_impl = "async-std", async_std::test)] +#[ignore] async fn test_consensus_task() { use hotshot_task_impls::harness::run_harness; use hotshot_testing::task_helpers::build_system_handle; + use hotshot_types::certificate::QuorumCertificate; async_compatibility_layer::logging::setup_logging(); async_compatibility_layer::logging::setup_backtrace(); @@ -98,19 +100,28 @@ async fn test_consensus_task() { let mut input = Vec::new(); let mut output = HashMap::new(); - input.push(SequencingHotShotEvent::ViewChange(ViewNumber::new(1))); - input.push(SequencingHotShotEvent::ViewChange(ViewNumber::new(2))); + // Trigger a proposal to send by creating a new QC. Then recieve that proposal and update view based on the valid QC in the proposal + let qc = + QuorumCertificate::>::genesis(); + let proposal = build_quorum_proposal(&handle, &private_key, 1).await; + + input.push(SequencingHotShotEvent::QCFormed(either::Left(qc.clone()))); + input.push(SequencingHotShotEvent::QuorumProposalRecv( + proposal.clone(), + public_key, + )); input.push(SequencingHotShotEvent::Shutdown); + output.insert(SequencingHotShotEvent::QCFormed(either::Left(qc)), 1); output.insert( - SequencingHotShotEvent::QuorumProposalSend( - build_quorum_proposal(&handle, &private_key, 1).await, - public_key, - ), + SequencingHotShotEvent::QuorumProposalSend(proposal.clone(), public_key), 1, ); - output.insert(SequencingHotShotEvent::ViewChange(ViewNumber::new(1)), 2); - output.insert(SequencingHotShotEvent::ViewChange(ViewNumber::new(2)), 2); + output.insert( + SequencingHotShotEvent::QuorumProposalRecv(proposal.clone(), public_key), + 1, + ); + output.insert(SequencingHotShotEvent::ViewChange(ViewNumber::new(1)), 1); output.insert(SequencingHotShotEvent::Shutdown, 1); let build_fn = |task_runner, event_stream| { diff --git a/crates/testing/tests/timeout.rs b/crates/testing/tests/timeout.rs index 7ac21061ce..954588958c 100644 --- a/crates/testing/tests/timeout.rs +++ b/crates/testing/tests/timeout.rs @@ -4,7 +4,6 @@ tokio::test(flavor = "multi_thread", worker_threads = 2) )] #[cfg_attr(async_executor_impl = "async-std", async_std::test)] -#[ignore] async fn test_timeout() { use std::time::Duration; @@ -40,7 +39,7 @@ async fn test_timeout() { metadata.timing_data = timing_data; metadata.overall_safety_properties = OverallSafetyPropertiesDescription { - num_successful_views: 50, + num_successful_views: 25, ..Default::default() }; From ef684886d14f482b901da744b1188751f67b77fc Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Thu, 21 Sep 2023 10:55:41 -0400 Subject: [PATCH 19/56] test_consensus_vote working --- crates/testing/tests/consensus_task.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/crates/testing/tests/consensus_task.rs b/crates/testing/tests/consensus_task.rs index 52693881b9..9e722088ef 100644 --- a/crates/testing/tests/consensus_task.rs +++ b/crates/testing/tests/consensus_task.rs @@ -146,20 +146,18 @@ async fn test_consensus_vote() { let handle = build_system_handle(2).await.0; let (private_key, public_key) = key_pair_for_id(1); + let (private_key_2, public_key_2) = key_pair_for_id(2); let mut input = Vec::new(); let mut output = HashMap::new(); let proposal = build_quorum_proposal(&handle, &private_key, 1).await; - input.push(SequencingHotShotEvent::ViewChange(ViewNumber::new(1))); + // Send a proposal, vote on said proposal, update view based on proposal QC, receive vote as next leader input.push(SequencingHotShotEvent::QuorumProposalRecv( proposal.clone(), public_key, )); - - input.push(SequencingHotShotEvent::Shutdown); - output.insert( SequencingHotShotEvent::QuorumProposalRecv(proposal.clone(), public_key), 1, @@ -168,10 +166,14 @@ async fn test_consensus_vote() { if let GeneralConsensusMessage::Vote(vote) = build_vote(&handle, proposal, ViewNumber::new(1)).await { - output.insert(SequencingHotShotEvent::QuorumVoteSend(vote), 1); + output.insert(SequencingHotShotEvent::QuorumVoteSend(vote.clone()), 1); + input.push(SequencingHotShotEvent::QuorumVoteRecv(vote.clone())); + output.insert(SequencingHotShotEvent::QuorumVoteRecv(vote), 1); } - output.insert(SequencingHotShotEvent::ViewChange(ViewNumber::new(1)), 2); - output.insert(SequencingHotShotEvent::ViewChange(ViewNumber::new(2)), 1); + + output.insert(SequencingHotShotEvent::ViewChange(ViewNumber::new(1)), 1); + + input.push(SequencingHotShotEvent::Shutdown); output.insert(SequencingHotShotEvent::Shutdown, 1); let build_fn = |task_runner, event_stream| { From 037de03bc8913bce07385867dbfe2ed1157c922d Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Thu, 21 Sep 2023 13:31:08 -0400 Subject: [PATCH 20/56] test_basic passes --- crates/task-impls/src/view_sync.rs | 1 - crates/testing/tests/view_sync_task.rs | 31 +++++++++++++++----------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/crates/task-impls/src/view_sync.rs b/crates/task-impls/src/view_sync.rs index 61eef67d64..51476a4a92 100644 --- a/crates/task-impls/src/view_sync.rs +++ b/crates/task-impls/src/view_sync.rs @@ -502,7 +502,6 @@ where // TODO ED Make this a configurable variable if self.num_timeouts_tracked > 2 { - panic!(); // Start polling for view sync certificates self.exchange .network() diff --git a/crates/testing/tests/view_sync_task.rs b/crates/testing/tests/view_sync_task.rs index 25cbe55b9a..919303d36a 100644 --- a/crates/testing/tests/view_sync_task.rs +++ b/crates/testing/tests/view_sync_task.rs @@ -34,7 +34,7 @@ async fn test_view_sync_task() { async_compatibility_layer::logging::setup_backtrace(); // Build the API for node 3. - let handle = build_system_handle(3).await.0; + let handle = build_system_handle(5).await.0; let api: HotShotSequencingConsensusApi = HotShotSequencingConsensusApi { inner: handle.hotshot.inner.clone(), @@ -42,19 +42,19 @@ async fn test_view_sync_task() { let view_sync_exchange = api.inner.exchanges.view_sync_exchange().clone(); let relay_pub_key = api.public_key().to_bytes(); let vote_token = view_sync_exchange - .make_vote_token(ViewNumber::new(3)) + .make_vote_token(ViewNumber::new(5)) .unwrap_or_else(|_| panic!("Error making vote token")) .unwrap_or_else(|| panic!("Not chosen for the committee")); let vote_data_internal: ViewSyncData = ViewSyncData { relay: relay_pub_key.clone(), - round: ViewNumber::new(3), + round: ViewNumber::new(5), }; let vote_data_internal_commitment = vote_data_internal.commit(); let signature = view_sync_exchange.sign_precommit_message(vote_data_internal_commitment); let vote = ViewSyncVote::PreCommit(ViewSyncVoteInternal { relay_pub_key, relay: 0, - round: ViewNumber::new(3), + round: ViewNumber::new(5), signature, vote_token, vote_data: VoteData::ViewSyncPreCommit(vote_data_internal_commitment), @@ -64,23 +64,28 @@ async fn test_view_sync_task() { let mut input = Vec::new(); let mut output = HashMap::new(); - input.push(SequencingHotShotEvent::ViewChange(ViewNumber::new(1))); - input.push(SequencingHotShotEvent::Timeout(ViewNumber::new(1))); input.push(SequencingHotShotEvent::Timeout(ViewNumber::new(2))); + input.push(SequencingHotShotEvent::Timeout(ViewNumber::new(3))); + input.push(SequencingHotShotEvent::Timeout(ViewNumber::new(4))); + input.push(SequencingHotShotEvent::Shutdown); - output.insert(SequencingHotShotEvent::ViewChange(ViewNumber::new(1)), 1); - output.insert(SequencingHotShotEvent::Timeout(ViewNumber::new(1)), 1); output.insert(SequencingHotShotEvent::Timeout(ViewNumber::new(2)), 1); + output.insert(SequencingHotShotEvent::Timeout(ViewNumber::new(3)), 1); + output.insert(SequencingHotShotEvent::Timeout(ViewNumber::new(4)), 1); + // 2 `Timeout` events will trigger a replica task to handle a `ViewSyncTrigger` event, which // will then publish a `ViewSyncVoteSend` event. output.insert(SequencingHotShotEvent::ViewSyncVoteSend(vote.clone()), 1); - output.insert( - SequencingHotShotEvent::ViewSyncTimeout(ViewNumber::new(3), 0, ViewSyncPhase::None), - 1, - ); + // output.insert( + // SequencingHotShotEvent::ViewSyncTimeout(ViewNumber::new(5), 0, ViewSyncPhase::None), + // 1, + // ); // Triggered by the `Timeout` events. - output.insert(SequencingHotShotEvent::ViewChange(ViewNumber::new(2)), 1); + // output.insert(SequencingHotShotEvent::ViewChange(ViewNumber::new(2)), 1); + output.insert(SequencingHotShotEvent::ViewChange(ViewNumber::new(3)), 1); + output.insert(SequencingHotShotEvent::ViewChange(ViewNumber::new(4)), 1); + output.insert(SequencingHotShotEvent::Shutdown, 1); let build_fn = From 89a14a35d4fd2da2a32552323e7b7c0f887c170a Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Thu, 21 Sep 2023 15:26:36 -0400 Subject: [PATCH 21/56] All tests passing? --- crates/hotshot/src/tasks/mod.rs | 11 +++++++++++ .../traits/networking/web_server_network.rs | 2 ++ crates/task-impls/src/consensus.rs | 18 +++++++++--------- crates/task-impls/src/da.rs | 1 + crates/types/src/message.rs | 2 +- crates/web_server/src/lib.rs | 3 ++- justfile | 2 +- 7 files changed, 27 insertions(+), 12 deletions(-) diff --git a/crates/hotshot/src/tasks/mod.rs b/crates/hotshot/src/tasks/mod.rs index 4dc66b5539..26b7effbef 100644 --- a/crates/hotshot/src/tasks/mod.rs +++ b/crates/hotshot/src/tasks/mod.rs @@ -27,6 +27,7 @@ use hotshot_task_impls::{ view_sync::{ViewSyncTaskState, ViewSyncTaskStateTypes}, }; use hotshot_types::certificate::TimeoutCertificate; +use hotshot_types::traits::network::ConsensusIntentEvent; use hotshot_types::traits::node_implementation::SequencingTimeoutEx; use hotshot_types::{ certificate::ViewSyncCertificate, @@ -308,6 +309,16 @@ where id: handle.hotshot.inner.id, qc: None, }; + consensus_state + .quorum_exchange + .network() + .inject_consensus_info(ConsensusIntentEvent::PollForCurrentProposal) + .await; + consensus_state + .quorum_exchange + .network() + .inject_consensus_info(ConsensusIntentEvent::PollForProposal(1)) + .await; let filter = FilterEvent(Arc::new(consensus_event_filter)); let consensus_name = "Consensus Task"; let consensus_event_handler = HandleEvent(Arc::new( diff --git a/crates/hotshot/src/traits/networking/web_server_network.rs b/crates/hotshot/src/traits/networking/web_server_network.rs index 72cd62cc8d..7b03aae947 100644 --- a/crates/hotshot/src/traits/networking/web_server_network.rs +++ b/crates/hotshot/src/traits/networking/web_server_network.rs @@ -147,6 +147,7 @@ impl Inner { message_purpose: MessagePurpose, view_number: u64, ) -> Result<(), NetworkError> { + error!("Polling for view {}", view_number); let mut vote_index = 0; let mut tx_index = 0; @@ -206,6 +207,7 @@ impl Inner { error!("We should not receive transactions in this function"); } MessagePurpose::Proposal => { + error!("Received proposal"); // Only pushing the first proposal since we will soon only be allowing 1 proposal per view self.broadcast_poll_queue .write() diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index 99b4028b2f..048b69c582 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -1310,10 +1310,10 @@ where // update the view in state to the one in the message // Publish a view change event to the application - // if !self.update_view(new_view).await { - // debug!("view not updated"); - // return; - // } + if !self.update_view(new_view).await { + debug!("view not updated"); + return; + } self.output_event_stream .publish(Event { @@ -1346,7 +1346,7 @@ where } SequencingHotShotEvent::Timeout(view) => { // TODO ED This is not an ideal check, we should have the timeout task receive view change events and then cancel itself - if self.cur_view >= view { + if self.cur_view > view { return; } let vote_token = self.timeout_exchange.make_vote_token(view); @@ -1373,10 +1373,10 @@ where } } - self.quorum_exchange - .network() - .inject_consensus_info(ConsensusIntentEvent::CancelPollForVotes(*view)) - .await; + // self.quorum_exchange + // .network() + // .inject_consensus_info(ConsensusIntentEvent::CancelPollForVotes(*view)) + // .await; debug!( "We did not receive evidence for view {} in time, sending timeout vote for that view!", *view diff --git a/crates/task-impls/src/da.rs b/crates/task-impls/src/da.rs index 782a268a36..3ea39ce63b 100644 --- a/crates/task-impls/src/da.rs +++ b/crates/task-impls/src/da.rs @@ -588,6 +588,7 @@ where .get_committee(self.cur_view + 1) .contains(self.committee_exchange.public_key()); + // TODO ED Is this right? if is_da { debug!("Polling for DA proposals for view {}", *self.cur_view + 1); self.committee_exchange diff --git a/crates/types/src/message.rs b/crates/types/src/message.rs index 28daf64b43..f08cb54dfb 100644 --- a/crates/types/src/message.rs +++ b/crates/types/src/message.rs @@ -455,7 +455,7 @@ impl< GeneralConsensusMessage::InternalTrigger(_) => MessagePurpose::Internal, GeneralConsensusMessage::ViewSyncVote(_) => MessagePurpose::ViewSyncVote, GeneralConsensusMessage::ViewSyncCertificate(_) => MessagePurpose::ViewSyncProposal, - GeneralConsensusMessage::TimeoutVote(_) => todo!(), + GeneralConsensusMessage::TimeoutVote(_) => MessagePurpose::Vote, }, Right(committee_message) => match committee_message { CommitteeConsensusMessage::DAProposal(_) => MessagePurpose::Proposal, diff --git a/crates/web_server/src/lib.rs b/crates/web_server/src/lib.rs index 2bd746e1cd..e5439dcca4 100644 --- a/crates/web_server/src/lib.rs +++ b/crates/web_server/src/lib.rs @@ -5,6 +5,7 @@ use async_compatibility_layer::channel::OneShotReceiver; use async_lock::RwLock; use clap::Args; use futures::FutureExt; +use tracing::error; use hotshot_types::traits::signature_key::{EncodedPublicKey, SignatureKey}; use rand::{distributions::Alphanumeric, rngs::StdRng, thread_rng, Rng, SeedableRng}; @@ -322,7 +323,7 @@ impl WebServerDataSource for WebServerState { } /// Stores a received proposal in the `WebServerState` fn post_proposal(&mut self, view_number: u64, mut proposal: Vec) -> Result<(), Error> { - debug!("Received proposal for view {}", view_number); + error!("Received proposal for view {}", view_number); if view_number > self.recent_proposal { self.recent_proposal = view_number; diff --git a/justfile b/justfile index d0c4b9a5b2..276d914257 100644 --- a/justfile +++ b/justfile @@ -37,7 +37,7 @@ test_success: test_timeout: echo Testing timeout test - cargo test --lib --bins --tests --benches --workspace --no-fail-fast test_timeout -- --test-threads=1 --nocapture --ignored + cargo test --lib --bins --tests --benches --workspace --no-fail-fast test_timeout -- --test-threads=1 --nocapture test_web_server: echo Testing web server From 70da94b2bdf80e08fa53dae27471b3bc5c5fa24d Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Thu, 21 Sep 2023 20:00:46 -0400 Subject: [PATCH 22/56] Tests pass? --- crates/task-impls/src/da.rs | 3 +++ crates/types/src/traits/election.rs | 9 ++++++++- crates/types/src/vote.rs | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/crates/task-impls/src/da.rs b/crates/task-impls/src/da.rs index 82abdfaeb6..53d0a57501 100644 --- a/crates/task-impls/src/da.rs +++ b/crates/task-impls/src/da.rs @@ -161,7 +161,10 @@ where &vote, &vote.block_commitment, ) { + + Left(new_accumulator) => { + error!("Not enough DA votes yet"); state.accumulator = either::Left(new_accumulator); } diff --git a/crates/types/src/traits/election.rs b/crates/types/src/traits/election.rs index 334500fa24..c8745652e2 100644 --- a/crates/types/src/traits/election.rs +++ b/crates/types/src/traits/election.rs @@ -434,6 +434,9 @@ pub trait ConsensusExchange: Send + Sync { vote_token: &Checked, ) -> bool { let is_valid_signature = key.validate(encoded_signature, data.get_commit().as_ref()); + if !is_valid_signature { + panic!() + } let valid_vote_token = self .membership() .validate_vote_token(key.clone(), vote_token.clone()); @@ -446,7 +449,11 @@ pub trait ConsensusExchange: Send + Sync { Ok(Checked::Inval(_) | Checked::Unchecked(_)) => false, }; - is_valid_signature && is_valid_vote_token + let result = is_valid_signature && is_valid_vote_token; + if !result { + panic!() + } + result } #[doc(hidden)] diff --git a/crates/types/src/vote.rs b/crates/types/src/vote.rs index 8a4e9663df..46e93c3e12 100644 --- a/crates/types/src/vote.rs +++ b/crates/types/src/vote.rs @@ -435,7 +435,7 @@ impl< stake_table_entries: Vec<::StakeTableEntry>, ) -> Either> { - let VoteData::Timeout(vote_commitment) = vote.get_data() else { + let VoteData::DA(vote_commitment) = vote.get_data() else { return Either::Left(self); }; From 86183cb9845c5d7718f75fb7f248e8efb9c5528f Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Thu, 21 Sep 2023 20:42:19 -0400 Subject: [PATCH 23/56] Cleaning up TODOs --- crates/hotshot/Cargo.toml | 1 - crates/hotshot/examples/infra/modDA.rs | 1 - .../traits/networking/web_server_network.rs | 2 - crates/task-impls/src/consensus.rs | 266 ++++++------------ crates/types/src/certificate.rs | 2 +- 5 files changed, 90 insertions(+), 182 deletions(-) diff --git a/crates/hotshot/Cargo.toml b/crates/hotshot/Cargo.toml index 671519ec84..452ff4de63 100644 --- a/crates/hotshot/Cargo.toml +++ b/crates/hotshot/Cargo.toml @@ -76,7 +76,6 @@ required-features = ["demo", "libp2p/rsa"] path = "examples/web-server-da/multi-web-server.rs" [dependencies] -# TODO ED We should upgrade ark libraries to 0.4 async-compatibility-layer = { workspace = true } async-lock = { workspace = true } async-trait = { workspace = true } diff --git a/crates/hotshot/examples/infra/modDA.rs b/crates/hotshot/examples/infra/modDA.rs index ae0f0e40f5..95cffd94ed 100644 --- a/crates/hotshot/examples/infra/modDA.rs +++ b/crates/hotshot/examples/infra/modDA.rs @@ -256,7 +256,6 @@ pub trait RunDA< let adjusted_padding = if padding < size { 0 } else { padding - size }; let mut txns: VecDeque = VecDeque::new(); - // TODO ED: In the future we should have each node generate transactions every round to simulate a more realistic network let tx_to_gen = transactions_per_round * rounds * 3; { let mut txn_rng = rand::thread_rng(); diff --git a/crates/hotshot/src/traits/networking/web_server_network.rs b/crates/hotshot/src/traits/networking/web_server_network.rs index 7b03aae947..14409fecb7 100644 --- a/crates/hotshot/src/traits/networking/web_server_network.rs +++ b/crates/hotshot/src/traits/networking/web_server_network.rs @@ -116,8 +116,6 @@ struct Inner { /// The last tx_index we saw from the web server tx_index: Arc>, - // TODO ED This should be TYPES::Time - // Theoretically there should never be contention for this lock... /// Task map for quorum proposals. proposal_task_map: Arc>>>>, diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index d0db84cf03..a0f878cdd4 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -39,6 +39,8 @@ use hotshot_types::{ vote::{QuorumVote, VoteType}, }; +use tracing::warn; + use snafu::Snafu; use std::{ collections::{HashMap, HashSet}, @@ -328,7 +330,6 @@ where } Either::Right(qc) => { debug!("QCFormed! {:?}", qc.view_number); - // TODO ED Make Timeout QC Formed Event state .event_stream .publish(SequencingHotShotEvent::QCFormed(either::Right(qc.clone()))) @@ -583,22 +584,12 @@ where ); // Remove old certs, we won't vote on past views - // TODO ED Put back in once we fix other errors - // for view in *self.cur_view..*new_view - 1 { - // let v = TYPES::Time::new(view); - // self.certs.remove(&v); - // } + for view in *self.cur_view..*new_view - 1 { + let v = TYPES::Time::new(view); + self.certs.remove(&v); + } self.cur_view = new_view; - // TODO ED These injects aren't right - // Commenting out because HotShot doesn't start on 1 now - // if new_view == TYPES::Time::new(1) { - // self.quorum_exchange - // .network() - // .inject_consensus_info(ConsensusIntentEvent::PollForCurrentProposal) - // .await; - // } - // Poll the future leader for lookahead let lookahead_view = new_view + LOOK_AHEAD; if !self.quorum_exchange.is_leader(lookahead_view) { @@ -622,7 +613,6 @@ where .inject_consensus_info(ConsensusIntentEvent::PollForDAC(*self.cur_view + 1)) .await; - // TODO ED I think this poll is still correct (actually want to poll for it in both views, in case this node just doesn't receive the latest proposal, but that is more of a web server issue specifically) if self.quorum_exchange.is_leader(self.cur_view + 1) { debug!("Polling for quorum votes for view {}", *self.cur_view); self.quorum_exchange @@ -639,7 +629,8 @@ where let timeout = self.timeout; self.timeout_task = async_spawn({ let stream = self.event_stream.clone(); - // TODO ED + 1 here because of the logic change to view update. This indicates we haven't seen evidence for view change for this view within the time window + // Nuance: We timeout on the view + 1 here because that means that we have + // not seen evidence to transition to this new view let view_number = self.cur_view + 1; async move { async_sleep(Duration::from_millis(timeout)).await; @@ -678,28 +669,77 @@ where return; } - if proposal.data.justify_qc.view_number() != proposal.data.view_number - 1 { - // TODO ED Add timeout cert logic - if proposal.data.timeout_certificate.is_none() { - error!( - "Proposal needed a timeout cert but didn't have one {:?}", - proposal.data.clone() - ); + // Verify a timeout certificate exists and is valid + if proposal.data.justify_qc.view_number() != view - 1 { + let Some(timeout_cert) = proposal.data.timeout_certificate.clone() else { + warn!( + "Quorum proposal for view {} needed a timeout certificate but did not have one", + *view); + return; + }; + + if !self + .timeout_exchange + .is_valid_cert(&timeout_cert.clone(), view.commit()) + { + warn!("Timeout certificate for view {} was invalid", *view); return; - } else { - error!("Proposal for view {} had timeout certificate", *view); } - // TODO ED Check timeout cert validity } - // TODO ED This needs to be moved further down so we only update the view after fully validating the qc. + let justify_qc = proposal.data.justify_qc.clone(); + + if !self + .quorum_exchange + .is_valid_cert(&justify_qc, justify_qc.leaf_commitment) + { + error!("Invalid justify_qc in proposal for view {}", *view); + return; + } + + // NOTE: We could update our view with a valid TC but invalid QC, but that is not what we do here self.update_view(view).await; - // TODO ED How does this play in with the timeout cert? self.current_proposal = Some(proposal.data.clone()); + let consensus = self.consensus.upgradable_read().await; + + // Construct the leaf. + let parent = if justify_qc.is_genesis() { + self.genesis_leaf().await + } else { + consensus + .saved_leaves + .get(&justify_qc.leaf_commitment()) + .cloned() + }; + + // Justify qc's leaf commitment is not the same as the parent's leaf commitment, but it should be (in this case) + let Some(parent) = parent else { + error!( + "Proposal's parent missing from storage with commitment: {:?}", + justify_qc.leaf_commitment() + ); + return; + }; + let parent_commitment = parent.commit(); + let leaf: SequencingLeaf<_> = SequencingLeaf { + view_number: view, + height: proposal.data.height, + justify_qc: justify_qc.clone(), + parent_commitment, + deltas: Right(proposal.data.block_commitment), + rejected: Vec::new(), + timestamp: time::OffsetDateTime::now_utc().unix_timestamp_nanos(), + proposer_id: sender.to_bytes(), + }; + let justify_qc_commitment = justify_qc.commit(); + let leaf_commitment = leaf.commit(); + let vote_token = self.quorum_exchange.make_vote_token(view); - // TODO: do some of this logic without the vote token check, only do that when voting. + + // TODO Put vote token creation inside message creation functions + // https://github.com/EspressoSystems/HotShot/issues/1795. match vote_token { Err(e) => { error!("Failed to generate vote token for {:?} {:?}", view, e); @@ -709,81 +749,23 @@ where } Ok(Some(vote_token)) => { debug!("We were chosen for consensus committee on {:?}", view); - let consensus = self.consensus.upgradable_read().await; - let message; - - // Construct the leaf. - let justify_qc = proposal.data.justify_qc; - let parent = if justify_qc.is_genesis() { - self.genesis_leaf().await - } else { - consensus - .saved_leaves - .get(&justify_qc.leaf_commitment()) - .cloned() - }; - // Justify qc's leaf commitment is not the same as the parent's leaf commitment, but it should be (in this case) - let Some(parent) = parent else { - error!( - "Proposal's parent missing from storage with commitment: {:?}", - justify_qc.leaf_commitment() - ); - return; - }; - let parent_commitment = parent.commit(); - let leaf: SequencingLeaf<_> = SequencingLeaf { - view_number: view, - height: proposal.data.height, - justify_qc: justify_qc.clone(), - parent_commitment, - deltas: Right(proposal.data.block_commitment), - rejected: Vec::new(), - timestamp: time::OffsetDateTime::now_utc().unix_timestamp_nanos(), - proposer_id: sender.to_bytes(), - }; - let justify_qc_commitment = justify_qc.commit(); - let leaf_commitment = leaf.commit(); - - // Validate the `justify_qc`. - if !self - .quorum_exchange - .is_valid_cert(&justify_qc, parent_commitment) - { - error!("Invalid justify_qc in proposal!. parent commitment is {:?} justify qc is {:?}", parent_commitment, justify_qc.clone()); - - message = self.quorum_exchange.create_no_message::( - justify_qc_commitment, - leaf_commitment, - view, - vote_token, - ); - } - // Validate the `height`. - else if leaf.height != parent.height + 1 { + // Validate the `height` + // TODO Remove height from proposal validation; view number is sufficient + // https://github.com/EspressoSystems/HotShot/issues/1796 + if leaf.height != parent.height + 1 { error!( "Incorrect height in proposal (expected {}, got {})", parent.height + 1, leaf.height ); - message = self.quorum_exchange.create_no_message( - justify_qc_commitment, - leaf_commitment, - view, - vote_token, - ); + return; } // Validate the signature. else if !view_leader_key .validate(&proposal.signature, leaf_commitment.as_ref()) { error!(?proposal.signature, "Could not verify proposal."); - message = self.quorum_exchange.create_no_message( - justify_qc_commitment, - leaf_commitment, - view, - vote_token, - ); } // Create a positive vote if either liveness or safety check // passes. @@ -811,20 +793,6 @@ where // Skip if both saftey and liveness checks fail. if !safety_check && !liveness_check { error!("Failed safety check and liveness check"); - message = self.quorum_exchange.create_no_message( - justify_qc_commitment, - leaf_commitment, - view, - vote_token, - ); - } else { - // Generate a message with yes vote. - message = self.quorum_exchange.create_yes_message( - justify_qc_commitment, - leaf_commitment, - view, - vote_token, - ); } } @@ -976,7 +944,6 @@ where .await; } if !self.vote_if_able().await { - // TOOD ED This means we publish the proposal without updating our own view, which doesn't seem right return; } self.current_proposal = None; @@ -985,13 +952,6 @@ where let time = TYPES::Time::new(v); self.certs.remove(&time); } - - // Update current view and publish a view change event so other tasks also update - // self.update_view(new_view).await; - - if let GeneralConsensusMessage::Vote(vote) = message { - // debug!("Sending vote to next leader {:?}", vote); - }; } } } @@ -1007,8 +967,6 @@ where return; } - // TODO ED Insert TimeoutVote accumulator stuff here - match vote.clone() { QuorumVote::Yes(vote_internal) => { let handle_event = HandleEvent(Arc::new(move |event, state| { @@ -1046,14 +1004,13 @@ where &vote_internal.clone().leaf_commitment, ); + // TODO Create default functions for accumulators + // https://github.com/EspressoSystems/HotShot/issues/1797 let timeout_accumulator = TimeoutVoteAccumulator { da_vote_outcomes: HashMap::new(), - - // TODO ED Don't use quorum exchange here - success_threshold: self.quorum_exchange.success_threshold(), - + success_threshold: self.timeout_exchange.success_threshold(), sig_lists: Vec::new(), - signers: bitvec![0; self.quorum_exchange.total_nodes()], + signers: bitvec![0; self.timeout_exchange.total_nodes()], phantom: PhantomData, }; @@ -1124,10 +1081,6 @@ where return; } - // // TODO ED Insert TimeoutVote accumulator stuff here - - // match vote.clone() { - // QuorumVote::Yes(vote_internal)=> { let handle_event = HandleEvent(Arc::new(move |event, state| { async move { vote_handle(state, event).await }.boxed() })); @@ -1143,15 +1096,13 @@ where }; // // Todo check if we are the leader - // TODO ED Make this a default accum let new_accumulator = TimeoutVoteAccumulator { da_vote_outcomes: HashMap::new(), - // TODO ED Don't use quorum exchange here - success_threshold: self.quorum_exchange.success_threshold(), + success_threshold: self.timeout_exchange.success_threshold(), sig_lists: Vec::new(), - signers: bitvec![0; self.quorum_exchange.total_nodes()], + signers: bitvec![0; self.timeout_exchange.total_nodes()], phantom: PhantomData, }; @@ -1222,24 +1173,23 @@ where debug!("QC Formed event happened!"); if let either::Right(qc) = cert.clone() { - // So we don't create a QC on the first view unless we are the leader debug!( - "Attempting to publish proposal after forming a QC for view {}", + "Attempting to publish proposal after forming a TC for view {}", *qc.view_number ); - // TODO ED Clean this up, get rid of clones + let view = qc.view_number + 1; + if self .publish_proposal_if_able( self.consensus.read().await.high_qc.clone(), - qc.clone().view_number + 1, + view, Some(qc.clone()), ) .await { - // self.update_view(qc.view_number + 1).await; } else { - error!("Wasn't able to publish proposal"); + warn!("Wasn't able to publish proposal"); } } if let either::Left(qc) = cert { @@ -1247,25 +1197,6 @@ where consensus.high_qc = qc.clone(); drop(consensus); - - // View may have already been updated by replica if they voted for this QC - // TODO ED We should separate leader state from replica state, they shouldn't share the same view - // Leader task should only run for a specific view, and never update its current view, but instead spawn another task - // let _res = self.update_view(qc.view_number + 1).await; - - // Start polling for votes for the next view - // if _res { - // if self.quorum_exchange.is_leader(qc.view_number + 2) { - // self.quorum_exchange - // .network() - // .inject_consensus_info( - // (ConsensusIntentEvent::PollForVotes(*qc.view_number + 1)), - // ) - // .await; - // } - // } - - // So we don't create a QC on the first view unless we are the leader debug!( "Attempting to publish proposal after forming a QC for view {}", *qc.view_number @@ -1275,7 +1206,7 @@ where .publish_proposal_if_able(qc.clone(), qc.view_number + 1, None) .await { - // self.update_view(qc.view_number + 1).await; + warn!("Wasn't able to publish proposal"); } } } @@ -1324,11 +1255,6 @@ where }) .await; - debug!("View changed to {}", *new_view); - - // ED Need to update the view here? What does otherwise? - // self.update_view(qc.view_number + 1).await; - // So we don't create a QC on the first view unless we are the leader if !self.quorum_exchange.is_leader(self.cur_view) { return; } @@ -1336,16 +1262,9 @@ where let consensus = self.consensus.read().await; let qc = consensus.high_qc.clone(); drop(consensus); - // TODO ED Do not want to publish proposal on view change - // if !self.publish_proposal_if_able(qc, self.cur_view, None).await { - // error!( - // "Failed to publish proposal on view change. View = {:?}", - // self.cur_view - // ); - // } } SequencingHotShotEvent::Timeout(view) => { - // TODO ED This is not an ideal check, we should have the timeout task receive view change events and then cancel itself + // NOTE: We may optionally have the timeout task listen for view change events if self.cur_view > view { return; } @@ -1359,12 +1278,11 @@ where debug!("We were not chosen for consensus committee on {:?}", view); } Ok(Some(vote_token)) => { - // TODO ED Why I here and not in quorum exchange? let message = self .timeout_exchange .create_timeout_message::(view, vote_token); - // error!("Sending timeout vote for view {}", *view); + debug!("Sending timeout vote for view {}", *view); if let GeneralConsensusMessage::TimeoutVote(vote) = message { self.event_stream .publish(SequencingHotShotEvent::TimeoutVoteSend(vote)) @@ -1372,18 +1290,12 @@ where } } } - - // self.quorum_exchange - // .network() - // .inject_consensus_info(ConsensusIntentEvent::CancelPollForVotes(*view)) - // .await; debug!( "We did not receive evidence for view {} in time, sending timeout vote for that view!", *view ); } SequencingHotShotEvent::SendDABlockData(block) => { - // ED TODO Should make sure this is actually the most recent block self.block = block; } _ => {} diff --git a/crates/types/src/certificate.rs b/crates/types/src/certificate.rs index 04adbbc134..37350facda 100644 --- a/crates/types/src/certificate.rs +++ b/crates/types/src/certificate.rs @@ -121,7 +121,7 @@ impl SignedCertificate bool { - todo!() + false } fn genesis() -> Self { From a9eb94e214929feeb2f17761002901a097af3633 Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Thu, 21 Sep 2023 21:13:32 -0400 Subject: [PATCH 24/56] More todos --- crates/task-impls/src/da.rs | 18 +---- crates/task-impls/src/harness.rs | 2 - crates/testing/tests/timeout.rs | 5 +- crates/types/src/certificate.rs | 10 +-- crates/types/src/traits/election.rs | 2 - crates/types/src/vote.rs | 110 +--------------------------- 6 files changed, 9 insertions(+), 138 deletions(-) diff --git a/crates/task-impls/src/da.rs b/crates/task-impls/src/da.rs index 53d0a57501..dee213a1e1 100644 --- a/crates/task-impls/src/da.rs +++ b/crates/task-impls/src/da.rs @@ -161,8 +161,6 @@ where &vote, &vote.block_commitment, ) { - - Left(new_accumulator) => { error!("Not enough DA votes yet"); state.accumulator = either::Left(new_accumulator); @@ -582,16 +580,14 @@ where error!("View changed by more than 1 going to view {:?}", view); } self.cur_view = view; - // Inject view info into network - // ED I think it is possible that you receive a quorum proposal, vote on it and update your view before the da leader has sent their proposal, and therefore you skip polling for this view? + // Inject view info into network let is_da = self .committee_exchange .membership() .get_committee(self.cur_view + 1) .contains(self.committee_exchange.public_key()); - // TODO ED Is this right? if is_da { debug!("Polling for DA proposals for view {}", *self.cur_view + 1); self.committee_exchange @@ -640,22 +636,12 @@ where }; debug!("Sending DA proposal for view {:?}", data.view_number); - // let message = SequencingMessage::(Right( - // CommitteeConsensusMessage::DAProposal(Proposal { data, signature }), - // )); let message = Proposal { data, signature }; - // Brodcast DA proposal - // TODO ED We should send an event to do this, but just getting it to work for now self.event_stream .publish(SequencingHotShotEvent::SendDABlockData(block.clone())) .await; - // if let Err(e) = self.api.send_da_broadcast(message.clone()).await { - // consensus.metrics.failed_to_send_messages.add(1); - // warn!(?message, ?e, "Could not broadcast leader proposal"); - // } else { - // consensus.metrics.outgoing_broadcast_messages.add(1); - // } + self.event_stream .publish(SequencingHotShotEvent::DAProposalSend( message.clone(), diff --git a/crates/task-impls/src/harness.rs b/crates/task-impls/src/harness.rs index 4978b27492..464217a383 100644 --- a/crates/task-impls/src/harness.rs +++ b/crates/task-impls/src/harness.rs @@ -107,8 +107,6 @@ pub fn handle_event>( *num_expected -= 1; } - // TODO ED Remove - error!("Event is {:?}", event); if state.expected_output.is_empty() { return (Some(HotShotTaskCompleted::ShutDown), state); diff --git a/crates/testing/tests/timeout.rs b/crates/testing/tests/timeout.rs index 954588958c..2fd6023224 100644 --- a/crates/testing/tests/timeout.rs +++ b/crates/testing/tests/timeout.rs @@ -26,6 +26,7 @@ async fn test_timeout() { }; // TODO ED Reduce down to 5 nodes once memory network issues is resolved + // https://github.com/EspressoSystems/HotShot/issues/1790 let mut metadata = TestMetadata { total_nodes: 10, start_nodes: 10, @@ -47,7 +48,6 @@ async fn test_timeout() { node_changes: vec![(Duration::from_millis(500), dead_nodes)], }; - // TODO ED Add safety task, etc to confirm TCs are being formed metadata.completion_task_description = CompletionTaskDescription::TimeBasedCompletionTaskBuilder( @@ -56,7 +56,8 @@ async fn test_timeout() { }, ); - // TODO ED Test with memory network once issue is resolved. + // TODO ED Test with memory network once issue is resolved + // https://github.com/EspressoSystems/HotShot/issues/1790 metadata .gen_launcher::() .launch() diff --git a/crates/types/src/certificate.rs b/crates/types/src/certificate.rs index 37350facda..d0ed806083 100644 --- a/crates/types/src/certificate.rs +++ b/crates/types/src/certificate.rs @@ -86,7 +86,8 @@ pub struct TimeoutCertificate { pub signatures: AssembledSignature, } -impl SignedCertificate> +impl + SignedCertificate> for TimeoutCertificate { type Vote = TimeoutVote2; @@ -334,17 +335,10 @@ impl Committable for ViewSyncCertificate { let signatures_bytes = serialize_signature(&self.signatures()); let mut builder = commit::RawCommitmentBuilder::new("View Sync Certificate Commitment") - // .field("leaf commitment", self.leaf_commitment) - // .u64_field("view number", *self.view_number.deref()) .constant_str("justify_qc signatures") .var_size_bytes(&signatures_bytes); - // builder = builder - // .field("Leaf commitment", self.leaf_commitment) - // .u64_field("View number", *self.view_number.deref()); - let certificate_internal = match &self { - // TODO ED Not the best way to do this ViewSyncCertificate::PreCommit(certificate_internal) => { builder = builder.var_size_field("View Sync Phase", "PreCommit".as_bytes()); certificate_internal diff --git a/crates/types/src/traits/election.rs b/crates/types/src/traits/election.rs index c8745652e2..93e8a14abc 100644 --- a/crates/types/src/traits/election.rs +++ b/crates/types/src/traits/election.rs @@ -1500,7 +1500,6 @@ impl< } pub trait TimeoutExchangeType: ConsensusExchange { - // TODO ED Clean this function up fn create_timeout_message>( &self, view: TYPES::Time, @@ -1515,7 +1514,6 @@ pub trait TimeoutExchangeType: ConsensusExchange .get_commit().as_ref() ); - // TODO ED Should not use le bytes GeneralConsensusMessage::::TimeoutVote(TimeoutVote2 { signature: (self.public_key().to_bytes(), signature), diff --git a/crates/types/src/vote.rs b/crates/types/src/vote.rs index 46e93c3e12..8ba9f060d7 100644 --- a/crates/types/src/vote.rs +++ b/crates/types/src/vote.rs @@ -404,7 +404,8 @@ pub trait Accumulator2< ) -> Either>; } -// TODO ED Make a default accumulator +// TODO Make a default accumulator +// https://github.com/EspressoSystems/HotShot/issues/1797 pub struct TimeoutVoteAccumulator< TYPES: NodeType, COMMITMENT: Serialize + Clone + for<'a> Deserialize<'a>, @@ -434,7 +435,6 @@ impl< vote_node_id: usize, stake_table_entries: Vec<::StakeTableEntry>, ) -> Either> { - let VoteData::DA(vote_commitment) = vote.get_data() else { return Either::Left(self); }; @@ -477,7 +477,6 @@ impl< if *da_stake_casted >= u64::from(self.success_threshold) { // Assemble QC let real_qc_pp = ::get_public_parameter( - // TODO ED Something about stake table entries. Might be easier to just pass in membership? stake_table_entries.clone(), U256::from(self.success_threshold.get()), ); @@ -496,43 +495,22 @@ impl< } } -// TODO ED Make a default accumulator -// pub struct TimeoutVoteAccumulator< -// TYPES: NodeType, -// COMMITMENT: for<'a> Deserialize<'a> + Serialize + Clone, -// VOTE: VoteType, -// > { -// /// Map of all da signatures accumlated so far -// pub da_vote_outcomes: VoteMap, -// /// A quorum's worth of stake, generally 2f + 1 -// pub success_threshold: NonZeroU64, -// /// A list of valid signatures for certificate aggregation -// pub sig_lists: Vec<::PureAssembledSignatureType>, -// /// A bitvec to indicate which node is active and send out a valid signature for certificate aggregation, this automatically do uniqueness check -// pub signers: BitVec, -// /// Phantom data to specify the vote this accumulator is for -// pub phantom: PhantomData, -// } - impl< TYPES: NodeType, COMMITMENT: for<'a> Deserialize<'a> + Serialize + Clone + Copy + PartialEq + Eq + Hash, VOTE: VoteType, > Accumulator2 for TimeoutVoteAccumulator { - // TODO ED Make commitment instead of committable fn append( mut self, vote: VOTE, vote_node_id: usize, stake_table_entries: Vec<::StakeTableEntry>, ) -> Either> { - let VoteData::Timeout(vote_commitment) = vote.get_data() else { return Either::Left(self); }; - let encoded_key = vote.get_key().to_bytes(); // Deserialize the signature so that it can be assembeld into a QC @@ -571,7 +549,6 @@ impl< if *da_stake_casted >= u64::from(self.success_threshold) { // Assemble QC let real_qc_pp = ::get_public_parameter( - // TODO ED Something about stake table entries. Might be easier to just pass in membership? stake_table_entries.clone(), U256::from(self.success_threshold.get()), ); @@ -608,87 +585,6 @@ pub struct DAVoteAccumulator< pub phantom: PhantomData, } -// impl< -// TYPES: NodeType, -// COMMITMENT: for<'a> Deserialize<'a> + Serialize + Clone + Copy + PartialEq + Eq + Hash, -// VOTE: VoteType, -// > Accumulator2 for DAVoteAccumulator -// { -// fn append( -// mut self, -// vote: VOTE, -// vote_node_id: usize, -// stake_table_entries: Vec<::StakeTableEntry>, -// ) -> Either> { - -// match vote.get_data() { -// VoteData::DA(_) => warn!("DA vote data"), -// VoteData::Timeout(_) => panic!(), -// _ => error!("Wrong vote data") - -// } - -// let VoteData::DA(vote_commitment) = vote.get_data() else { -// return Either::Left(self); -// }; - -// let encoded_key = vote.get_key().to_bytes(); - -// // Deserialize the signature so that it can be assembeld into a QC -// // TODO ED Update this once we've gotten rid of EncodedSignature -// let original_signature: ::PureAssembledSignatureType = -// bincode_opts() -// .deserialize(&vote.get_signature().0) -// .expect("Deserialization on the signature shouldn't be able to fail."); - -// let (da_stake_casted, da_vote_map) = self -// .da_vote_outcomes -// .entry(vote_commitment) -// .or_insert_with(|| (0, BTreeMap::new())); - -// // Check for duplicate vote -// // TODO ED Re-encoding signature key to bytes until we get rid of EncodedKey -// // Have to do this because SignatureKey is not hashable -// if da_vote_map.contains_key(&encoded_key) { -// return Either::Left(self); -// } - -// if self.signers.get(vote_node_id).as_deref() == Some(&true) { -// error!("Node id is already in signers list"); -// return Either::Left(self); -// } -// self.signers.set(vote_node_id, true); -// self.sig_lists.push(original_signature); - -// // Already checked that vote data was for a DA vote above -// *da_stake_casted += u64::from(vote.get_vote_token().vote_count()); -// da_vote_map.insert( -// encoded_key, -// (vote.get_signature(), vote.get_data(), vote.get_vote_token()), -// ); - -// if *da_stake_casted >= u64::from(self.success_threshold) { -// // Assemble QC -// let real_qc_pp = ::get_public_parameter( -// // TODO ED Something about stake table entries. Might be easier to just pass in membership? -// stake_table_entries.clone(), -// U256::from(self.success_threshold.get()), -// ); - -// let real_qc_sig = ::assemble( -// &real_qc_pp, -// self.signers.as_bitslice(), -// &self.sig_lists[..], -// ); - -// self.da_vote_outcomes.remove(&vote_commitment); - -// return Either::Right(AssembledSignature::DA(real_qc_sig)); -// } -// Either::Left(self) -// } -// } - /// Accumulate quorum votes pub struct QuorumVoteAccumulator< TYPES: NodeType, @@ -797,7 +693,6 @@ impl< if *total_stake_casted >= u64::from(self.success_threshold) { // Assemble QC let real_qc_pp = ::get_public_parameter( - // TODO ED Something about stake table entries. Might be easier to just pass in membership? stake_table_entries.clone(), U256::from(self.success_threshold.get()), ); @@ -1021,7 +916,6 @@ impl< } /// Mapping of commitments to vote tokens by key. -// TODO ED Remove this whole token generic type VoteMap = HashMap< COMMITMENT, ( From e9d1424220ca08fb97cac2beda62b72569ce8c01 Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Thu, 21 Sep 2023 21:41:04 -0400 Subject: [PATCH 25/56] Fix lints --- crates/hotshot/src/lib.rs | 5 +- crates/task-impls/src/consensus.rs | 311 ++++++++---------- crates/task-impls/src/da.rs | 8 +- crates/task-impls/src/events.rs | 8 +- crates/task-impls/src/harness.rs | 2 - crates/task-impls/src/network.rs | 6 +- crates/task-impls/src/view_sync.rs | 2 +- crates/testing/tests/consensus_task.rs | 1 - crates/testing/tests/timeout.rs | 4 +- crates/testing/tests/view_sync_task.rs | 2 +- crates/types/src/certificate.rs | 42 +-- crates/types/src/message.rs | 16 +- crates/types/src/traits/election.rs | 281 +--------------- .../types/src/traits/node_implementation.rs | 18 +- crates/types/src/vote.rs | 297 +---------------- 15 files changed, 217 insertions(+), 786 deletions(-) diff --git a/crates/hotshot/src/lib.rs b/crates/hotshot/src/lib.rs index 52118f7eea..19fa2d9cde 100644 --- a/crates/hotshot/src/lib.rs +++ b/crates/hotshot/src/lib.rs @@ -257,8 +257,8 @@ impl> SystemContext { self.inner .internal_event_stream .publish(SequencingHotShotEvent::QCFormed(either::Left( - QuorumCertificate::genesis())), - ) + QuorumCertificate::genesis(), + ))) .await; } @@ -693,7 +693,6 @@ where let quorum_exchange = self.inner.exchanges.quorum_exchange().clone(); let committee_exchange = self.inner.exchanges.committee_exchange().clone(); let view_sync_exchange = self.inner.exchanges.view_sync_exchange().clone(); - let timeout_exchange = self.inner.exchanges.timeout_exchange().clone(); let handle = SystemContextHandle { registry, diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index a0f878cdd4..7c62af9500 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -167,7 +167,7 @@ pub struct VoteCollectionTaskState< { /// the quorum exchange pub quorum_exchange: Arc>, - + /// the timeout exchange pub timeout_exchange: Arc>, #[allow(clippy::type_complexity)] @@ -183,6 +183,7 @@ pub struct VoteCollectionTaskState< >, /// Accumulator for votes + #[allow(clippy::type_complexity)] pub timeout_accumulator: Either< as SignedCertificate< TYPES, @@ -263,7 +264,7 @@ where let accumulator = state.accumulator.left().unwrap(); - match state.quorum_exchange.accumulate_vote_2( + match state.quorum_exchange.accumulate_vote( accumulator, &vote, &vote_internal.leaf_commitment, @@ -293,11 +294,6 @@ where } } } - - QuorumVote::Timeout(_vote) => { - error!("The next leader has received an unexpected vote!"); - return (None, state); - } QuorumVote::No(_) => { error!("The next leader has received an unexpected vote!"); } @@ -319,7 +315,7 @@ where let accumulator = state.timeout_accumulator.left().unwrap(); - match state.timeout_exchange.accumulate_vote_2( + match state.timeout_exchange.accumulate_vote( accumulator, &vote, &vote.get_view().commit(), @@ -353,7 +349,7 @@ where return (Some(HotShotTaskCompleted::ShutDown), state); } _ => { - error!("Unexpected event") + error!("Unexpected event"); } } (None, state) @@ -733,84 +729,67 @@ where timestamp: time::OffsetDateTime::now_utc().unix_timestamp_nanos(), proposer_id: sender.to_bytes(), }; - let justify_qc_commitment = justify_qc.commit(); let leaf_commitment = leaf.commit(); - let vote_token = self.quorum_exchange.make_vote_token(view); - - // TODO Put vote token creation inside message creation functions - // https://github.com/EspressoSystems/HotShot/issues/1795. - match vote_token { - Err(e) => { - error!("Failed to generate vote token for {:?} {:?}", view, e); - } - Ok(None) => { - debug!("We were not chosen for consensus committee on {:?}", view); + // Validate the `height` + // TODO Remove height from proposal validation; view number is sufficient + // https://github.com/EspressoSystems/HotShot/issues/1796 + if leaf.height != parent.height + 1 { + error!( + "Incorrect height in proposal (expected {}, got {})", + parent.height + 1, + leaf.height + ); + return; + } + // Validate the signature. + else if !view_leader_key.validate(&proposal.signature, leaf_commitment.as_ref()) { + error!(?proposal.signature, "Could not verify proposal."); + } + // Create a positive vote if either liveness or safety check + // passes. + else { + // Liveness check. + let liveness_check = justify_qc.view_number > consensus.locked_view; + + // Safety check. + // Check if proposal extends from the locked leaf. + let outcome = consensus.visit_leaf_ancestors( + justify_qc.view_number, + Terminator::Inclusive(consensus.locked_view), + false, + |leaf| { + // if leaf view no == locked view no then we're done, report success by + // returning true + leaf.view_number != consensus.locked_view + }, + ); + let safety_check = outcome.is_ok(); + if let Err(e) = outcome { + self.api.send_view_error(view, Arc::new(e)).await; } - Ok(Some(vote_token)) => { - debug!("We were chosen for consensus committee on {:?}", view); - // Validate the `height` - // TODO Remove height from proposal validation; view number is sufficient - // https://github.com/EspressoSystems/HotShot/issues/1796 - if leaf.height != parent.height + 1 { - error!( - "Incorrect height in proposal (expected {}, got {})", - parent.height + 1, - leaf.height - ); - return; - } - // Validate the signature. - else if !view_leader_key - .validate(&proposal.signature, leaf_commitment.as_ref()) - { - error!(?proposal.signature, "Could not verify proposal."); - } - // Create a positive vote if either liveness or safety check - // passes. - else { - // Liveness check. - let liveness_check = justify_qc.view_number > consensus.locked_view; - - // Safety check. - // Check if proposal extends from the locked leaf. - let outcome = consensus.visit_leaf_ancestors( - justify_qc.view_number, - Terminator::Inclusive(consensus.locked_view), - false, - |leaf| { - // if leaf view no == locked view no then we're done, report success by - // returning true - leaf.view_number != consensus.locked_view - }, - ); - let safety_check = outcome.is_ok(); - if let Err(e) = outcome { - self.api.send_view_error(view, Arc::new(e)).await; - } - - // Skip if both saftey and liveness checks fail. - if !safety_check && !liveness_check { - error!("Failed safety check and liveness check"); - } - } + // Skip if both saftey and liveness checks fail. + if !safety_check && !liveness_check { + error!("Failed safety check and liveness check"); + } + } - let high_qc = leaf.justify_qc.clone(); - let mut new_anchor_view = consensus.last_decided_view; - let mut new_locked_view = consensus.locked_view; - let mut last_view_number_visited = view; - let mut new_commit_reached: bool = false; - let mut new_decide_reached = false; - let mut new_decide_qc = None; - let mut leaf_views = Vec::new(); - let mut included_txns = HashSet::new(); - let old_anchor_view = consensus.last_decided_view; - let parent_view = leaf.justify_qc.view_number; - let mut current_chain_length = 0usize; - if parent_view + 1 == view { - current_chain_length += 1; - if let Err(e) = consensus.visit_leaf_ancestors( + let high_qc = leaf.justify_qc.clone(); + let mut new_anchor_view = consensus.last_decided_view; + let mut new_locked_view = consensus.locked_view; + let mut last_view_number_visited = view; + let mut new_commit_reached: bool = false; + let mut new_decide_reached = false; + let mut new_decide_qc = None; + let mut leaf_views = Vec::new(); + let mut included_txns = HashSet::new(); + let old_anchor_view = consensus.last_decided_view; + let parent_view = leaf.justify_qc.view_number; + let mut current_chain_length = 0usize; + if parent_view + 1 == view { + current_chain_length += 1; + if let Err(e) = consensus.visit_leaf_ancestors( parent_view, Terminator::Exclusive(old_anchor_view), true, @@ -869,90 +848,88 @@ where event: EventType::Error { error: e.into() }, }).await; } - } + } - let included_txns_set: HashSet<_> = if new_decide_reached { - included_txns - } else { - HashSet::new() - }; + let included_txns_set: HashSet<_> = if new_decide_reached { + included_txns + } else { + HashSet::new() + }; - // promote lock here to add proposal to statemap - let mut consensus = RwLockUpgradableReadGuard::upgrade(consensus).await; - if high_qc.view_number > consensus.high_qc.view_number { - consensus.high_qc = high_qc; - } - consensus.state_map.insert( - view, - View { - view_inner: ViewInner::Leaf { - leaf: leaf.commit(), - }, - }, - ); - consensus.saved_leaves.insert(leaf.commit(), leaf.clone()); - if new_commit_reached { - consensus.locked_view = new_locked_view; - } - #[allow(clippy::cast_precision_loss)] - if new_decide_reached { - debug!("about to publish decide"); - self.event_stream - .publish(SequencingHotShotEvent::LeafDecided(leaf_views.clone())) - .await; - let decide_sent = self.output_event_stream.publish(Event { - view_number: consensus.last_decided_view, - event: EventType::Decide { - leaf_chain: Arc::new(leaf_views), - qc: Arc::new(new_decide_qc.unwrap()), - block_size: Some(included_txns_set.len().try_into().unwrap()), - }, - }); - let old_anchor_view = consensus.last_decided_view; - consensus - .collect_garbage(old_anchor_view, new_anchor_view) - .await; - consensus.last_decided_view = new_anchor_view; - consensus.invalid_qc = 0; + // promote lock here to add proposal to statemap + let mut consensus = RwLockUpgradableReadGuard::upgrade(consensus).await; + if high_qc.view_number > consensus.high_qc.view_number { + consensus.high_qc = high_qc; + } + consensus.state_map.insert( + view, + View { + view_inner: ViewInner::Leaf { + leaf: leaf.commit(), + }, + }, + ); + consensus.saved_leaves.insert(leaf.commit(), leaf.clone()); + if new_commit_reached { + consensus.locked_view = new_locked_view; + } + #[allow(clippy::cast_precision_loss)] + if new_decide_reached { + debug!("about to publish decide"); + self.event_stream + .publish(SequencingHotShotEvent::LeafDecided(leaf_views.clone())) + .await; + let decide_sent = self.output_event_stream.publish(Event { + view_number: consensus.last_decided_view, + event: EventType::Decide { + leaf_chain: Arc::new(leaf_views), + qc: Arc::new(new_decide_qc.unwrap()), + block_size: Some(included_txns_set.len().try_into().unwrap()), + }, + }); + let old_anchor_view = consensus.last_decided_view; + consensus + .collect_garbage(old_anchor_view, new_anchor_view) + .await; + consensus.last_decided_view = new_anchor_view; + consensus.invalid_qc = 0; - // We're only storing the last QC. We could store more but we're realistically only going to retrieve the last one. - if let Err(e) = self.api.store_leaf(old_anchor_view, leaf).await { - error!("Could not insert new anchor into the storage API: {:?}", e); - } + // We're only storing the last QC. We could store more but we're realistically only going to retrieve the last one. + if let Err(e) = self.api.store_leaf(old_anchor_view, leaf).await { + error!("Could not insert new anchor into the storage API: {:?}", e); + } - debug!("Sending Decide for view {:?}", consensus.last_decided_view); - debug!("Decided txns len {:?}", included_txns_set.len()); - decide_sent.await; - } + debug!("Sending Decide for view {:?}", consensus.last_decided_view); + debug!("Decided txns len {:?}", included_txns_set.len()); + decide_sent.await; + } - let new_view = self.current_proposal.clone().unwrap().view_number + 1; - // In future we can use the mempool model where we fetch the proposal if we don't have it, instead of having to wait for it here - // This is for the case where we form a QC but have not yet seen the previous proposal ourselves - let should_propose = self.quorum_exchange.is_leader(new_view) - && consensus.high_qc.view_number - == self.current_proposal.clone().unwrap().view_number; - // todo get rid of this clone - let qc = consensus.high_qc.clone(); - - drop(consensus); - if should_propose { - debug!( - "Attempting to publish proposal after voting; now in view: {}", - *new_view - ); - self.publish_proposal_if_able(qc.clone(), qc.view_number + 1, None) - .await; - } - if !self.vote_if_able().await { - return; - } - self.current_proposal = None; + let new_view = self.current_proposal.clone().unwrap().view_number + 1; + // In future we can use the mempool model where we fetch the proposal if we don't have it, instead of having to wait for it here + // This is for the case where we form a QC but have not yet seen the previous proposal ourselves + let should_propose = self.quorum_exchange.is_leader(new_view) + && consensus.high_qc.view_number + == self.current_proposal.clone().unwrap().view_number; + // todo get rid of this clone + let qc = consensus.high_qc.clone(); - for v in (*self.cur_view)..=(*view) { - let time = TYPES::Time::new(v); - self.certs.remove(&time); - } - } + drop(consensus); + if should_propose { + debug!( + "Attempting to publish proposal after voting; now in view: {}", + *new_view + ); + self.publish_proposal_if_able(qc.clone(), qc.view_number + 1, None) + .await; + } + if !self.vote_if_able().await { + return; + } + self.current_proposal = None; + + for v in (*self.cur_view)..=(*view) { + let time = TYPES::Time::new(v); + self.certs.remove(&time); } } SequencingHotShotEvent::QuorumVoteRecv(vote) => { @@ -998,7 +975,7 @@ where phantom: PhantomData, }; - let accumulator = self.quorum_exchange.accumulate_vote_2( + let accumulator = self.quorum_exchange.accumulate_vote( new_accumulator, &vote, &vote_internal.clone().leaf_commitment, @@ -1064,7 +1041,7 @@ where .await; } } - QuorumVote::Timeout(_) | QuorumVote::No(_) => { + QuorumVote::No(_) => { error!("The next leader has received an unexpected vote!"); } } @@ -1106,7 +1083,7 @@ where phantom: PhantomData, }; - let timeout_accumulator = self.timeout_exchange.accumulate_vote_2( + let timeout_accumulator = self.timeout_exchange.accumulate_vote( new_accumulator, &vote, &vote.get_view().commit(), @@ -1254,14 +1231,6 @@ where }, }) .await; - - if !self.quorum_exchange.is_leader(self.cur_view) { - return; - } - - let consensus = self.consensus.read().await; - let qc = consensus.high_qc.clone(); - drop(consensus); } SequencingHotShotEvent::Timeout(view) => { // NOTE: We may optionally have the timeout task listen for view change events diff --git a/crates/task-impls/src/da.rs b/crates/task-impls/src/da.rs index dee213a1e1..96a4d18a48 100644 --- a/crates/task-impls/src/da.rs +++ b/crates/task-impls/src/da.rs @@ -156,7 +156,7 @@ where let accumulator = state.accumulator.left().unwrap(); - match state.committee_exchange.accumulate_vote_2( + match state.committee_exchange.accumulate_vote( accumulator, &vote, &vote.block_commitment, @@ -198,7 +198,7 @@ where let accumulator = state.accumulator.left().unwrap(); - match state.committee_exchange.accumulate_vote_2( + match state.committee_exchange.accumulate_vote( accumulator, &vote, &vote.block_commitment, @@ -374,7 +374,7 @@ where phantom: PhantomData, }; - let accumulator = self.committee_exchange.accumulate_vote_2( + let accumulator = self.committee_exchange.accumulate_vote( new_accumulator, &vote, &vote.clone().block_commitment, @@ -456,7 +456,7 @@ where phantom: PhantomData, }; - let accumulator = self.committee_exchange.accumulate_vote_2( + let accumulator = self.committee_exchange.accumulate_vote( new_accumulator, &vote, &vote.clone().block_commitment, diff --git a/crates/task-impls/src/events.rs b/crates/task-impls/src/events.rs index ded16bac6e..92e0013615 100644 --- a/crates/task-impls/src/events.rs +++ b/crates/task-impls/src/events.rs @@ -6,7 +6,7 @@ use hotshot_types::{ traits::node_implementation::{ NodeImplementation, NodeType, QuorumProposalType, ViewSyncProposalType, }, - vote::{DAVote, QuorumVote, ViewSyncVote, TimeoutVote2}, + vote::{DAVote, QuorumVote, TimeoutVote, ViewSyncVote}, }; use crate::view_sync::ViewSyncPhase; @@ -20,8 +20,10 @@ pub enum SequencingHotShotEvent> { QuorumProposalRecv(Proposal>, TYPES::SignatureKey), /// A quorum vote has been received from the network; handled by the consensus task QuorumVoteRecv(QuorumVote), - TimeoutVoteRecv(TimeoutVote2), - TimeoutVoteSend(TimeoutVote2), + /// A timeout vote recevied from the network; handled by consensus task + TimeoutVoteRecv(TimeoutVote), + /// Send a timeout vote to the network; emitted by consensus task replicas + TimeoutVoteSend(TimeoutVote), /// A DA proposal has been received from the network; handled by the DA task DAProposalRecv(Proposal>, TYPES::SignatureKey), /// A DA vote has been received by the network; handled by the DA task diff --git a/crates/task-impls/src/harness.rs b/crates/task-impls/src/harness.rs index 464217a383..136093bc3e 100644 --- a/crates/task-impls/src/harness.rs +++ b/crates/task-impls/src/harness.rs @@ -11,7 +11,6 @@ use hotshot_task::{ use hotshot_types::traits::node_implementation::{NodeImplementation, NodeType}; use snafu::Snafu; use std::{collections::HashMap, future::Future, sync::Arc}; -use tracing::error; /// The state for the test harness task. Keeps track of which events and how many we expect to get pub struct TestHarnessState> { @@ -107,7 +106,6 @@ pub fn handle_event>( *num_expected -= 1; } - if state.expected_output.is_empty() { return (Some(HotShotTaskCompleted::ShutDown), state); } diff --git a/crates/task-impls/src/network.rs b/crates/task-impls/src/network.rs index 233190a6d5..c81463e5c7 100644 --- a/crates/task-impls/src/network.rs +++ b/crates/task-impls/src/network.rs @@ -100,7 +100,10 @@ impl< }, Either::Right(committee_message) => match committee_message { CommitteeConsensusMessage::DAProposal(proposal) => { - error!("Received da proposal for view {:?}", proposal.clone().data.view_number); + error!( + "Received da proposal for view {:?}", + proposal.clone().data.view_number + ); SequencingHotShotEvent::DAProposalRecv(proposal.clone(), sender) } CommitteeConsensusMessage::DAVote(vote) => { @@ -354,7 +357,6 @@ impl< | SequencingHotShotEvent::VidCertSend(_, _) | SequencingHotShotEvent::ViewChange(_) | SequencingHotShotEvent::TimeoutVoteSend(_) - ) } diff --git a/crates/task-impls/src/view_sync.rs b/crates/task-impls/src/view_sync.rs index d5b2480c11..86be8ba62e 100644 --- a/crates/task-impls/src/view_sync.rs +++ b/crates/task-impls/src/view_sync.rs @@ -1004,7 +1004,7 @@ where *vote_internal.round, vote_internal.relay ); - let accumulator = self.exchange.accumulate_vote_2( + let accumulator = self.exchange.accumulate_vote( self.accumulator.left().unwrap(), &vote, &view_sync_data, diff --git a/crates/testing/tests/consensus_task.rs b/crates/testing/tests/consensus_task.rs index 9e722088ef..faa08e7c20 100644 --- a/crates/testing/tests/consensus_task.rs +++ b/crates/testing/tests/consensus_task.rs @@ -146,7 +146,6 @@ async fn test_consensus_vote() { let handle = build_system_handle(2).await.0; let (private_key, public_key) = key_pair_for_id(1); - let (private_key_2, public_key_2) = key_pair_for_id(2); let mut input = Vec::new(); let mut output = HashMap::new(); diff --git a/crates/testing/tests/timeout.rs b/crates/testing/tests/timeout.rs index 2fd6023224..64ea239321 100644 --- a/crates/testing/tests/timeout.rs +++ b/crates/testing/tests/timeout.rs @@ -7,13 +7,12 @@ async fn test_timeout() { use std::time::Duration; - use hotshot_testing::node_types::SequencingLibp2pImpl; use hotshot_testing::node_types::SequencingWebImpl; use hotshot_testing::overall_safety_task::OverallSafetyPropertiesDescription; use hotshot_testing::{ completion_task::{CompletionTaskDescription, TimeBasedCompletionTaskDescription}, - node_types::{SequencingMemoryImpl, SequencingTestTypes}, + node_types::SequencingTestTypes, spinning_task::{ChangeNode, SpinningTaskDescription, UpDown}, test_builder::{TestMetadata, TimingData}, }; @@ -48,7 +47,6 @@ async fn test_timeout() { node_changes: vec![(Duration::from_millis(500), dead_nodes)], }; - metadata.completion_task_description = CompletionTaskDescription::TimeBasedCompletionTaskBuilder( TimeBasedCompletionTaskDescription { diff --git a/crates/testing/tests/view_sync_task.rs b/crates/testing/tests/view_sync_task.rs index 919303d36a..99255e8209 100644 --- a/crates/testing/tests/view_sync_task.rs +++ b/crates/testing/tests/view_sync_task.rs @@ -23,7 +23,7 @@ async fn test_view_sync_task() { use core::panic; use hotshot::tasks::add_view_sync_task; - use hotshot_task_impls::{harness::run_harness, view_sync::ViewSyncPhase}; + use hotshot_task_impls::harness::run_harness; use hotshot_testing::task_helpers::build_system_handle; use hotshot_types::{ traits::election::VoteData, diff --git a/crates/types/src/certificate.rs b/crates/types/src/certificate.rs index d0ed806083..c540a349fc 100644 --- a/crates/types/src/certificate.rs +++ b/crates/types/src/certificate.rs @@ -1,19 +1,16 @@ //! Provides two types of cerrtificates and their accumulators. -use crate::vote::AccumulatorPlaceholder; use crate::vote::DAVoteAccumulator; use crate::vote::QuorumVote; use crate::vote::QuorumVoteAccumulator; -use crate::vote::TimeoutVote2; +use crate::vote::TimeoutVote; use crate::vote::TimeoutVoteAccumulator; use crate::vote::ViewSyncVoteAccumulator; use crate::vote::VoteType; use crate::{ data::{fake_commitment, serialize_signature, LeafType}, traits::{ - election::{SignedCertificate, VoteData, VoteToken}, - node_implementation::NodeType, - signature_key::{EncodedPublicKey, EncodedSignature, SignatureKey}, + election::SignedCertificate, node_implementation::NodeType, signature_key::SignatureKey, state::ConsensusTime, }, vote::{DAVote, ViewSyncData, ViewSyncVote}, @@ -90,7 +87,7 @@ impl SignedCertificate> for TimeoutCertificate { - type Vote = TimeoutVote2; + type Vote = TimeoutVote; type VoteAccumulator = TimeoutVoteAccumulator, Self::Vote>; @@ -98,11 +95,10 @@ impl signatures: AssembledSignature, vote: Self::Vote, ) -> Self { - let qc = TimeoutCertificate { + TimeoutCertificate { view_number: vote.get_view(), signatures, - }; - qc + } } fn view_number(&self) -> TYPES::Time { @@ -117,8 +113,8 @@ impl self.view_number.commit() } - fn set_leaf_commitment(&mut self, commitment: Commitment) { - todo!() + fn set_leaf_commitment(&mut self, _commitment: Commitment) { + unimplemented!() } fn is_genesis(&self) -> bool { @@ -126,7 +122,7 @@ impl } fn genesis() -> Self { - todo!() + unimplemented!() } } @@ -174,7 +170,7 @@ pub enum AssembledSignature { No(::QCType), /// These signatures are for a 'DA' certificate DA(::QCType), - + /// These signatures are for a `Timeout` certificate Timeout(::QCType), /// These signatures are for genesis certificate Genesis(), @@ -186,25 +182,6 @@ pub enum AssembledSignature { ViewSyncFinalize(::QCType), } -/// Data from a vote needed to accumulate into a `SignedCertificate` -pub struct VoteMetaData { - /// Voter's public key - pub encoded_key: EncodedPublicKey, - /// Votes signature - pub encoded_signature: EncodedSignature, - /// Commitment to what's voted on. E.g. the leaf for a `QuorumCertificate` - pub commitment: Commitment, - /// Data of the vote, yes, no, timeout, or DA - pub data: VoteData>, - /// The votes's token - pub vote_token: T, - /// View number for the vote - pub view_number: TIME, - /// The relay index for view sync - // TODO ED Make VoteMetaData more generic to avoid this variable that only ViewSync uses - pub relay: Option, -} - impl> SignedCertificate> for QuorumCertificate @@ -220,7 +197,6 @@ impl> QuorumVote::Yes(vote_internal) | QuorumVote::No(vote_internal) => { vote_internal.leaf_commitment } - QuorumVote::Timeout(_) => unimplemented!(), }; let qc = QuorumCertificate { leaf_commitment, diff --git a/crates/types/src/message.rs b/crates/types/src/message.rs index f08cb54dfb..1885a95d3d 100644 --- a/crates/types/src/message.rs +++ b/crates/types/src/message.rs @@ -13,7 +13,7 @@ use crate::{ }, signature_key::EncodedSignature, }, - vote::{DAVote, QuorumVote, TimeoutVote, TimeoutVote2, ViewSyncVote, VoteType}, + vote::{DAVote, QuorumVote, TimeoutVote, ViewSyncVote, VoteType}, }; use derivative::Derivative; use either::Either::{self, Left, Right}; @@ -201,8 +201,7 @@ where } GeneralConsensusMessage::ViewSyncVote(_) | GeneralConsensusMessage::ViewSyncCertificate(_) => todo!(), - | GeneralConsensusMessage::TimeoutVote(_) => todo!(), - + GeneralConsensusMessage::TimeoutVote(_) => todo!(), } } } @@ -325,7 +324,7 @@ where ViewSyncCertificate(Proposal>), /// Message with a Timeout vote - TimeoutVote(TimeoutVote2), + TimeoutVote(TimeoutVote), /// Internal ONLY message indicating a view interrupt. #[serde(skip)] @@ -421,9 +420,7 @@ impl< GeneralConsensusMessage::ViewSyncCertificate(message) => { message.data.get_view_number() } - GeneralConsensusMessage::TimeoutVote(message) => { - message.get_view() - } + GeneralConsensusMessage::TimeoutVote(message) => message.get_view(), } } Right(committee_message) => { @@ -451,11 +448,12 @@ impl< match &self.0 { Left(general_message) => match general_message { GeneralConsensusMessage::Proposal(_) => MessagePurpose::Proposal, - GeneralConsensusMessage::Vote(_) => MessagePurpose::Vote, + GeneralConsensusMessage::Vote(_) | GeneralConsensusMessage::TimeoutVote(_) => { + MessagePurpose::Vote + } GeneralConsensusMessage::InternalTrigger(_) => MessagePurpose::Internal, GeneralConsensusMessage::ViewSyncVote(_) => MessagePurpose::ViewSyncVote, GeneralConsensusMessage::ViewSyncCertificate(_) => MessagePurpose::ViewSyncProposal, - GeneralConsensusMessage::TimeoutVote(_) => MessagePurpose::Vote, }, Right(committee_message) => match committee_message { CommitteeConsensusMessage::DAProposal(_) => MessagePurpose::Proposal, diff --git a/crates/types/src/traits/election.rs b/crates/types/src/traits/election.rs index 93e8a14abc..db9c46f9ea 100644 --- a/crates/types/src/traits/election.rs +++ b/crates/types/src/traits/election.rs @@ -10,10 +10,10 @@ use super::{ use crate::{ certificate::{ AssembledSignature, DACertificate, QuorumCertificate, TimeoutCertificate, - ViewSyncCertificate, VoteMetaData, + ViewSyncCertificate, }, data::{DAProposal, ProposalType}, - vote::TimeoutVote2, + vote::TimeoutVote, }; use crate::{ @@ -21,7 +21,7 @@ use crate::{ vote::ViewSyncVoteInternal, }; -use crate::vote::Accumulator2; +use crate::vote::Accumulator; use crate::{ data::LeafType, traits::{ @@ -30,10 +30,7 @@ use crate::{ signature_key::SignatureKey, state::ConsensusTime, }, - vote::{ - DAVote, QuorumVote, TimeoutVote, ViewSyncData, ViewSyncVote, VoteAccumulator, VoteType, - YesOrNoVote, - }, + vote::{DAVote, QuorumVote, ViewSyncData, ViewSyncVote, VoteType, YesOrNoVote}, }; use bincode::Options; use commit::{Commitment, Committable}; @@ -170,7 +167,7 @@ where type Vote: VoteType; /// `Accumulator` type to accumulate votes. - type VoteAccumulator: Accumulator2; + type VoteAccumulator: Accumulator; /// Build a QC from the threshold signature and commitment // TODO ED Rename this function and rework this function parameters @@ -331,12 +328,6 @@ pub trait ConsensusExchange: Send + Sync { .make_vote_token(view_number, self.private_key()) } - /// The contents of a vote on `commit`. - fn vote_data( - &self, - commit: Commitment, - ) -> VoteData>; - /// Validate a QC. fn is_valid_cert(&self, qc: &Self::Certificate, commit: Commitment) -> bool { if qc.is_genesis() && qc.view_number() == TYPES::Time::genesis() { @@ -382,14 +373,6 @@ pub trait ConsensusExchange: Send + Sync { ); ::check(&real_qc_pp, real_commit.as_ref(), &qc) } - AssembledSignature::Timeout(qc) => { - let real_commit = VoteData::Timeout(leaf_commitment).get_commit(); - let real_qc_pp = ::get_public_parameter( - self.membership().get_committee_qc_stake_table(), - U256::from(self.membership().success_threshold().get()), - ); - ::check(&real_qc_pp, real_commit.as_ref(), &qc) - } AssembledSignature::Genesis() => true, AssembledSignature::ViewSyncPreCommit(_) | AssembledSignature::ViewSyncCommit(_) @@ -402,31 +385,6 @@ pub trait ConsensusExchange: Send + Sync { /// Validate a vote by checking its signature and token. fn is_valid_vote( - &self, - encoded_key: &EncodedPublicKey, - encoded_signature: &EncodedSignature, - data: VoteData>, - vote_token: Checked, - ) -> bool { - let mut is_valid_vote_token = false; - let mut is_valid_signature = false; - if let Some(key) = ::from_bytes(encoded_key) { - is_valid_signature = key.validate(encoded_signature, data.get_commit().as_ref()); - let valid_vote_token = self.membership().validate_vote_token(key, vote_token); - is_valid_vote_token = match valid_vote_token { - Err(_) => { - error!("Vote token was invalid"); - false - } - Ok(Checked::Valid(_)) => true, - Ok(Checked::Inval(_) | Checked::Unchecked(_)) => false, - }; - } - is_valid_signature && is_valid_vote_token - } - - /// Validate a vote by checking its signature and token. - fn is_valid_vote_2( &self, key: &TYPES::SignatureKey, encoded_signature: &EncodedSignature, @@ -434,9 +392,6 @@ pub trait ConsensusExchange: Send + Sync { vote_token: &Checked, ) -> bool { let is_valid_signature = key.validate(encoded_signature, data.get_commit().as_ref()); - if !is_valid_signature { - panic!() - } let valid_vote_token = self .membership() .validate_vote_token(key.clone(), vote_token.clone()); @@ -449,45 +404,15 @@ pub trait ConsensusExchange: Send + Sync { Ok(Checked::Inval(_) | Checked::Unchecked(_)) => false, }; - let result = is_valid_signature && is_valid_vote_token; - if !result { - panic!() - } - result - } - - #[doc(hidden)] - - fn accumulate_internal( - &self, - _vota_meta: VoteMetaData, - _accumulator: VoteAccumulator, - ) -> Either, Self::Certificate> - { - todo!() // TODO ED Remove this function + is_valid_signature && is_valid_vote_token } - /// Add a vote to the accumulating signature. Return The certificate if the vote - /// brings us over the threshould, Else return the accumulator. - #[allow(clippy::too_many_arguments)] - fn accumulate_vote( - &self, - encoded_key: &EncodedPublicKey, - encoded_signature: &EncodedSignature, - leaf_commitment: Commitment, - vote_data: VoteData>, - vote_token: TYPES::VoteTokenType, - view_number: TYPES::Time, - accumlator: VoteAccumulator, - relay: Option, - ) -> Either, Self::Certificate>; - // TODO ED Depending on what we do in the future with the exchanges trait, we can move the accumulator out of the `SignedCertificate` // trait. Logically, I feel it makes sense to accumulate on the certificate rather than the exchange, however. /// Accumulate vote /// Returns either the accumulate if no threshold was reached, or a `SignedCertificate` if the threshold was reached #[allow(clippy::type_complexity)] - fn accumulate_vote_2( + fn accumulate_vote( &self, accumulator: <>::Certificate as SignedCertificate< TYPES, @@ -511,7 +436,7 @@ pub trait ConsensusExchange: Send + Sync { >>::VoteAccumulator, Self::Certificate, > { - if !self.is_valid_vote_2( + if !self.is_valid_vote( &vote.get_key(), &vote.get_signature(), &vote.get_data(), @@ -742,38 +667,6 @@ impl< .make_vote_token(view_number, &self.private_key) } - fn vote_data( - &self, - commit: Commitment, - ) -> VoteData> { - VoteData::DA(commit) - } - - /// Add a vote to the accumulating signature. Return The certificate if the vote - /// brings us over the threshould, Else return the accumulator. - fn accumulate_vote( - &self, - encoded_key: &EncodedPublicKey, - encoded_signature: &EncodedSignature, - leaf_commitment: Commitment, - vote_data: VoteData>, - vote_token: TYPES::VoteTokenType, - view_number: TYPES::Time, - accumlator: VoteAccumulator, - _relay: Option, - ) -> Either, Self::Certificate> - { - let meta = VoteMetaData { - encoded_key: encoded_key.clone(), - encoded_signature: encoded_signature.clone(), - commitment: leaf_commitment, - data: vote_data, - vote_token, - view_number, - relay: None, - }; - self.accumulate_internal(meta, accumlator) - } fn membership(&self) -> &Self::Membership { &self.membership } @@ -790,7 +683,7 @@ pub trait QuorumExchangeType, ConsensusExchange { /// Create a message with a positive vote on validating or commitment proposal. - // TODO ED This returns just a general message type, it's not even bound to a proposal, and this is just a function on the QC. Make proprosal doesn't really apply to all cert types. + // TODO ED This returns just a general message type, it's not even bound to a proposal, and this is just a function on the QC. Make proprosal doesn't really apply to all cert types. fn create_yes_message>( &self, justify_qc_commitment: Commitment, @@ -829,15 +722,6 @@ pub trait QuorumExchangeType, leaf_commitment: Commitment, ) -> (EncodedPublicKey, EncodedSignature); - /// Sign a timeout vote. - /// - /// We only sign the view number, which is the minimum amount of information necessary for - /// checking that this node timed out on that view. - /// - /// This also allows for the high QC included with the vote to be spoofed in a MITM scenario, - /// but it is outside our threat model. - fn sign_timeout_vote(&self, view_number: TYPES::Time) -> (EncodedPublicKey, EncodedSignature); - /// Create a message with a negative vote on validating or commitment proposal. fn create_no_message>( &self, @@ -848,16 +732,6 @@ pub trait QuorumExchangeType, ) -> GeneralConsensusMessage where I::Exchanges: ExchangesType>; - - /// Create a message with a timeout vote on validating or commitment proposal. - fn create_timeout_message>( - &self, - justify_qc: QuorumCertificate, - current_view: TYPES::Time, - vote_token: TYPES::VoteTokenType, - ) -> GeneralConsensusMessage - where - I::Exchanges: ExchangesType>; } /// Standard implementation of [`QuroumExchangeType`] based on Hot Stuff consensus. @@ -959,22 +833,6 @@ impl< (self.public_key.to_bytes(), signature) } - /// Sign a timeout vote. - /// - /// We only sign the view number, which is the minimum amount of information necessary for - /// checking that this node timed out on that view. - /// - /// This also allows for the high QC included with the vote to be spoofed in a MITM scenario, - /// but it is outside our threat model. - fn sign_timeout_vote(&self, view_number: TYPES::Time) -> (EncodedPublicKey, EncodedSignature) { - let signature = TYPES::SignatureKey::sign( - &self.private_key, - VoteData::Timeout(view_number.commit()) - .get_commit() - .as_ref(), - ); - (self.public_key.to_bytes(), signature) - } /// Create a message with a negative vote on validating or commitment proposal. fn create_no_message>( &self, @@ -996,26 +854,6 @@ impl< vote_data: VoteData::No(leaf_commitment), })) } - - /// Create a message with a timeout vote on validating or commitment proposal. - fn create_timeout_message>( - &self, - high_qc: QuorumCertificate, - current_view: TYPES::Time, - vote_token: TYPES::VoteTokenType, - ) -> GeneralConsensusMessage - where - I::Exchanges: ExchangesType>, - { - let signature = self.sign_timeout_vote(current_view); - GeneralConsensusMessage::::Vote(QuorumVote::Timeout(TimeoutVote { - high_qc, - signature, - current_view, - vote_token, - vote_data: VoteData::Timeout(current_view.commit()), - })) - } } impl< @@ -1059,37 +897,6 @@ impl< &self.network } - fn vote_data( - &self, - commit: Commitment, - ) -> VoteData> { - VoteData::Yes(commit) - } - - /// Add a vote to the accumulating signature. Return The certificate if the vote - /// brings us over the threshould, Else return the accumulator. - fn accumulate_vote( - &self, - encoded_key: &EncodedPublicKey, - encoded_signature: &EncodedSignature, - leaf_commitment: Commitment, - vote_data: VoteData>, - vote_token: TYPES::VoteTokenType, - view_number: TYPES::Time, - accumlator: VoteAccumulator, - _relay: Option, - ) -> Either, Self::Certificate> { - let meta = VoteMetaData { - encoded_key: encoded_key.clone(), - encoded_signature: encoded_signature.clone(), - commitment: leaf_commitment, - data: vote_data, - vote_token, - view_number, - relay: None, - }; - self.accumulate_internal(meta, accumlator) - } fn membership(&self) -> &Self::Membership { &self.membership } @@ -1421,37 +1228,6 @@ impl< &self.network } - fn vote_data( - &self, - _commit: Commitment, - ) -> VoteData> { - unimplemented!() - } - - fn accumulate_vote( - &self, - encoded_key: &EncodedPublicKey, - encoded_signature: &EncodedSignature, - leaf_commitment: Commitment>, - vote_data: VoteData>, - vote_token: TYPES::VoteTokenType, - view_number: TYPES::Time, - accumlator: VoteAccumulator, TYPES>, - relay: Option, - ) -> Either, TYPES>, Self::Certificate> - { - let meta = VoteMetaData { - encoded_key: encoded_key.clone(), - encoded_signature: encoded_signature.clone(), - commitment: leaf_commitment, - data: vote_data, - vote_token, - view_number, - relay, - }; - self.accumulate_internal(meta, accumlator) - } - fn membership(&self) -> &Self::Membership { &self.membership } @@ -1499,7 +1275,9 @@ impl< { } +/// Trait defining functiosn for a `TimeoutExchange` pub trait TimeoutExchangeType: ConsensusExchange { + /// Create and sign a timeout message fn create_timeout_message>( &self, view: TYPES::Time, @@ -1509,19 +1287,17 @@ pub trait TimeoutExchangeType: ConsensusExchange I::Exchanges: ExchangesType>, { let signature = TYPES::SignatureKey::sign( - &self.private_key(), - &VoteData::>::Timeout(view.commit()) - .get_commit().as_ref() - + self.private_key(), + VoteData::>::Timeout(view.commit()) + .get_commit() + .as_ref(), ); - GeneralConsensusMessage::::TimeoutVote(TimeoutVote2 { + GeneralConsensusMessage::::TimeoutVote(TimeoutVote { signature: (self.public_key().to_bytes(), signature), current_view: view, - vote_token + vote_token, }) - - } } @@ -1545,7 +1321,7 @@ impl< > ConsensusExchange for TimeoutExchange { type Proposal = PROPOSAL; - type Vote = TimeoutVote2; + type Vote = TimeoutVote; type Certificate = TimeoutCertificate; type Membership = MEMBERSHIP; type Networking = NETWORK; @@ -1575,10 +1351,6 @@ impl< &self.network } - fn vote_data(&self, _commit: Commitment) -> VoteData> { - unimplemented!() - } - fn membership(&self) -> &Self::Membership { &self.membership } @@ -1588,23 +1360,6 @@ impl< fn private_key(&self) -> &<::SignatureKey as SignatureKey>::PrivateKey { &self.private_key } - - fn accumulate_vote( - &self, - _encoded_key: &EncodedPublicKey, - _encoded_signature: &EncodedSignature, - _leaf_commitment: Commitment, - _vote_data: VoteData>, - _vote_token: ::VoteTokenType, - _view_number: ::Time, - _accumlator: VoteAccumulator<::VoteTokenType, Self::Commitment, TYPES>, - _relay: Option, - ) -> Either< - VoteAccumulator<::VoteTokenType, Self::Commitment, TYPES>, - Self::Certificate, - > { - todo!() - } } /// Testable implementation of a [`Membership`]. Will expose a method to generate a vote token used for testing. diff --git a/crates/types/src/traits/node_implementation.rs b/crates/types/src/traits/node_implementation.rs index f7aafe8615..5e3d30f2f4 100644 --- a/crates/types/src/traits/node_implementation.rs +++ b/crates/types/src/traits/node_implementation.rs @@ -151,9 +151,10 @@ pub trait ExchangesType, MESSA /// Protocol for exchanging data availability proposals and votes. type CommitteeExchange: CommitteeExchangeType + Clone + Debug; - /// Get the committee exchange. + /// Get the committee exchange fn committee_exchange(&self) -> &Self::CommitteeExchange; + /// Get the timeout exchange fn timeout_exchange(&self) -> &Self::TimeoutExchange; /// Protocol for exchanging quorum proposals and votes. @@ -162,6 +163,7 @@ pub trait ExchangesType, MESSA /// Protocol for exchanging view sync proposals and votes. type ViewSyncExchange: ViewSyncExchangeType + Clone + Debug; + /// Protocol for receiving timeout votes type TimeoutExchange: TimeoutExchangeType + Clone + Debug; /// Election configurations for exchanges @@ -234,10 +236,15 @@ pub struct SequencingExchanges< /// Committee exchange. committee_exchange: COMMITTEEEXCHANGE, - // TODO ED Make this not public - pub timeout_exchange: TimeoutExchange as ExchangesType, MESSAGE>>::QuorumExchange as ConsensusExchange>::Proposal, < as ExchangesType, MESSAGE>>::QuorumExchange as ConsensusExchange>::Membership, >::Networking, MESSAGE>, + /// Timeout exchange + // This type can be simplified once we rework the exchanges trait + // It is here to avoid needing to instantiate it where all the other exchanges are instantiated + // https://github.com/EspressoSystems/HotShot/issues/1799 + #[allow(clippy::type_complexity)] + + pub timeout_exchange: TimeoutExchange as ExchangesType, MESSAGE>>::QuorumExchange as ConsensusExchange>::Proposal, < as ExchangesType, MESSAGE>>::QuorumExchange as ConsensusExchange>::Membership, >::Networking, MESSAGE>, - /// Phantom data. + /// Phantom data _phantom: PhantomData<(TYPES, MESSAGE)>, } @@ -255,6 +262,7 @@ where type CommitteeExchange = COMMITTEEEXCHANGE; type QuorumExchange = QUORUMEXCHANGE; type ViewSyncExchange = VIEWSYNCEXCHANGE; + #[allow(clippy::type_complexity)] type TimeoutExchange = TimeoutExchange as ExchangesType, MESSAGE>>::QuorumExchange as ConsensusExchange>::Proposal, < as ExchangesType, MESSAGE>>::QuorumExchange as ConsensusExchange>::Membership, >::Networking, MESSAGE>; type ElectionConfigs = (TYPES::ElectionConfigType, TYPES::ElectionConfigType); @@ -286,6 +294,7 @@ where entry.clone(), sk.clone(), ); + #[allow(clippy::type_complexity)] let timeout_exchange: TimeoutExchange as ExchangesType, MESSAGE>>::QuorumExchange as ConsensusExchange>::Proposal, < as ExchangesType, MESSAGE>>::QuorumExchange as ConsensusExchange>::Membership, >::Networking, MESSAGE> = TimeoutExchange::create( entries.clone(), configs.0.clone(), @@ -349,6 +358,7 @@ pub type SequencingQuorumEx = Message, >>::QuorumExchange; +/// Alias for `TimeoutExchange` type pub type SequencingTimeoutEx = <>::Exchanges as ExchangesType< TYPES, diff --git a/crates/types/src/vote.rs b/crates/types/src/vote.rs index 8ba9f060d7..33a0c9eccb 100644 --- a/crates/types/src/vote.rs +++ b/crates/types/src/vote.rs @@ -26,7 +26,7 @@ use std::{ marker::PhantomData, num::NonZeroU64, }; -use tracing::{error, warn}; +use tracing::error; /// The vote sent by consensus messages. pub trait VoteType Deserialize<'a> + Serialize + Clone>: @@ -80,26 +80,10 @@ pub struct YesOrNoVote> { pub vote_data: VoteData>, } -/// A timeout vote. -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)] -#[serde(bound(deserialize = ""))] -pub struct TimeoutVote> { - /// The highest valid QC this node knows about - pub high_qc: QuorumCertificate, - /// The signature share associated with this vote - pub signature: (EncodedPublicKey, EncodedSignature), - /// The view this vote was cast for - pub current_view: TYPES::Time, - /// The vote token generated by this replica - pub vote_token: TYPES::VoteTokenType, - /// The vote data this vote is signed over - pub vote_data: VoteData>, -} - /// A timeout vote #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)] #[serde(bound(deserialize = ""))] -pub struct TimeoutVote2 { +pub struct TimeoutVote { /// The signature share associated with this vote pub signature: (EncodedPublicKey, EncodedSignature), /// The view this vote was cast for @@ -108,7 +92,7 @@ pub struct TimeoutVote2 { pub vote_token: TYPES::VoteTokenType, } -impl VoteType> for TimeoutVote2 { +impl VoteType> for TimeoutVote { fn get_view(&self) -> ::Time { self.current_view } @@ -133,7 +117,7 @@ impl VoteType> for TimeoutVote2< /// A timeout vote // #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)] // #[serde(bound(deserialize = ""))] -// pub struct TimeoutVote2 { +// pub struct TimeoutVote { // /// The signature share associated with this vote // pub signature: (EncodedPublicKey, EncodedSignature), // /// The view this vote was cast for @@ -142,7 +126,7 @@ impl VoteType> for TimeoutVote2< // pub vote_token: TYPES::VoteTokenType, // } -// impl VoteType for TimeoutVote2 { +// impl VoteType for TimeoutVote { // fn get_view(&self) -> ::Time { // self.current_view // } @@ -262,8 +246,6 @@ pub enum QuorumVote> { Yes(YesOrNoVote), /// Negative vote. No(YesOrNoVote), - /// Timeout vote. - Timeout(TimeoutVote), } impl VoteType> for DAVote { @@ -299,7 +281,6 @@ impl> VoteType TYPES::Time { match self { QuorumVote::Yes(v) | QuorumVote::No(v) => v.current_view, - QuorumVote::Timeout(v) => v.current_view, } } @@ -312,13 +293,11 @@ impl> VoteType VoteData> { match self { QuorumVote::Yes(v) | QuorumVote::No(v) => v.vote_data.clone(), - QuorumVote::Timeout(_) => unimplemented!(), } } fn get_vote_token(&self) -> ::VoteTokenType { match self { QuorumVote::Yes(v) | QuorumVote::No(v) => v.vote_token.clone(), - QuorumVote::Timeout(_) => unimplemented!(), } } } @@ -329,7 +308,6 @@ impl> QuorumVote pub fn signature(&self) -> EncodedSignature { match &self { Self::Yes(vote) | Self::No(vote) => vote.signature.1.clone(), - Self::Timeout(vote) => vote.signature.1.clone(), } } /// Get the signature key. @@ -339,7 +317,6 @@ impl> QuorumVote pub fn signature_key(&self) -> TYPES::SignatureKey { let encoded = match &self { Self::Yes(vote) | Self::No(vote) => vote.signature.0.clone(), - Self::Timeout(vote) => vote.signature.0.clone(), }; ::from_bytes(&encoded).unwrap() } @@ -377,17 +354,8 @@ impl VoteType>> for ViewS } } -/// The aggreation of votes, implemented by `VoteAccumulator`. -pub trait Accumulator: Sized { - /// Accumate the `val` to the current state. - /// - /// If a threshold is reached, returns `U` (e.g., a certificate). Else, returns `Self` and - /// continues accumulating items. - fn append(self, val: T) -> Either; -} - /// Accumulator trait used to accumulate votes into an `AssembledSignature` -pub trait Accumulator2< +pub trait Accumulator< TYPES: NodeType, COMMITMENT: for<'a> Deserialize<'a> + Serialize + Clone, VOTE: VoteType, @@ -406,6 +374,7 @@ pub trait Accumulator2< // TODO Make a default accumulator // https://github.com/EspressoSystems/HotShot/issues/1797 +/// Accumulator for `TimeoutVote`s pub struct TimeoutVoteAccumulator< TYPES: NodeType, COMMITMENT: Serialize + Clone + for<'a> Deserialize<'a>, @@ -427,7 +396,7 @@ impl< TYPES: NodeType, COMMITMENT: for<'a> Deserialize<'a> + Serialize + Clone + Copy + PartialEq + Eq + Hash, VOTE: VoteType, - > Accumulator2 for DAVoteAccumulator + > Accumulator for DAVoteAccumulator { fn append( mut self, @@ -499,7 +468,7 @@ impl< TYPES: NodeType, COMMITMENT: for<'a> Deserialize<'a> + Serialize + Clone + Copy + PartialEq + Eq + Hash, VOTE: VoteType, - > Accumulator2 for TimeoutVoteAccumulator + > Accumulator for TimeoutVoteAccumulator { fn append( mut self, @@ -614,7 +583,7 @@ impl< TYPES: NodeType, COMMITMENT: for<'a> Deserialize<'a> + Serialize + Clone + Copy + PartialEq + Eq + Hash, VOTE: VoteType, - > Accumulator2 for QuorumVoteAccumulator + > Accumulator for QuorumVoteAccumulator { fn append( mut self, @@ -744,7 +713,7 @@ impl< TYPES: NodeType, COMMITMENT: for<'a> Deserialize<'a> + Serialize + Clone + Copy + PartialEq + Eq + Hash, VOTE: VoteType, - > Accumulator2 for ViewSyncVoteAccumulator + > Accumulator for ViewSyncVoteAccumulator { #[allow(clippy::too_many_lines)] fn append( @@ -889,32 +858,6 @@ impl< } } -/// Placeholder accumulator; will be replaced by accumulator for each certificate type -pub struct AccumulatorPlaceholder< - TYPES: NodeType, - COMMITMENT: for<'a> Deserialize<'a> + Serialize + Clone, - VOTE: VoteType, -> { - /// Phantom data to make compiler happy - pub phantom: PhantomData<(TYPES, VOTE, COMMITMENT)>, -} - -impl< - TYPES: NodeType, - COMMITMENT: for<'a> Deserialize<'a> + Serialize + Clone, - VOTE: VoteType, - > Accumulator2 for AccumulatorPlaceholder -{ - fn append( - self, - _vote: VOTE, - _vote_node_id: usize, - _stake_table_entries: Vec<::StakeTableEntry>, - ) -> Either> { - either::Left(self) - } -} - /// Mapping of commitments to vote tokens by key. type VoteMap = HashMap< COMMITMENT, @@ -923,221 +866,3 @@ type VoteMap = HashMap< BTreeMap, TOKEN)>, ), >; - -/// Describe the process of collecting signatures on block or leaf commitment, to form a DAC or QC, -/// respectively. -/// -/// TODO GG used only in election.rs; move this to there and make it private? -pub struct VoteAccumulator< - TOKEN, - COMMITMENT: Serialize + for<'a> Deserialize<'a> + Clone, - TYPES: NodeType, -> { - /// Map of all signatures accumlated so far - pub total_vote_outcomes: VoteMap, - /// Map of all da signatures accumlated so far - pub da_vote_outcomes: VoteMap, - /// Map of all yes signatures accumlated so far - pub yes_vote_outcomes: VoteMap, - /// Map of all no signatures accumlated so far - pub no_vote_outcomes: VoteMap, - /// Map of all view sync precommit votes accumulated thus far - pub viewsync_precommit_vote_outcomes: VoteMap, - /// Map of all view sync commit votes accumulated thus far - pub viewsync_commit_vote_outcomes: VoteMap, - /// Map of all view sync finalize votes accumulated thus far - pub viewsync_finalize_vote_outcomes: VoteMap, - /// A quorum's worth of stake, generall 2f + 1 - pub success_threshold: NonZeroU64, - /// Enough stake to know that we cannot possibly get a quorum, generally f + 1 - pub failure_threshold: NonZeroU64, - /// A list of valid signatures for certificate aggregation - pub sig_lists: Vec<::PureAssembledSignatureType>, - /// A bitvec to indicate which node is active and send out a valid signature for certificate aggregation, this automatically do uniqueness check - pub signers: BitVec, -} - -impl - Accumulator< - ( - Commitment, - ( - EncodedPublicKey, - ( - EncodedSignature, - Vec<::StakeTableEntry>, - usize, - VoteData>, - TOKEN, - ), - ), - ), - AssembledSignature, - > for VoteAccumulator, TYPES> -where - TOKEN: Clone + VoteToken, -{ - #![allow(clippy::too_many_lines)] - fn append( - mut self, - val: ( - Commitment, - ( - EncodedPublicKey, - ( - EncodedSignature, - Vec<::StakeTableEntry>, - usize, - VoteData>, - TOKEN, - ), - ), - ), - ) -> Either> { - let (commitment, (key, (sig, entries, node_id, vote_data, token))) = val; - - // Desereialize the sig so that it can be assembeld into a QC - let original_signature: ::PureAssembledSignatureType = - bincode_opts() - .deserialize(&sig.0) - .expect("Deserialization on the signature shouldn't be able to fail."); - - let (total_stake_casted, total_vote_map) = self - .total_vote_outcomes - .entry(commitment) - .or_insert_with(|| (0, BTreeMap::new())); - - // Check for duplicate vote - if total_vote_map.contains_key(&key) { - return Either::Left(self); - } - let (da_stake_casted, da_vote_map) = self - .da_vote_outcomes - .entry(commitment) - .or_insert_with(|| (0, BTreeMap::new())); - - let (yes_stake_casted, yes_vote_map) = self - .yes_vote_outcomes - .entry(commitment) - .or_insert_with(|| (0, BTreeMap::new())); - - let (no_stake_casted, no_vote_map) = self - .no_vote_outcomes - .entry(commitment) - .or_insert_with(|| (0, BTreeMap::new())); - - let (viewsync_precommit_stake_casted, viewsync_precommit_vote_map) = self - .viewsync_precommit_vote_outcomes - .entry(commitment) - .or_insert_with(|| (0, BTreeMap::new())); - - let (viewsync_commit_stake_casted, viewsync_commit_vote_map) = self - .viewsync_commit_vote_outcomes - .entry(commitment) - .or_insert_with(|| (0, BTreeMap::new())); - - let (viewsync_finalize_stake_casted, viewsync_finalize_vote_map) = self - .viewsync_finalize_vote_outcomes - .entry(commitment) - .or_insert_with(|| (0, BTreeMap::new())); - - // Accumulate the stake for each leaf commitment rather than the total - // stake of all votes, in case they correspond to inconsistent - // commitments. - - // update the active_keys and sig_lists - if self.signers.get(node_id).as_deref() == Some(&true) { - error!("node id already in signers"); - return Either::Left(self); - } - self.signers.set(node_id, true); - self.sig_lists.push(original_signature); - - *total_stake_casted += u64::from(token.vote_count()); - total_vote_map.insert(key.clone(), (sig.clone(), vote_data.clone(), token.clone())); - - match vote_data { - VoteData::DA(_) => { - *da_stake_casted += u64::from(token.vote_count()); - da_vote_map.insert(key, (sig, vote_data, token)); - } - VoteData::Yes(_) => { - *yes_stake_casted += u64::from(token.vote_count()); - yes_vote_map.insert(key, (sig, vote_data, token)); - } - VoteData::No(_) => { - *no_stake_casted += u64::from(token.vote_count()); - no_vote_map.insert(key, (sig, vote_data, token)); - } - VoteData::ViewSyncPreCommit(_) => { - *viewsync_precommit_stake_casted += u64::from(token.vote_count()); - viewsync_precommit_vote_map.insert(key, (sig, vote_data, token)); - } - VoteData::ViewSyncCommit(_) => { - *viewsync_commit_stake_casted += u64::from(token.vote_count()); - viewsync_commit_vote_map.insert(key, (sig, vote_data, token)); - } - VoteData::ViewSyncFinalize(_) => { - *viewsync_finalize_stake_casted += u64::from(token.vote_count()); - viewsync_finalize_vote_map.insert(key, (sig, vote_data, token)); - } - VoteData::Timeout(_) => { - unimplemented!() - } - } - - // This is a messy way of accounting for the different vote types, but we will be replacing this code very soon - if *total_stake_casted >= u64::from(self.success_threshold) { - // Do assemble for QC here - let real_qc_pp = ::get_public_parameter( - entries.clone(), - U256::from(self.success_threshold.get()), - ); - - let real_qc_sig = ::assemble( - &real_qc_pp, - self.signers.as_bitslice(), - &self.sig_lists[..], - ); - - if *yes_stake_casted >= u64::from(self.success_threshold) { - self.yes_vote_outcomes.remove(&commitment); - return Either::Right(AssembledSignature::Yes(real_qc_sig)); - } else if *no_stake_casted >= u64::from(self.failure_threshold) { - self.total_vote_outcomes.remove(&commitment); - return Either::Right(AssembledSignature::No(real_qc_sig)); - } else if *da_stake_casted >= u64::from(self.success_threshold) { - self.da_vote_outcomes.remove(&commitment); - return Either::Right(AssembledSignature::DA(real_qc_sig)); - } else if *viewsync_commit_stake_casted >= u64::from(self.success_threshold) { - self.viewsync_commit_vote_outcomes - .remove(&commitment) - .unwrap(); - return Either::Right(AssembledSignature::ViewSyncCommit(real_qc_sig)); - } else if *viewsync_finalize_stake_casted >= u64::from(self.success_threshold) { - self.viewsync_finalize_vote_outcomes - .remove(&commitment) - .unwrap(); - return Either::Right(AssembledSignature::ViewSyncFinalize(real_qc_sig)); - } - } - if *viewsync_precommit_stake_casted >= u64::from(self.failure_threshold) { - let real_qc_pp = ::get_public_parameter( - entries, - U256::from(self.failure_threshold.get()), - ); - - let real_qc_sig = ::assemble( - &real_qc_pp, - self.signers.as_bitslice(), - &self.sig_lists[..], - ); - - self.viewsync_precommit_vote_outcomes - .remove(&commitment) - .unwrap(); - return Either::Right(AssembledSignature::ViewSyncPreCommit(real_qc_sig)); - } - Either::Left(self) - } -} From 3fe3d226ac60d0662be9109b3a2101a8689dceee Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Thu, 21 Sep 2023 21:48:30 -0400 Subject: [PATCH 26/56] rename functions --- crates/types/src/certificate.rs | 20 ++++---------------- crates/types/src/traits/election.rs | 7 ++----- 2 files changed, 6 insertions(+), 21 deletions(-) diff --git a/crates/types/src/certificate.rs b/crates/types/src/certificate.rs index c540a349fc..7aaa28640c 100644 --- a/crates/types/src/certificate.rs +++ b/crates/types/src/certificate.rs @@ -91,10 +91,7 @@ impl type VoteAccumulator = TimeoutVoteAccumulator, Self::Vote>; - fn from_signatures_and_commitment( - signatures: AssembledSignature, - vote: Self::Vote, - ) -> Self { + fn create_certificateure(signatures: AssembledSignature, vote: Self::Vote) -> Self { TimeoutCertificate { view_number: vote.get_view(), signatures, @@ -189,10 +186,7 @@ impl> type Vote = QuorumVote; type VoteAccumulator = QuorumVoteAccumulator, Self::Vote>; - fn from_signatures_and_commitment( - signatures: AssembledSignature, - vote: Self::Vote, - ) -> Self { + fn create_certificateure(signatures: AssembledSignature, vote: Self::Vote) -> Self { let leaf_commitment = match vote.clone() { QuorumVote::Yes(vote_internal) | QuorumVote::No(vote_internal) => { vote_internal.leaf_commitment @@ -266,10 +260,7 @@ impl type Vote = DAVote; type VoteAccumulator = DAVoteAccumulator, Self::Vote>; - fn from_signatures_and_commitment( - signatures: AssembledSignature, - vote: Self::Vote, - ) -> Self { + fn create_certificateure(signatures: AssembledSignature, vote: Self::Vote) -> Self { DACertificate { view_number: vote.get_view(), signatures, @@ -349,10 +340,7 @@ impl type VoteAccumulator = ViewSyncVoteAccumulator>, Self::Vote>; /// Build a QC from the threshold signature and commitment - fn from_signatures_and_commitment( - signatures: AssembledSignature, - vote: Self::Vote, - ) -> Self { + fn create_certificateure(signatures: AssembledSignature, vote: Self::Vote) -> Self { let certificate_internal = ViewSyncCertificateInternal { round: vote.get_view(), relay: vote.relay(), diff --git a/crates/types/src/traits/election.rs b/crates/types/src/traits/election.rs index db9c46f9ea..a5f98d47fc 100644 --- a/crates/types/src/traits/election.rs +++ b/crates/types/src/traits/election.rs @@ -173,10 +173,7 @@ where // TODO ED Rename this function and rework this function parameters // Assumes last vote was valid since it caused a QC to form. // Removes need for relay on other cert specific fields - fn from_signatures_and_commitment( - signatures: AssembledSignature, - vote: Self::Vote, - ) -> Self; + fn create_certificateure(signatures: AssembledSignature, vote: Self::Vote) -> Self; /// Get the view number. fn view_number(&self) -> TIME; @@ -465,7 +462,7 @@ pub trait ConsensusExchange: Send + Sync { Either::Left(accumulator) => Either::Left(accumulator), Either::Right(signatures) => { // TODO ED Update this function to just take in the signatures and most recent vote - Either::Right(Self::Certificate::from_signatures_and_commitment( + Either::Right(Self::Certificate::create_certificateure( signatures, vote.clone(), )) From 7767f5e0f202b6df97018b41e82097f07836aaad Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Thu, 21 Sep 2023 21:50:02 -0400 Subject: [PATCH 27/56] Lint imports --- crates/hotshot/examples/infra/modDA.rs | 5 ++--- crates/hotshot/src/lib.rs | 15 ++++----------- crates/hotshot/src/tasks/mod.rs | 10 ++++------ crates/orchestrator/src/config.rs | 2 +- crates/task-impls/src/consensus.rs | 15 ++++++--------- crates/task-impls/src/da.rs | 9 +++------ crates/task-impls/src/transactions.rs | 7 +++---- crates/testing/src/spinning_task.rs | 4 +--- crates/testing/src/test_runner.rs | 9 +++++---- crates/testing/tests/timeout.rs | 2 +- crates/types/src/certificate.rs | 12 ++++-------- crates/types/src/consensus.rs | 8 +++++--- crates/types/src/traits/election.rs | 3 +-- 13 files changed, 40 insertions(+), 61 deletions(-) diff --git a/crates/hotshot/examples/infra/modDA.rs b/crates/hotshot/examples/infra/modDA.rs index 95cffd94ed..e6081e2da1 100644 --- a/crates/hotshot/examples/infra/modDA.rs +++ b/crates/hotshot/examples/infra/modDA.rs @@ -20,7 +20,6 @@ use hotshot_orchestrator::{ config::{NetworkConfig, WebServerConfig}, }; use hotshot_task::task::FilterEvent; -use hotshot_types::HotShotConfig; use hotshot_types::{ certificate::ViewSyncCertificate, data::{QuorumProposal, SequencingLeaf, TestableLeaf}, @@ -37,6 +36,7 @@ use hotshot_types::{ }, state::{ConsensusTime, TestableBlock, TestableState}, }, + HotShotConfig, }; use libp2p_identity::{ ed25519::{self, SecretKey}, @@ -46,8 +46,7 @@ use libp2p_networking::{ network::{MeshParams, NetworkNodeConfigBuilder, NetworkNodeType}, reexport::Multiaddr, }; -use std::{collections::BTreeSet, sync::Arc}; -use std::{num::NonZeroUsize, str::FromStr}; +use std::{collections::BTreeSet, num::NonZeroUsize, str::FromStr, sync::Arc}; // use libp2p::{ // identity::{ // ed25519::{Keypair as EdKeypair, SecretKey}, diff --git a/crates/hotshot/src/lib.rs b/crates/hotshot/src/lib.rs index 19fa2d9cde..320dbd892f 100644 --- a/crates/hotshot/src/lib.rs +++ b/crates/hotshot/src/lib.rs @@ -54,8 +54,9 @@ use hotshot_task::{ task_launcher::TaskRunner, }; use hotshot_task_impls::{events::SequencingHotShotEvent, network::NetworkTaskKind}; -use hotshot_types::certificate::TimeoutCertificate; -use hotshot_types::traits::node_implementation::SequencingTimeoutEx; +use hotshot_types::{ + certificate::TimeoutCertificate, traits::node_implementation::SequencingTimeoutEx, +}; use hotshot_types::{ certificate::{DACertificate, ViewSyncCertificate}, @@ -244,16 +245,8 @@ impl> SystemContext { Ok(Self { inner }) } - /// "Starts" consensus by sending a `ViewChange` event + /// "Starts" consensus by sending a `QCFormed` event pub async fn start_consensus(&self) { - // self.inner - // .internal_event_stream - // .publish(SequencingHotShotEvent::ViewChange(TYPES::Time::new(1))) - // .await; - - // ED This isn't ideal... - // async_sleep(Duration::new(1, 0)).await; - self.inner .internal_event_stream .publish(SequencingHotShotEvent::QCFormed(either::Left( diff --git a/crates/hotshot/src/tasks/mod.rs b/crates/hotshot/src/tasks/mod.rs index a7aba740ab..0ebfb22535 100644 --- a/crates/hotshot/src/tasks/mod.rs +++ b/crates/hotshot/src/tasks/mod.rs @@ -26,19 +26,17 @@ use hotshot_task_impls::{ transactions::{TransactionTaskState, TransactionsTaskTypes}, view_sync::{ViewSyncTaskState, ViewSyncTaskStateTypes}, }; -use hotshot_types::certificate::TimeoutCertificate; -use hotshot_types::traits::network::ConsensusIntentEvent; -use hotshot_types::traits::node_implementation::SequencingTimeoutEx; use hotshot_types::{ - certificate::ViewSyncCertificate, + certificate::{TimeoutCertificate, ViewSyncCertificate}, data::{ProposalType, QuorumProposal, SequencingLeaf}, event::Event, message::{Message, Messages, SequencingMessage}, traits::{ election::{ConsensusExchange, Membership}, - network::{CommunicationChannel, TransmitType}, + network::{CommunicationChannel, ConsensusIntentEvent, TransmitType}, node_implementation::{ - CommitteeEx, ExchangesType, NodeImplementation, NodeType, ViewSyncEx, + CommitteeEx, ExchangesType, NodeImplementation, NodeType, SequencingTimeoutEx, + ViewSyncEx, }, state::ConsensusTime, BlockPayload, diff --git a/crates/orchestrator/src/config.rs b/crates/orchestrator/src/config.rs index 05a38b615c..c233ea33ee 100644 --- a/crates/orchestrator/src/config.rs +++ b/crates/orchestrator/src/config.rs @@ -1,6 +1,6 @@ use hotshot_types::{ExecutionType, HotShotConfig}; -use std::marker::PhantomData; use std::{ + marker::PhantomData, net::{IpAddr, Ipv4Addr, SocketAddr}, num::NonZeroUsize, time::Duration, diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index 7c62af9500..9d6261e25e 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -15,28 +15,25 @@ use hotshot_task::{ task::{FilterEvent, HandleEvent, HotShotTaskCompleted, HotShotTaskTypes, TS}, task_impls::{HSTWithEvent, TaskBuilder}, }; -use hotshot_types::certificate::TimeoutCertificate; -use hotshot_types::traits::election::TimeoutExchangeType; -use hotshot_types::traits::node_implementation::SequencingTimeoutEx; -use hotshot_types::vote::QuorumVoteAccumulator; -use hotshot_types::vote::TimeoutVoteAccumulator; use hotshot_types::{ - certificate::{DACertificate, QuorumCertificate}, + certificate::{DACertificate, QuorumCertificate, TimeoutCertificate}, consensus::{Consensus, View}, data::{LeafType, ProposalType, QuorumProposal, SequencingLeaf}, event::{Event, EventType}, message::{GeneralConsensusMessage, Message, Proposal, SequencingMessage}, traits::{ consensus_api::SequencingConsensusApi, - election::{ConsensusExchange, QuorumExchangeType, SignedCertificate}, + election::{ConsensusExchange, QuorumExchangeType, SignedCertificate, TimeoutExchangeType}, network::{CommunicationChannel, ConsensusIntentEvent}, - node_implementation::{CommitteeEx, NodeImplementation, NodeType, SequencingQuorumEx}, + node_implementation::{ + CommitteeEx, NodeImplementation, NodeType, SequencingQuorumEx, SequencingTimeoutEx, + }, signature_key::SignatureKey, state::ConsensusTime, BlockPayload, }, utils::{Terminator, ViewInner}, - vote::{QuorumVote, VoteType}, + vote::{QuorumVote, QuorumVoteAccumulator, TimeoutVoteAccumulator, VoteType}, }; use tracing::warn; diff --git a/crates/task-impls/src/da.rs b/crates/task-impls/src/da.rs index 96a4d18a48..2c80b9eaff 100644 --- a/crates/task-impls/src/da.rs +++ b/crates/task-impls/src/da.rs @@ -12,9 +12,6 @@ use hotshot_task::{ task::{FilterEvent, HandleEvent, HotShotTaskCompleted, HotShotTaskTypes, TS}, task_impls::{HSTWithEvent, TaskBuilder}, }; -use hotshot_types::traits::election::SignedCertificate; -use hotshot_types::vote::DAVoteAccumulator; -use hotshot_types::vote::VoteType; use hotshot_types::{ certificate::DACertificate, consensus::{Consensus, View}, @@ -22,7 +19,7 @@ use hotshot_types::{ message::{Message, Proposal, SequencingMessage}, traits::{ consensus_api::SequencingConsensusApi, - election::{CommitteeExchangeType, ConsensusExchange, Membership}, + election::{CommitteeExchangeType, ConsensusExchange, Membership, SignedCertificate}, network::{CommunicationChannel, ConsensusIntentEvent}, node_implementation::{CommitteeEx, NodeImplementation, NodeType}, signature_key::SignatureKey, @@ -30,11 +27,11 @@ use hotshot_types::{ BlockPayload, }, utils::ViewInner, + vote::{DAVoteAccumulator, VoteType}, }; use snafu::Snafu; -use std::marker::PhantomData; -use std::{collections::HashMap, sync::Arc}; +use std::{collections::HashMap, marker::PhantomData, sync::Arc}; use tracing::{debug, error, instrument, warn}; #[derive(Snafu, Debug)] diff --git a/crates/task-impls/src/transactions.rs b/crates/task-impls/src/transactions.rs index b41604376d..e75011fb31 100644 --- a/crates/task-impls/src/transactions.rs +++ b/crates/task-impls/src/transactions.rs @@ -1,12 +1,11 @@ use crate::events::SequencingHotShotEvent; -use async_compatibility_layer::async_primitives::subscribable_rwlock::SubscribableRwLock; use async_compatibility_layer::{ - art::async_timeout, async_primitives::subscribable_rwlock::ReadView, + art::async_timeout, + async_primitives::subscribable_rwlock::{ReadView, SubscribableRwLock}, }; use async_lock::RwLock; use bincode::config::Options; -use commit::Commitment; -use commit::Committable; +use commit::{Commitment, Committable}; use either::{Either, Left, Right}; use hotshot_task::{ event_stream::{ChannelStream, EventStream}, diff --git a/crates/testing/src/spinning_task.rs b/crates/testing/src/spinning_task.rs index a9d9e5d586..1144dbd2d8 100644 --- a/crates/testing/src/spinning_task.rs +++ b/crates/testing/src/spinning_task.rs @@ -7,9 +7,7 @@ use std::{ use crate::{test_launcher::TaskGenerator, test_runner::Node, GlobalTestEvent}; use async_compatibility_layer::art::async_sleep; use futures::FutureExt; -use hotshot::traits::TestableNodeImplementation; -use hotshot::HotShotType; -use hotshot::SystemContext; +use hotshot::{traits::TestableNodeImplementation, HotShotType, SystemContext}; use hotshot_task::{ boxed_sync, event_stream::ChannelStream, diff --git a/crates/testing/src/test_runner.rs b/crates/testing/src/test_runner.rs index 056f0f8a26..036a88df32 100644 --- a/crates/testing/src/test_runner.rs +++ b/crates/testing/src/test_runner.rs @@ -3,8 +3,10 @@ use super::{ overall_safety_task::{OverallSafetyTask, RoundCtx}, txn_task::TxnTask, }; -use crate::spinning_task::UpDown; -use crate::test_launcher::{Networks, TestLauncher}; +use crate::{ + spinning_task::UpDown, + test_launcher::{Networks, TestLauncher}, +}; use hotshot::types::SystemContextHandle; use hotshot::{traits::TestableNodeImplementation, HotShotInitializer, HotShotType, SystemContext}; @@ -22,8 +24,7 @@ use hotshot_types::{ }, HotShotConfig, }; -use std::collections::HashMap; -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; #[allow(deprecated)] use tracing::info; diff --git a/crates/testing/tests/timeout.rs b/crates/testing/tests/timeout.rs index 64ea239321..6988c78a28 100644 --- a/crates/testing/tests/timeout.rs +++ b/crates/testing/tests/timeout.rs @@ -9,10 +9,10 @@ async fn test_timeout() { use hotshot_testing::node_types::SequencingWebImpl; - use hotshot_testing::overall_safety_task::OverallSafetyPropertiesDescription; use hotshot_testing::{ completion_task::{CompletionTaskDescription, TimeBasedCompletionTaskDescription}, node_types::SequencingTestTypes, + overall_safety_task::OverallSafetyPropertiesDescription, spinning_task::{ChangeNode, SpinningTaskDescription, UpDown}, test_builder::{TestMetadata, TimingData}, }; diff --git a/crates/types/src/certificate.rs b/crates/types/src/certificate.rs index 7aaa28640c..b52720ad1a 100644 --- a/crates/types/src/certificate.rs +++ b/crates/types/src/certificate.rs @@ -1,19 +1,15 @@ //! Provides two types of cerrtificates and their accumulators. -use crate::vote::DAVoteAccumulator; -use crate::vote::QuorumVote; -use crate::vote::QuorumVoteAccumulator; -use crate::vote::TimeoutVote; -use crate::vote::TimeoutVoteAccumulator; -use crate::vote::ViewSyncVoteAccumulator; -use crate::vote::VoteType; use crate::{ data::{fake_commitment, serialize_signature, LeafType}, traits::{ election::SignedCertificate, node_implementation::NodeType, signature_key::SignatureKey, state::ConsensusTime, }, - vote::{DAVote, ViewSyncData, ViewSyncVote}, + vote::{ + DAVote, DAVoteAccumulator, QuorumVote, QuorumVoteAccumulator, TimeoutVote, + TimeoutVoteAccumulator, ViewSyncData, ViewSyncVote, ViewSyncVoteAccumulator, VoteType, + }, }; use bincode::Options; use commit::{Commitment, Committable}; diff --git a/crates/types/src/consensus.rs b/crates/types/src/consensus.rs index ce27d7a547..3a977b7461 100644 --- a/crates/types/src/consensus.rs +++ b/crates/types/src/consensus.rs @@ -1,9 +1,10 @@ //! Provides the core consensus types -pub use crate::traits::node_implementation::ViewQueue; -pub use crate::utils::{View, ViewInner}; +pub use crate::{ + traits::node_implementation::ViewQueue, + utils::{View, ViewInner}, +}; -use crate::utils::Terminator; use crate::{ certificate::QuorumCertificate, data::LeafType, @@ -12,6 +13,7 @@ use crate::{ metrics::{Counter, Gauge, Histogram, Metrics}, node_implementation::NodeType, }, + utils::Terminator, }; use commit::{Commitment, Committable}; use derivative::Derivative; diff --git a/crates/types/src/traits/election.rs b/crates/types/src/traits/election.rs index a5f98d47fc..760ceeb1ef 100644 --- a/crates/types/src/traits/election.rs +++ b/crates/types/src/traits/election.rs @@ -21,7 +21,6 @@ use crate::{ vote::ViewSyncVoteInternal, }; -use crate::vote::Accumulator; use crate::{ data::LeafType, traits::{ @@ -30,7 +29,7 @@ use crate::{ signature_key::SignatureKey, state::ConsensusTime, }, - vote::{DAVote, QuorumVote, ViewSyncData, ViewSyncVote, VoteType, YesOrNoVote}, + vote::{Accumulator, DAVote, QuorumVote, ViewSyncData, ViewSyncVote, VoteType, YesOrNoVote}, }; use bincode::Options; use commit::{Commitment, Committable}; From 24c6b2287bcb7593ac9a63eb110af7e68e92d86d Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Thu, 21 Sep 2023 22:02:40 -0400 Subject: [PATCH 28/56] Additional cleanup --- .../traits/networking/web_server_network.rs | 2 - crates/hotshot/src/types/handle.rs | 2 +- crates/task-impls/src/consensus.rs | 9 ++--- crates/task-impls/src/network.rs | 25 +++++-------- crates/testing/tests/view_sync_task.rs | 8 ---- crates/types/src/certificate.rs | 25 ++----------- crates/types/src/traits/election.rs | 7 +--- crates/types/src/vote.rs | 37 ------------------- 8 files changed, 20 insertions(+), 95 deletions(-) diff --git a/crates/hotshot/src/traits/networking/web_server_network.rs b/crates/hotshot/src/traits/networking/web_server_network.rs index 14409fecb7..5a67880e9f 100644 --- a/crates/hotshot/src/traits/networking/web_server_network.rs +++ b/crates/hotshot/src/traits/networking/web_server_network.rs @@ -145,7 +145,6 @@ impl Inner { message_purpose: MessagePurpose, view_number: u64, ) -> Result<(), NetworkError> { - error!("Polling for view {}", view_number); let mut vote_index = 0; let mut tx_index = 0; @@ -205,7 +204,6 @@ impl Inner { error!("We should not receive transactions in this function"); } MessagePurpose::Proposal => { - error!("Received proposal"); // Only pushing the first proposal since we will soon only be allowing 1 proposal per view self.broadcast_poll_queue .write() diff --git a/crates/hotshot/src/types/handle.rs b/crates/hotshot/src/types/handle.rs index 5b8e737c1a..3c2a618b58 100644 --- a/crates/hotshot/src/types/handle.rs +++ b/crates/hotshot/src/types/handle.rs @@ -191,7 +191,7 @@ impl + 'static> SystemContextHandl if anchor_leaf.view_number == TYPES::Time::genesis() { let leaf: I::Leaf = I::Leaf::from_stored_view(anchor_leaf); let mut qc = QuorumCertificate::::genesis(); - qc.set_leaf_commitment(leaf.commit()); + qc.leaf_commitment = leaf.commit(); let event = Event { view_number: TYPES::Time::genesis(), event: EventType::Decide { diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index 9d6261e25e..1247d2ba8a 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -295,8 +295,10 @@ where error!("The next leader has received an unexpected vote!"); } }, + // TODO: Code below is redundant of code above; can be fixed + // during exchange refactor + // https://github.com/EspressoSystems/HotShot/issues/1799 SequencingHotShotEvent::TimeoutVoteRecv(vote) => { - error!("received timeout vote for view {}", *vote.get_view()); if state.timeout_accumulator.is_right() { return (None, state); } @@ -1044,8 +1046,6 @@ where } } SequencingHotShotEvent::TimeoutVoteRecv(vote) => { - // debug!("Received quroum vote: {:?}", vote.get_view()); - if !self.timeout_exchange.is_leader(vote.get_view() + 1) { error!( "We are not the leader for view {} are we the leader for view + 1? {}", @@ -1190,9 +1190,7 @@ where let view = cert.view_number; self.certs.insert(view, cert); - // TODO Make sure we aren't voting for an arbitrarily old round for no reason if self.vote_if_able().await { - // self.update_view(view + 1).await; self.current_proposal = None; } } @@ -1204,7 +1202,6 @@ where // TODO Make sure we aren't voting for an arbitrarily old round for no reason if self.vote_if_able().await { - // self.update_view(view + 1).await; self.current_proposal = None; } } diff --git a/crates/task-impls/src/network.rs b/crates/task-impls/src/network.rs index c81463e5c7..e376d25945 100644 --- a/crates/task-impls/src/network.rs +++ b/crates/task-impls/src/network.rs @@ -90,7 +90,6 @@ impl< SequencingHotShotEvent::ViewSyncCertificateRecv(view_sync_message) } GeneralConsensusMessage::TimeoutVote(message) => { - // error!("Recv timeout vote in network task for view {:?}", message.get_view()); SequencingHotShotEvent::TimeoutVoteRecv(message) } GeneralConsensusMessage::InternalTrigger(_) => { @@ -107,7 +106,6 @@ impl< SequencingHotShotEvent::DAProposalRecv(proposal.clone(), sender) } CommitteeConsensusMessage::DAVote(vote) => { - // error!("DA Vote message recv {:?}", vote.current_view); SequencingHotShotEvent::DAVoteRecv(vote.clone()) } CommitteeConsensusMessage::DACertificate(cert) => { @@ -244,7 +242,7 @@ impl< CommitteeConsensusMessage::VidVote(vote.clone()), ))), TransmitType::Direct, - Some(membership.get_leader(vote.current_view)), // TODO who is VID leader? https://github.com/EspressoSystems/HotShot/issues/1699 + Some(membership.get_leader(vote.get_view())), // TODO who is VID leader? https://github.com/EspressoSystems/HotShot/issues/1699 ), SequencingHotShotEvent::DAVoteSend(vote) => ( vote.signature_key(), @@ -252,7 +250,7 @@ impl< CommitteeConsensusMessage::DAVote(vote.clone()), ))), TransmitType::Direct, - Some(membership.get_leader(vote.current_view)), + Some(membership.get_leader(vote.get_view())), ), SequencingHotShotEvent::VidCertSend(certificate, sender) => ( sender, @@ -290,17 +288,14 @@ impl< Some(membership.get_leader(vote.round() + vote.relay())), ) } - SequencingHotShotEvent::TimeoutVoteSend(vote) => { - // error!("Sending timeout vote to leader of view {}", *vote.get_view() + 1); - ( - vote.get_key(), - MessageKind::::from_consensus_message(SequencingMessage(Left( - GeneralConsensusMessage::TimeoutVote(vote.clone()), - ))), - TransmitType::Direct, - Some(membership.get_leader(vote.get_view() + 1)), - ) - } + SequencingHotShotEvent::TimeoutVoteSend(vote) => ( + vote.get_key(), + MessageKind::::from_consensus_message(SequencingMessage(Left( + GeneralConsensusMessage::TimeoutVote(vote.clone()), + ))), + TransmitType::Direct, + Some(membership.get_leader(vote.get_view() + 1)), + ), SequencingHotShotEvent::ViewChange(view) => { self.view = view; return None; diff --git a/crates/testing/tests/view_sync_task.rs b/crates/testing/tests/view_sync_task.rs index 99255e8209..3bbcdafd70 100644 --- a/crates/testing/tests/view_sync_task.rs +++ b/crates/testing/tests/view_sync_task.rs @@ -74,15 +74,7 @@ async fn test_view_sync_task() { output.insert(SequencingHotShotEvent::Timeout(ViewNumber::new(3)), 1); output.insert(SequencingHotShotEvent::Timeout(ViewNumber::new(4)), 1); - // 2 `Timeout` events will trigger a replica task to handle a `ViewSyncTrigger` event, which - // will then publish a `ViewSyncVoteSend` event. output.insert(SequencingHotShotEvent::ViewSyncVoteSend(vote.clone()), 1); - // output.insert( - // SequencingHotShotEvent::ViewSyncTimeout(ViewNumber::new(5), 0, ViewSyncPhase::None), - // 1, - // ); - // Triggered by the `Timeout` events. - // output.insert(SequencingHotShotEvent::ViewChange(ViewNumber::new(2)), 1); output.insert(SequencingHotShotEvent::ViewChange(ViewNumber::new(3)), 1); output.insert(SequencingHotShotEvent::ViewChange(ViewNumber::new(4)), 1); diff --git a/crates/types/src/certificate.rs b/crates/types/src/certificate.rs index b52720ad1a..6b31b68d1a 100644 --- a/crates/types/src/certificate.rs +++ b/crates/types/src/certificate.rs @@ -87,7 +87,7 @@ impl type VoteAccumulator = TimeoutVoteAccumulator, Self::Vote>; - fn create_certificateure(signatures: AssembledSignature, vote: Self::Vote) -> Self { + fn create_certificate(signatures: AssembledSignature, vote: Self::Vote) -> Self { TimeoutCertificate { view_number: vote.get_view(), signatures, @@ -106,10 +106,6 @@ impl self.view_number.commit() } - fn set_leaf_commitment(&mut self, _commitment: Commitment) { - unimplemented!() - } - fn is_genesis(&self) -> bool { false } @@ -182,7 +178,7 @@ impl> type Vote = QuorumVote; type VoteAccumulator = QuorumVoteAccumulator, Self::Vote>; - fn create_certificateure(signatures: AssembledSignature, vote: Self::Vote) -> Self { + fn create_certificate(signatures: AssembledSignature, vote: Self::Vote) -> Self { let leaf_commitment = match vote.clone() { QuorumVote::Yes(vote_internal) | QuorumVote::No(vote_internal) => { vote_internal.leaf_commitment @@ -210,10 +206,6 @@ impl> self.leaf_commitment } - fn set_leaf_commitment(&mut self, commitment: Commitment) { - self.leaf_commitment = commitment; - } - fn is_genesis(&self) -> bool { self.is_genesis } @@ -256,7 +248,7 @@ impl type Vote = DAVote; type VoteAccumulator = DAVoteAccumulator, Self::Vote>; - fn create_certificateure(signatures: AssembledSignature, vote: Self::Vote) -> Self { + fn create_certificate(signatures: AssembledSignature, vote: Self::Vote) -> Self { DACertificate { view_number: vote.get_view(), signatures, @@ -276,10 +268,6 @@ impl self.block_commitment } - fn set_leaf_commitment(&mut self, _commitment: Commitment) { - // This function is only useful for QC. Will be removed after we have separated cert traits. - } - fn is_genesis(&self) -> bool { // This function is only useful for QC. Will be removed after we have separated cert traits. false @@ -336,7 +324,7 @@ impl type VoteAccumulator = ViewSyncVoteAccumulator>, Self::Vote>; /// Build a QC from the threshold signature and commitment - fn create_certificateure(signatures: AssembledSignature, vote: Self::Vote) -> Self { + fn create_certificate(signatures: AssembledSignature, vote: Self::Vote) -> Self { let certificate_internal = ViewSyncCertificateInternal { round: vote.get_view(), relay: vote.relay(), @@ -382,11 +370,6 @@ impl todo!() } - /// Set the leaf commitment. - fn set_leaf_commitment(&mut self, _commitment: Commitment>) { - todo!() - } - /// Get whether the certificate is for the genesis block. fn is_genesis(&self) -> bool { todo!() diff --git a/crates/types/src/traits/election.rs b/crates/types/src/traits/election.rs index 760ceeb1ef..45d171eb38 100644 --- a/crates/types/src/traits/election.rs +++ b/crates/types/src/traits/election.rs @@ -172,7 +172,7 @@ where // TODO ED Rename this function and rework this function parameters // Assumes last vote was valid since it caused a QC to form. // Removes need for relay on other cert specific fields - fn create_certificateure(signatures: AssembledSignature, vote: Self::Vote) -> Self; + fn create_certificate(signatures: AssembledSignature, vote: Self::Vote) -> Self; /// Get the view number. fn view_number(&self) -> TIME; @@ -186,9 +186,6 @@ where /// Get the leaf commitment. fn leaf_commitment(&self) -> COMMITMENT; - /// Set the leaf commitment. - fn set_leaf_commitment(&mut self, commitment: COMMITMENT); - /// Get whether the certificate is for the genesis block. fn is_genesis(&self) -> bool; @@ -461,7 +458,7 @@ pub trait ConsensusExchange: Send + Sync { Either::Left(accumulator) => Either::Left(accumulator), Either::Right(signatures) => { // TODO ED Update this function to just take in the signatures and most recent vote - Either::Right(Self::Certificate::create_certificateure( + Either::Right(Self::Certificate::create_certificate( signatures, vote.clone(), )) diff --git a/crates/types/src/vote.rs b/crates/types/src/vote.rs index 33a0c9eccb..d6b823dcd5 100644 --- a/crates/types/src/vote.rs +++ b/crates/types/src/vote.rs @@ -114,41 +114,6 @@ impl VoteType> for TimeoutVote { -// /// The signature share associated with this vote -// pub signature: (EncodedPublicKey, EncodedSignature), -// /// The view this vote was cast for -// pub current_view: TYPES::Time, -// /// The vote token generated by this replica -// pub vote_token: TYPES::VoteTokenType, -// } - -// impl VoteType for TimeoutVote { -// fn get_view(&self) -> ::Time { -// self.current_view -// } - -// fn get_key(&self) -> ::SignatureKey { -// ::from_bytes(&self.signature.0).unwrap() -// } - -// fn get_signature(&self) -> EncodedSignature { -// self.signature.1.clone() -// } - -// fn get_data(&self) -> VoteData { -// VoteData::Timeout(self.get_view().commit()) -// } - -// fn get_vote_token(&self) -> ::VoteTokenType { -// self.vote_token.clone() -// } - -// } - /// The internals of a view sync vote #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)] #[serde(bound(deserialize = ""))] @@ -313,7 +278,6 @@ impl> QuorumVote /// Get the signature key. /// # Panics /// If the deserialization fails. - pub fn signature_key(&self) -> TYPES::SignatureKey { let encoded = match &self { Self::Yes(vote) | Self::No(vote) => vote.signature.0.clone(), @@ -634,7 +598,6 @@ impl< self.signers.set(vote_node_id, true); self.sig_lists.push(original_signature); - // TODO ED Make all these get calls as local variables to avoid constantly calling them *total_stake_casted += u64::from(vote.get_vote_token().vote_count()); total_vote_map.insert( encoded_key.clone(), From a8027c7b93e384af65078bb71f22e4f861dcff1a Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Thu, 21 Sep 2023 22:05:05 -0400 Subject: [PATCH 29/56] Additional cleanup --- crates/hotshot/src/types/handle.rs | 42 ----------------------------- crates/types/src/traits/election.rs | 2 -- 2 files changed, 44 deletions(-) diff --git a/crates/hotshot/src/types/handle.rs b/crates/hotshot/src/types/handle.rs index 3c2a618b58..b61eda8e93 100644 --- a/crates/hotshot/src/types/handle.rs +++ b/crates/hotshot/src/types/handle.rs @@ -215,48 +215,6 @@ impl + 'static> SystemContextHandl self.maybe_do_genesis_init().await; } - /// iterate through all events on a [`NodeImplementation`] and determine if the node finished - /// successfully - /// # Errors - /// Errors if unable to obtain storage - /// # Panics - /// Panics if the event stream is shut down while this is running - // pub async fn collect_round_events( - // &mut self, - // ) -> Result< - // ( - // Vec<>::Leaf>, - // QuorumCertificate>::Leaf>, - // ), - // HotShotError, - // > { - // // TODO we should probably do a view check - // // but we can do that later. It's non-obvious how to get the view number out - // // to check against - // - // // drain all events from this node - // let mut results = Ok((vec![], QuorumCertificate::genesis())); - // loop { - // // unwrap is fine here since the thing hasn't been shut down - // let event = self.next_event().await.unwrap(); - // match event.event { - // EventType::ReplicaViewTimeout { view_number: time } => { - // error!(?event, "Replica timed out!"); - // results = Err(HotShotError::ViewTimeoutError { - // view_number: time, - // state: RoundTimedoutState::TestCollectRoundEventsTimedOut, - // }); - // } - // EventType::Decide { leaf_chain, qc } => { - // results = Ok((leaf_chain.to_vec(), (*qc).clone())); - // } - // EventType::ViewFinished { view_number: _ } => return results, - // event => { - // debug!("recv-ed event {:?}", event); - // } - // } - // } - // } /// Provides a reference to the underlying storage for this [`SystemContext`], allowing access to /// historical data diff --git a/crates/types/src/traits/election.rs b/crates/types/src/traits/election.rs index 45d171eb38..9fef1bdc48 100644 --- a/crates/types/src/traits/election.rs +++ b/crates/types/src/traits/election.rs @@ -433,7 +433,6 @@ pub trait ConsensusExchange: Send + Sync { &vote.get_key(), &vote.get_signature(), &vote.get_data(), - // TODO ED We've had this comment for a while: Ignoring deserialization errors below since we are getting rid of it soon &Checked::Unchecked(vote.get_vote_token()), ) { error!("Invalid vote!"); @@ -457,7 +456,6 @@ pub trait ConsensusExchange: Send + Sync { ) { Either::Left(accumulator) => Either::Left(accumulator), Either::Right(signatures) => { - // TODO ED Update this function to just take in the signatures and most recent vote Either::Right(Self::Certificate::create_certificate( signatures, vote.clone(), From c248d2ebbf446c06df805e39ffbf116d8c2dd4c4 Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Thu, 21 Sep 2023 22:07:32 -0400 Subject: [PATCH 30/56] Additional cleanup --- crates/hotshot/src/types/handle.rs | 1 - crates/types/src/traits/election.rs | 10 ++++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/crates/hotshot/src/types/handle.rs b/crates/hotshot/src/types/handle.rs index b61eda8e93..7a6a7b53f5 100644 --- a/crates/hotshot/src/types/handle.rs +++ b/crates/hotshot/src/types/handle.rs @@ -215,7 +215,6 @@ impl + 'static> SystemContextHandl self.maybe_do_genesis_init().await; } - /// Provides a reference to the underlying storage for this [`SystemContext`], allowing access to /// historical data pub fn storage(&self) -> &I::Storage { diff --git a/crates/types/src/traits/election.rs b/crates/types/src/traits/election.rs index 9fef1bdc48..90562f2822 100644 --- a/crates/types/src/traits/election.rs +++ b/crates/types/src/traits/election.rs @@ -455,12 +455,10 @@ pub trait ConsensusExchange: Send + Sync { self.membership().get_committee_qc_stake_table(), ) { Either::Left(accumulator) => Either::Left(accumulator), - Either::Right(signatures) => { - Either::Right(Self::Certificate::create_certificate( - signatures, - vote.clone(), - )) - } + Either::Right(signatures) => Either::Right(Self::Certificate::create_certificate( + signatures, + vote.clone(), + )), } } From bb0eb5f403baa0271c6897e0ea403cee3edc0af2 Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Fri, 22 Sep 2023 08:06:04 -0400 Subject: [PATCH 31/56] Fix lint bug --- crates/task-impls/src/consensus.rs | 6 +++++- crates/task-impls/src/da.rs | 1 - crates/task-impls/src/network.rs | 4 ---- crates/types/src/traits/election.rs | 5 ++++- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index 1247d2ba8a..cf49cc15a4 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -673,9 +673,13 @@ where return; }; + if timeout_cert.view_number != view - 1 { + warn!("Timeout certificate for view {} was not for the immediately preceding view", *view); + } + if !self .timeout_exchange - .is_valid_cert(&timeout_cert.clone(), view.commit()) + .is_valid_cert(&timeout_cert.clone(), timeout_cert.view_number.commit()) { warn!("Timeout certificate for view {} was invalid", *view); return; diff --git a/crates/task-impls/src/da.rs b/crates/task-impls/src/da.rs index 2c80b9eaff..15fc9b1cfe 100644 --- a/crates/task-impls/src/da.rs +++ b/crates/task-impls/src/da.rs @@ -159,7 +159,6 @@ where &vote.block_commitment, ) { Left(new_accumulator) => { - error!("Not enough DA votes yet"); state.accumulator = either::Left(new_accumulator); } diff --git a/crates/task-impls/src/network.rs b/crates/task-impls/src/network.rs index e376d25945..38b020e79a 100644 --- a/crates/task-impls/src/network.rs +++ b/crates/task-impls/src/network.rs @@ -99,10 +99,6 @@ impl< }, Either::Right(committee_message) => match committee_message { CommitteeConsensusMessage::DAProposal(proposal) => { - error!( - "Received da proposal for view {:?}", - proposal.clone().data.view_number - ); SequencingHotShotEvent::DAProposalRecv(proposal.clone(), sender) } CommitteeConsensusMessage::DAVote(vote) => { diff --git a/crates/types/src/traits/election.rs b/crates/types/src/traits/election.rs index 90562f2822..3c5cfc713e 100644 --- a/crates/types/src/traits/election.rs +++ b/crates/types/src/traits/election.rs @@ -329,7 +329,10 @@ pub trait ConsensusExchange: Send + Sync { let leaf_commitment = qc.leaf_commitment(); if leaf_commitment != commit { - error!("Leaf commitment does not equal parent commitment"); + error!( + "Leaf commitment does not equal parent commitment {:?}", + qc.signatures() + ); return false; } From 5259ca68b4877f1f86a7ecca1efdac927707a65c Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Fri, 22 Sep 2023 08:07:02 -0400 Subject: [PATCH 32/56] Fix lint bug --- crates/task-impls/src/consensus.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index cf49cc15a4..a5267325f4 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -675,6 +675,7 @@ where if timeout_cert.view_number != view - 1 { warn!("Timeout certificate for view {} was not for the immediately preceding view", *view); + return; } if !self From 3d6fcb25b7ed2796d504d44236e55bfa1eab7ef8 Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Fri, 22 Sep 2023 08:13:27 -0400 Subject: [PATCH 33/56] Fix fmt --- crates/task-impls/src/consensus.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index a5267325f4..7ea35fd1d8 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -675,7 +675,7 @@ where if timeout_cert.view_number != view - 1 { warn!("Timeout certificate for view {} was not for the immediately preceding view", *view); - return; + return; } if !self From f3ce354924d63c4ebed272a65a403462d5f4cd88 Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Fri, 22 Sep 2023 08:44:32 -0400 Subject: [PATCH 34/56] Update view sync view update --- crates/task-impls/src/view_sync.rs | 2 +- crates/testing/tests/view_sync_task.rs | 3 ++- justfile | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/task-impls/src/view_sync.rs b/crates/task-impls/src/view_sync.rs index 86be8ba62e..bdbbb19a36 100644 --- a/crates/task-impls/src/view_sync.rs +++ b/crates/task-impls/src/view_sync.rs @@ -578,7 +578,7 @@ where }); } else { // If this is the first timeout we've seen advance to the next view - self.current_view = view_number + 1; + self.current_view = view_number; self.event_stream .publish(SequencingHotShotEvent::ViewChange(TYPES::Time::new( *self.current_view, diff --git a/crates/testing/tests/view_sync_task.rs b/crates/testing/tests/view_sync_task.rs index 3bbcdafd70..d42a3aa2ab 100644 --- a/crates/testing/tests/view_sync_task.rs +++ b/crates/testing/tests/view_sync_task.rs @@ -75,8 +75,9 @@ async fn test_view_sync_task() { output.insert(SequencingHotShotEvent::Timeout(ViewNumber::new(4)), 1); output.insert(SequencingHotShotEvent::ViewSyncVoteSend(vote.clone()), 1); + output.insert(SequencingHotShotEvent::ViewChange(ViewNumber::new(2)), 1); output.insert(SequencingHotShotEvent::ViewChange(ViewNumber::new(3)), 1); - output.insert(SequencingHotShotEvent::ViewChange(ViewNumber::new(4)), 1); + // output.insert(SequencingHotShotEvent::ViewChange(ViewNumber::new(4)), 1); output.insert(SequencingHotShotEvent::Shutdown, 1); diff --git a/justfile b/justfile index 276d914257..643b2430ca 100644 --- a/justfile +++ b/justfile @@ -37,7 +37,7 @@ test_success: test_timeout: echo Testing timeout test - cargo test --lib --bins --tests --benches --workspace --no-fail-fast test_timeout -- --test-threads=1 --nocapture + ASYNC_STD_THREAD_COUNT=1 cargo test --lib --bins --tests --benches --workspace --no-fail-fast test_timeout -- --test-threads=1 --nocapture test_web_server: echo Testing web server From 539964d355715e3275d314c0c156bad216f7c441 Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Fri, 22 Sep 2023 09:11:02 -0400 Subject: [PATCH 35/56] Add 1 thread to catchup just command --- crates/testing/tests/view_sync_task.rs | 1 - justfile | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/testing/tests/view_sync_task.rs b/crates/testing/tests/view_sync_task.rs index d42a3aa2ab..8116dcf2a2 100644 --- a/crates/testing/tests/view_sync_task.rs +++ b/crates/testing/tests/view_sync_task.rs @@ -77,7 +77,6 @@ async fn test_view_sync_task() { output.insert(SequencingHotShotEvent::ViewSyncVoteSend(vote.clone()), 1); output.insert(SequencingHotShotEvent::ViewChange(ViewNumber::new(2)), 1); output.insert(SequencingHotShotEvent::ViewChange(ViewNumber::new(3)), 1); - // output.insert(SequencingHotShotEvent::ViewChange(ViewNumber::new(4)), 1); output.insert(SequencingHotShotEvent::Shutdown, 1); diff --git a/justfile b/justfile index 643b2430ca..a78e9e5ccc 100644 --- a/justfile +++ b/justfile @@ -29,7 +29,7 @@ test_basic: test_success test_with_failures test_network_task test_consensus_tas test_catchup: echo Testing with async std executor - cargo test --lib --bins --tests --benches --workspace --no-fail-fast test_catchup -- --test-threads=1 --nocapture + ASYNC_STD_THREAD_COUNT=1 cargo test --lib --bins --tests --benches --workspace --no-fail-fast test_catchup -- --test-threads=1 --nocapture test_success: echo Testing success test From 148937ddc60ad942f3516040a570a0dc5a32c18d Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Wed, 11 Oct 2023 11:51:32 -0400 Subject: [PATCH 36/56] basic tests pass --- crates/task-impls/src/consensus.rs | 2 +- crates/task-impls/src/da.rs | 18 ++++++++++-------- crates/types/src/traits/election.rs | 14 +++++--------- crates/types/src/vote.rs | 2 +- 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index ff1c92395d..77ac7dfb33 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -550,7 +550,7 @@ where cert.view_number, vote_token) } else { - error!("Invalid DAC in proposal! Skipping proposal. {:?} cur view is: {:?}", cert.view_number, self.cur_view ); + error!("Invalid DAC in proposal! Skipping proposal. {:?} cur view is: {:?}", cert, self.cur_view ); return false; }; diff --git a/crates/task-impls/src/da.rs b/crates/task-impls/src/da.rs index 04db0b25fa..767e149289 100644 --- a/crates/task-impls/src/da.rs +++ b/crates/task-impls/src/da.rs @@ -272,10 +272,11 @@ where // `self.cur_view` should be at least 1 since there is a view change before getting // the `DAProposalRecv` event. Otherewise, the view number subtraction below will // cause an overflow error. - if view < self.cur_view - 1 { - warn!("Throwing away DA proposal that is more than one view older"); - return None; - } + // TODO ED Come back to this - we probably don't need this, but we should also never receive a DAC where this fails, investigate block ready so it doesn't make one for the genesis block + // if view < self.cur_view - 1 { + // warn!("Throwing away DA proposal that is more than one view older"); + // return None; + // } debug!( "Got a DA block with {} transactions!", @@ -507,10 +508,11 @@ where // `self.cur_view` should be at least 1 since there is a view change before getting // the `DAProposalRecv` event. Otherewise, the view number subtraction below will // cause an overflow error. - if view < self.cur_view - 1 { - warn!("Throwing away VID disperse data that is more than one view older"); - return None; - } + // TODO ED Revisit this + // if view < self.cur_view - 1 { + // warn!("Throwing away VID disperse data that is more than one view older"); + // return None; + // } debug!("VID disperse data is fresh."); let block_commitment = disperse.data.commitment; diff --git a/crates/types/src/traits/election.rs b/crates/types/src/traits/election.rs index 282d85ba52..465d1aa03a 100644 --- a/crates/types/src/traits/election.rs +++ b/crates/types/src/traits/election.rs @@ -354,7 +354,11 @@ pub trait ConsensusExchange: Send + Sync { self.membership().get_committee_qc_stake_table(), U256::from(self.membership().success_threshold().get()), ); - ::check(&real_qc_pp, real_commit.as_ref(), &qc) + ::check( + &real_qc_pp, + real_commit.as_ref(), + &qc, + ) } AssembledSignature::Yes(qc) => { let real_commit = VoteData::Yes(leaf_commitment).commit(); @@ -380,14 +384,6 @@ pub trait ConsensusExchange: Send + Sync { ); ::check(&real_qc_pp, real_commit.as_ref(), &qc) } - AssembledSignature::Timeout(qc) => { - let real_commit = VoteData::Timeout(leaf_commitment).get_commit(); - let real_qc_pp = ::get_public_parameter( - self.membership().get_committee_qc_stake_table(), - U256::from(self.membership().success_threshold().get()), - ); - ::check(&real_qc_pp, real_commit.as_ref(), &qc) - } AssembledSignature::Genesis() => true, AssembledSignature::ViewSyncPreCommit(_) | AssembledSignature::ViewSyncCommit(_) diff --git a/crates/types/src/vote.rs b/crates/types/src/vote.rs index 4fea6ab472..bb4542ebe3 100644 --- a/crates/types/src/vote.rs +++ b/crates/types/src/vote.rs @@ -421,7 +421,7 @@ impl< self.da_vote_outcomes.remove(&vote_commitment); - return Either::Right(AssembledSignature::Timeout(real_qc_sig)); + return Either::Right(AssembledSignature::DA(real_qc_sig)); } Either::Left(self) } From 3028fa056dfd261eeba6e819cbf9afb5b0e4ee3b Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Wed, 11 Oct 2023 14:05:47 -0400 Subject: [PATCH 37/56] Tests passing? --- crates/task-impls/src/consensus.rs | 74 ++++++++++++----------------- crates/task-impls/src/view_sync.rs | 2 +- crates/testing/tests/timeout.rs | 4 +- crates/types/src/traits/election.rs | 6 ++- 4 files changed, 38 insertions(+), 48 deletions(-) diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index 77ac7dfb33..7e7adad4c8 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -83,7 +83,6 @@ pub struct SequencingConsensusTaskState< Certificate = TimeoutCertificate, Commitment = Commitment, >, - { /// The global task registry pub registry: GlobalRegistry, @@ -178,7 +177,7 @@ pub struct VoteCollectionTaskState< Commitment>, >>::VoteAccumulator, QuorumCertificate>>, - >, + >, /// Accumulator for votes #[allow(clippy::type_complexity)] @@ -216,7 +215,6 @@ where Certificate = TimeoutCertificate, Commitment = Commitment, >, - { } @@ -244,7 +242,6 @@ where Certificate = TimeoutCertificate, Commitment = Commitment, >, - { match event { SequencingHotShotEvent::QuorumVoteRecv(vote) => match vote.clone() { @@ -302,6 +299,7 @@ where // during exchange refactor // https://github.com/EspressoSystems/HotShot/issues/1799 SequencingHotShotEvent::TimeoutVoteRecv(vote) => { + debug!("Received timeout vote for view {}", *vote.get_view()); if state.timeout_accumulator.is_right() { return (None, state); } @@ -688,11 +686,8 @@ where return; } - // TODO ED Do we need to check that the commit in the cert is in fact a commit to the correct view? I think we do. - if !self - .timeout_exchange - .is_valid_cert(&timeout_cert.clone()) - { + // TODO ED Do we need to check that the commit in the cert is in fact a commit to the correct view? I think we do. + if !self.timeout_exchange.is_valid_cert(&timeout_cert.clone()) { warn!("Timeout certificate for view {} was invalid", *view); return; } @@ -700,10 +695,7 @@ where let justify_qc = proposal.data.justify_qc.clone(); - if !self - .quorum_exchange - .is_valid_cert(&justify_qc) - { + if !self.quorum_exchange.is_valid_cert(&justify_qc) { error!("Invalid justify_qc in proposal for view {}", *view); return; } @@ -1173,11 +1165,7 @@ where let high_qc = self.consensus.read().await.high_qc.clone(); if self - .publish_proposal_if_able( - high_qc, - view, - Some(qc.clone()), - ) + .publish_proposal_if_able(high_qc, view, Some(qc.clone())) .await { } else { @@ -1194,7 +1182,7 @@ where *qc.view_number ); - if self + if !self .publish_proposal_if_able(qc.clone(), qc.view_number + 1, None) .await { @@ -1246,7 +1234,7 @@ where } SequencingHotShotEvent::Timeout(view) => { // NOTE: We may optionally have the timeout task listen for view change events - if self.cur_view > view { + if self.cur_view >= view { return; } let vote_token = self.timeout_exchange.make_vote_token(view); @@ -1317,6 +1305,7 @@ where return false; }; if leaf_commitment != consensus.high_qc.leaf_commitment() { + // TODO ED This happens on the genesis block debug!( "They don't equal: {:?} {:?}", leaf_commitment, @@ -1367,28 +1356,28 @@ where proposer_id: self.api.public_key().to_bytes(), }; - let signature = self - .quorum_exchange - .sign_validating_or_commitment_proposal::(&leaf.commit()); - // TODO: DA cert is sent as part of the proposal here, we should split this out so we don't have to wait for it. - let proposal = QuorumProposal { - block_commitment, - view_number: leaf.view_number, - height: leaf.height, - justify_qc: consensus.high_qc.clone(), - timeout_certificate: timeout_certificate.or_else(|| None), - proposer_id: leaf.proposer_id, - dac: None, - }; + let signature = self + .quorum_exchange + .sign_validating_or_commitment_proposal::(&leaf.commit()); + // TODO: DA cert is sent as part of the proposal here, we should split this out so we don't have to wait for it. + let proposal = QuorumProposal { + block_commitment, + view_number: leaf.view_number, + height: leaf.height, + justify_qc: consensus.high_qc.clone(), + timeout_certificate: timeout_certificate.or_else(|| None), + proposer_id: leaf.proposer_id, + dac: None, + }; - let message = Proposal { - data: proposal, - signature, - }; - debug!( - "Sending proposal for view {:?} \n {:?}", - leaf.view_number, "" - ); + let message = Proposal { + data: proposal, + signature, + }; + debug!( + "Sending proposal for view {:?} \n {:?}", + leaf.view_number, "" + ); self.event_stream .publish(SequencingHotShotEvent::QuorumProposalSend( @@ -1399,6 +1388,7 @@ where self.block = None; return true; } + debug!("Self block was None"); false } } @@ -1433,7 +1423,6 @@ where Certificate = TimeoutCertificate, Commitment = Commitment, >, - { } @@ -1490,7 +1479,6 @@ where Certificate = TimeoutCertificate, Commitment = Commitment, >, - { if let SequencingHotShotEvent::Shutdown = event { (Some(HotShotTaskCompleted::ShutDown), state) diff --git a/crates/task-impls/src/view_sync.rs b/crates/task-impls/src/view_sync.rs index c44dfb328f..c7efa7e212 100644 --- a/crates/task-impls/src/view_sync.rs +++ b/crates/task-impls/src/view_sync.rs @@ -490,7 +490,7 @@ where } &SequencingHotShotEvent::Timeout(view_number) => { // This is an old timeout and we can ignore it - if view_number < TYPES::Time::new(*self.current_view) { + if view_number <= TYPES::Time::new(*self.current_view) { return; } diff --git a/crates/testing/tests/timeout.rs b/crates/testing/tests/timeout.rs index 6988c78a28..913a529462 100644 --- a/crates/testing/tests/timeout.rs +++ b/crates/testing/tests/timeout.rs @@ -7,7 +7,7 @@ async fn test_timeout() { use std::time::Duration; - use hotshot_testing::node_types::SequencingWebImpl; + use hotshot_testing::node_types::SequencingLibp2pImpl; use hotshot_testing::{ completion_task::{CompletionTaskDescription, TimeBasedCompletionTaskDescription}, @@ -57,7 +57,7 @@ async fn test_timeout() { // TODO ED Test with memory network once issue is resolved // https://github.com/EspressoSystems/HotShot/issues/1790 metadata - .gen_launcher::() + .gen_launcher::() .launch() .run_test() .await; diff --git a/crates/types/src/traits/election.rs b/crates/types/src/traits/election.rs index 465d1aa03a..2879f89414 100644 --- a/crates/types/src/traits/election.rs +++ b/crates/types/src/traits/election.rs @@ -377,7 +377,7 @@ pub trait ConsensusExchange: Send + Sync { ::check(&real_qc_pp, real_commit.as_ref(), &qc) } AssembledSignature::Timeout(qc) => { - let real_commit = VoteData::Timeout(leaf_commitment).get_commit(); + let real_commit = VoteData::Timeout(leaf_commitment).commit(); let real_qc_pp = ::get_public_parameter( self.membership().get_committee_qc_stake_table(), U256::from(self.membership().success_threshold().get()), @@ -453,6 +453,8 @@ pub trait ConsensusExchange: Send + Sync { &vote.get_data(), &Checked::Unchecked(vote.get_vote_token()), ) { + error!("Vote data is {:?}", vote.get_data()); + error!("Invalid vote!"); return Either::Left(accumulator); } @@ -1310,7 +1312,7 @@ pub trait TimeoutExchangeType: ConsensusExchange let signature = TYPES::SignatureKey::sign( self.private_key(), VoteData::>::Timeout(view.commit()) - .get_commit() + .commit() .as_ref(), ); From f1921905640d2668594ae0d182e9f992435645eb Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Wed, 11 Oct 2023 14:20:02 -0400 Subject: [PATCH 38/56] lints --- crates/hotshot-signature-key/src/bn254/bn254_priv.rs | 1 + crates/hotshot-signature-key/src/bn254/bn254_pub.rs | 1 + crates/hotshot/src/tasks/mod.rs | 4 ++-- crates/hotshot/src/traits/networking/combined_network.rs | 4 ++-- crates/hotshot/src/traits/networking/memory_network.rs | 4 ++-- crates/libp2p-networking/src/network/node/handle.rs | 2 +- crates/task-impls/src/da.rs | 2 +- crates/task-impls/src/harness.rs | 6 +++--- crates/task-impls/src/transactions.rs | 2 +- crates/task/src/event_stream.rs | 4 ++-- crates/task/src/task.rs | 2 +- crates/testing/tests/consensus_task.rs | 8 +++++--- crates/types/src/certificate.rs | 4 +--- crates/types/src/traits/election.rs | 8 ++------ 14 files changed, 25 insertions(+), 27 deletions(-) diff --git a/crates/hotshot-signature-key/src/bn254/bn254_priv.rs b/crates/hotshot-signature-key/src/bn254/bn254_priv.rs index 439b52ff39..87d80c8dfe 100644 --- a/crates/hotshot-signature-key/src/bn254/bn254_priv.rs +++ b/crates/hotshot-signature-key/src/bn254/bn254_priv.rs @@ -54,6 +54,7 @@ impl BLSPrivKey { } } +#[allow(clippy::incorrect_partial_ord_impl_on_ord_type)] impl PartialOrd for BLSPrivKey { fn partial_cmp(&self, other: &Self) -> Option { let self_bytes = &self.priv_key.to_string(); diff --git a/crates/hotshot-signature-key/src/bn254/bn254_pub.rs b/crates/hotshot-signature-key/src/bn254/bn254_pub.rs index 025d455129..410a0e12fb 100644 --- a/crates/hotshot-signature-key/src/bn254/bn254_pub.rs +++ b/crates/hotshot-signature-key/src/bn254/bn254_pub.rs @@ -27,6 +27,7 @@ pub struct BLSPubKey { pub_key: VerKey, } +#[allow(clippy::incorrect_partial_ord_impl_on_ord_type)] impl PartialOrd for BLSPubKey { fn partial_cmp(&self, other: &Self) -> Option { let self_bytes = &self.pub_key.to_string(); diff --git a/crates/hotshot/src/tasks/mod.rs b/crates/hotshot/src/tasks/mod.rs index 48a33574f8..b1ee195e63 100644 --- a/crates/hotshot/src/tasks/mod.rs +++ b/crates/hotshot/src/tasks/mod.rs @@ -36,8 +36,8 @@ use hotshot_types::{ election::{ConsensusExchange, Membership}, network::{CommunicationChannel, ConsensusIntentEvent, TransmitType}, node_implementation::{ - CommitteeEx, ExchangesType, NodeImplementation, NodeType, QuorumEx, SequencingTimeoutEx, - ViewSyncEx, + CommitteeEx, ExchangesType, NodeImplementation, NodeType, QuorumEx, + SequencingTimeoutEx, ViewSyncEx, }, state::ConsensusTime, }, diff --git a/crates/hotshot/src/traits/networking/combined_network.rs b/crates/hotshot/src/traits/networking/combined_network.rs index 63fdb6df88..e717c40b1b 100644 --- a/crates/hotshot/src/traits/networking/combined_network.rs +++ b/crates/hotshot/src/traits/networking/combined_network.rs @@ -287,7 +287,7 @@ impl, MEMBERSHIP: Membership { + Ok(()) => { self.primary_down.store(0, Ordering::Relaxed); } Err(e) => { @@ -318,7 +318,7 @@ impl, MEMBERSHIP: Membership { + Ok(()) => { self.primary_down.store(0, Ordering::Relaxed); } Err(e) => { diff --git a/crates/hotshot/src/traits/networking/memory_network.rs b/crates/hotshot/src/traits/networking/memory_network.rs index 9d9e751ee5..9b68c805e6 100644 --- a/crates/hotshot/src/traits/networking/memory_network.rs +++ b/crates/hotshot/src/traits/networking/memory_network.rs @@ -329,7 +329,7 @@ impl ConnectedNetwork for Memory } else { let res = node.broadcast_input(vec.clone()).await; match res { - Ok(_) => { + Ok(()) => { self.inner.metrics.outgoing_message_count.add(1); trace!(?key, "Delivered message to remote"); } @@ -373,7 +373,7 @@ impl ConnectedNetwork for Memory } else { let res = node.direct_input(vec).await; match res { - Ok(_) => { + Ok(()) => { self.inner.metrics.outgoing_message_count.add(1); trace!(?recipient, "Delivered message to remote"); Ok(()) diff --git a/crates/libp2p-networking/src/network/node/handle.rs b/crates/libp2p-networking/src/network/node/handle.rs index c4e6460666..4c66d1e840 100644 --- a/crates/libp2p-networking/src/network/node/handle.rs +++ b/crates/libp2p-networking/src/network/node/handle.rs @@ -188,7 +188,7 @@ impl NetworkNodeHandle { } } } - }).map(|_| ()) + }).map(|()| ()) } /// Wait until at least `num_peers` have connected, or until `timeout` time has passed. diff --git a/crates/task-impls/src/da.rs b/crates/task-impls/src/da.rs index 767e149289..1915704e08 100644 --- a/crates/task-impls/src/da.rs +++ b/crates/task-impls/src/da.rs @@ -32,7 +32,7 @@ use hotshot_types::{ use snafu::Snafu; use std::{collections::HashMap, marker::PhantomData, sync::Arc}; -use tracing::{debug, error, instrument, warn}; +use tracing::{debug, error, instrument}; #[derive(Snafu, Debug)] /// Error type for consensus tasks diff --git a/crates/task-impls/src/harness.rs b/crates/task-impls/src/harness.rs index 136093bc3e..c2e2f0be50 100644 --- a/crates/task-impls/src/harness.rs +++ b/crates/task-impls/src/harness.rs @@ -3,7 +3,7 @@ use async_compatibility_layer::art::async_spawn; use futures::FutureExt; use hotshot_task::{ - event_stream::{self, ChannelStream, EventStream}, + event_stream::{ChannelStream, EventStream}, task::{FilterEvent, HandleEvent, HotShotTaskCompleted, HotShotTaskTypes, TS}, task_impls::{HSTWithEvent, TaskBuilder}, task_launcher::TaskRunner, @@ -52,7 +52,7 @@ pub async fn run_harness( { let task_runner = TaskRunner::new(); let registry = task_runner.registry.clone(); - let event_stream = event_stream.unwrap_or(event_stream::ChannelStream::new()); + let event_stream = event_stream.unwrap_or_default(); let state = TestHarnessState { expected_output }; let handler = HandleEvent(Arc::new(move |event, state| { async move { handle_event(event, state) }.boxed() @@ -76,7 +76,7 @@ pub async fn run_harness( let runner = async_spawn(async move { task_runner.launch().await }); for event in input { - let _ = event_stream.publish(event).await; + let () = event_stream.publish(event).await; } let _ = runner.await; diff --git a/crates/task-impls/src/transactions.rs b/crates/task-impls/src/transactions.rs index 88ccd973a8..cb939d0ca7 100644 --- a/crates/task-impls/src/transactions.rs +++ b/crates/task-impls/src/transactions.rs @@ -6,7 +6,7 @@ use async_compatibility_layer::{ use async_lock::RwLock; use bincode::config::Options; use commit::{Commitment, Committable}; -use either::{Either, Left, Right}; +use either::{Left, Right}; use hotshot_task::{ event_stream::{ChannelStream, EventStream}, global_registry::GlobalRegistry, diff --git a/crates/task/src/event_stream.rs b/crates/task/src/event_stream.rs index 875d045994..5248fe4373 100644 --- a/crates/task/src/event_stream.rs +++ b/crates/task/src/event_stream.rs @@ -129,7 +129,7 @@ impl EventStream for ChannelStream { Some((filter, sender)) => { if filter(&event) { match sender.send(event.clone()).await { - Ok(_) => (), + Ok(()) => (), // error sending => stream is closed so remove it Err(_) => self.unsubscribe(id).await, } @@ -147,7 +147,7 @@ impl EventStream for ChannelStream { for (uid, (filter, sender)) in &inner.subscribers { if filter(&event) { match sender.send(event.clone()).await { - Ok(_) => (), + Ok(()) => (), // error sending => stream is closed so remove it Err(_) => { self.unsubscribe(*uid).await; diff --git a/crates/task/src/task.rs b/crates/task/src/task.rs index 37b6f2d56f..8435ff0fcf 100644 --- a/crates/task/src/task.rs +++ b/crates/task/src/task.rs @@ -381,7 +381,7 @@ impl<'pin, HSTT: HotShotTaskTypes> ProjectedHST<'pin, HSTT> { cx: &mut Context<'_>, ) -> Poll { match fut.as_mut().poll(cx) { - Poll::Ready(_) => Poll::Ready( + Poll::Ready(()) => Poll::Ready( self.r_val .take() .unwrap_or_else(|| HotShotTaskCompleted::LostReturnValue), diff --git a/crates/testing/tests/consensus_task.rs b/crates/testing/tests/consensus_task.rs index 9c9059e380..dc8439a85d 100644 --- a/crates/testing/tests/consensus_task.rs +++ b/crates/testing/tests/consensus_task.rs @@ -1,5 +1,5 @@ -use commit::Committable; use commit::Commitment; +use commit::Committable; use either::Right; use hotshot::{ tasks::add_consensus_task, @@ -102,8 +102,10 @@ async fn test_consensus_task() { let mut output = HashMap::new(); // Trigger a proposal to send by creating a new QC. Then recieve that proposal and update view based on the valid QC in the proposal - let qc = - QuorumCertificate::>>::genesis(); + let qc = QuorumCertificate::< + SequencingTestTypes, + Commitment>, + >::genesis(); let proposal = build_quorum_proposal(&handle, &private_key, 1).await; input.push(SequencingHotShotEvent::QCFormed(either::Left(qc.clone()))); diff --git a/crates/types/src/certificate.rs b/crates/types/src/certificate.rs index 1601f01587..bf02d2f871 100644 --- a/crates/types/src/certificate.rs +++ b/crates/types/src/certificate.rs @@ -20,7 +20,6 @@ use serde::{Deserialize, Serialize}; use std::{ fmt::{self, Debug, Display, Formatter}, hash::Hash, - ops::Deref, }; use tracing::debug; @@ -209,7 +208,6 @@ impl self.leaf_commitment } - fn is_genesis(&self) -> bool { self.is_genesis } @@ -232,7 +230,7 @@ impl Committable commit::RawCommitmentBuilder::new("Quorum Certificate Commitment") .var_size_field("leaf commitment", self.leaf_commitment.as_ref()) - .u64_field("view number", *self.view_number.deref()) + .u64_field("view number", *self.view_number) .constant_str("justify_qc signatures") .var_size_bytes(&signatures_bytes) .finalize() diff --git a/crates/types/src/traits/election.rs b/crates/types/src/traits/election.rs index 2879f89414..8d3b795267 100644 --- a/crates/types/src/traits/election.rs +++ b/crates/types/src/traits/election.rs @@ -128,7 +128,7 @@ where use VoteData::*; match self { DA(c) | Yes(c) | No(c) | Timeout(c) | ViewSyncPreCommit(c) | ViewSyncCommit(c) - | ViewSyncFinalize(c) => c.clone(), + | ViewSyncFinalize(c) => *c, } } @@ -354,11 +354,7 @@ pub trait ConsensusExchange: Send + Sync { self.membership().get_committee_qc_stake_table(), U256::from(self.membership().success_threshold().get()), ); - ::check( - &real_qc_pp, - real_commit.as_ref(), - &qc, - ) + ::check(&real_qc_pp, real_commit.as_ref(), &qc) } AssembledSignature::Yes(qc) => { let real_commit = VoteData::Yes(leaf_commitment).commit(); From 50eb9cb5e76fcb9cd78e7bed76fe007f0cbf633f Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Wed, 11 Oct 2023 14:28:05 -0400 Subject: [PATCH 39/56] lints --- crates/libp2p-networking/src/network/node/handle.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/libp2p-networking/src/network/node/handle.rs b/crates/libp2p-networking/src/network/node/handle.rs index 4c66d1e840..ad91d8096a 100644 --- a/crates/libp2p-networking/src/network/node/handle.rs +++ b/crates/libp2p-networking/src/network/node/handle.rs @@ -136,6 +136,9 @@ impl NetworkNodeHandle { /// /// Will panic if a handler is already spawned #[allow(clippy::unused_async)] + // Tokio and async_std disagree how this function should be linted + #[allow(clippy::ignored_unit_patterns)] + pub async fn spawn_handler(self: &Arc, cb: F) -> impl Future where F: Fn(NetworkEvent, Arc>) -> RET + Sync + Send + 'static, @@ -188,7 +191,7 @@ impl NetworkNodeHandle { } } } - }).map(|()| ()) + }).map(|_| ()) } /// Wait until at least `num_peers` have connected, or until `timeout` time has passed. From e0b737165343337afde8bdbdc3ee44484a7566e5 Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Wed, 11 Oct 2023 14:31:41 -0400 Subject: [PATCH 40/56] lints --- crates/libp2p-networking/src/network/node/handle.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/libp2p-networking/src/network/node/handle.rs b/crates/libp2p-networking/src/network/node/handle.rs index ad91d8096a..fe68d679ce 100644 --- a/crates/libp2p-networking/src/network/node/handle.rs +++ b/crates/libp2p-networking/src/network/node/handle.rs @@ -136,8 +136,8 @@ impl NetworkNodeHandle { /// /// Will panic if a handler is already spawned #[allow(clippy::unused_async)] - // Tokio and async_std disagree how this function should be linted - #[allow(clippy::ignored_unit_patterns)] + // // Tokio and async_std disagree how this function should be linted + // #[allow(clippy::ignored_unit_patterns)] pub async fn spawn_handler(self: &Arc, cb: F) -> impl Future where From d7e8ebfbaa191a069da82a8e4d304f43d249ffbc Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Wed, 11 Oct 2023 14:33:37 -0400 Subject: [PATCH 41/56] lints --- crates/hotshot-signature-key/src/bn254/bn254_priv.rs | 2 +- crates/hotshot-signature-key/src/bn254/bn254_pub.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/hotshot-signature-key/src/bn254/bn254_priv.rs b/crates/hotshot-signature-key/src/bn254/bn254_priv.rs index 87d80c8dfe..5b16c7a93d 100644 --- a/crates/hotshot-signature-key/src/bn254/bn254_priv.rs +++ b/crates/hotshot-signature-key/src/bn254/bn254_priv.rs @@ -54,7 +54,7 @@ impl BLSPrivKey { } } -#[allow(clippy::incorrect_partial_ord_impl_on_ord_type)] +// #[allow(clippy::incorrect_partial_ord_impl_on_ord_type)] impl PartialOrd for BLSPrivKey { fn partial_cmp(&self, other: &Self) -> Option { let self_bytes = &self.priv_key.to_string(); diff --git a/crates/hotshot-signature-key/src/bn254/bn254_pub.rs b/crates/hotshot-signature-key/src/bn254/bn254_pub.rs index 410a0e12fb..43fc3a6c43 100644 --- a/crates/hotshot-signature-key/src/bn254/bn254_pub.rs +++ b/crates/hotshot-signature-key/src/bn254/bn254_pub.rs @@ -27,7 +27,7 @@ pub struct BLSPubKey { pub_key: VerKey, } -#[allow(clippy::incorrect_partial_ord_impl_on_ord_type)] +// #[allow(clippy::incorrect_partial_ord_impl_on_ord_type)] impl PartialOrd for BLSPubKey { fn partial_cmp(&self, other: &Self) -> Option { let self_bytes = &self.pub_key.to_string(); From fba4909650f32534031ac25a797a0c45d1efa6d5 Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Wed, 11 Oct 2023 14:38:44 -0400 Subject: [PATCH 42/56] remove get_commit func --- crates/types/src/traits/election.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/crates/types/src/traits/election.rs b/crates/types/src/traits/election.rs index 8d3b795267..b44ea5082c 100644 --- a/crates/types/src/traits/election.rs +++ b/crates/types/src/traits/election.rs @@ -121,17 +121,6 @@ impl VoteData where COMMITMENT: CommitmentBounds, { - /// Return the underlying commitment. - #[must_use] - pub fn get_commit(&self) -> COMMITMENT { - #[allow(clippy::enum_glob_use)] - use VoteData::*; - match self { - DA(c) | Yes(c) | No(c) | Timeout(c) | ViewSyncPreCommit(c) | ViewSyncCommit(c) - | ViewSyncFinalize(c) => *c, - } - } - #[must_use] /// Convert vote data into bytes. /// From 5e01be8f2f605fe2e69cfe1b8dbb38b29ae10d75 Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Wed, 11 Oct 2023 17:37:37 -0400 Subject: [PATCH 43/56] Fix subtraction overflow --- crates/task-impls/src/consensus.rs | 1 + crates/task-impls/src/da.rs | 20 +++++++++++--------- scripts/test.sh | 4 ++-- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index 7e7adad4c8..bad34b19cf 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -723,6 +723,7 @@ where "Proposal's parent missing from storage with commitment: {:?}", justify_qc.leaf_commitment() ); + // TODO ED Remove this return return; }; let parent_commitment = parent.commit(); diff --git a/crates/task-impls/src/da.rs b/crates/task-impls/src/da.rs index 1915704e08..8d686d1685 100644 --- a/crates/task-impls/src/da.rs +++ b/crates/task-impls/src/da.rs @@ -32,7 +32,7 @@ use hotshot_types::{ use snafu::Snafu; use std::{collections::HashMap, marker::PhantomData, sync::Arc}; -use tracing::{debug, error, instrument}; +use tracing::{debug, error, instrument, warn}; #[derive(Snafu, Debug)] /// Error type for consensus tasks @@ -273,10 +273,11 @@ where // the `DAProposalRecv` event. Otherewise, the view number subtraction below will // cause an overflow error. // TODO ED Come back to this - we probably don't need this, but we should also never receive a DAC where this fails, investigate block ready so it doesn't make one for the genesis block - // if view < self.cur_view - 1 { - // warn!("Throwing away DA proposal that is more than one view older"); - // return None; - // } + + if self.cur_view != TYPES::Time::genesis() && view < self.cur_view - 1 { + warn!("Throwing away DA proposal that is more than one view older"); + return None; + } debug!( "Got a DA block with {} transactions!", @@ -509,10 +510,11 @@ where // the `DAProposalRecv` event. Otherewise, the view number subtraction below will // cause an overflow error. // TODO ED Revisit this - // if view < self.cur_view - 1 { - // warn!("Throwing away VID disperse data that is more than one view older"); - // return None; - // } + + if self.cur_view != TYPES::Time::genesis() && view < self.cur_view - 1 { + warn!("Throwing away VID disperse data that is more than one view older"); + return None; + } debug!("VID disperse data is fresh."); let block_commitment = disperse.data.commitment; diff --git a/scripts/test.sh b/scripts/test.sh index 9f3da6d864..019eb0ca1d 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -7,8 +7,8 @@ counter=0 while true; do ((counter++)) echo "Iteration: $counter" - rm "test_log.txt" || true - just test_async_std_pkg_test hotshot-testing ten_tx_seven_nodes >> "test_log.txt" 2>&1 + rm "output.json" || true + just async_std test_basic >> "output.json" 2>&1 error_code=$? if [ "$error_code" -ne 0 ]; then break From 5eb3afa7cba4f979ad7c33c4ce82cf8698ef1bff Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Wed, 11 Oct 2023 19:54:22 -0400 Subject: [PATCH 44/56] Update state map even if no parent --- crates/task-impls/src/consensus.rs | 34 +++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index 0833eaa874..c2d359ec5c 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -726,13 +726,37 @@ where .cloned() }; + // // Justify qc's leaf commitment is not the same as the parent's leaf commitment, but it should be (in this case) let Some(parent) = parent else { + // If no parent then just update our state map and return. We will not vote. error!( "Proposal's parent missing from storage with commitment: {:?}", justify_qc.leaf_commitment() ); - // TODO ED Remove this return + let leaf = SequencingLeaf { + view_number: view, + height: proposal.data.height, + justify_qc: justify_qc.clone(), + parent_commitment: justify_qc.leaf_commitment(), + deltas: Right(proposal.data.block_commitment), + rejected: Vec::new(), + timestamp: time::OffsetDateTime::now_utc().unix_timestamp_nanos(), + proposer_id: sender.to_bytes(), + }; + + let mut consensus = RwLockUpgradableReadGuard::upgrade(consensus).await; + consensus.state_map.insert( + view, + View { + view_inner: ViewInner::Leaf { + leaf: leaf.commit(), + }, + }, + ); + consensus.saved_leaves.insert(leaf.commit(), leaf.clone()); + + return; }; let parent_commitment = parent.commit(); @@ -750,7 +774,6 @@ where // consensus.metrics.invalid_qc.update(1); - // Validate the `height` // TODO Remove height from proposal validation; view number is sufficient // https://github.com/EspressoSystems/HotShot/issues/1796 @@ -913,9 +936,10 @@ where .await; consensus.last_decided_view = new_anchor_view; consensus.metrics.invalid_qc.set(0); - consensus.metrics.last_decided_view.set( - usize::try_from(consensus.last_decided_view.get_u64()).unwrap(), - ); + consensus + .metrics + .last_decided_view + .set(usize::try_from(consensus.last_decided_view.get_u64()).unwrap()); // We're only storing the last QC. We could store more but we're realistically only going to retrieve the last one. if let Err(e) = self.api.store_leaf(old_anchor_view, leaf).await { From cf095c4c885b0db920576bde7938ecf1b7b6279c Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Wed, 11 Oct 2023 20:03:42 -0400 Subject: [PATCH 45/56] Test timeout on both libp2p and web --- crates/testing/tests/timeout.rs | 67 ++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/crates/testing/tests/timeout.rs b/crates/testing/tests/timeout.rs index 913a529462..9ffaa21009 100644 --- a/crates/testing/tests/timeout.rs +++ b/crates/testing/tests/timeout.rs @@ -4,7 +4,72 @@ tokio::test(flavor = "multi_thread", worker_threads = 2) )] #[cfg_attr(async_executor_impl = "async-std", async_std::test)] -async fn test_timeout() { +async fn test_timeout_web() { + use std::time::Duration; + + use hotshot_testing::node_types::SequencingWebImpl; + + use hotshot_testing::{ + completion_task::{CompletionTaskDescription, TimeBasedCompletionTaskDescription}, + node_types::SequencingTestTypes, + overall_safety_task::OverallSafetyPropertiesDescription, + spinning_task::{ChangeNode, SpinningTaskDescription, UpDown}, + test_builder::{TestMetadata, TimingData}, + }; + + async_compatibility_layer::logging::setup_logging(); + async_compatibility_layer::logging::setup_backtrace(); + let timing_data = TimingData { + next_view_timeout: 1000, + ..Default::default() + }; + + // TODO ED Reduce down to 5 nodes once memory network issues is resolved + // https://github.com/EspressoSystems/HotShot/issues/1790 + let mut metadata = TestMetadata { + total_nodes: 10, + start_nodes: 10, + ..Default::default() + }; + let dead_nodes = vec![ChangeNode { + idx: 0, + updown: UpDown::Down, + }]; + + metadata.timing_data = timing_data; + + metadata.overall_safety_properties = OverallSafetyPropertiesDescription { + num_successful_views: 25, + ..Default::default() + }; + + metadata.spinning_properties = SpinningTaskDescription { + node_changes: vec![(Duration::from_millis(500), dead_nodes)], + }; + + metadata.completion_task_description = + CompletionTaskDescription::TimeBasedCompletionTaskBuilder( + TimeBasedCompletionTaskDescription { + duration: Duration::from_secs(30), + }, + ); + + // TODO ED Test with memory network once issue is resolved + // https://github.com/EspressoSystems/HotShot/issues/1790 + metadata + .gen_launcher::() + .launch() + .run_test() + .await; +} + +#[cfg(test)] +#[cfg_attr( + async_executor_impl = "tokio", + tokio::test(flavor = "multi_thread", worker_threads = 2) +)] +#[cfg_attr(async_executor_impl = "async-std", async_std::test)] +async fn test_timeout_libp2p() { use std::time::Duration; use hotshot_testing::node_types::SequencingLibp2pImpl; From 721abe569c348636b8de806bbed2d17d830a7edd Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Wed, 11 Oct 2023 20:04:47 -0400 Subject: [PATCH 46/56] lint --- crates/task-impls/src/consensus.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index c2d359ec5c..eb4a8355b0 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -726,10 +726,10 @@ where .cloned() }; - // + // // Justify qc's leaf commitment is not the same as the parent's leaf commitment, but it should be (in this case) let Some(parent) = parent else { - // If no parent then just update our state map and return. We will not vote. + // If no parent then just update our state map and return. We will not vote. error!( "Proposal's parent missing from storage with commitment: {:?}", justify_qc.leaf_commitment() @@ -755,7 +755,6 @@ where }, ); consensus.saved_leaves.insert(leaf.commit(), leaf.clone()); - return; }; From b3fb79b58d3b0902f2cad6c688cdabb521a933fa Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Wed, 11 Oct 2023 20:24:23 -0400 Subject: [PATCH 47/56] comments --- crates/testing/tests/timeout.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/testing/tests/timeout.rs b/crates/testing/tests/timeout.rs index 9ffaa21009..10409d2519 100644 --- a/crates/testing/tests/timeout.rs +++ b/crates/testing/tests/timeout.rs @@ -4,6 +4,8 @@ tokio::test(flavor = "multi_thread", worker_threads = 2) )] #[cfg_attr(async_executor_impl = "async-std", async_std::test)] +// TODO Add memory network tests after this issue is finished: +// https://github.com/EspressoSystems/HotShot/issues/1790 async fn test_timeout_web() { use std::time::Duration; @@ -24,8 +26,6 @@ async fn test_timeout_web() { ..Default::default() }; - // TODO ED Reduce down to 5 nodes once memory network issues is resolved - // https://github.com/EspressoSystems/HotShot/issues/1790 let mut metadata = TestMetadata { total_nodes: 10, start_nodes: 10, @@ -89,8 +89,6 @@ async fn test_timeout_libp2p() { ..Default::default() }; - // TODO ED Reduce down to 5 nodes once memory network issues is resolved - // https://github.com/EspressoSystems/HotShot/issues/1790 let mut metadata = TestMetadata { total_nodes: 10, start_nodes: 10, From 1e7eb39526891c997025f2b93f9a209390c238fc Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Wed, 11 Oct 2023 20:27:58 -0400 Subject: [PATCH 48/56] update metrics --- crates/task-impls/src/consensus.rs | 4 ++-- crates/testing/tests/timeout.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index eb4a8355b0..9a8bb5b40f 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -706,6 +706,8 @@ where if !self.quorum_exchange.is_valid_cert(&justify_qc) { error!("Invalid justify_qc in proposal for view {}", *view); + let consensus = self.consensus.write().await; + consensus.metrics.invalid_qc.update(1); return; } @@ -771,8 +773,6 @@ where }; let leaf_commitment = leaf.commit(); - // consensus.metrics.invalid_qc.update(1); - // Validate the `height` // TODO Remove height from proposal validation; view number is sufficient // https://github.com/EspressoSystems/HotShot/issues/1796 diff --git a/crates/testing/tests/timeout.rs b/crates/testing/tests/timeout.rs index 10409d2519..e8e4278195 100644 --- a/crates/testing/tests/timeout.rs +++ b/crates/testing/tests/timeout.rs @@ -4,7 +4,7 @@ tokio::test(flavor = "multi_thread", worker_threads = 2) )] #[cfg_attr(async_executor_impl = "async-std", async_std::test)] -// TODO Add memory network tests after this issue is finished: +// TODO Add memory network tests after this issue is finished: // https://github.com/EspressoSystems/HotShot/issues/1790 async fn test_timeout_web() { use std::time::Duration; From bdd8012974db89446e3da92fbf463ccb3d41015b Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Wed, 11 Oct 2023 20:31:11 -0400 Subject: [PATCH 49/56] metric updates --- crates/task-impls/src/consensus.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index 9a8bb5b40f..c359d4b9f6 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -858,6 +858,10 @@ where // starting from the first iteration with a three chain, e.g. right after the else if case nested in the if case above if new_decide_reached { let mut leaf = leaf.clone(); + consensus + .metrics + .last_synced_block_height + .set(usize::try_from(leaf.height).unwrap_or(0)); // If the full block is available for this leaf, include it in the leaf // chain that we send to the client. @@ -939,6 +943,12 @@ where .metrics .last_decided_view .set(usize::try_from(consensus.last_decided_view.get_u64()).unwrap()); + let cur_number_of_views_per_decide_event = + *self.cur_view - consensus.last_decided_view.get_u64(); + consensus + .metrics + .number_of_views_per_decide_event + .add_point(cur_number_of_views_per_decide_event as f64); // We're only storing the last QC. We could store more but we're realistically only going to retrieve the last one. if let Err(e) = self.api.store_leaf(old_anchor_view, leaf).await { From 8acca288b8b3451d5c1cc04f30dd0f240ff5e83e Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Wed, 11 Oct 2023 20:43:14 -0400 Subject: [PATCH 50/56] Create own timeout cert validation function --- crates/task-impls/src/consensus.rs | 8 +++++--- crates/types/src/traits/election.rs | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index c359d4b9f6..011eeec800 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -695,8 +695,10 @@ where return; } - // TODO ED Do we need to check that the commit in the cert is in fact a commit to the correct view? I think we do. - if !self.timeout_exchange.is_valid_cert(&timeout_cert.clone()) { + if !self + .timeout_exchange + .is_valid_timeout_cert(&timeout_cert.clone(), view - 1) + { warn!("Timeout certificate for view {} was invalid", *view); return; } @@ -1356,7 +1358,7 @@ where return false; }; if leaf_commitment != consensus.high_qc.leaf_commitment() { - // TODO ED This happens on the genesis block + // NOTE: This happens on the genesis block debug!( "They don't equal: {:?} {:?}", leaf_commitment, diff --git a/crates/types/src/traits/election.rs b/crates/types/src/traits/election.rs index b44ea5082c..14affde19b 100644 --- a/crates/types/src/traits/election.rs +++ b/crates/types/src/traits/election.rs @@ -1307,6 +1307,28 @@ pub trait TimeoutExchangeType: ConsensusExchange vote_token, }) } + + /// Validate a timeout certificate. + /// This is separate from other certificate verification functions because we also need to + /// verify the certificate is signed over the view we expect + fn is_valid_timeout_cert(&self, qc: &Self::Certificate, view_number: TYPES::Time) -> bool { + let comparison_commitment = view_number.commit(); + + match qc.signatures() { + AssembledSignature::Timeout(qc) => { + let real_commit = VoteData::Timeout(comparison_commitment).commit(); + let real_qc_pp = ::get_public_parameter( + self.membership().get_committee_qc_stake_table(), + U256::from(self.membership().success_threshold().get()), + ); + ::check(&real_qc_pp, real_commit.as_ref(), &qc) + } + _ => { + error!("Expected TimeoutCertificate, received another certificate variant"); + false + } + } + } } impl< From 944c98a8fc541bbce18de5a145a50a424ae23fb9 Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Wed, 11 Oct 2023 20:45:38 -0400 Subject: [PATCH 51/56] lints: --- crates/types/src/traits/election.rs | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/crates/types/src/traits/election.rs b/crates/types/src/traits/election.rs index 14affde19b..f4c8126715 100644 --- a/crates/types/src/traits/election.rs +++ b/crates/types/src/traits/election.rs @@ -1314,19 +1314,16 @@ pub trait TimeoutExchangeType: ConsensusExchange fn is_valid_timeout_cert(&self, qc: &Self::Certificate, view_number: TYPES::Time) -> bool { let comparison_commitment = view_number.commit(); - match qc.signatures() { - AssembledSignature::Timeout(qc) => { - let real_commit = VoteData::Timeout(comparison_commitment).commit(); - let real_qc_pp = ::get_public_parameter( - self.membership().get_committee_qc_stake_table(), - U256::from(self.membership().success_threshold().get()), - ); - ::check(&real_qc_pp, real_commit.as_ref(), &qc) - } - _ => { - error!("Expected TimeoutCertificate, received another certificate variant"); - false - } + if let AssembledSignature::Timeout(qc) = qc.signatures() { + let real_commit = VoteData::Timeout(comparison_commitment).commit(); + let real_qc_pp = ::get_public_parameter( + self.membership().get_committee_qc_stake_table(), + U256::from(self.membership().success_threshold().get()), + ); + ::check(&real_qc_pp, real_commit.as_ref(), &qc) + } else { + error!("Expected TimeoutCertificate, received another certificate variant"); + false } } } From dff85fd7c8becc85f09d56aa3798e7559d841a80 Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Wed, 11 Oct 2023 20:53:20 -0400 Subject: [PATCH 52/56] Comments --- crates/types/src/traits/election.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/crates/types/src/traits/election.rs b/crates/types/src/traits/election.rs index f4c8126715..9f5c6f4e38 100644 --- a/crates/types/src/traits/election.rs +++ b/crates/types/src/traits/election.rs @@ -362,12 +362,7 @@ pub trait ConsensusExchange: Send + Sync { ::check(&real_qc_pp, real_commit.as_ref(), &qc) } AssembledSignature::Timeout(qc) => { - let real_commit = VoteData::Timeout(leaf_commitment).commit(); - let real_qc_pp = ::get_public_parameter( - self.membership().get_committee_qc_stake_table(), - U256::from(self.membership().success_threshold().get()), - ); - ::check(&real_qc_pp, real_commit.as_ref(), &qc) + error!("QC type should not be timeout here"); } AssembledSignature::Genesis() => true, AssembledSignature::ViewSyncPreCommit(_) From 914e8900c28e568dc9b4e21b0a371dbb3473fa7d Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Wed, 11 Oct 2023 20:53:57 -0400 Subject: [PATCH 53/56] Comments --- crates/types/src/traits/election.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/types/src/traits/election.rs b/crates/types/src/traits/election.rs index 9f5c6f4e38..0383c83bcd 100644 --- a/crates/types/src/traits/election.rs +++ b/crates/types/src/traits/election.rs @@ -363,6 +363,7 @@ pub trait ConsensusExchange: Send + Sync { } AssembledSignature::Timeout(qc) => { error!("QC type should not be timeout here"); + false } AssembledSignature::Genesis() => true, AssembledSignature::ViewSyncPreCommit(_) From 0fb23e3f1f44d9afaf222f77eec0b2e8dc2d2865 Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Wed, 11 Oct 2023 20:55:54 -0400 Subject: [PATCH 54/56] lints --- crates/types/src/traits/election.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/types/src/traits/election.rs b/crates/types/src/traits/election.rs index 0383c83bcd..0b660a4221 100644 --- a/crates/types/src/traits/election.rs +++ b/crates/types/src/traits/election.rs @@ -361,7 +361,7 @@ pub trait ConsensusExchange: Send + Sync { ); ::check(&real_qc_pp, real_commit.as_ref(), &qc) } - AssembledSignature::Timeout(qc) => { + AssembledSignature::Timeout(_) => { error!("QC type should not be timeout here"); false } From 689f959abbd34ba1ffdca30d2c623db64df6c49d Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Thu, 12 Oct 2023 10:00:45 -0400 Subject: [PATCH 55/56] Add return if proposal is invliad --- crates/task-impls/src/consensus.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index 011eeec800..7887b7e245 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -786,9 +786,10 @@ where ); return; } - // Validate the signature. + // Validate the signature. This should also catch if the leaf_commitment does not equal our calculated parent commitment else if !view_leader_key.validate(&proposal.signature, leaf_commitment.as_ref()) { error!(?proposal.signature, "Could not verify proposal."); + return; } // Create a positive vote if either liveness or safety check // passes. @@ -811,11 +812,13 @@ where let safety_check = outcome.is_ok(); if let Err(e) = outcome { self.api.send_view_error(view, Arc::new(e)).await; + return; } // Skip if both saftey and liveness checks fail. if !safety_check && !liveness_check { error!("Failed safety check and liveness check"); + return; } } From 6fd0f4024f2ea9814728fb5607ef09cff1faf08e Mon Sep 17 00:00:00 2001 From: elliedavidson <118024407+elliedavidson@users.noreply.github.com> Date: Thu, 12 Oct 2023 10:03:26 -0400 Subject: [PATCH 56/56] lints --- crates/task-impls/src/consensus.rs | 51 +++++++++++++++--------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index 7887b7e245..748e6e9f1d 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -793,33 +793,32 @@ where } // Create a positive vote if either liveness or safety check // passes. - else { - // Liveness check. - let liveness_check = justify_qc.view_number > consensus.locked_view; - - // Safety check. - // Check if proposal extends from the locked leaf. - let outcome = consensus.visit_leaf_ancestors( - justify_qc.view_number, - Terminator::Inclusive(consensus.locked_view), - false, - |leaf| { - // if leaf view no == locked view no then we're done, report success by - // returning true - leaf.view_number != consensus.locked_view - }, - ); - let safety_check = outcome.is_ok(); - if let Err(e) = outcome { - self.api.send_view_error(view, Arc::new(e)).await; - return; - } - // Skip if both saftey and liveness checks fail. - if !safety_check && !liveness_check { - error!("Failed safety check and liveness check"); - return; - } + // Liveness check. + let liveness_check = justify_qc.view_number > consensus.locked_view; + + // Safety check. + // Check if proposal extends from the locked leaf. + let outcome = consensus.visit_leaf_ancestors( + justify_qc.view_number, + Terminator::Inclusive(consensus.locked_view), + false, + |leaf| { + // if leaf view no == locked view no then we're done, report success by + // returning true + leaf.view_number != consensus.locked_view + }, + ); + let safety_check = outcome.is_ok(); + if let Err(e) = outcome { + self.api.send_view_error(view, Arc::new(e)).await; + return; + } + + // Skip if both saftey and liveness checks fail. + if !safety_check && !liveness_check { + error!("Failed safety check and liveness check"); + return; } let high_qc = leaf.justify_qc.clone();