Skip to content
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

Vote and Cert validation #3521

Closed
wants to merge 15 commits into from
Closed

Vote and Cert validation #3521

wants to merge 15 commits into from

Conversation

lukaszrzasik
Copy link
Contributor

@lukaszrzasik lukaszrzasik commented Jul 31, 2024

Closes #3520

This PR:

This PR tries to improve a vote and a certificate validation. The existing problem is that both a view and a certificate include a view number. These view numbers are not signed in any way and can be easily spoofed by a malicious actor, e.g. a valid certificate can be received by a malicious leader which can then re-send the received certificate with changed view numbers. These will be accepted as valid certificates by the other nodes and can wreck havoc on the consensus.

The approach taken by this PR is following:

Changes in the Vote trait:
The Vote trait is now Committable on its own (not only its data). Explained below for SimpleVote.
create_signed_vote is now a trait associated function.

Changes to SimpleVote:
vote_commitment calculates the commitment for the whole vote (data + view number).
There's a new private associated function called commit which calculates the commit based on the data and the view number. This method is used in create_signed_vote and in the commit method.

Changes to SimpleCertificate:
The vote_commitment field is now private and should not be used in any way. There is a new associated function which created a certificate.
vote_commitment method calculates and returns the vote's commitment instead of returning the stored the commitment. This means the commitment is calculated from the data and the view number.
is_valid_cert now correctly checks the received signature against the locally calculated commitment which includes the vote's data and the view number.

This PR does not:

Key places to review:

The crucial parts are in:
is_valid_cert method and vote_commitment method of the SimpleCertificate.

@@ -70,7 +70,7 @@ pub struct SimpleCertificate<TYPES: NodeType, VOTEABLE: Voteable, THRESHOLD: Thr
/// The data this certificate is for. I.e the thing that was voted on to create this Certificate
pub data: VOTEABLE,
/// commitment of all the votes this cert should be signed over
pub vote_commitment: Commitment<VOTEABLE>,
pub data_commitment: Commitment<VOTEABLE>,
Copy link
Contributor

Choose a reason for hiding this comment

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

We may not be able to change this, but I'm not completely sure. I would lean towards keeping the old name out of caution.

This doesn't affect the bincode serialization of the struct, but I believe it will change the JSON serialization. I'm not sure what repositories use JSON (if any), though I think the sequencer at the very least has a test that might catch this.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah I think @jbearer mentioned a few weeks ago that changing the schema will cause downstream changes. Are we still in the "try not to change public interfaces" mode? Or are we past that.

@@ -375,11 +375,11 @@ impl<TYPES: NodeType, I: NodeImplementation<TYPES>> ConsensusTaskState<TYPES, I>

