Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion core/src/replay_stage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4139,7 +4139,6 @@ impl ReplayStage {
)
},
)
.expect("rooting must succeed")
Comment thread
AshwinSekar marked this conversation as resolved.
}

// To avoid code duplication and keep compatibility with alpenglow, we add this
Expand Down
4 changes: 4 additions & 0 deletions dev-bins/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions programs/sbf/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions votor-messages/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ frozen-abi = [
[dependencies]
agave-feature-set = { workspace = true }
agave-logger = { workspace = true }
bitvec = { workspace = true }
bytemuck = { workspace = true }
log = { workspace = true }
num_enum = { workspace = true }
serde = { workspace = true }
solana-address = { workspace = true, features = ["curve25519"] }
Comment thread
AshwinSekar marked this conversation as resolved.
solana-bls-signatures = { workspace = true, features = [
Expand All @@ -39,5 +42,9 @@ solana-frozen-abi-macro = { workspace = true, optional = true, features = [
solana-hash = { workspace = true, features = ["serde"] }
solana-pubkey = { workspace = true }

[dev-dependencies]
agave-votor-messages = { path = ".", features = ["dev-context-only-utils"] }
tempfile = { workspace = true }

[lints]
workspace = true
107 changes: 96 additions & 11 deletions votor-messages/src/consensus_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,13 @@ pub const BLS_KEYPAIR_DERIVE_SEED: &[u8; 9] = b"alpenglow";

/// Block, a (slot, hash) tuple
pub type Block = (Slot, Hash);

/// A consensus vote.
#[cfg_attr(
feature = "frozen-abi",
derive(AbiExample),
frozen_abi(digest = "5eorzdc18a1sNEUDLAKPgrHCqHmA8ssuTwKSGsZLwBqR")
)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct VoteMessage {
/// The type of the vote.
pub vote: Vote,
Expand Down Expand Up @@ -55,23 +54,96 @@ impl CertificateType {
/// Get the slot of the certificate
pub fn slot(&self) -> Slot {
match self {
Self::Finalize(slot)
| Self::FinalizeFast(slot, _)
| Self::Notarize(slot, _)
| Self::NotarizeFallback(slot, _)
| Self::Skip(slot)
| Self::Genesis(slot, _) => *slot,
CertificateType::Finalize(slot)
| CertificateType::FinalizeFast(slot, _)
| CertificateType::Notarize(slot, _)
| CertificateType::NotarizeFallback(slot, _)
| CertificateType::Genesis(slot, _)
| CertificateType::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(_))
}

/// Is this a genesis certificate?
pub fn is_genesis(&self) -> bool {
matches!(self, Self::Genesis(_, _))
}

/// Gets the block associated with this certificate, if present
pub fn to_block(self) -> Option<Block> {
match self {
Self::Finalize(_) | Self::Skip(_) => None,
CertificateType::Finalize(_) | CertificateType::Skip(_) => None,
CertificateType::Notarize(slot, block_id)
| CertificateType::NotarizeFallback(slot, block_id)
| CertificateType::Genesis(slot, block_id)
| CertificateType::FinalizeFast(slot, block_id) => Some((slot, block_id)),
}
}

/// Reconstructs the single source `Vote` payload for this certificate.
///
/// This method is used primarily by the signature verifier. For
/// certificates formed by aggregating a single type of vote
/// (e.g., a `Notarize` certificate from `Notarize` votes), this function
/// reconstructs the canonical message payload that was signed by validators.
///
/// For `NotarizeFallback` and `Skip` certificates, this function returns the
/// appropriate payload *only* if the certificate was formed from a single
/// vote type (e.g., exclusively from `Notarize` or `Skip` votes). For
/// certificates formed from a mix of two vote types, use the `to_source_votes`
/// function.
pub fn to_source_vote(self) -> Vote {
match self {
Self::Notarize(slot, block_id)
| Self::NotarizeFallback(slot, block_id)
| Self::FinalizeFast(slot, block_id)
| Self::Genesis(slot, block_id) => Some((slot, block_id)),
| Self::NotarizeFallback(slot, block_id) => Vote::new_notarization_vote(slot, block_id),
Self::Finalize(slot) => Vote::new_finalization_vote(slot),
Self::Skip(slot) => Vote::new_skip_vote(slot),
Self::Genesis(slot, block_id) => Vote::new_genesis_vote(slot, block_id),
}
}

/// Reconstructs the two distinct source `Vote` payloads for this certificate.
///
/// This method is primarily used by the signature verifier for certificates that
/// can be formed by aggregating two different types of votes. For example, a
/// `NotarizeFallback` certificate accepts both `Notarize` and `NotarizeFallback`.
///
/// It reconstructs both potential message payloads that were signed by validators, which
/// the verifier uses to check the single aggregate signature.
pub fn to_source_votes(self) -> Option<(Vote, Vote)> {
match self {
Self::NotarizeFallback(slot, block_id) => {
let vote1 = Vote::new_notarization_vote(slot, block_id);
let vote2 = Vote::new_notarization_fallback_vote(slot, block_id);
Some((vote1, vote2))
}
Self::Skip(slot) => {
let vote1 = Vote::new_skip_vote(slot);
let vote2 = Vote::new_skip_fallback_vote(slot);
Some((vote1, vote2))
}
// Other certificate types do not use Base3 encoding.
_ => None,
}
}
}
Expand Down Expand Up @@ -118,4 +190,17 @@ impl ConsensusMessage {
rank,
})
}

/// Create a new certificate.
pub fn new_certificate(
cert_type: CertificateType,
bitmap: Vec<u8>,
signature: BLSSignature,
) -> Self {
Self::Certificate(Certificate {
cert_type,
signature,
bitmap,
})
}
}
53 changes: 41 additions & 12 deletions votor-messages/src/vote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use {
derive(AbiExample, AbiEnumVisitor),
frozen_abi(digest = "AgKoR2cpjUSVCW7Cpihob5nDiPcFt1PXmoPKWJg3zuSB")
)]
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Vote {
/// A notarization vote
Notarize(NotarizationVote),
Expand All @@ -28,6 +28,23 @@ pub enum Vote {
Genesis(GenesisVote),
}

/// Enum of different types of [`Vote`]s.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum VoteType {
/// Finalize vote.
Finalize,
/// Notarize vote.
Notarize,
/// Notarize fallback vote.
NotarizeFallback,
/// Skip vote
Skip,
/// Skip fallback vote.
SkipFallback,
/// Genesis vote.
Genesis,
}

impl Vote {
/// Create a new notarization vote
pub fn new_notarization_vote(slot: Slot, block_id: Hash) -> Self {
Expand All @@ -54,7 +71,7 @@ impl Vote {
Self::from(SkipFallbackVote { slot })
}

/// Create a new skip fallback vote
/// Create a new genesis vote
pub fn new_genesis_vote(slot: Slot, block_id: Hash) -> Self {
Self::from(GenesisVote { slot, block_id })
}
Expand Down Expand Up @@ -106,14 +123,26 @@ impl Vote {
matches!(self, Self::SkipFallback(_))
}

/// Whether the vote is a genesis vote
pub fn is_genesis_vote(&self) -> bool {
matches!(self, Self::Genesis(_))
}

/// Whether the vote is a notarization or finalization
pub fn is_notarization_or_finalization(&self) -> bool {
matches!(self, Self::Notarize(_) | Self::Finalize(_))
}

/// Whether the vote is a genesis vote
pub fn is_genesis_vote(&self) -> bool {
matches!(self, Self::Genesis(_))
/// Returns the [`VoteType`] for the vote.
pub fn get_type(&self) -> VoteType {
match self {
Vote::Notarize(_) => VoteType::Notarize,
Vote::NotarizeFallback(_) => VoteType::NotarizeFallback,
Vote::Skip(_) => VoteType::Skip,
Vote::SkipFallback(_) => VoteType::SkipFallback,
Vote::Finalize(_) => VoteType::Finalize,
Vote::Genesis(_) => VoteType::Genesis,
}
}
}

Expand Down Expand Up @@ -159,7 +188,7 @@ impl From<GenesisVote> for Vote {
derive(AbiExample),
frozen_abi(digest = "5AdwChAjsj5QUXLdpDnGGK2L2nA8y8EajVXi6jsmTv1m")
)]
#[derive(Clone, Copy, Debug, PartialEq, Default, Serialize, Deserialize)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
pub struct NotarizationVote {
/// The slot this vote is cast for.
pub slot: Slot,
Expand All @@ -173,7 +202,7 @@ pub struct NotarizationVote {
derive(AbiExample),
frozen_abi(digest = "2XQ5N6YLJjF28w7cMFFUQ9SDgKuf9JpJNtAiXSPA8vR2")
)]
#[derive(Clone, Copy, Debug, PartialEq, Default, Serialize, Deserialize)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
pub struct FinalizationVote {
/// The slot this vote is cast for.
pub slot: Slot,
Expand All @@ -187,7 +216,7 @@ pub struct FinalizationVote {
derive(AbiExample),
frozen_abi(digest = "G8Nrx3sMYdnLpHsCNark3BGA58BmW2sqNnqjkYhQHtN")
)]
#[derive(Clone, Copy, Debug, PartialEq, Default, Serialize, Deserialize)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
pub struct SkipVote {
/// The slot this vote is cast for.
pub slot: Slot,
Expand All @@ -199,7 +228,7 @@ pub struct SkipVote {
derive(AbiExample),
frozen_abi(digest = "7j5ZPwwyz1FaG3fpyQv5PVnQXicdSmqSk8NvqzkG1Eqz")
)]
#[derive(Clone, Copy, Debug, PartialEq, Default, Serialize, Deserialize)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
pub struct NotarizationFallbackVote {
/// The slot this vote is cast for.
pub slot: Slot,
Expand All @@ -213,7 +242,7 @@ pub struct NotarizationFallbackVote {
derive(AbiExample),
frozen_abi(digest = "WsUNum8V62gjRU1yAnPuBMAQui4YvMwD1RwrzHeYkeF")
)]
#[derive(Clone, Copy, Debug, PartialEq, Default, Serialize, Deserialize)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
pub struct SkipFallbackVote {
/// The slot this vote is cast for.
pub slot: Slot,
Expand All @@ -227,8 +256,8 @@ pub struct SkipFallbackVote {
)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
pub struct GenesisVote {
/// The slot this genesis vote is for
/// The slot this vote is cast for.
pub slot: Slot,
/// The block hash being voted on
/// The block id this vote is for.
pub block_id: Hash,
}
Loading
Loading