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
[R4R] Fixed sized and reordered fields for Vote/Proposal/Heartbeat SignBytes #2598
Conversation
types/vote_test.go
Outdated
0x11, // (field_number << 3) | wire_type | ||
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // round | ||
// remaining fields: | ||
0x22, 0x9, 0x9, 0x0, 0x9, 0x6e, 0x88, 0xf1, 0xff, 0xff, 0xff}, |
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.
@jleni do this kind of tests tests suit your need for test-cases (as mentioned in #2545 (comment))?
I'll add a few more (also for the other types). I can remove the explanations/comments if you'd prefer sth you can easily copy&paste.
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.
That is great. What about version number?
It would be good that it is there too. So breaking changes can be signaled and the HSM can reject signing.
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.
btw, what checks would you recommend in HSM code? I am planning to check at least:
- total length == blob size
- version == whatever we agree on
- field_number == 0x09 (or the corresponding value)
- block number >= 0
- field_number == 0x11 (or the corresponding value)
- round number >= 0
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.
Thanks! Let me sync up with @ebuchman or someone from the tm-core team about the version field.
The checks looks reasonable! I'll think about it and let you know if there should be more checks.
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.
So, the story around the version field is that the version isn't fully integrated yet. I'll add the field to CanocialVote
and could add it manually for a few test cases so it would unblock your work on the HSM parsing side.
@ebuchman does it make sense to change the Signable
interface to take a version before we integrating the "Block Protocol Version"?
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.
That is fine, what we have now is enough for me to continue. I can now use these offsets and layout, complete things and later adjust.
We will eventually need this field anyway.
Maybe it makes sense that you add it and set to zero for now, until the other code starts using it but I don't actually need this anyway.
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.
Did you notice that fields are skipped whenever they are the default value? Does that pose a problem for you? I think you checks would look slightly different, e.g. if the first field is missing field_number == 0x09 would be false but field_number == 0x11 would be true (in case the second field isn't zero too etc).
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.
Does that behaviour (skipping defaults) have an analog in proto3? Or is it amino specific?
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.
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.
Does that behaviour (skipping defaults) have an analog in proto3? Or is it amino specific?
@jleni is right. It is the default behaviour in proto3, too. In amino this ought to be configurable via the write_empty
tag, though: https://github.com/tendermint/go-amino/blob/faa6e731944e2b7b6a46ad202902851e8ce85bee/binary-encode.go#L404-L408
Surprisingly, this doesn't work here. If this isn't a problem while parsing, let's keep it that way. I'll investigate and open an issue in amino if necessary.
Codecov Report
@@ Coverage Diff @@
## develop #2598 +/- ##
===========================================
- Coverage 61.32% 61.31% -0.01%
===========================================
Files 203 203
Lines 16779 16781 +2
===========================================
Hits 10289 10289
- Misses 5619 5620 +1
- Partials 871 872 +1
|
@jleni @ebuchman I think this is ready for review. Let me know if we need to add, or reorder anything. The The ADR needs to be updated / improved, too. I propose to do this in a separate PR / issue. |
types/canonical.go
Outdated
Height int64 `json:"height" binary:"fixed64"` | ||
Round int64 `json:"round" binary:"fixed64"` | ||
POLRound int64 `json:"pol_round" binary:"fixed64"` | ||
Timestamp time.Time `json:"timestamp"` | ||
ChainID string `json:"@chain_id"` |
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.
Should we be consistent with putting the ChainID at the end?
Also, do we even JSON marshal this ever? If not, should we drop the JSON tags too?
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.
Good point. Moved to the end.
Also, do we even JSON marshal this ever? If not, should we drop the JSON tags too?
No, I kept them in case someone wants to marshal them to JSON though. But yeah, it makes sense to delete those JSON tags and re-add them if we see a need.
ChainID string `json:"@chain_id"` | ||
Type string `json:"@type"` |
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 guess one problem with removing type
is that we have to be careful that eg. the encoding of a CanonicalProposal could not be mistaken for that of a CanonicalVote.
Are we certain that marhsalBinary(x CanonicalVote) != marshalBinary(y CanonicalProposal)
?
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.
You are right. They would actually be the same if for instance the first 3 fields are equal (because of the same order and nothing else to differentiate between the two). I'll add a test-case and add back the type.
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.
Maybe we can just use the existing VoteType byte
?
Instead of VoteType, could be SignedMsgType, and then we could add SignedMsgType Proposal = byte(0x10)
for example.
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.
So eg. Proposal/Vote would both start like
Version uint64 `binary:"fixed64"`
Height int64 `binary:"fixed64"`
Round int64 `binary:"fixed64"`
Type byte
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.
That's much better. Thanks for the thorough review and the great suggestions ❤️
This change will touch quite a lot of files though. I'll rebase (and resolve conflicts) and this should be ready for another review.
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.
🥗 🔥 🌯
Height: proposal.Height, | ||
Round: int64(proposal.Round), // cast int->int64 to make amino encode it fixed64 (does not work for int) |
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.
Should we standardize Round's type? make it uint16
or int32
?
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.
Can you open a new issue with the case for it?
{ | ||
CanonicalizeVote("", &Vote{}), | ||
// NOTE: Height and Round are skipped here. This case needs to be considered while parsing. | ||
[]byte{0xb, 0x2a, 0x9, 0x9, 0x0, 0x9, 0x6e, 0x88, 0xf1, 0xff, 0xff, 0xff}, |
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.
how do I get this something changes?
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.
It's just the output of MarshalBinary
. The test serves two purposes: 1) mainly documentation for people wanting to parse this without having to fully implement amino/proto3 (like @jleni) 2) get notified if the encoding changes for whatever reason.
types/canonical.go
Outdated
@@ -27,6 +27,7 @@ type CanonicalProposal struct { | |||
Height int64 `binary:"fixed64"` | |||
Round int64 `binary:"fixed64"` | |||
POLRound int64 `binary:"fixed64"` | |||
Type string |
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.
- it doesn't affect if default fixed size fields ((u)int64) are written or not - add comment about int->int64 casting
- SignedMsgType replaces VoteTypePrevote, VoteTypePrecommit and adds new ProposalType to separate votes from proposal when signed - update test-vectors
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.
Thanks Ismail. I'll make the final adjustments.
types/canonical.go
Outdated
Version uint64 `binary:"fixed64"` | ||
Height int64 `binary:"fixed64"` | ||
Round int64 `binary:"fixed64"` | ||
POLRound int64 `binary:"fixed64"` |
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.
Shouldn't it be
Round
Type
POLRound
ValidatorIndex int `json:"validator_index"` | ||
Version uint64 `binary:"fixed64"` | ||
Height int64 `binary:"fixed64"` | ||
Round int `binary:"fixed64"` |
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.
add Type
types/signed_msg_type.go
Outdated
PrevoteType SignedMsgType = 0x01 | ||
PrecommitType SignedMsgType = 0x02 | ||
// To separate (canonicalized) Vote and Proposal messages: | ||
ProposalType SignedMsgType = 0x03 |
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.
Let's distinguish categories of msg type like "vote-associated", "proposal-associated", "heartbeat-associated". So eg.
// Vote types
PrevoteType SignedMsgType = 0x01
PrecommitType SignedMsgType = 0x02
// Proposal Types
ProposalType SignedMsgType = 0x10
// Heartbeat Types
HeartbeatType SignedMsgType = 0x20
ExampleNewHeartbeatType SignedMsgType = 0x21
FYI: #2626 Sorry I didn't open that sooner |
Just saw this. I've added a commit that uses |
Oh, I changed the order of some fields in that commit and the tests didn't break. Could that be an issue? Also, is |
That's probably because those where not included in the tests (because they are skipped if they are empty). The tests include only those fields which are relevant for the HSM (and currently only for Votes as requested per @jleni; Vote didn't change besides renaming VoteType -> Type).
Yes, amino treats |
resolves #2545