Conversation
creachadair
left a comment
There was a problem hiding this comment.
A couple nits and a couple questions, but this seems on the right track to me.
types/vote.go
Outdated
| } | ||
|
|
||
| func voteFromProto(pv *tmproto.Vote) (*Vote, error) { | ||
| if pv == nil { |
There was a problem hiding this comment.
As a general rule, please don't inject nil checks for these. The pointer is the actionable type for a protobuf message, so if the caller passes nil here it's not a recoverable error and should probably be allowed to panic.
(A program that calls this function with a nil Vote message is already statically incorrect. If there needs to e a check, it should be on the caller's side. But the way we use it, that isn't necessary).
There was a problem hiding this comment.
I just kept the existing code as-is as far as possible, but I agree - I'll change this.
types/vote_test.go
Outdated
| tc.malleateVote(vote) | ||
| assert.Equal(t, tc.expectErr, vote.ValidateBasic() != nil, "Validate Basic had an unexpected result") | ||
| votes := []*Vote{examplePrevote(t), examplePrecommit(t)} | ||
| for i, vote := range votes { |
There was a problem hiding this comment.
Some of this is pre-existing, but I'd like to put in a plug for getting rid of this loop (instead test each vote separately). I'd also suggest separating these test cases into batches: "ok", "basic validation errors", "prevote errors", and "precommit errors" that do not require flags in the test case.
The fiddly logic to check "is this the specially designated good case" and the various legerdemain with computing Booleans makes it really hard to decipher what is going on when a test fails. The check per vote is short, it's ok to copy paste. It'll all be on the same screenful. 🙂
It would make a little more code, but IMO tests are a good place to strongly favor legibility and obviousness over compactness IMO.
Schematically:
type testCase struct {
name string
malleateVote func(*Vote)
}
okTests := []testCase{
// …
}
for _, test := range okTests {
t.Run(test.name, func(t *testing.T) {
require.NoError(t, examplePrevote(t).ValidateBasic(), "valid prevote")
require.NoError(t, examplePrecommit(t).ValidateBasic(), "valid precommit")
})
}
basicErrTests := []testCase{
// …
}
for _, test := range basicErrTests {
t.Run(test.name, func(t *testing.T) {
require.Error(t, examplePrevote(t).ValidateBasic(), "invalid prevote")
require.Error(t, examplePrecommit(t).ValidateBasic(), "invalid precommit")
})
}
// etc.
types/vote_test.go
Outdated
| err := privVal.SignVote(ctx, "test_chain_id", v) | ||
| vote.Signature = v.Signature | ||
| require.NoError(t, err) | ||
| tc.malleateVote(vote) |
types/vote_test.go
Outdated
| require.NoError(t, err) | ||
| tc.malleateVote(vote) | ||
| // ValidateBasic errors should be consistent across prevotes and precommits | ||
| assert.Equal(t, tc.expectBasicErr, vote.ValidateBasic() != nil, "ValidateBasic had an unexpected result") |
There was a problem hiding this comment.
Is there some reason these are assert instead of require?
There was a problem hiding this comment.
It seems as though different authors had different conventions throughout the history of the codebase. Do you have a preference for one of them?
There was a problem hiding this comment.
The answer is complicated.
With t.Error (analogous to assert) and t.Fatal (analogous to require), I prefer Error where practical so that the test can keep providing useful feedback while it's running anyway. When I write tests with the standard handlers I usually only Fatal for cases where it doesn't make sense to continue.
But with testify, I default to "require" (the fatal form) because it obviates the need to check the result and explicitly continue (which defeats the point of the assertion). Since the standard case already has the condition it's not a big addition.
If it were up to me we wouldn't use either, but on the above rationale I usually default "require". YMMV.
types/vote.go
Outdated
| // our Vote domain type, performing basic validation with ValidateBasic. Note | ||
| // that this performs no vote extension validation - to additionally validate | ||
| // vote extensions, use VoteWithExtensionFromProto instead. | ||
| func VoteFromProto(pv *tmproto.Vote) (*Vote, error) { |
There was a problem hiding this comment.
I find the names of these two slightly misleading: "VoteFromProto" suggests I'm getting the entire vote, implying all its parts (including the extension). Reading "VoteWithExtensionFromProto" feels like "this one HAS the extension, the other doesn't".
But both return the extension—it's just this one doesn't verify it while the other does.
If this were Java, we'd just say ExtractAndVerifyVoteWithoutExtensionFromProto and ExtractAndVerifyVoteWithExtensionFromProto. But here, I wonder if maybe we should consider separating "extract" and "verify", i.e., just let the caller call ValidateBasic or ValidateWithExtension as they prefer. I don't think we're adding much value by hiding that call, when they already have to know which they want.
There was a problem hiding this comment.
Yes, it does make sense to rather separate the logic for extracting and validating votes. I think it's usually better to have each function just do one thing. I'll separate them 👍
0860e2c to
ae26068
Compare
beda38e to
6cddb4c
Compare
|
I think I've addressed all of the outstanding comments here. Let me know if there's anything else that anyone feels needs to be improved here. |
creachadair
left a comment
There was a problem hiding this comment.
Looks good to me! I don't know the answer to your TODO, though.
No problem! The idea was to address it in a follow-up PR. The approach implemented in this PR doesn't change the existing behavior. |
sergio-mena
left a comment
There was a problem hiding this comment.
Thanks for this patch. It improves upon my eleventh-hour introduction of a boolean before my vacation break.
However, I still don't clearly see how we can guarantee we check the extensions at all callsites where it is needed.
internal/consensus/msgs.go
Outdated
| func (m *VoteMessage) ValidateBasic() error { | ||
| return m.Vote.ValidateBasic() | ||
| // Here we validate votes with vote extensions, since we require vote | ||
| // extensions to be sent in vote messages during consensus. |
There was a problem hiding this comment.
Is it also the case with prevotes?
I mean... may they also come in a VoteMessage
There was a problem hiding this comment.
I'll update the comment. The ValidateWithExtension method ensures (through its internal call to ValidateBasic) that prevotes don't have extensions, but precommits do.
types/vote_set.go
Outdated
| } | ||
|
|
||
| // Check signature. | ||
| // TODO(thane): Should we always require vote extensions in vote sets? |
There was a problem hiding this comment.
I think we should, as long as they are precommits
The only case we don't want precommits to have extensions is when embedded into an evidence, which is what complicates this problem.
Would it make sense to also requires extensions in evidences (as long as they refer to precommits) ?
If yes, then all this logic would be greatly simplified
There was a problem hiding this comment.
It doesn't look like VoteSet is used in relation to evidence. I've now updated the code to use ValidateWithExtension instead and it seems to work, but we'll see when we enforce the vote extension requirement in a subsequent PR.
6cddb4c to
1249fb6
Compare
There was a problem hiding this comment.
Thanks. I still don't 100% the validation of votes w.r.t vote extensions, but I understand from your answers this will only be possible after the whole #8375 is completed
types/vote_test.go
Outdated
| require.NoError(t, pv.SignVote(ctx, chainID, v)) | ||
| vote.Signature = v.Signature | ||
| vote.ExtensionSignature = v.ExtensionSignature | ||
| malleateVote(vote) |
There was a problem hiding this comment.
It seems like this could be just a separate function, is there a strong reason to inject this? I think it's a bit more clear if this is separately called directly after the sign vote method.
There was a problem hiding this comment.
Sure, I've separated it out now. I originally did it like this because it seemed slightly neater to have this functionality handled on a single line, but I see your point.
7b9f7f5 to
e111425
Compare
Some parts of the code need vote extensions to be verified and validated (mostly in consensus), and other parts of the code don't because its possible that, in some cases (as per RFC 017), we won't have vote extensions. This explicitly facilitates that split. Signed-off-by: Thane Thomson <connect@thanethomson.com>
Signed-off-by: Thane Thomson <connect@thanethomson.com>
Co-authored-by: M. J. Fromberger <fromberger@interchain.io>
Co-authored-by: M. J. Fromberger <fromberger@interchain.io>
Signed-off-by: Thane Thomson <connect@thanethomson.com>
Signed-off-by: Thane Thomson <connect@thanethomson.com>
Signed-off-by: Thane Thomson <connect@thanethomson.com>
Signed-off-by: Thane Thomson <connect@thanethomson.com>
Signed-off-by: Thane Thomson <connect@thanethomson.com>
Signed-off-by: Thane Thomson <connect@thanethomson.com>
Signed-off-by: Thane Thomson <connect@thanethomson.com>
Signed-off-by: Thane Thomson <connect@thanethomson.com>
Signed-off-by: Thane Thomson <connect@thanethomson.com>
Signed-off-by: Thane Thomson <connect@thanethomson.com>
Signed-off-by: Thane Thomson <connect@thanethomson.com>
Signed-off-by: Thane Thomson <connect@thanethomson.com>
Signed-off-by: Thane Thomson <connect@thanethomson.com>
e111425 to
1e6fdce
Compare
* Split vote verification/validation based on vote extensions Some parts of the code need vote extensions to be verified and validated (mostly in consensus), and other parts of the code don't because its possible that, in some cases (as per RFC 017), we won't have vote extensions. This explicitly facilitates that split. Signed-off-by: Thane Thomson <connect@thanethomson.com> * Only sign extensions in precommits, not prevotes Signed-off-by: Thane Thomson <connect@thanethomson.com> * Update privval/file.go Co-authored-by: M. J. Fromberger <fromberger@interchain.io> * Apply suggestions from code review Co-authored-by: M. J. Fromberger <fromberger@interchain.io> * Temporarily disable extension requirement again for E2E testing Signed-off-by: Thane Thomson <connect@thanethomson.com> * Reorganize comment for clarity Signed-off-by: Thane Thomson <connect@thanethomson.com> * Leave vote validation and pre-call nil check up to caller of VoteToProto Signed-off-by: Thane Thomson <connect@thanethomson.com> * Split complex vote validation test into multiple tests Signed-off-by: Thane Thomson <connect@thanethomson.com> * Universally enforce no vote extensions on any vote type but precommits Signed-off-by: Thane Thomson <connect@thanethomson.com> * Make error messages more generic Signed-off-by: Thane Thomson <connect@thanethomson.com> * Verify with vote extensions when constructing a VoteSet Signed-off-by: Thane Thomson <connect@thanethomson.com> * Expand comment for clarity Signed-off-by: Thane Thomson <connect@thanethomson.com> * Add extension check for prevotes prior to signing votes Signed-off-by: Thane Thomson <connect@thanethomson.com> * Fix supporting test code to only inject extensions into precommits Signed-off-by: Thane Thomson <connect@thanethomson.com> * Separate vote malleation from signing in vote tests for clarity Signed-off-by: Thane Thomson <connect@thanethomson.com> * Add extension signature length check and corresponding test Signed-off-by: Thane Thomson <connect@thanethomson.com> * Perform basic vote validation in CommitToVoteSet Signed-off-by: Thane Thomson <connect@thanethomson.com> Co-authored-by: M. J. Fromberger <fromberger@interchain.io>
* Split vote verification/validation based on vote extensions Some parts of the code need vote extensions to be verified and validated (mostly in consensus), and other parts of the code don't because its possible that, in some cases (as per RFC 017), we won't have vote extensions. This explicitly facilitates that split. Signed-off-by: Thane Thomson <connect@thanethomson.com> * Only sign extensions in precommits, not prevotes Signed-off-by: Thane Thomson <connect@thanethomson.com> * Update privval/file.go Co-authored-by: M. J. Fromberger <fromberger@interchain.io> * Apply suggestions from code review Co-authored-by: M. J. Fromberger <fromberger@interchain.io> * Temporarily disable extension requirement again for E2E testing Signed-off-by: Thane Thomson <connect@thanethomson.com> * Reorganize comment for clarity Signed-off-by: Thane Thomson <connect@thanethomson.com> * Leave vote validation and pre-call nil check up to caller of VoteToProto Signed-off-by: Thane Thomson <connect@thanethomson.com> * Split complex vote validation test into multiple tests Signed-off-by: Thane Thomson <connect@thanethomson.com> * Universally enforce no vote extensions on any vote type but precommits Signed-off-by: Thane Thomson <connect@thanethomson.com> * Make error messages more generic Signed-off-by: Thane Thomson <connect@thanethomson.com> * Verify with vote extensions when constructing a VoteSet Signed-off-by: Thane Thomson <connect@thanethomson.com> * Expand comment for clarity Signed-off-by: Thane Thomson <connect@thanethomson.com> * Add extension check for prevotes prior to signing votes Signed-off-by: Thane Thomson <connect@thanethomson.com> * Fix supporting test code to only inject extensions into precommits Signed-off-by: Thane Thomson <connect@thanethomson.com> * Separate vote malleation from signing in vote tests for clarity Signed-off-by: Thane Thomson <connect@thanethomson.com> * Add extension signature length check and corresponding test Signed-off-by: Thane Thomson <connect@thanethomson.com> * Perform basic vote validation in CommitToVoteSet Signed-off-by: Thane Thomson <connect@thanethomson.com> Co-authored-by: M. J. Fromberger <fromberger@interchain.io>
* Split vote verification/validation based on vote extensions Some parts of the code need vote extensions to be verified and validated (mostly in consensus), and other parts of the code don't because its possible that, in some cases (as per RFC 017), we won't have vote extensions. This explicitly facilitates that split. Signed-off-by: Thane Thomson <connect@thanethomson.com> * Only sign extensions in precommits, not prevotes Signed-off-by: Thane Thomson <connect@thanethomson.com> * Update privval/file.go Co-authored-by: M. J. Fromberger <fromberger@interchain.io> * Apply suggestions from code review Co-authored-by: M. J. Fromberger <fromberger@interchain.io> * Temporarily disable extension requirement again for E2E testing Signed-off-by: Thane Thomson <connect@thanethomson.com> * Reorganize comment for clarity Signed-off-by: Thane Thomson <connect@thanethomson.com> * Leave vote validation and pre-call nil check up to caller of VoteToProto Signed-off-by: Thane Thomson <connect@thanethomson.com> * Split complex vote validation test into multiple tests Signed-off-by: Thane Thomson <connect@thanethomson.com> * Universally enforce no vote extensions on any vote type but precommits Signed-off-by: Thane Thomson <connect@thanethomson.com> * Make error messages more generic Signed-off-by: Thane Thomson <connect@thanethomson.com> * Verify with vote extensions when constructing a VoteSet Signed-off-by: Thane Thomson <connect@thanethomson.com> * Expand comment for clarity Signed-off-by: Thane Thomson <connect@thanethomson.com> * Add extension check for prevotes prior to signing votes Signed-off-by: Thane Thomson <connect@thanethomson.com> * Fix supporting test code to only inject extensions into precommits Signed-off-by: Thane Thomson <connect@thanethomson.com> * Separate vote malleation from signing in vote tests for clarity Signed-off-by: Thane Thomson <connect@thanethomson.com> * Add extension signature length check and corresponding test Signed-off-by: Thane Thomson <connect@thanethomson.com> * Perform basic vote validation in CommitToVoteSet Signed-off-by: Thane Thomson <connect@thanethomson.com> Co-authored-by: M. J. Fromberger <fromberger@interchain.io>
* Split vote verification/validation based on vote extensions Some parts of the code need vote extensions to be verified and validated (mostly in consensus), and other parts of the code don't because its possible that, in some cases (as per RFC 017), we won't have vote extensions. This explicitly facilitates that split. Signed-off-by: Thane Thomson <connect@thanethomson.com> * Only sign extensions in precommits, not prevotes Signed-off-by: Thane Thomson <connect@thanethomson.com> * Update privval/file.go Co-authored-by: M. J. Fromberger <fromberger@interchain.io> * Apply suggestions from code review Co-authored-by: M. J. Fromberger <fromberger@interchain.io> * Temporarily disable extension requirement again for E2E testing Signed-off-by: Thane Thomson <connect@thanethomson.com> * Reorganize comment for clarity Signed-off-by: Thane Thomson <connect@thanethomson.com> * Leave vote validation and pre-call nil check up to caller of VoteToProto Signed-off-by: Thane Thomson <connect@thanethomson.com> * Split complex vote validation test into multiple tests Signed-off-by: Thane Thomson <connect@thanethomson.com> * Universally enforce no vote extensions on any vote type but precommits Signed-off-by: Thane Thomson <connect@thanethomson.com> * Make error messages more generic Signed-off-by: Thane Thomson <connect@thanethomson.com> * Verify with vote extensions when constructing a VoteSet Signed-off-by: Thane Thomson <connect@thanethomson.com> * Expand comment for clarity Signed-off-by: Thane Thomson <connect@thanethomson.com> * Add extension check for prevotes prior to signing votes Signed-off-by: Thane Thomson <connect@thanethomson.com> * Fix supporting test code to only inject extensions into precommits Signed-off-by: Thane Thomson <connect@thanethomson.com> * Separate vote malleation from signing in vote tests for clarity Signed-off-by: Thane Thomson <connect@thanethomson.com> * Add extension signature length check and corresponding test Signed-off-by: Thane Thomson <connect@thanethomson.com> * Perform basic vote validation in CommitToVoteSet Signed-off-by: Thane Thomson <connect@thanethomson.com> Co-authored-by: M. J. Fromberger <fromberger@interchain.io>
* Split vote verification/validation based on vote extensions Some parts of the code need vote extensions to be verified and validated (mostly in consensus), and other parts of the code don't because its possible that, in some cases (as per RFC 017), we won't have vote extensions. This explicitly facilitates that split. Signed-off-by: Thane Thomson <connect@thanethomson.com> * Only sign extensions in precommits, not prevotes Signed-off-by: Thane Thomson <connect@thanethomson.com> * Update privval/file.go Co-authored-by: M. J. Fromberger <fromberger@interchain.io> * Apply suggestions from code review Co-authored-by: M. J. Fromberger <fromberger@interchain.io> * Temporarily disable extension requirement again for E2E testing Signed-off-by: Thane Thomson <connect@thanethomson.com> * Reorganize comment for clarity Signed-off-by: Thane Thomson <connect@thanethomson.com> * Leave vote validation and pre-call nil check up to caller of VoteToProto Signed-off-by: Thane Thomson <connect@thanethomson.com> * Split complex vote validation test into multiple tests Signed-off-by: Thane Thomson <connect@thanethomson.com> * Universally enforce no vote extensions on any vote type but precommits Signed-off-by: Thane Thomson <connect@thanethomson.com> * Make error messages more generic Signed-off-by: Thane Thomson <connect@thanethomson.com> * Verify with vote extensions when constructing a VoteSet Signed-off-by: Thane Thomson <connect@thanethomson.com> * Expand comment for clarity Signed-off-by: Thane Thomson <connect@thanethomson.com> * Add extension check for prevotes prior to signing votes Signed-off-by: Thane Thomson <connect@thanethomson.com> * Fix supporting test code to only inject extensions into precommits Signed-off-by: Thane Thomson <connect@thanethomson.com> * Separate vote malleation from signing in vote tests for clarity Signed-off-by: Thane Thomson <connect@thanethomson.com> * Add extension signature length check and corresponding test Signed-off-by: Thane Thomson <connect@thanethomson.com> * Perform basic vote validation in CommitToVoteSet Signed-off-by: Thane Thomson <connect@thanethomson.com> Co-authored-by: M. J. Fromberger <fromberger@interchain.io>
* Split vote verification/validation based on vote extensions Some parts of the code need vote extensions to be verified and validated (mostly in consensus), and other parts of the code don't because its possible that, in some cases (as per RFC 017), we won't have vote extensions. This explicitly facilitates that split. Signed-off-by: Thane Thomson <connect@thanethomson.com> * Only sign extensions in precommits, not prevotes Signed-off-by: Thane Thomson <connect@thanethomson.com> * Update privval/file.go Co-authored-by: M. J. Fromberger <fromberger@interchain.io> * Apply suggestions from code review Co-authored-by: M. J. Fromberger <fromberger@interchain.io> * Temporarily disable extension requirement again for E2E testing Signed-off-by: Thane Thomson <connect@thanethomson.com> * Reorganize comment for clarity Signed-off-by: Thane Thomson <connect@thanethomson.com> * Leave vote validation and pre-call nil check up to caller of VoteToProto Signed-off-by: Thane Thomson <connect@thanethomson.com> * Split complex vote validation test into multiple tests Signed-off-by: Thane Thomson <connect@thanethomson.com> * Universally enforce no vote extensions on any vote type but precommits Signed-off-by: Thane Thomson <connect@thanethomson.com> * Make error messages more generic Signed-off-by: Thane Thomson <connect@thanethomson.com> * Verify with vote extensions when constructing a VoteSet Signed-off-by: Thane Thomson <connect@thanethomson.com> * Expand comment for clarity Signed-off-by: Thane Thomson <connect@thanethomson.com> * Add extension check for prevotes prior to signing votes Signed-off-by: Thane Thomson <connect@thanethomson.com> * Fix supporting test code to only inject extensions into precommits Signed-off-by: Thane Thomson <connect@thanethomson.com> * Separate vote malleation from signing in vote tests for clarity Signed-off-by: Thane Thomson <connect@thanethomson.com> * Add extension signature length check and corresponding test Signed-off-by: Thane Thomson <connect@thanethomson.com> * Perform basic vote validation in CommitToVoteSet Signed-off-by: Thane Thomson <connect@thanethomson.com> Co-authored-by: M. J. Fromberger <fromberger@interchain.io>
* [cherry-picked] abci++: Vote extension cleanup (#8402) * Split vote verification/validation based on vote extensions Some parts of the code need vote extensions to be verified and validated (mostly in consensus), and other parts of the code don't because its possible that, in some cases (as per RFC 017), we won't have vote extensions. This explicitly facilitates that split. Signed-off-by: Thane Thomson <connect@thanethomson.com> * Only sign extensions in precommits, not prevotes Signed-off-by: Thane Thomson <connect@thanethomson.com> * Update privval/file.go Co-authored-by: M. J. Fromberger <fromberger@interchain.io> * Apply suggestions from code review Co-authored-by: M. J. Fromberger <fromberger@interchain.io> * Temporarily disable extension requirement again for E2E testing Signed-off-by: Thane Thomson <connect@thanethomson.com> * Reorganize comment for clarity Signed-off-by: Thane Thomson <connect@thanethomson.com> * Leave vote validation and pre-call nil check up to caller of VoteToProto Signed-off-by: Thane Thomson <connect@thanethomson.com> * Split complex vote validation test into multiple tests Signed-off-by: Thane Thomson <connect@thanethomson.com> * Universally enforce no vote extensions on any vote type but precommits Signed-off-by: Thane Thomson <connect@thanethomson.com> * Make error messages more generic Signed-off-by: Thane Thomson <connect@thanethomson.com> * Verify with vote extensions when constructing a VoteSet Signed-off-by: Thane Thomson <connect@thanethomson.com> * Expand comment for clarity Signed-off-by: Thane Thomson <connect@thanethomson.com> * Add extension check for prevotes prior to signing votes Signed-off-by: Thane Thomson <connect@thanethomson.com> * Fix supporting test code to only inject extensions into precommits Signed-off-by: Thane Thomson <connect@thanethomson.com> * Separate vote malleation from signing in vote tests for clarity Signed-off-by: Thane Thomson <connect@thanethomson.com> * Add extension signature length check and corresponding test Signed-off-by: Thane Thomson <connect@thanethomson.com> * Perform basic vote validation in CommitToVoteSet Signed-off-by: Thane Thomson <connect@thanethomson.com> Co-authored-by: M. J. Fromberger <fromberger@interchain.io> * Appease TestMempoolProgressAfterCreateEmptyBlocksInterval Co-authored-by: Thane Thomson <connect@thanethomson.com> Co-authored-by: M. J. Fromberger <fromberger@interchain.io>
Follows from #8141. Relates to #8272, but does not close it.
I realized that we're signing prevotes' vote extensions too, which is incorrect (we should only be signing and verifying/validating precommits' vote extensions).
After trying to work with #8375 and discussion with @tychoish, we realized that we should rather expose different validation methods than introduce a boolean flag into the
ValidateBasicmethod forVote.I'll be submitting several separate PRs that build on this one and each other (eventually including some commits from #8375) in order to implement RFC 017 (#8317).