debug!(
"Attempting to publish proposal after forming a TC for view {}",
*qc.view_number
*qc.view_number()
Copy link
Contributor

Choose a reason for hiding this comment

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

You're a hero for making these little fixes!!

@lukaszrzasik lukaszrzasik changed the title Lr/vote cert validation Vote and Cert validation Aug 6, 2024
@lukaszrzasik lukaszrzasik marked this pull request as ready for review August 14, 2024 19:43
Copy link
Collaborator

@bfish713 bfish713 left a comment

Choose a reason for hiding this comment

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

I've been trying to wrap my head around this and I think there is some room to clear things up. I keep getting confused on the various usages of vote.commit(), and vote_commentment() as well as where we use the "data commitment". I think maybe we can remove one or the other, and possibly the Committable bound from a few places and that would help clear things up.

/// commitment of all the votes this cert should be signed over
pub vote_commitment: Commitment<VOTEABLE>,
/// Do not use this field, the commitment needs to be calculated instead
vote_commitment: Commitment<VOTE>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we keep this type the same as it was before, since we don't need it anyway: Commitment<VOTE::Data>

Comment on lines 145 to 146
fn vote_commitment(&self) -> Commitment<Self> {
self.commit()
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we just remove if we can just call commit() anyway?

@@ -90,33 +105,39 @@ type SignersMap<COMMITMENT, KEY> = HashMap<
/// Accumulates votes until a certificate is formed. This implementation works for all simple vote and certificate pairs
pub struct VoteAccumulator<
TYPES: NodeType,
VOTE: Vote<TYPES>,
CERT: Certificate<TYPES, Voteable = VOTE::Commitment>,
VOTE: Vote<TYPES> + Committable,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Adding the Commitable bound shouldn't be needed

{
/// Add a vote to the total accumulated votes. Returns the accumulator or the certificate if we
/// have accumulated enough votes to exceed the threshold for creating a certificate.
pub fn accumulate(&mut self, vote: &VOTE, membership: &TYPES::Membership) -> Either<(), CERT> {
let key = vote.signing_key();

let vote_commitment = vote.date_commitment();
let vote_commitment = vote.vote_commitment();
Copy link
Collaborator

Choose a reason for hiding this comment

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

vote.commit()?

VoteAccumulator<TYPES, VOTE, CERT>
impl<
TYPES: NodeType,
VOTE: Vote<TYPES> + Committable + Clone,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same do we need Committable

match TYPES::SignatureKey::sign(private_key, data.commit().as_ref()) {
match TYPES::SignatureKey::sign(
private_key,
<SimpleVote<TYPES, DATA> as Vote<TYPES>>::commit(&data, view).as_ref(),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does Self::commit() not work here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm getting a compiler error because of the name clash. I can change the Vote::commit name if it helps with the readability. Wdyt?

For now, I've simplified it to:

<Self as Vote<TYPES>>::commit(&data, view).as_ref()

The error:

error[E0034]: multiple applicable items in scope
   --> crates/types/src/simple_vote.rs:153:19
    |
153 |             Self::commit(&data, view).as_ref(),
    |                   ^^^^^^ multiple `commit` found
    |
note: candidate #1 is defined in an impl of the trait `Vote` for the type `SimpleVote<TYPES, DATA>`
   --> crates/types/src/simple_vote.rs:164:5
    |
164 |     fn commit(data: &DATA, view: TYPES::Time) -> Commitment<Self> {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: candidate #2 is defined in an impl of the trait `Committable` for the type `SimpleVote<TYPES, DATA>`
   --> crates/types/src/simple_vote.rs:173:5
    |
173 |     fn commit(&self) -> Commitment<Self> {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: use fully-qualified syntax to disambiguate
    |
153 |             <SimpleVote<TYPES, DATA> as Vote>::commit(&data, view).as_ref(),
    |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
153 |             Committable::commit(&data, view).as_ref(),
    |             ~~~~~~~~~~~~~

Comment on lines 39 to 40
/// Gets the (Data + view number) commitment of the vote
fn vote_commitment(&self) -> Commitment<Self>;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same as other comment if Vote must be committable then this is redundant right?

@@ -156,7 +177,7 @@ impl<TYPES: NodeType, VOTE: Vote<TYPES>, CERT: Certificate<TYPES, Voteable = VOT

// TODO: Get the stake from the stake table entry.
*total_stake_casted += stake_table_entry.stake();
total_vote_map.insert(key, (vote.signature(), vote.date_commitment()));
total_vote_map.insert(key, (vote.signature(), vote.vote_commitment()));
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think now that we are calculating a commitment on this call we should not be calling vote_commitment repeatedly and just reuse the value already calculated above

@@ -173,8 +194,8 @@ impl<TYPES: NodeType, VOTE: Vote<TYPES>, CERT: Certificate<TYPES, Voteable = VOT
);

let cert = CERT::create_signed_certificate(
vote.date_commitment(),
vote.date().clone(),
vote.commit(),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same here, we already calculated this value, lets not calculated it again

@@ -94,22 +116,21 @@ impl<TYPES: NodeType, VOTEABLE: Voteable + Committable, THRESHOLD: Threshold<TYP
};
committable::RawCommitmentBuilder::new("Certificate")
.field("data", self.data.commit())
.field("vote_commitment", self.vote_commitment)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we leave this in? It would be easier, because we wouldn't have to do any version switch on the certificate

@bfish713
Copy link
Collaborator

Can we close this now?

@Ancient123 Ancient123 deleted the lr/vote-cert-validation branch October 7, 2024 21:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[AUDIT] - TIQV-1 Validate DA Certificate
4 participants