Skip to content
This repository was archived by the owner on Sep 24, 2025. It is now read-only.

Conversation

@samkim-crypto
Copy link
Collaborator

Currently, the type BLSMessage can be either a vote message or a certificate message. However, the processing logic for these two types of messages are verify different and we generally need to write functions for one type or the other. It is much more convenient if these two types of BLS messages is formally split into two types BLSVoteMessage and BLSCertificateMessage.

I split the BLSMessage into the two types. I also moved the signature field so that it precedes the vote and certificate data. Since the certificate data contains the bit vector, it would be much easier to truncate the bit vector if it comes last in the message format.

/// The message data
pub message_data: BLSMessageData,
/// BLS vote message to be sent all to all in Alpenglow
pub struct BLSVoteMessage {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I'm generally fine with this PR. However, we will have BLS vote and certs on the same port. Are you saying we should put them into different ports? Or we should try deserialize as vote first, then try cert?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I think it is fine to use the same ports, but the validator logic does need to process Vote and Certificate differently upon receiving them before verifying the signatures:

  • on Vote message, fetch the corresponding BLS public key pertaining to the validator rank
  • on Certificate message, build an aggregate BLS public key corresponding to the bit-vector
    The certificate case is much more expensive compared to simple votes.

The certificate will also be generally larger due to the bit-vector. So maybe we can check the length to determine which type of BLS message it is. Excluding the signatures, Vote seems to be at most 82 bytes (8 bytes slot x 2 + 32 bytes hash x 2) while a certificate will contain:

  • 1 byte for certificate type
  • 8 bytes for slot number
  • 32 bytes for block id
  • 32 bytes for replayed bank hash
  • variable length bit vector
    for a total of 73 bytes excluding bit vector. So when we truncate the bit-vector we can make sure that the certificate message will be at least 83 bytes to make it easily distinguishable from a vote message.

I guess another simple option is to just add a byte identifier at the beginning of Vote and Cert messages to easily distinguish between the two.

Copy link
Collaborator

@wen-coding wen-coding May 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which is why I made it enum in the first place. I think relying on the size is a little bit shady, as we might invent smart ways to make certificate small later. Any problem you can see with enum?
@0x0ece what do you think? What's more convenient for Jump?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would still advocate having two separate types for vote and certificate since many functions make sense only for one of vote or certificate. For example, vote_count only makes sense for a certificate message, but we need to define it for BLSMessage as of right now (considered defining it for the Certificate type, but it is a bit awkward...). We can still add a byte to indicate which type of BLS message it is (in this case, it would be the same serialization as using enums, but just using different types in rust). Or I think it also makes sense to just send it to different ports without the byte.

But I would also be interested to hear what @0x0ece thinks as well though this is more of a rust type issue than an encoding issue.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with that, is it possible to define the extra byte in here (and define serialize/deserialize here if possible) so we don't get out of sync?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I merged signatures into each of the VoteMessageData (now called VoteMessage) and Certificate. We still have a single BLSMessage, but since signatures are part of VoteMessage and Certificate, it will be easier to create dedicated functions for each of these types.

Vote(BLSMessageVoteData),
Vote(VoteMessage),
/// Certificate message
Certificate(Certificate),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be consistent, do you want to rename Certificate(Certificate) to Certificate(CertificateMessage) ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I divided Certificate into Certificate excluding the signature and bit vector and then CertificateMessage, which includes them. I think this is the most consistent with Vote. Let me know what you think.

I did realize that we would ultimately need to make additional modifications to the Certificate format for the fallback certificates where we use a base3 encoding to signify the validators that signed.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, can we just pack the base3 encoding into the bitvec? Do we need other data structures?

Copy link
Collaborator Author

@samkim-crypto samkim-crypto May 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah the encoding itself will be the same. At the end of the day, BitVec is just encoded in the same way as Vec<u8> (in bincode, length prefix followed by bytes). A base3 encoding should also be encoded the same in binary.

We will probably need to replace BitVec with a dedicated type for base3 encoding though so that we have a nice interface to easily add/remove base3 elements and to also check which elements have been added. This is just for fallback/skip certificates.

Alternatively, we can replace BitVec with just Vec<u8> here. Then in consumer logic (in alpenglow), we can just interpret/convert Vec<u8> into a base3 encoding type before we do logic.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@0x0ece what do you think? Which is more convenient for Firedancer? I think Vec is probably easier?

@samkim-crypto samkim-crypto merged commit 21f0fa1 into master May 20, 2025
6 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants