From 720b11fae90ffd0b901b26a7f37ace37b1f0977f Mon Sep 17 00:00:00 2001 From: Brennan Date: Thu, 4 Sep 2025 20:42:21 +0000 Subject: [PATCH 1/8] add votor-messages --- Cargo.lock | 25 +++ Cargo.toml | 3 + votor-messages/Cargo.toml | 39 ++++ votor-messages/src/consensus_message.rs | 178 ++++++++++++++++ votor-messages/src/lib.rs | 12 ++ votor-messages/src/vote.rs | 263 ++++++++++++++++++++++++ 6 files changed, 520 insertions(+) create mode 100644 votor-messages/Cargo.toml create mode 100644 votor-messages/src/consensus_message.rs create mode 100644 votor-messages/src/lib.rs create mode 100644 votor-messages/src/vote.rs diff --git a/Cargo.lock b/Cargo.lock index 63332c88edacc1..fbab0771e53377 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1365,6 +1365,7 @@ checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ "funty", "radium", + "serde", "tap", "wyz", ] @@ -7451,6 +7452,7 @@ dependencies = [ "base64 0.22.1", "blst", "blstrs", + "bytemuck", "cfg_eval", "ff", "group", @@ -7461,6 +7463,9 @@ dependencies = [ "serde_with", "solana-frozen-abi", "solana-frozen-abi-macro", + "solana-signature", + "solana-signer", + "subtle", "thiserror 2.0.16", ] @@ -11855,6 +11860,26 @@ dependencies = [ "thiserror 2.0.16", ] +[[package]] +name = "solana-votor-messages" +version = "3.1.0" +dependencies = [ + "bitvec", + "bytemuck", + "num_enum", + "serde", + "solana-account", + "solana-bls-signatures", + "solana-clock", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-hash", + "solana-logger", + "solana-program", + "solana-vote-interface", + "spl-pod", +] + [[package]] name = "solana-wen-restart" version = "3.1.0" diff --git a/Cargo.toml b/Cargo.toml index f913b7872240f6..f591c2be7835a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -133,6 +133,7 @@ members = [ "vortexor", "vote", "votor", + "votor-messages", "watchtower", "wen-restart", "xdp", @@ -210,6 +211,7 @@ base64 = "0.22.1" bencher = "0.1.5" bincode = "1.3.3" bitflags = { version = "2.9.4" } +bitvec = { version = "1.0.1", features = ["serde"] } blake3 = "1.8.2" borsh = { version = "1.5.7", features = ["derive", "unstable__schema"] } bs58 = { version = "0.5.1", default-features = false } @@ -553,6 +555,7 @@ solana-validator-exit = "3.0.0" solana-version = { path = "version", version = "=3.1.0" } solana-vote = { path = "vote", version = "=3.1.0" } solana-vote-interface = "3.0.0" +solana-votor-messages = { path = "votor-messages", version = "=3.1.0" } solana-vote-program = { path = "programs/vote", version = "=3.1.0", default-features = false } solana-wen-restart = { path = "wen-restart", version = "=3.1.0" } solana-zk-elgamal-proof-program = { path = "programs/zk-elgamal-proof", version = "=3.1.0" } diff --git a/votor-messages/Cargo.toml b/votor-messages/Cargo.toml new file mode 100644 index 00000000000000..397cee3055d7ef --- /dev/null +++ b/votor-messages/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "solana-votor-messages" +description = "Blockchain, Rebuilt for Scale" +documentation = "https://docs.rs/solana-votor-messages" +readme = "../README.md" +version = { workspace = true } +authors = { workspace = true } +repository = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +edition = { workspace = true } + +[features] +frozen-abi = ["dep:solana-frozen-abi", "dep:solana-frozen-abi-macro"] + +[dependencies] +bitvec = { workspace = true } +bytemuck = { workspace = true } +num_enum = { workspace = true } +serde = { workspace = true } +solana-account = { workspace = true } +solana-bls-signatures = { workspace = true, features = [ + "bytemuck", "solana-signer-derive", +] } +solana-clock = { workspace = true } +solana-frozen-abi = { workspace = true, optional = true, features = [ + "frozen-abi", +] } +solana-frozen-abi-macro = { workspace = true, optional = true, features = [ + "frozen-abi", +] } +solana-hash = { workspace = true } +solana-logger = { workspace = true } +solana-program = { workspace = true } +solana-vote-interface = { workspace = true } +spl-pod = { workspace = true } + +[lints] +workspace = true diff --git a/votor-messages/src/consensus_message.rs b/votor-messages/src/consensus_message.rs new file mode 100644 index 00000000000000..118cad0a28b2dc --- /dev/null +++ b/votor-messages/src/consensus_message.rs @@ -0,0 +1,178 @@ +//! Put BLS message here so all clients can agree on the format +use { + crate::vote::Vote, + serde::{Deserialize, Serialize}, + solana_bls_signatures::Signature as BLSSignature, + solana_clock::Slot, + solana_hash::Hash, +}; + +/// The seed used to derive the BLS keypair +pub const BLS_KEYPAIR_DERIVE_SEED: &[u8; 9] = b"alpenglow"; + +/// Block, a (slot, hash) tuple +pub type Block = (Slot, Hash); + +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +/// BLS vote message, we need rank to look up pubkey +pub struct VoteMessage { + /// The vote + pub vote: Vote, + /// The signature + pub signature: BLSSignature, + /// The rank of the validator + pub rank: u16, +} + +/// Certificate details +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)] +pub enum Certificate { + /// Finalize certificate + Finalize(Slot), + /// Fast finalize certificate + FinalizeFast(Slot, Hash), + /// Notarize certificate + Notarize(Slot, Hash), + /// Notarize fallback certificate + NotarizeFallback(Slot, Hash), + /// Skip certificate + Skip(Slot), +} + +/// Certificate type +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)] +pub enum CertificateType { + /// Finalize certificate + Finalize, + /// Fast finalize certificate + FinalizeFast, + /// Notarize certificate + Notarize, + /// Notarize fallback certificate + NotarizeFallback, + /// Skip certificate + Skip, +} + +impl Certificate { + /// Create a new certificate ID from a CertificateType, Option, and Option + pub fn new(certificate_type: CertificateType, slot: Slot, hash: Option) -> Self { + match (certificate_type, hash) { + (CertificateType::Finalize, None) => Certificate::Finalize(slot), + (CertificateType::FinalizeFast, Some(hash)) => Certificate::FinalizeFast(slot, hash), + (CertificateType::Notarize, Some(hash)) => Certificate::Notarize(slot, hash), + (CertificateType::NotarizeFallback, Some(hash)) => { + Certificate::NotarizeFallback(slot, hash) + } + (CertificateType::Skip, None) => Certificate::Skip(slot), + _ => panic!("Invalid certificate type and hash combination"), + } + } + + /// Get the certificate type + pub fn certificate_type(&self) -> CertificateType { + match self { + Certificate::Finalize(_) => CertificateType::Finalize, + Certificate::FinalizeFast(_, _) => CertificateType::FinalizeFast, + Certificate::Notarize(_, _) => CertificateType::Notarize, + Certificate::NotarizeFallback(_, _) => CertificateType::NotarizeFallback, + Certificate::Skip(_) => CertificateType::Skip, + } + } + + /// Get the slot of the certificate + pub fn slot(&self) -> Slot { + match self { + Certificate::Finalize(slot) + | Certificate::FinalizeFast(slot, _) + | Certificate::Notarize(slot, _) + | Certificate::NotarizeFallback(slot, _) + | Certificate::Skip(slot) => *slot, + } + } + + /// Is this a fast finalize certificate? + pub fn is_fast_finalization(&self) -> bool { + matches!(self, Self::FinalizeFast(_, _)) + } + + /// Is this a finalize / fast finalize certificate? + pub fn is_finalization(&self) -> bool { + matches!(self, Self::Finalize(_) | Self::FinalizeFast(_, _)) + } + + /// Is this a notarize fallback certificate? + pub fn is_notarize_fallback(&self) -> bool { + matches!(self, Self::NotarizeFallback(_, _)) + } + + /// Is this a skip certificate? + pub fn is_skip(&self) -> bool { + matches!(self, Self::Skip(_)) + } + + /// Gets the block associated with this certificate, if present + pub fn to_block(self) -> Option { + match self { + Certificate::Finalize(_) | Certificate::Skip(_) => None, + Certificate::Notarize(slot, block_id) + | Certificate::NotarizeFallback(slot, block_id) + | Certificate::FinalizeFast(slot, block_id) => Some((slot, block_id)), + } + } + + /// "Critical" certs are the certificates necessary to make progress + /// We do not consider the next slot for voting until we've seen either + /// a Skip certificate or a NotarizeFallback certificate for ParentReady + /// + /// Note: Notarization certificates necessarily generate a + /// NotarizeFallback certificate as well + pub fn is_critical(&self) -> bool { + matches!(self, Self::NotarizeFallback(_, _) | Self::Skip(_)) + } +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +/// BLS vote message, we need rank to look up pubkey +pub struct CertificateMessage { + /// The certificate + pub certificate: Certificate, + /// The signature + pub signature: BLSSignature, + /// The bitmap for validators, see solana-signer-store for encoding format + pub bitmap: Vec, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[allow(clippy::large_enum_variant)] +/// BLS message data in Alpenglow +pub enum ConsensusMessage { + /// Vote message, with the vote and the rank of the validator. + Vote(VoteMessage), + /// Certificate message + Certificate(CertificateMessage), +} + +impl ConsensusMessage { + /// Create a new vote message + pub fn new_vote(vote: Vote, signature: BLSSignature, rank: u16) -> Self { + Self::Vote(VoteMessage { + vote, + signature, + rank, + }) + } + + /// Create a new certificate message + pub fn new_certificate( + certificate: Certificate, + bitmap: Vec, + signature: BLSSignature, + ) -> Self { + Self::Certificate(CertificateMessage { + certificate, + signature, + bitmap, + }) + } +} diff --git a/votor-messages/src/lib.rs b/votor-messages/src/lib.rs new file mode 100644 index 00000000000000..c78d22cf828c93 --- /dev/null +++ b/votor-messages/src/lib.rs @@ -0,0 +1,12 @@ +//! Alpenglow Vote program +#![cfg_attr(feature = "frozen-abi", feature(min_specialization))] +#![deny(missing_docs)] + +pub mod consensus_message; +pub mod vote; + +#[cfg_attr(feature = "frozen-abi", macro_use)] +#[cfg(feature = "frozen-abi")] +extern crate solana_frozen_abi_macro; + +solana_program::declare_id!("Vote222222222222222222222222222222222222222"); diff --git a/votor-messages/src/vote.rs b/votor-messages/src/vote.rs new file mode 100644 index 00000000000000..939d3ad5a562b3 --- /dev/null +++ b/votor-messages/src/vote.rs @@ -0,0 +1,263 @@ +//! Vote data types for use by clients +use { + serde::{Deserialize, Serialize}, + solana_hash::Hash, + solana_program::clock::Slot, +}; + +/// Enum that clients can use to parse and create the vote +/// structures expected by the program +#[cfg_attr( + feature = "frozen-abi", + derive(AbiExample, AbiEnumVisitor), + frozen_abi(digest = "FRn4f3PTtbvw3uv2r3qF8K49a5UF4QqDuVdyeshtipTW") +)] +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +pub enum Vote { + /// A notarization vote + Notarize(NotarizationVote), + /// A finalization vote + Finalize(FinalizationVote), + /// A skip vote + Skip(SkipVote), + /// A notarization fallback vote + NotarizeFallback(NotarizationFallbackVote), + /// A skip fallback vote + SkipFallback(SkipFallbackVote), +} + +impl Vote { + /// Create a new notarization vote + pub fn new_notarization_vote(slot: Slot, block_id: Hash) -> Self { + Self::from(NotarizationVote::new(slot, block_id)) + } + + /// Create a new finalization vote + pub fn new_finalization_vote(slot: Slot) -> Self { + Self::from(FinalizationVote::new(slot)) + } + + /// Create a new skip vote + pub fn new_skip_vote(slot: Slot) -> Self { + Self::from(SkipVote::new(slot)) + } + + /// Create a new notarization fallback vote + pub fn new_notarization_fallback_vote(slot: Slot, block_id: Hash) -> Self { + Self::from(NotarizationFallbackVote::new(slot, block_id)) + } + + /// Create a new skip fallback vote + pub fn new_skip_fallback_vote(slot: Slot) -> Self { + Self::from(SkipFallbackVote::new(slot)) + } + + /// The slot which was voted for + pub fn slot(&self) -> Slot { + match self { + Self::Notarize(vote) => vote.slot(), + Self::Finalize(vote) => vote.slot(), + Self::Skip(vote) => vote.slot(), + Self::NotarizeFallback(vote) => vote.slot(), + Self::SkipFallback(vote) => vote.slot(), + } + } + + /// The block id associated with the block which was voted for + pub fn block_id(&self) -> Option<&Hash> { + match self { + Self::Notarize(vote) => Some(vote.block_id()), + Self::NotarizeFallback(vote) => Some(vote.block_id()), + Self::Finalize(_) | Self::Skip(_) | Self::SkipFallback(_) => None, + } + } + + /// Whether the vote is a notarization vote + pub fn is_notarization(&self) -> bool { + matches!(self, Self::Notarize(_)) + } + + /// Whether the vote is a finalization vote + pub fn is_finalize(&self) -> bool { + matches!(self, Self::Finalize(_)) + } + + /// Whether the vote is a skip vote + pub fn is_skip(&self) -> bool { + matches!(self, Self::Skip(_)) + } + + /// Whether the vote is a notarization fallback vote + pub fn is_notarize_fallback(&self) -> bool { + matches!(self, Self::NotarizeFallback(_)) + } + + /// Whether the vote is a skip fallback vote + pub fn is_skip_fallback(&self) -> bool { + matches!(self, Self::SkipFallback(_)) + } + + /// Whether the vote is a notarization or finalization + pub fn is_notarization_or_finalization(&self) -> bool { + matches!(self, Self::Notarize(_) | Self::Finalize(_)) + } +} + +impl From for Vote { + fn from(vote: NotarizationVote) -> Self { + Self::Notarize(vote) + } +} + +impl From for Vote { + fn from(vote: FinalizationVote) -> Self { + Self::Finalize(vote) + } +} + +impl From for Vote { + fn from(vote: SkipVote) -> Self { + Self::Skip(vote) + } +} + +impl From for Vote { + fn from(vote: NotarizationFallbackVote) -> Self { + Self::NotarizeFallback(vote) + } +} + +impl From for Vote { + fn from(vote: SkipFallbackVote) -> Self { + Self::SkipFallback(vote) + } +} + +/// A notarization vote +#[cfg_attr( + feature = "frozen-abi", + derive(AbiExample), + frozen_abi(digest = "5AdwChAjsj5QUXLdpDnGGK2L2nA8y8EajVXi6jsmTv1m") +)] +#[derive(Clone, Copy, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct NotarizationVote { + slot: Slot, + block_id: Hash, +} + +impl NotarizationVote { + /// Construct a notarization vote for `slot` + pub fn new(slot: Slot, block_id: Hash) -> Self { + Self { slot, block_id } + } + + /// The slot to notarize + pub fn slot(&self) -> Slot { + self.slot + } + + /// The block_id of the notarization slot + pub fn block_id(&self) -> &Hash { + &self.block_id + } +} + +/// A finalization vote +#[cfg_attr( + feature = "frozen-abi", + derive(AbiExample), + frozen_abi(digest = "2XQ5N6YLJjF28w7cMFFUQ9SDgKuf9JpJNtAiXSPA8vR2") +)] +#[derive(Clone, Copy, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct FinalizationVote { + slot: Slot, +} + +impl FinalizationVote { + /// Construct a finalization vote for `slot` + pub fn new(slot: Slot) -> Self { + Self { slot } + } + + /// The slot to finalize + pub fn slot(&self) -> Slot { + self.slot + } +} + +/// A skip vote +/// Represents a range of slots to skip +/// inclusive on both ends +#[cfg_attr( + feature = "frozen-abi", + derive(AbiExample), + frozen_abi(digest = "G8Nrx3sMYdnLpHsCNark3BGA58BmW2sqNnqjkYhQHtN") +)] +#[derive(Clone, Copy, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct SkipVote { + pub(crate) slot: Slot, +} + +impl SkipVote { + /// Construct a skip vote for `slot` + pub fn new(slot: Slot) -> Self { + Self { slot } + } + + /// The slot to skip + pub fn slot(&self) -> Slot { + self.slot + } +} + +/// A notarization fallback vote +#[cfg_attr( + feature = "frozen-abi", + derive(AbiExample), + frozen_abi(digest = "7j5ZPwwyz1FaG3fpyQv5PVnQXicdSmqSk8NvqzkG1Eqz") +)] +#[derive(Clone, Copy, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct NotarizationFallbackVote { + slot: Slot, + block_id: Hash, +} + +impl NotarizationFallbackVote { + /// Construct a notarization vote for `slot` + pub fn new(slot: Slot, block_id: Hash) -> Self { + Self { slot, block_id } + } + + /// The slot to notarize + pub fn slot(&self) -> Slot { + self.slot + } + + /// The block_id of the notarization slot + pub fn block_id(&self) -> &Hash { + &self.block_id + } +} + +/// A skip fallback vote +#[cfg_attr( + feature = "frozen-abi", + derive(AbiExample), + frozen_abi(digest = "WsUNum8V62gjRU1yAnPuBMAQui4YvMwD1RwrzHeYkeF") +)] +#[derive(Clone, Copy, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct SkipFallbackVote { + pub(crate) slot: Slot, +} + +impl SkipFallbackVote { + /// Construct a skip fallback vote for `slot` + pub fn new(slot: Slot) -> Self { + Self { slot } + } + + /// The slot to skip + pub fn slot(&self) -> Slot { + self.slot + } +} From b89aea288f731ddda3d1a628bc0ecc6e412e388e Mon Sep 17 00:00:00 2001 From: Brennan Date: Thu, 4 Sep 2025 20:50:54 +0000 Subject: [PATCH 2/8] update lib --- votor-messages/src/lib.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/votor-messages/src/lib.rs b/votor-messages/src/lib.rs index c78d22cf828c93..ba2ee9995d3c7c 100644 --- a/votor-messages/src/lib.rs +++ b/votor-messages/src/lib.rs @@ -1,4 +1,4 @@ -//! Alpenglow Vote program +//! Alpenglow vote message types #![cfg_attr(feature = "frozen-abi", feature(min_specialization))] #![deny(missing_docs)] @@ -8,5 +8,3 @@ pub mod vote; #[cfg_attr(feature = "frozen-abi", macro_use)] #[cfg(feature = "frozen-abi")] extern crate solana_frozen_abi_macro; - -solana_program::declare_id!("Vote222222222222222222222222222222222222222"); From 961f435df678129a6ed71f34a52e035578bcfca1 Mon Sep 17 00:00:00 2001 From: Brennan Date: Thu, 4 Sep 2025 21:01:18 +0000 Subject: [PATCH 3/8] clean up deps --- Cargo.lock | 9 --------- Cargo.toml | 1 - votor-messages/Cargo.toml | 10 +--------- votor-messages/src/vote.rs | 2 +- 4 files changed, 2 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fbab0771e53377..abbdb436a936b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1365,7 +1365,6 @@ checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ "funty", "radium", - "serde", "tap", "wyz", ] @@ -11864,20 +11863,12 @@ dependencies = [ name = "solana-votor-messages" version = "3.1.0" dependencies = [ - "bitvec", - "bytemuck", - "num_enum", "serde", - "solana-account", "solana-bls-signatures", "solana-clock", "solana-frozen-abi", "solana-frozen-abi-macro", "solana-hash", - "solana-logger", - "solana-program", - "solana-vote-interface", - "spl-pod", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f591c2be7835a8..ea21e895e41d84 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -211,7 +211,6 @@ base64 = "0.22.1" bencher = "0.1.5" bincode = "1.3.3" bitflags = { version = "2.9.4" } -bitvec = { version = "1.0.1", features = ["serde"] } blake3 = "1.8.2" borsh = { version = "1.5.7", features = ["derive", "unstable__schema"] } bs58 = { version = "0.5.1", default-features = false } diff --git a/votor-messages/Cargo.toml b/votor-messages/Cargo.toml index 397cee3055d7ef..0bc480d8be8754 100644 --- a/votor-messages/Cargo.toml +++ b/votor-messages/Cargo.toml @@ -14,11 +14,7 @@ edition = { workspace = true } frozen-abi = ["dep:solana-frozen-abi", "dep:solana-frozen-abi-macro"] [dependencies] -bitvec = { workspace = true } -bytemuck = { workspace = true } -num_enum = { workspace = true } serde = { workspace = true } -solana-account = { workspace = true } solana-bls-signatures = { workspace = true, features = [ "bytemuck", "solana-signer-derive", ] } @@ -29,11 +25,7 @@ solana-frozen-abi = { workspace = true, optional = true, features = [ solana-frozen-abi-macro = { workspace = true, optional = true, features = [ "frozen-abi", ] } -solana-hash = { workspace = true } -solana-logger = { workspace = true } -solana-program = { workspace = true } -solana-vote-interface = { workspace = true } -spl-pod = { workspace = true } +solana-hash = { workspace = true, features = ["serde"] } [lints] workspace = true diff --git a/votor-messages/src/vote.rs b/votor-messages/src/vote.rs index 939d3ad5a562b3..7aaedc501a8f00 100644 --- a/votor-messages/src/vote.rs +++ b/votor-messages/src/vote.rs @@ -1,8 +1,8 @@ //! Vote data types for use by clients use { serde::{Deserialize, Serialize}, + solana_clock::Slot, solana_hash::Hash, - solana_program::clock::Slot, }; /// Enum that clients can use to parse and create the vote From 063c374188f159c97babe0687baac680ebd8d32f Mon Sep 17 00:00:00 2001 From: Brennan Date: Thu, 4 Sep 2025 21:06:24 +0000 Subject: [PATCH 4/8] fix ABI --- Cargo.lock | 1 + votor-messages/Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index abbdb436a936b2..ba7ee457cfa5ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11869,6 +11869,7 @@ dependencies = [ "solana-frozen-abi", "solana-frozen-abi-macro", "solana-hash", + "solana-logger", ] [[package]] diff --git a/votor-messages/Cargo.toml b/votor-messages/Cargo.toml index 0bc480d8be8754..8fee4707445856 100644 --- a/votor-messages/Cargo.toml +++ b/votor-messages/Cargo.toml @@ -26,6 +26,7 @@ solana-frozen-abi-macro = { workspace = true, optional = true, features = [ "frozen-abi", ] } solana-hash = { workspace = true, features = ["serde"] } +solana-logger = { workspace = true } [lints] workspace = true From 01ba090fbe24e177f10faf6d95ee68d47a143055 Mon Sep 17 00:00:00 2001 From: Brennan Date: Thu, 4 Sep 2025 21:28:33 +0000 Subject: [PATCH 5/8] sort --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ea21e895e41d84..361403b1925620 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -554,8 +554,8 @@ solana-validator-exit = "3.0.0" solana-version = { path = "version", version = "=3.1.0" } solana-vote = { path = "vote", version = "=3.1.0" } solana-vote-interface = "3.0.0" -solana-votor-messages = { path = "votor-messages", version = "=3.1.0" } solana-vote-program = { path = "programs/vote", version = "=3.1.0", default-features = false } +solana-votor-messages = { path = "votor-messages", version = "=3.1.0" } solana-wen-restart = { path = "wen-restart", version = "=3.1.0" } solana-zk-elgamal-proof-program = { path = "programs/zk-elgamal-proof", version = "=3.1.0" } solana-zk-sdk = "4.0.0" From 71632ffd9491cadb2b4482ad6fc1ec6ed3b6bc75 Mon Sep 17 00:00:00 2001 From: Brennan Date: Fri, 5 Sep 2025 16:31:01 +0000 Subject: [PATCH 6/8] PR feedback --- Cargo.lock | 1 + votor-messages/Cargo.toml | 3 + votor-messages/src/consensus_message.rs | 177 +++++++++++++++++++----- 3 files changed, 149 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ba7ee457cfa5ab..91ab20a7243824 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11863,6 +11863,7 @@ dependencies = [ name = "solana-votor-messages" version = "3.1.0" dependencies = [ + "bincode", "serde", "solana-bls-signatures", "solana-clock", diff --git a/votor-messages/Cargo.toml b/votor-messages/Cargo.toml index 8fee4707445856..00cbd153e6f438 100644 --- a/votor-messages/Cargo.toml +++ b/votor-messages/Cargo.toml @@ -28,5 +28,8 @@ solana-frozen-abi-macro = { workspace = true, optional = true, features = [ solana-hash = { workspace = true, features = ["serde"] } solana-logger = { workspace = true } +[dev-dependencies] +bincode = { workspace = true } + [lints] workspace = true diff --git a/votor-messages/src/consensus_message.rs b/votor-messages/src/consensus_message.rs index 118cad0a28b2dc..32f5bd8591794e 100644 --- a/votor-messages/src/consensus_message.rs +++ b/votor-messages/src/consensus_message.rs @@ -1,4 +1,4 @@ -//! Put BLS message here so all clients can agree on the format +//! Put Alpenglow consensus messages here so all clients can agree on the format. use { crate::vote::Vote, serde::{Deserialize, Serialize}, @@ -7,12 +7,60 @@ use { solana_hash::Hash, }; +const VERSION_MAJOR: u8 = 1; +const VERSION_MINOR: u8 = 0; + /// The seed used to derive the BLS keypair pub const BLS_KEYPAIR_DERIVE_SEED: &[u8; 9] = b"alpenglow"; +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +/// The main consensus message containing versioning and the message payload. +pub struct ConsensusMessage { + version_major: u8, + version_minor: u8, + message: Message, +} + +impl ConsensusMessage { + /// Create a new vote message + pub fn new_vote(vote: Vote, signature: BLSSignature, rank: u16) -> Self { + Self { + version_major: VERSION_MAJOR, + version_minor: VERSION_MINOR, + message: Message::Vote(VoteMessage { + vote, + signature, + rank, + }), + } + } + + /// Create a new certificate message + pub fn new_certificate( + certificate: Certificate, + bitmap: Vec, + signature: BLSSignature, + ) -> Self { + Self { + version_major: VERSION_MAJOR, + version_minor: VERSION_MINOR, + message: Message::Certificate(CertificateMessage { + certificate, + signature, + bitmap, + }), + } + } +} + /// Block, a (slot, hash) tuple pub type Block = (Slot, Hash); +#[cfg_attr( + feature = "frozen-abi", + derive(AbiExample), + frozen_abi(digest = "B6rf5Zh4zcGhKdxKVpW6An4Ns2yujVqGvAK6cM5YGFhP") +)] #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] /// BLS vote message, we need rank to look up pubkey pub struct VoteMessage { @@ -24,6 +72,11 @@ pub struct VoteMessage { pub rank: u16, } +#[cfg_attr( + feature = "frozen-abi", + derive(AbiExample, AbiEnumVisitor), + frozen_abi(digest = "APmpbbqEiJtCrxgjSs8FuMNcM1Qyzc5HtMW7KR79DGcF") +)] /// Certificate details #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)] pub enum Certificate { @@ -39,6 +92,11 @@ pub enum Certificate { Skip(Slot), } +#[cfg_attr( + feature = "frozen-abi", + derive(AbiExample, AbiEnumVisitor), + frozen_abi(digest = "3en2tmFekuD3SWbBnNPqeJSrxDeTJkKJe3CCimANrrpQ") +)] /// Certificate type #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)] pub enum CertificateType { @@ -55,7 +113,7 @@ pub enum CertificateType { } impl Certificate { - /// Create a new certificate ID from a CertificateType, Option, and Option + /// Create a new certificate from a CertificateType, Slot, and Option pub fn new(certificate_type: CertificateType, slot: Slot, hash: Option) -> Self { match (certificate_type, hash) { (CertificateType::Finalize, None) => Certificate::Finalize(slot), @@ -120,18 +178,13 @@ impl Certificate { | Certificate::FinalizeFast(slot, block_id) => Some((slot, block_id)), } } - - /// "Critical" certs are the certificates necessary to make progress - /// We do not consider the next slot for voting until we've seen either - /// a Skip certificate or a NotarizeFallback certificate for ParentReady - /// - /// Note: Notarization certificates necessarily generate a - /// NotarizeFallback certificate as well - pub fn is_critical(&self) -> bool { - matches!(self, Self::NotarizeFallback(_, _) | Self::Skip(_)) - } } +#[cfg_attr( + feature = "frozen-abi", + derive(AbiExample), + frozen_abi(digest = "2mt3bVxZBf2QzS7uZknbgP7kun4eEfzjpbW7QwXqz6Qo") +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] /// BLS vote message, we need rank to look up pubkey pub struct CertificateMessage { @@ -143,36 +196,96 @@ pub struct CertificateMessage { pub bitmap: Vec, } +#[cfg_attr( + feature = "frozen-abi", + derive(AbiExample, AbiEnumVisitor), + frozen_abi(digest = "CwKtX5nWfGbQZSBh1YTr3NABwThE4zFTxzQL9K5xkqYW") +)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[allow(clippy::large_enum_variant)] /// BLS message data in Alpenglow -pub enum ConsensusMessage { +pub enum Message { /// Vote message, with the vote and the rank of the validator. Vote(VoteMessage), /// Certificate message Certificate(CertificateMessage), } -impl ConsensusMessage { - /// Create a new vote message - pub fn new_vote(vote: Vote, signature: BLSSignature, rank: u16) -> Self { - Self::Vote(VoteMessage { - vote, - signature, - rank, - }) +#[cfg(test)] +mod test { + use {super::*, bincode}; + + #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] + struct ConsensusMessageUnknown { + version_major: u8, + version_minor: u8, + message: MessageUnknown, } - /// Create a new certificate message - pub fn new_certificate( - certificate: Certificate, - bitmap: Vec, - signature: BLSSignature, - ) -> Self { - Self::Certificate(CertificateMessage { - certificate, - signature, - bitmap, - }) + #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] + pub enum MessageUnknown { + /// Vote message, with the vote and the rank of the validator. + Vote(VoteMessage), + /// Certificate message + Certificate(CertificateMessage), + Unknown, + } + + #[test] + fn test_new_vote() { + let vote_message = ConsensusMessage::new_vote( + Vote::new_notarization_vote(1, Hash::new_unique()), + BLSSignature::default(), + 42, + ); + + // Ensure SerDes works. + let vote_message_serialized = bincode::serialize(&vote_message).unwrap(); + let vote_message_deserialized: ConsensusMessage = + bincode::deserialize(&vote_message_serialized).unwrap(); + assert_eq!(vote_message, vote_message_deserialized); + + // Ensure version fields are properly populated. + assert_eq!(vote_message_deserialized.version_major, VERSION_MAJOR); + assert_eq!(vote_message_deserialized.version_minor, VERSION_MINOR); + } + + #[test] + fn test_unknown_message() { + let unknown_message = ConsensusMessageUnknown { + version_major: VERSION_MAJOR + 1, + version_minor: VERSION_MINOR, + message: MessageUnknown::Unknown, + }; + + // SerDes fails for new, unknown version. + let message_serialized = bincode::serialize(&unknown_message).unwrap(); + assert!(bincode::deserialize::(&message_serialized).is_err()); + + let vote_message_new_version = ConsensusMessageUnknown { + version_major: VERSION_MAJOR + 1, + version_minor: VERSION_MINOR, + message: MessageUnknown::Vote(VoteMessage { + vote: Vote::new_notarization_vote(1, Hash::new_unique()), + signature: BLSSignature::default(), + rank: 42, + }), + }; + + // SerDes succeeds for new version. + let vote_message_new_version_serialized = + bincode::serialize(&vote_message_new_version).unwrap(); + let vote_message_new_version_deserialized: ConsensusMessage = + bincode::deserialize(&vote_message_new_version_serialized).unwrap(); + + // Advanced major version field is detected. + assert_eq!( + vote_message_new_version_deserialized.version_major, + VERSION_MAJOR + 1 + ); + assert_eq!( + vote_message_new_version_deserialized.version_minor, + VERSION_MINOR + ); } } From 3ef21feec74a5cd0d2f32f004b9018630f6ea8e5 Mon Sep 17 00:00:00 2001 From: Brennan Date: Fri, 5 Sep 2025 16:51:19 +0000 Subject: [PATCH 7/8] fix ABI hash --- votor-messages/src/consensus_message.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/votor-messages/src/consensus_message.rs b/votor-messages/src/consensus_message.rs index 32f5bd8591794e..51ceee652ce9d3 100644 --- a/votor-messages/src/consensus_message.rs +++ b/votor-messages/src/consensus_message.rs @@ -199,7 +199,7 @@ pub struct CertificateMessage { #[cfg_attr( feature = "frozen-abi", derive(AbiExample, AbiEnumVisitor), - frozen_abi(digest = "CwKtX5nWfGbQZSBh1YTr3NABwThE4zFTxzQL9K5xkqYW") + frozen_abi(digest = "A153ivdrfucQXtKNtYh9ySFg9t8mrcz3oa4j5MUB3wPV") )] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[allow(clippy::large_enum_variant)] From f78a576c2db1e1c0af62c02c05c90556b2e7dfcd Mon Sep 17 00:00:00 2001 From: Brennan Date: Mon, 8 Sep 2025 16:11:49 +0000 Subject: [PATCH 8/8] remove versioning --- Cargo.lock | 1 - votor-messages/Cargo.toml | 3 - votor-messages/src/consensus_message.rs | 140 ++++-------------------- 3 files changed, 21 insertions(+), 123 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 91ab20a7243824..ba7ee457cfa5ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11863,7 +11863,6 @@ dependencies = [ name = "solana-votor-messages" version = "3.1.0" dependencies = [ - "bincode", "serde", "solana-bls-signatures", "solana-clock", diff --git a/votor-messages/Cargo.toml b/votor-messages/Cargo.toml index 00cbd153e6f438..8fee4707445856 100644 --- a/votor-messages/Cargo.toml +++ b/votor-messages/Cargo.toml @@ -28,8 +28,5 @@ solana-frozen-abi-macro = { workspace = true, optional = true, features = [ solana-hash = { workspace = true, features = ["serde"] } solana-logger = { workspace = true } -[dev-dependencies] -bincode = { workspace = true } - [lints] workspace = true diff --git a/votor-messages/src/consensus_message.rs b/votor-messages/src/consensus_message.rs index 51ceee652ce9d3..5d0a2f62e9e080 100644 --- a/votor-messages/src/consensus_message.rs +++ b/votor-messages/src/consensus_message.rs @@ -7,52 +7,9 @@ use { solana_hash::Hash, }; -const VERSION_MAJOR: u8 = 1; -const VERSION_MINOR: u8 = 0; - /// The seed used to derive the BLS keypair pub const BLS_KEYPAIR_DERIVE_SEED: &[u8; 9] = b"alpenglow"; -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -/// The main consensus message containing versioning and the message payload. -pub struct ConsensusMessage { - version_major: u8, - version_minor: u8, - message: Message, -} - -impl ConsensusMessage { - /// Create a new vote message - pub fn new_vote(vote: Vote, signature: BLSSignature, rank: u16) -> Self { - Self { - version_major: VERSION_MAJOR, - version_minor: VERSION_MINOR, - message: Message::Vote(VoteMessage { - vote, - signature, - rank, - }), - } - } - - /// Create a new certificate message - pub fn new_certificate( - certificate: Certificate, - bitmap: Vec, - signature: BLSSignature, - ) -> Self { - Self { - version_major: VERSION_MAJOR, - version_minor: VERSION_MINOR, - message: Message::Certificate(CertificateMessage { - certificate, - signature, - bitmap, - }), - } - } -} - /// Block, a (slot, hash) tuple pub type Block = (Slot, Hash); @@ -199,93 +156,38 @@ pub struct CertificateMessage { #[cfg_attr( feature = "frozen-abi", derive(AbiExample, AbiEnumVisitor), - frozen_abi(digest = "A153ivdrfucQXtKNtYh9ySFg9t8mrcz3oa4j5MUB3wPV") + frozen_abi(digest = "CwKtX5nWfGbQZSBh1YTr3NABwThE4zFTxzQL9K5xkqYW") )] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[allow(clippy::large_enum_variant)] /// BLS message data in Alpenglow -pub enum Message { +pub enum ConsensusMessage { /// Vote message, with the vote and the rank of the validator. Vote(VoteMessage), /// Certificate message Certificate(CertificateMessage), } -#[cfg(test)] -mod test { - use {super::*, bincode}; - - #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] - struct ConsensusMessageUnknown { - version_major: u8, - version_minor: u8, - message: MessageUnknown, - } - - #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] - pub enum MessageUnknown { - /// Vote message, with the vote and the rank of the validator. - Vote(VoteMessage), - /// Certificate message - Certificate(CertificateMessage), - Unknown, - } - - #[test] - fn test_new_vote() { - let vote_message = ConsensusMessage::new_vote( - Vote::new_notarization_vote(1, Hash::new_unique()), - BLSSignature::default(), - 42, - ); - - // Ensure SerDes works. - let vote_message_serialized = bincode::serialize(&vote_message).unwrap(); - let vote_message_deserialized: ConsensusMessage = - bincode::deserialize(&vote_message_serialized).unwrap(); - assert_eq!(vote_message, vote_message_deserialized); - - // Ensure version fields are properly populated. - assert_eq!(vote_message_deserialized.version_major, VERSION_MAJOR); - assert_eq!(vote_message_deserialized.version_minor, VERSION_MINOR); +impl ConsensusMessage { + /// Create a new vote message + pub fn new_vote(vote: Vote, signature: BLSSignature, rank: u16) -> Self { + Self::Vote(VoteMessage { + vote, + signature, + rank, + }) } - #[test] - fn test_unknown_message() { - let unknown_message = ConsensusMessageUnknown { - version_major: VERSION_MAJOR + 1, - version_minor: VERSION_MINOR, - message: MessageUnknown::Unknown, - }; - - // SerDes fails for new, unknown version. - let message_serialized = bincode::serialize(&unknown_message).unwrap(); - assert!(bincode::deserialize::(&message_serialized).is_err()); - - let vote_message_new_version = ConsensusMessageUnknown { - version_major: VERSION_MAJOR + 1, - version_minor: VERSION_MINOR, - message: MessageUnknown::Vote(VoteMessage { - vote: Vote::new_notarization_vote(1, Hash::new_unique()), - signature: BLSSignature::default(), - rank: 42, - }), - }; - - // SerDes succeeds for new version. - let vote_message_new_version_serialized = - bincode::serialize(&vote_message_new_version).unwrap(); - let vote_message_new_version_deserialized: ConsensusMessage = - bincode::deserialize(&vote_message_new_version_serialized).unwrap(); - - // Advanced major version field is detected. - assert_eq!( - vote_message_new_version_deserialized.version_major, - VERSION_MAJOR + 1 - ); - assert_eq!( - vote_message_new_version_deserialized.version_minor, - VERSION_MINOR - ); + /// Create a new certificate message + pub fn new_certificate( + certificate: Certificate, + bitmap: Vec, + signature: BLSSignature, + ) -> Self { + Self::Certificate(CertificateMessage { + certificate, + signature, + bitmap, + }) } }