-
Couldn't load subscription status.
- Fork 29
[votor] Replace VoteCertificate with either VoteCertificateBuilder or CertificateMessage
#304
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
| *signature = Signature::from(current_signature); | ||
| Ok(()) | ||
| let signature_iter = messages.iter().map(|vote_message| &vote_message.signature); | ||
| Ok(self.signature.aggregate_with(signature_iter)?) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I forgot to ask, do you think this function should really go into CertificateMessage?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry are you stating that the aggregate function should go into CertificateMessage instead of VoteCertificate (now VoteCertificateBuilder) or are you stating that maybe we should remove aggregate from votor entirely?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The former, now all we need from VoteCertificate is the aggregate function, I wonder whether we should just move that into BLSMessage::CertificateMessage in the vote program and remove VoteCertificate entirely. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yeah this is actually what I just addressed in this PR.
I removed VoteCertificate and replaced it with CertificateMessage in most places. For example see here.
But I did add VoteCertificateBuilder (maybe I can rename this to CertificateMessageBuilder instead) that is primarily used for signature aggregation. The issue is that CertificateMessage stores the signature in a more compact Signature form while signature aggregation can only be done in SignatureProjective form. So the previous aggregation function took the Signature type, converted it to SignatureProjective, then convert it back to Signature type after aggregation.
The VoteCertificateBuilder now just holds the SignatureProjective type from the start. Once the signatures are all aggregated, then it can use the build function to create CertificateMessage. It should be more efficient this way since it removes unnecessary costs related to type conversions.
I was planning on writing a detailed PR description, but I just wanted to check if the CI check passed first.
votor/src/certificate_pool.rs
Outdated
| #[cfg(test)] | ||
| pub fn slot_notarized_fallback(&self, slot: Slot) -> Option<usize> { | ||
| // TODO: remove this line once `CertificateMessage` is moved into the repo` | ||
| use crate::certificate_pool::vote_certificate::get_vote_count; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need get_vote_count any more, it was only used in tests and those tests have since been removed, I'm removing it in #300
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay great! I removed this during the rebase.
votor/src/certificate_pool.rs
Outdated
| .find_map(|(cert_id, cert)| { | ||
| matches!(cert_id, CertificateId::NotarizeFallback(s,_,_) if *s == slot) | ||
| .then_some(cert.vote_count()) | ||
| .then_some(get_vote_count(cert)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need to return vote count here as I mentioned above, that test only checks if the certificate exists.
| /// A builder for creating a `CertificateMessage` by efficiently aggregating BLS signatures. | ||
| #[derive(Clone)] | ||
| pub struct VoteCertificate(CertificateMessage); | ||
| pub struct VoteCertificateBuilder { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, let's change the file name as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
| // aggregate the votes | ||
| let bitmap = &mut self.0.bitmap; | ||
| /// Aggregates a slice of `VoteMessage`s into the builder. | ||
| #[allow(dead_code)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we will only use aggregate_unchecked in the future, we can remove this one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Related to your comment below, I just removed the unchecked version and kept the safe one. A potential panic will now happen in add_to_certificate.
| output.aggregate(self.vote_entry.transactions.iter())?; | ||
| Ok(()) | ||
| pub fn add_to_certificate(&self, output: &mut VoteCertificateBuilder) { | ||
| output.aggregate_unchecked(&self.vote_entry.transactions) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of calling a function which panics inside, can we use output.aggregate which returns an error and panic here? A bit easier for constructing tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, done!
| pub fn add_to_certificate(&self, output: &mut VoteCertificateBuilder) { | ||
| output | ||
| .aggregate(&self.vote_entry.transactions) | ||
| .expect("Incoming vote message signatures are assumed to be valid") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just double checking, we've made all the appropriate checks in the vote pool such that the two errors in aggregate can't happen right?
| return Err(CertificateError::ValidatorDoesNotExist( |
and
| Ok(self.signature.aggregate_with(signature_iter)?) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, they should not happen unless our check was screwed up, in that case we can panic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah both of these checks should be guaranteed by the sigverify stage.
- If the sigverify stage cannot find a valid public key to verify the signature, then it will discard the vote or cert packet
- The only way the
aggregate_withfunction will fail is if the signature is invalid. The sigverify stage will obviously guarantee this as well
…` or `CertificateMessage` (anza-xyz#304)
…` or `CertificateMessage` (anza-xyz#304)
…` or `CertificateMessage` (anza-xyz#304)
…` or `CertificateMessage` (anza-xyz#304)
…` or `CertificateMessage` (anza-xyz#304)
…` or `CertificateMessage` (anza-xyz#304)
…` or `CertificateMessage` (anza-xyz#304)
…` or `CertificateMessage` (anza-xyz#304)
Problem
The existing BLS vote certificate logic had an inefficiency related to costly type conversions during signature aggregation. The
VoteCertificatetype, a wrapper aroundCertificateMessage, stored its signature in the compact "affine" form. However, because signature aggregation is performed in "projective" form, each call to aggregate required the existing signature to be converted from affine to projective, incurring a non-trivial performance cost on a critical path.Summary of Changes
To address this, I added a
VoteCertificateBuildertype which holds the certificate signature in its projective form. This results in much cleaner and more performance aggregation function. Once all votes are aggregated,VoteCertificateBuilder::buildcan be called to produce the finalCertificateMessage, converting the signature back to the compact affine form only once at the end.With the new builder, I removed the redundant
VoteCertificate, as its role is now cleanly handled byVoteCertificateBuilderfor aggregation andCertificateMessagefor the final product.Additionally, I added both checked (
aggregate) and unchecked (aggregate_unchecked) versions of the aggregation function. Since signatures are pre-verified during the sigverify phase, the consensus logic can use the more performant unchecked version.Fixes #