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

internal/consensus: prevote nil if proposal timestamp does not match #7391

Merged

Conversation

williambanfield
Copy link
Contributor

@williambanfield williambanfield commented Dec 7, 2021

This change updates the proposal logic to use the block's timestamp in the proposal message. It adds an additional piece of validation logic to the prevote step to check that the block's timestamp matches the proposal message's timestamp.

This change also adds two tests that ensure that 1. if the proposal message timestamp does match, we prevote the proposal, 2. if the proposal message does not match we prevote nil.

related to: #6942

internal/consensus/state.go Outdated Show resolved Hide resolved
internal/consensus/state_test.go Outdated Show resolved Hide resolved
williambanfield and others added 2 commits December 7, 2021 21:22
Co-authored-by: M. J. Fromberger <fromberger@interchain.io>
Co-authored-by: M. J. Fromberger <fromberger@interchain.io>
williambanfield and others added 4 commits December 7, 2021 21:24
This change introduces the logic to have the proposer wait until the previous block time has passed before attempting to propose the next block.

The change achieves this by by adding a new clause into the enterPropose state machine method. The method now checks if the validator is the proposer and if the validator's clock is behind the previous block's time. If the validator's clock is behind the previous block time, it schedules a timeout to re-enter the enter propose method after enough time has passed.
* Remove MedianTime, set block time to Now()

* Fix goimports

* Fix import ordering
Copy link
Contributor

@cason cason left a comment

Choose a reason for hiding this comment

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

While I don't know the costs associated with this, I would consider right from now in the code that the Proposal time is a mandatory parameter, and having it matching the produced block's time is the default behavior.

@@ -1314,6 +1314,12 @@ func (cs *State) defaultDoPrevote(height int64, round int32) {
return
}

if !cs.Proposal.Timestamp.Equal(cs.ProposalBlock.Header.Time) {
logger.Debug("proposal timestamp not equal, prevoting nil")
Copy link
Contributor

Choose a reason for hiding this comment

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

I think that this means that the proposed block is not valid, should we not include this checking in the ValidateBlock logic? I don't know how this method is now, with the removal of bfftime checks.

Copy link
Contributor

Choose a reason for hiding this comment

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

In any case, a Error log message could be produced.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

should we not include this checking in the ValidateBlock logic?

This is a good question. I've been going back and forth on this trying to understand what the right way to shim this new information into that method would be. It means that one of three things would have to happen:

  1. state.State would need to have the proposal as a field. State is currently composed of all fields that can be determined from the previous block itself.
  2. ValidateBlock would need to take a proposal as a parameter.
  3. BlockExecutor would need a reference to the proposal.

I'm not convinced these are great ideas. For 1., the State is currently made up of only things that can be determined from previous blocks, so we'd be changing the purpose of that type.

For 2., we use ValidateBlock when replaying old blocks. This would mean keeping old proposals as part of block verification for old blocks. I'm not sure this is a meaningful thing to do, but maybe I don't see how it is yet.

Finally, 3. Is a similar issue to 2, with the added difficulty that block executor would need some concurrency safe way to access the current round's proposal.

I think overall that 1. would make the most sense, but I'm not certain it's necessary. Let me know if you disagree or if I've missed something.

// TestStateTimestamp_ProposalMatch tests that a validator prevotes a
// proposed block if the timestamp in the block matches the timestamp in the
// corresponding proposal message.
func TestStateTimestamp_ProposalMatch(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it be a regular check to be added in the tests of this round step? In other words, a matching proposal time and block time is the default, while the test above ensures that when it is not the case, the proposal is rejected.

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 think you're asking if we can just remove this test since it's effectively the default behavior anyway. I think that having an additional test is useful for documentation purposes. It makes it expressly clear that these two things should always match. If this relationship changes, many tests will break but this test being broken will help make it clear what the issue is for the engineer trying to understand how they broke something.

@@ -154,7 +155,7 @@ func TestProposalValidateBasic(t *testing.T) {
t.Run(tc.testName, func(t *testing.T) {
prop := NewProposal(
4, 2, 2,
blockID)
blockID, time.Now())
Copy link
Contributor

Choose a reason for hiding this comment

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

Are you using time and tmtime interchangeably?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, yeah, we canonicalize it anyway in the proposal method, but I'll change it to be consistent.

@williambanfield williambanfield merged commit 56a2005 into wb/proposer-based-timestamps Dec 15, 2021
@williambanfield williambanfield deleted the wb/proposer-sets-block-time branch December 15, 2021 19:46
williambanfield added a commit that referenced this pull request Jan 15, 2022
…7391)

This change updates the proposal logic to use the block's timestamp in the proposal message. It adds an additional piece of validation logic to the prevote step to check that the block's timestamp matches the proposal message's timestamp.
@williambanfield williambanfield restored the wb/proposer-sets-block-time branch September 9, 2022 16:12
adizere pushed a commit to cometbft/cometbft that referenced this pull request Feb 7, 2024
…2221)

Addresses #2119.

ADR 112, describing the PBTS implementation,
[states](https://github.com/cometbft/cometbft/pull/2223/files#diff-81d55f6e52eabd6cce264a76fba015de46092b9ccda241de10cd265890ab8ad9R302):

    The precommit step will not require much modification.
Its proposal validation rules will change in the same ways that
validation will change in the prevote step with the exception of the
`timely` check: precommit validation will never check that the timestamp
is `timely`.

The changes proposed here are in line with this description.

Moreover, this PR in fact revert the changes to the precommit step
(`enterPrecommit` method) added by
tendermint/tendermint#7480 and
tendermint/tendermint#7391.

---

#### PR checklist

- [x] Tests written/updated
- [ ] Changelog entry added in `.changelog` (we use
[unclog](https://github.com/informalsystems/unclog) to manage our
changelog)
- [ ] Updated relevant documentation (`docs/` or `spec/`) and code
comments
- [x] Title follows the [Conventional
Commits](https://www.conventionalcommits.org/en/v1.0.0/) spec
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.

None yet

4 participants