Skip to content

Conversation

sergio-mena
Copy link
Contributor

@sergio-mena sergio-mena commented Mar 8, 2023

Contributes to #485

(please also see second part of the changes in #509)

Contents of this PR:

  • This PR introduces the changes in the ABCI protobufs to pass the vote extension signature together with the extensions in PrepareProposal
  • It demonstrates how to use this data at the application level
    • In the unit tests, with a small example
    • In the e2e app:
      • The e2e app can now find all the data needed to validate the signature
        • some extra functionality was introduced to keep track of the chain ID and the validators' public keys.
      • The e2e app adds all that data in a special transaction at PrepareProposal so that it is also available at ProcessProposal
      • The e2e app uses the data to validate the signature:
        • In PrepareProposal which panics if something is unexpected, as the data comes from the local CometBFT, which should have validated everything itself
        • In ProcessProposal which, 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 PrepareProposal are 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 at PrepareProposal and thus trusting the local CometBFT. In that case PrepareProposal would just be forwarding the information in TXs to ProcessProposal (similarly to how it is done in this PR), and having ProcessProposal verify the extensions and signatures.


PR checklist

  • Tests written/updated
  • Changelog entry added in .changelog (we use unclog to manage our changelog)
  • Updated relevant documentation (docs/ or spec/) and code comments

@sergio-mena sergio-mena added the abci Application blockchain interface label Mar 8, 2023
@sergio-mena sergio-mena self-assigned this Mar 8, 2023
"github.com/cometbft/cometbft/libs/log"
"github.com/cometbft/cometbft/libs/protoio"
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
Copy link
Contributor Author

@sergio-mena sergio-mena Mar 9, 2023

Choose a reason for hiding this comment

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

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?

Copy link
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

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.

Copy link
Contributor

@thanethomson thanethomson left a comment

Choose a reason for hiding this comment

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

One minor nit, but otherwise seems good to me!

I'll approve once the SDK team's happy that the solution works for them.

cfg *Config
restoreSnapshot *abci.Snapshot
restoreChunks [][]byte
chanID string
Copy link
Contributor

Choose a reason for hiding this comment

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

chainID?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch!

Comment on lines 338 to 344
if !ok {
panic("PrepareProposal: received vote from unknown validator")
}

if !pub.VerifySignature(extSignBytes, vote.ExtensionSignature) {
panic("PrepareProposal: received vote with invalid signature")
}
Copy link
Contributor

Choose a reason for hiding this comment

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

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)

Copy link
Contributor Author

@sergio-mena sergio-mena Mar 9, 2023

Choose a reason for hiding this comment

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

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.

Copy link
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Contributor

Choose a reason for hiding this comment

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

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?

Copy link
Contributor

Choose a reason for hiding this comment

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

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.

@sergio-mena sergio-mena marked this pull request as ready for review March 10, 2023 14:09
@sergio-mena sergio-mena requested a review from a team as a code owner March 10, 2023 14:09
@@ -487,12 +487,14 @@ func buildExtendedCommitInfo(ec *types.ExtendedCommit, store Store, initialHeigh
panic(fmt.Errorf("commit at height %d received with missing vote extensions data", ec.Height))
}
ext = ecs.Extension
extSig = ecs.ExtensionSignature
Copy link
Contributor

Choose a reason for hiding this comment

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

in line 434, above, Tendermint -> CometBFT

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for spotting. I'm changing this in an upcoming related PR, which I am doing separately to avoid bloating this one.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

@alexanderbez alexanderbez left a comment

Choose a reason for hiding this comment

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

LGTM :)

Thanks for also providing an example in the tests for how to perform verification app-side 👍

@@ -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;
Copy link
Contributor

Choose a reason for hiding this comment

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

would calling it just extension cause confusion?

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 prefer to err on the safe side (long but clear name), unless someone has a strong opinion on this

@@ -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;
// Vote extension signature created by CometBFT
bytes extension_signature = 4;
Copy link
Contributor

Choose a reason for hiding this comment

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

and just signature?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

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()
Copy link
Contributor

Choose a reason for hiding this comment

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

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.

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 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)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

"github.com/cometbft/cometbft/version"
)

const (
appVersion = 1
voteExtensionKey string = "extensionSum"
voteExtensionMaxVal int64 = 128
reservedKey string = "reservedTxKey"
Copy link
Contributor

Choose a reason for hiding this comment

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

Eventhough addresses do not contain y, it might be a good idea to add a separator to the end of the key.

Suggested change
reservedKey string = "reservedTxKey"
reservedKey string = "reservedTxKey"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, good idea

Copy link
Contributor Author

@sergio-mena sergio-mena Mar 13, 2023

Choose a reason for hiding this comment

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

Also, coming in the next PR

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@sergio-mena sergio-mena merged commit e6d9639 into feature/abci++vef Mar 14, 2023
@sergio-mena sergio-mena deleted the sergio/485-pass-vote-extension-sign-prepareproposal branch March 14, 2023 15:13
sergio-mena added a commit that referenced this pull request Mar 14, 2023
* 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>
@cason
Copy link

cason commented Nov 8, 2024

This PR is missing a changelog entry. And it is a breaking change.

@cason
Copy link

cason commented Nov 8, 2024

Ref #4458.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
abci Application blockchain interface
Projects
No open projects
Status: Done
Development

Successfully merging this pull request may close these issues.

5 participants