Pass signatures of vote extensions in RequestPrepareProposal#489
Conversation
| cryptoenc "github.com/cometbft/cometbft/crypto/encoding" | ||
| "github.com/cometbft/cometbft/libs/log" | ||
| "github.com/cometbft/cometbft/libs/protoio" | ||
| cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" |
There was a problem hiding this comment.
Question to reviewers: Is it OK to import the core CometBFT proto types here? I'm really skeptic about that. I'm tempted to reshuffle the proto definitions to have what we need in ABCI protos.
Opinions?
There was a problem hiding this comment.
Doesn't really matter very much right now, I think.
With the reorg that @mzabaluev will be doing, this will end up changing anyways and will be more consistent across all code that imports our generated Protobuf code.
There was a problem hiding this comment.
Actually, I just did a quick search on the SDK repo and it's used in production code, even some protobufs import them (e.g., query, block, and evidence proto definitions).
So we should be good on e2e app.
thanethomson
left a comment
There was a problem hiding this comment.
One minor nit, but otherwise seems good to me!
I'll approve once the SDK team's happy that the solution works for them.
test/e2e/app/app.go
Outdated
| cfg *Config | ||
| restoreSnapshot *abci.Snapshot | ||
| restoreChunks [][]byte | ||
| chanID string |
test/e2e/app/app.go
Outdated
| if !ok { | ||
| panic("PrepareProposal: received vote from unknown validator") | ||
| } | ||
|
|
||
| if !pub.VerifySignature(extSignBytes, vote.ExtensionSignature) { | ||
| panic("PrepareProposal: received vote with invalid signature") | ||
| } |
There was a problem hiding this comment.
What would the recommended behaviour be for correct applications here? I assume it's not to panic, right? (Just asking preemptively for app developers' sake - I can see how this is useful for picking up problems in our E2E tests)
There was a problem hiding this comment.
So, my understanding is that the signature we're verifying is local (it comes from local CometBFT, which is supposed to have verified it itself when it received the info). A real app could even skip this verification in PrepareProposal under the argument it trusts that CometBFT has verified it.
The missing piece of this PR (hence it's still a draft) is shipping all necessary info into special transactions so that ProcessProposal can also verify (which is where a real app would verify). In that case it wouldn't be a panic, but a ResponseProcessProposal with reject.
There was a problem hiding this comment.
In the case of the app, which verifies vote exts "injected" by the proposer, if any such verification fails, rather if more than 2/3 voting power fails, then it would REJECT the proposal.
There was a problem hiding this comment.
I didn't get this last comment. The proposal contains extensions from 2/3 of the voting power from the previous round, out of which 1/3+1 is guaranteed to be from correct validators. What do you mean by "if more than 2/3 voting power fails"?
Maybe I am not understanding how you plan on using it. Is there anything you could share?
There was a problem hiding this comment.
The proposal contains extensions from 2/3 of the voting power from the previous round, out of which 1/3+1 is guaranteed to be from correct validators.
Yes, given to the proposer during PrepareProposal. The remaining validators that receive the manually injected vote extensions during ProcessProposal have to ensure they get at least 2/3 from the proposer.
| panic(fmt.Errorf("commit at height %d received with missing vote extensions data", ec.Height)) | ||
| } | ||
| ext = ecs.Extension | ||
| extSig = ecs.ExtensionSignature |
There was a problem hiding this comment.
in line 434, above, Tendermint -> CometBFT
There was a problem hiding this comment.
Thanks for spotting. I'm changing this in an upcoming related PR, which I am doing separately to avoid bloating this one.
| @@ -444,6 +444,8 @@ message ExtendedVoteInfo { | |||
| bool signed_last_block = 2; | |||
| // Non-deterministic extension provided by the sending validator's application. | |||
| bytes vote_extension = 3; | |||
There was a problem hiding this comment.
would calling it just extension cause confusion?
There was a problem hiding this comment.
I prefer to err on the safe side (long but clear name), unless someone has a strong opinion on this
| // Non-deterministic extension provided by the sending validator's application. | ||
| bytes vote_extension = 3; | ||
| // Vote extension signature created by CometBFT | ||
| bytes extension_signature = 4; |
There was a problem hiding this comment.
Not possible, we shouldn't confuse it with the signature of the vote itself
| // transaction that attempts to modify the "extensionSum" value. | ||
| txs := make([][]byte, len(req.Txs)+1) | ||
| for i, tx := range req.Txs { | ||
| extCommitBytes, err := req.LocalLastCommit.Marshal() |
There was a problem hiding this comment.
As this serves as an example app, this whole loop could be applied irrespective of extCount>0 to guard against transactions starting with extTxPrefix and reservedKey.
Then build and append the special transaction only if extCount>0.
There was a problem hiding this comment.
I like your suggestion, let me see what I can do.
Also, for the future, we can strengthen the logic of this function, because we do know when vote extensions are active and when they are not. The way it is now is a bit loose, and, since this is a testing app we should probable make if panic if we receive extensions when we shouldn't (or if they are missing when they shouldn't)
There was a problem hiding this comment.
Actually, I decided to go for the "tightening up" the checks we do on vote extensions. Everything is pretty optional in this PR. I have an upcoming PR lined up which does that, and simplifies this loop massively. Please take a look when it's up
| appVersion = 1 | ||
| voteExtensionKey string = "extensionSum" | ||
| voteExtensionMaxVal int64 = 128 | ||
| reservedKey string = "reservedTxKey" |
There was a problem hiding this comment.
Eventhough addresses do not contain y, it might be a good idea to add a separator to the end of the key.
| reservedKey string = "reservedTxKey" | |
| reservedKey string = "reservedTxKey" |
There was a problem hiding this comment.
Yeah, good idea
There was a problem hiding this comment.
Also, coming in the next PR
…gn-prepareproposal
…sign-prepareproposal
…gn-prepareproposal
* Move `apphash`from `Commit`to `FinalizeBlock` (leftovers) (#478) * [partial cherry-pick] abci: Move `app_hash` parameter from `Commit` to `FinalizeBlock` (#8664) * Removed from proto * make proto-gen * make build works * make some tests pass * Fix TestMempoolTxConcurrentWithCommit * Minor change * Update abci/types/types.go * Update internal/state/execution.go * Update test/e2e/app/state.go Co-authored-by: Callum Waters <cmwaters19@gmail.com> * Updated changelog and `UPGRADING.md` * Fixed abci-cli tests, and doc * Addressed @cmwaters' comments * Addressed @cmwaters' comments, part 2 Co-authored-by: Callum Waters <cmwaters19@gmail.com> * Levftover typo in spec --------- Co-authored-by: Callum Waters <cmwaters19@gmail.com> * Compare `types.proto` and `feature/abci++vef` and tip of `v0.36.x` (#488) * CometBFT renaming in types.proto * AppHash * Make proto-gen * Trying 1.20.2 explicitly * Votes can't have zero height (#511) * [partial cherry-pick] e2e: programmable ABCI method times (#8638) (#515) * e2e: programmable ABCI method times * fix linting error Co-authored-by: Callum Waters <cmwaters19@gmail.com> * Pass signatures of vote extensions in `RequestPrepareProposal` (#489) * Pass vote extension signature in `PrepareProposal` * make proto-gen * Verify extensions at PrepareProposal * Addressed @thanethomson's comments * Fix vote extension activation in e2e * ProcessProposal: 1st try * Working.... * Refactoring * Verify signatrue in unit test * spacing * Addressed @lasarojc's comments * Fix test --------- Co-authored-by: Callum Waters <cmwaters19@gmail.com>
|
This PR is missing a changelog entry. And it is a breaking change. |
|
Ref #4458. |
Contributes to #485
(please also see second part of the changes in #509)
Contents of this PR:
PrepareProposalPrepareProposalso that it is also available atProcessProposalPrepareProposalwhich panics if something is unexpected, as the data comes from the local CometBFT, which should have validated everything itselfProcessProposalwhich, rather than panicking, returns reject as the data cannot have come from a correct node (which, in this example, would have panicked while doing the same verification)Note: The checks on
PrepareProposalare done here because e2e app is for testing purposes, and also to demonstrate how to do it. However, real applications will probably be OK with skipping verification atPrepareProposaland thus trusting the local CometBFT. In that casePrepareProposalwould just be forwarding the information in TXs toProcessProposal(similarly to how it is done in this PR), and havingProcessProposalverify the extensions and signatures.PR checklist
.changelog(we use unclog to manage our changelog)docs/orspec/) and code comments